cborg 4.5.8 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +4 -0
- package/.github/workflows/test-and-release.yml +2 -4
- package/CHANGELOG.md +50 -0
- package/README.md +213 -9
- package/cborg.js +5 -4
- package/example-extended.js +122 -0
- package/interface.ts +15 -3
- package/lib/0uint.js +2 -2
- package/lib/1negint.js +2 -2
- package/lib/2bytes.js +2 -2
- package/lib/3string.js +2 -2
- package/lib/4array.js +2 -2
- package/lib/5map.js +2 -2
- package/lib/6tag.js +2 -2
- package/lib/7float.js +5 -4
- package/lib/decode.js +94 -4
- package/lib/encode.js +7 -7
- package/lib/extended/extended.js +250 -0
- package/lib/json/decode.js +2 -2
- package/lib/json/encode.js +3 -3
- package/lib/jump.js +1 -1
- package/lib/length.js +3 -3
- package/lib/taglib.js +452 -0
- package/package.json +21 -17
- package/test/common.js +2 -1
- package/test/test-6tag.js +2 -1
- package/test/test-cbor-vectors.js +14 -6
- package/test/test-extended-vectors.js +293 -0
- package/test/test-extended.js +684 -0
- package/test/test-taglib.js +634 -0
- package/tsconfig.json +7 -11
- package/types/cborg.d.ts +8 -4
- package/types/cborg.d.ts.map +1 -1
- package/types/interface.d.ts +14 -3
- package/types/interface.d.ts.map +1 -1
- package/types/lib/0uint.d.ts +4 -4
- package/types/lib/0uint.d.ts.map +1 -1
- package/types/lib/1negint.d.ts +4 -4
- package/types/lib/1negint.d.ts.map +1 -1
- package/types/lib/2bytes.d.ts +2 -2
- package/types/lib/2bytes.d.ts.map +1 -1
- package/types/lib/3string.d.ts +2 -2
- package/types/lib/3string.d.ts.map +1 -1
- package/types/lib/4array.d.ts +2 -2
- package/types/lib/4array.d.ts.map +1 -1
- package/types/lib/5map.d.ts +2 -2
- package/types/lib/5map.d.ts.map +1 -1
- package/types/lib/6tag.d.ts +4 -4
- package/types/lib/6tag.d.ts.map +1 -1
- package/types/lib/7float.d.ts +6 -6
- package/types/lib/7float.d.ts.map +1 -1
- package/types/lib/byte-utils.d.ts +5 -2
- package/types/lib/byte-utils.d.ts.map +1 -1
- package/types/lib/decode.d.ts +4 -3
- package/types/lib/decode.d.ts.map +1 -1
- package/types/lib/encode.d.ts +8 -8
- package/types/lib/encode.d.ts.map +1 -1
- package/types/lib/extended/extended.d.ts +78 -0
- package/types/lib/extended/extended.d.ts.map +1 -0
- package/types/lib/json/decode.d.ts +5 -5
- package/types/lib/json/decode.d.ts.map +1 -1
- package/types/lib/json/encode.d.ts +3 -3
- package/types/lib/json/encode.d.ts.map +1 -1
- package/types/lib/jump.d.ts +1 -1
- package/types/lib/jump.d.ts.map +1 -1
- package/types/lib/length.d.ts +3 -3
- package/types/lib/length.d.ts.map +1 -1
- package/types/lib/taglib.d.ts +143 -0
- package/types/lib/taglib.d.ts.map +1 -0
- package/types/tsconfig.tsbuildinfo +1 -1
- package/taglib.js +0 -73
- package/types/taglib.d.ts +0 -18
- package/types/taglib.d.ts.map +0 -1
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
3
|
+
import * as chai from 'chai'
|
|
4
|
+
import { encode, decode } from '../lib/extended/extended.js'
|
|
5
|
+
import { encode as cborgEncode } from '../cborg.js'
|
|
6
|
+
|
|
7
|
+
const { assert } = chai
|
|
8
|
+
|
|
9
|
+
describe('cborg/extended', () => {
|
|
10
|
+
describe('Date', () => {
|
|
11
|
+
it('round-trips a date', () => {
|
|
12
|
+
const d = new Date('2024-01-15T12:30:00.000Z')
|
|
13
|
+
const result = decode(encode(d))
|
|
14
|
+
assert.ok(result instanceof Date)
|
|
15
|
+
assert.strictEqual(result.getTime(), d.getTime())
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('round-trips date with milliseconds', () => {
|
|
19
|
+
const d = new Date('2024-01-15T12:30:00.123Z')
|
|
20
|
+
const result = decode(encode(d))
|
|
21
|
+
assert.strictEqual(result.getTime(), d.getTime())
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('round-trips epoch date', () => {
|
|
25
|
+
const d = new Date(0)
|
|
26
|
+
const result = decode(encode(d))
|
|
27
|
+
assert.strictEqual(result.getTime(), 0)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('round-trips negative epoch date', () => {
|
|
31
|
+
const d = new Date(-86400000) // 1 day before epoch
|
|
32
|
+
const result = decode(encode(d))
|
|
33
|
+
assert.strictEqual(result.getTime(), d.getTime())
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('RegExp', () => {
|
|
38
|
+
it('round-trips pattern without flags', () => {
|
|
39
|
+
const re = /foo.*bar/
|
|
40
|
+
const result = decode(encode(re))
|
|
41
|
+
assert.ok(result instanceof RegExp)
|
|
42
|
+
assert.strictEqual(result.source, re.source)
|
|
43
|
+
assert.strictEqual(result.flags, re.flags)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('round-trips pattern with flags', () => {
|
|
47
|
+
const re = /foo.*bar/gim
|
|
48
|
+
const result = decode(encode(re))
|
|
49
|
+
assert.strictEqual(result.source, re.source)
|
|
50
|
+
assert.strictEqual(result.flags, re.flags)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('round-trips complex pattern', () => {
|
|
54
|
+
const re = /^[a-z]+\d{2,4}$/i
|
|
55
|
+
const result = decode(encode(re))
|
|
56
|
+
assert.strictEqual(result.source, re.source)
|
|
57
|
+
assert.strictEqual(result.flags, re.flags)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('round-trips empty pattern', () => {
|
|
61
|
+
const re = /(?:)/
|
|
62
|
+
const result = decode(encode(re))
|
|
63
|
+
assert.strictEqual(result.source, '(?:)')
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe('Set', () => {
|
|
68
|
+
it('round-trips empty set', () => {
|
|
69
|
+
const s = new Set()
|
|
70
|
+
const result = decode(encode(s))
|
|
71
|
+
assert.ok(result instanceof Set)
|
|
72
|
+
assert.strictEqual(result.size, 0)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('round-trips set with primitives', () => {
|
|
76
|
+
const s = new Set([1, 2, 3, 'a', 'b'])
|
|
77
|
+
const result = decode(encode(s))
|
|
78
|
+
assert.ok(result instanceof Set)
|
|
79
|
+
assert.strictEqual(result.size, 5)
|
|
80
|
+
assert.ok(result.has(1))
|
|
81
|
+
assert.ok(result.has(2))
|
|
82
|
+
assert.ok(result.has(3))
|
|
83
|
+
assert.ok(result.has('a'))
|
|
84
|
+
assert.ok(result.has('b'))
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('round-trips set with objects', () => {
|
|
88
|
+
const s = new Set([{ x: 1 }, { y: 2 }])
|
|
89
|
+
const result = decode(encode(s))
|
|
90
|
+
assert.strictEqual(result.size, 2)
|
|
91
|
+
const arr = [...result]
|
|
92
|
+
// Objects inside the set stay as objects (useMaps: false)
|
|
93
|
+
assert.ok(!(arr[0] instanceof Map))
|
|
94
|
+
assert.ok(!(arr[1] instanceof Map))
|
|
95
|
+
assert.strictEqual(arr[0].x, 1)
|
|
96
|
+
assert.strictEqual(arr[1].y, 2)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe('Map', () => {
|
|
101
|
+
it('round-trips empty map', () => {
|
|
102
|
+
const m = new Map()
|
|
103
|
+
const result = decode(encode(m))
|
|
104
|
+
assert.ok(result instanceof Map)
|
|
105
|
+
assert.strictEqual(result.size, 0)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('round-trips map with string keys', () => {
|
|
109
|
+
const m = new Map([['a', 1], ['b', 2]])
|
|
110
|
+
const result = decode(encode(m))
|
|
111
|
+
assert.ok(result instanceof Map)
|
|
112
|
+
assert.strictEqual(result.get('a'), 1)
|
|
113
|
+
assert.strictEqual(result.get('b'), 2)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('round-trips map with number keys', () => {
|
|
117
|
+
const m = new Map([[1, 'one'], [2, 'two']])
|
|
118
|
+
const result = decode(encode(m))
|
|
119
|
+
assert.ok(result instanceof Map)
|
|
120
|
+
assert.strictEqual(result.get(1), 'one')
|
|
121
|
+
assert.strictEqual(result.get(2), 'two')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('round-trips map with mixed keys', () => {
|
|
125
|
+
const m = new Map([['str', 1], [42, 2], [true, 3]])
|
|
126
|
+
const result = decode(encode(m))
|
|
127
|
+
assert.strictEqual(result.get('str'), 1)
|
|
128
|
+
assert.strictEqual(result.get(42), 2)
|
|
129
|
+
assert.strictEqual(result.get(true), 3)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('round-trips map with object values', () => {
|
|
133
|
+
const m = new Map([
|
|
134
|
+
['user1', { name: 'Alice', age: 30 }],
|
|
135
|
+
['user2', { name: 'Bob', age: 25 }]
|
|
136
|
+
])
|
|
137
|
+
const result = decode(encode(m))
|
|
138
|
+
assert.ok(result instanceof Map)
|
|
139
|
+
// Values should be plain objects, not Maps
|
|
140
|
+
assert.ok(!(result.get('user1') instanceof Map))
|
|
141
|
+
assert.strictEqual(result.get('user1').name, 'Alice')
|
|
142
|
+
assert.strictEqual(result.get('user2').age, 25)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('round-trips map with nested map values', () => {
|
|
146
|
+
const m = new Map([
|
|
147
|
+
['outer', new Map([['inner', 'value']])]
|
|
148
|
+
])
|
|
149
|
+
const result = decode(encode(m))
|
|
150
|
+
assert.ok(result instanceof Map)
|
|
151
|
+
assert.ok(result.get('outer') instanceof Map)
|
|
152
|
+
assert.strictEqual(result.get('outer').get('inner'), 'value')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('round-trips map with BigInt keys', () => {
|
|
156
|
+
const m = new Map([
|
|
157
|
+
[123n, 'small bigint'],
|
|
158
|
+
[BigInt('9007199254740993'), 'large bigint']
|
|
159
|
+
])
|
|
160
|
+
const result = decode(encode(m))
|
|
161
|
+
assert.ok(result instanceof Map)
|
|
162
|
+
assert.strictEqual(result.get(123n), 'small bigint')
|
|
163
|
+
assert.strictEqual(result.get(BigInt('9007199254740993')), 'large bigint')
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('round-trips map with Date keys', () => {
|
|
167
|
+
const d1 = new Date('2024-01-01')
|
|
168
|
+
const d2 = new Date('2024-12-31')
|
|
169
|
+
const m = new Map([
|
|
170
|
+
[d1, 'new year'],
|
|
171
|
+
[d2, 'new year eve']
|
|
172
|
+
])
|
|
173
|
+
const result = decode(encode(m))
|
|
174
|
+
assert.ok(result instanceof Map)
|
|
175
|
+
// Date keys become new Date instances, so we need to find by time
|
|
176
|
+
const keys = [...result.keys()]
|
|
177
|
+
assert.ok(keys[0] instanceof Date)
|
|
178
|
+
assert.ok(keys[1] instanceof Date)
|
|
179
|
+
assert.strictEqual(keys[0].getTime(), d1.getTime())
|
|
180
|
+
assert.strictEqual(keys[1].getTime(), d2.getTime())
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
describe('object containing Map', () => {
|
|
185
|
+
it('round-trips object with Map property', () => {
|
|
186
|
+
const obj = {
|
|
187
|
+
name: 'test',
|
|
188
|
+
data: new Map([[1, 'one'], [2, 'two']])
|
|
189
|
+
}
|
|
190
|
+
const result = decode(encode(obj))
|
|
191
|
+
// Outer should be plain object
|
|
192
|
+
assert.ok(!(result instanceof Map))
|
|
193
|
+
assert.strictEqual(result.name, 'test')
|
|
194
|
+
// Inner Map should be Map with integer keys preserved
|
|
195
|
+
assert.ok(result.data instanceof Map)
|
|
196
|
+
assert.strictEqual(result.data.get(1), 'one')
|
|
197
|
+
assert.strictEqual(result.data.get(2), 'two')
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('round-trips deeply nested object/Map mix', () => {
|
|
201
|
+
const obj = {
|
|
202
|
+
level1: {
|
|
203
|
+
level2: new Map([
|
|
204
|
+
['key', { level3: new Map([[42, 'deep']]) }]
|
|
205
|
+
])
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const result = decode(encode(obj))
|
|
209
|
+
// Check structure preservation
|
|
210
|
+
assert.ok(!(result instanceof Map))
|
|
211
|
+
assert.ok(!(result.level1 instanceof Map))
|
|
212
|
+
assert.ok(result.level1.level2 instanceof Map)
|
|
213
|
+
assert.ok(!(result.level1.level2.get('key') instanceof Map))
|
|
214
|
+
assert.ok(result.level1.level2.get('key').level3 instanceof Map)
|
|
215
|
+
assert.strictEqual(result.level1.level2.get('key').level3.get(42), 'deep')
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
describe('BigInt', () => {
|
|
220
|
+
it('round-trips zero', () => {
|
|
221
|
+
const result = decode(encode(0n))
|
|
222
|
+
assert.strictEqual(typeof result, 'bigint')
|
|
223
|
+
assert.strictEqual(result, 0n)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it('round-trips small positive', () => {
|
|
227
|
+
const result = decode(encode(100n))
|
|
228
|
+
assert.strictEqual(typeof result, 'bigint')
|
|
229
|
+
assert.strictEqual(result, 100n)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('round-trips small negative', () => {
|
|
233
|
+
const result = decode(encode(-1n))
|
|
234
|
+
assert.strictEqual(typeof result, 'bigint')
|
|
235
|
+
assert.strictEqual(result, -1n)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
it('round-trips beyond MAX_SAFE_INTEGER', () => {
|
|
239
|
+
const n = 9007199254740993n
|
|
240
|
+
const result = decode(encode(n))
|
|
241
|
+
assert.strictEqual(result, n)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('round-trips large positive', () => {
|
|
245
|
+
const n = BigInt('123456789012345678901234567890')
|
|
246
|
+
const result = decode(encode(n))
|
|
247
|
+
assert.strictEqual(result, n)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
it('round-trips large negative', () => {
|
|
251
|
+
const n = BigInt('-123456789012345678901234567890')
|
|
252
|
+
const result = decode(encode(n))
|
|
253
|
+
assert.strictEqual(result, n)
|
|
254
|
+
})
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
describe('TypedArrays', () => {
|
|
258
|
+
it('Uint8Array', () => {
|
|
259
|
+
const arr = new Uint8Array([1, 2, 3, 4, 5])
|
|
260
|
+
const result = decode(encode(arr))
|
|
261
|
+
assert.ok(result instanceof Uint8Array)
|
|
262
|
+
assert.deepStrictEqual([...result], [1, 2, 3, 4, 5])
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('Uint8ClampedArray', () => {
|
|
266
|
+
const arr = new Uint8ClampedArray([0, 128, 255])
|
|
267
|
+
const result = decode(encode(arr))
|
|
268
|
+
assert.ok(result instanceof Uint8ClampedArray)
|
|
269
|
+
assert.deepStrictEqual([...result], [0, 128, 255])
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
it('Int8Array', () => {
|
|
273
|
+
const arr = new Int8Array([-128, 0, 127])
|
|
274
|
+
const result = decode(encode(arr))
|
|
275
|
+
assert.ok(result instanceof Int8Array)
|
|
276
|
+
assert.deepStrictEqual([...result], [-128, 0, 127])
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('Uint16Array', () => {
|
|
280
|
+
const arr = new Uint16Array([0, 256, 65535])
|
|
281
|
+
const result = decode(encode(arr))
|
|
282
|
+
assert.ok(result instanceof Uint16Array)
|
|
283
|
+
assert.deepStrictEqual([...result], [0, 256, 65535])
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('Int16Array', () => {
|
|
287
|
+
const arr = new Int16Array([-32768, 0, 32767])
|
|
288
|
+
const result = decode(encode(arr))
|
|
289
|
+
assert.ok(result instanceof Int16Array)
|
|
290
|
+
assert.deepStrictEqual([...result], [-32768, 0, 32767])
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('Uint32Array', () => {
|
|
294
|
+
const arr = new Uint32Array([0, 65536, 4294967295])
|
|
295
|
+
const result = decode(encode(arr))
|
|
296
|
+
assert.ok(result instanceof Uint32Array)
|
|
297
|
+
assert.deepStrictEqual([...result], [0, 65536, 4294967295])
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
it('Int32Array', () => {
|
|
301
|
+
const arr = new Int32Array([-2147483648, 0, 2147483647])
|
|
302
|
+
const result = decode(encode(arr))
|
|
303
|
+
assert.ok(result instanceof Int32Array)
|
|
304
|
+
assert.deepStrictEqual([...result], [-2147483648, 0, 2147483647])
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
it('Float32Array', () => {
|
|
308
|
+
const arr = new Float32Array([1.5, -2.5, 3.14])
|
|
309
|
+
const result = decode(encode(arr))
|
|
310
|
+
assert.ok(result instanceof Float32Array)
|
|
311
|
+
assert.strictEqual(result[0], 1.5)
|
|
312
|
+
assert.strictEqual(result[1], -2.5)
|
|
313
|
+
assert.ok(Math.abs(result[2] - 3.14) < 0.001)
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it('Float64Array', () => {
|
|
317
|
+
const arr = new Float64Array([1.1, -2.2, Math.PI, Infinity, -Infinity])
|
|
318
|
+
const result = decode(encode(arr))
|
|
319
|
+
assert.ok(result instanceof Float64Array)
|
|
320
|
+
assert.deepStrictEqual([...result], [1.1, -2.2, Math.PI, Infinity, -Infinity])
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('BigUint64Array', () => {
|
|
324
|
+
const arr = new BigUint64Array([0n, 1n, BigInt('18446744073709551615')])
|
|
325
|
+
const result = decode(encode(arr))
|
|
326
|
+
assert.ok(result instanceof BigUint64Array)
|
|
327
|
+
assert.deepStrictEqual([...result], [0n, 1n, BigInt('18446744073709551615')])
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
it('BigInt64Array', () => {
|
|
331
|
+
const arr = new BigInt64Array([BigInt('-9223372036854775808'), 0n, BigInt('9223372036854775807')])
|
|
332
|
+
const result = decode(encode(arr))
|
|
333
|
+
assert.ok(result instanceof BigInt64Array)
|
|
334
|
+
assert.deepStrictEqual([...result], [BigInt('-9223372036854775808'), 0n, BigInt('9223372036854775807')])
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
it('TypedArray view of larger buffer', () => {
|
|
338
|
+
const buffer = new ArrayBuffer(100)
|
|
339
|
+
const view = new Uint8Array(buffer, 10, 5)
|
|
340
|
+
view.set([1, 2, 3, 4, 5])
|
|
341
|
+
const result = decode(encode(view))
|
|
342
|
+
assert.strictEqual(result.length, 5)
|
|
343
|
+
assert.deepStrictEqual([...result], [1, 2, 3, 4, 5])
|
|
344
|
+
})
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
describe('nested structures', () => {
|
|
348
|
+
it('object with multiple types', () => {
|
|
349
|
+
const obj = {
|
|
350
|
+
date: new Date('2024-01-15T12:00:00Z'),
|
|
351
|
+
pattern: /test/gi,
|
|
352
|
+
mapping: new Map([['key', 'value']]),
|
|
353
|
+
collection: new Set([1, 2, 3]),
|
|
354
|
+
binary: new Uint8Array([1, 2, 3]),
|
|
355
|
+
bignum: 123n
|
|
356
|
+
}
|
|
357
|
+
const result = decode(encode(obj))
|
|
358
|
+
|
|
359
|
+
// Plain objects stay as objects (useMaps: false by default)
|
|
360
|
+
assert.ok(!(result instanceof Map))
|
|
361
|
+
assert.ok(typeof result === 'object')
|
|
362
|
+
|
|
363
|
+
assert.ok(result.date instanceof Date)
|
|
364
|
+
assert.strictEqual(result.date.getTime(), obj.date.getTime())
|
|
365
|
+
|
|
366
|
+
assert.ok(result.pattern instanceof RegExp)
|
|
367
|
+
assert.strictEqual(result.pattern.source, 'test')
|
|
368
|
+
assert.strictEqual(result.pattern.flags, 'gi')
|
|
369
|
+
|
|
370
|
+
// Tagged Maps decode as Maps
|
|
371
|
+
assert.ok(result.mapping instanceof Map)
|
|
372
|
+
assert.strictEqual(result.mapping.get('key'), 'value')
|
|
373
|
+
|
|
374
|
+
assert.ok(result.collection instanceof Set)
|
|
375
|
+
assert.strictEqual(result.collection.size, 3)
|
|
376
|
+
|
|
377
|
+
assert.ok(result.binary instanceof Uint8Array)
|
|
378
|
+
assert.deepStrictEqual([...result.binary], [1, 2, 3])
|
|
379
|
+
|
|
380
|
+
assert.strictEqual(typeof result.bignum, 'bigint')
|
|
381
|
+
assert.strictEqual(result.bignum, 123n)
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
it('deeply nested maps and sets', () => {
|
|
385
|
+
const obj = {
|
|
386
|
+
outer: new Map([
|
|
387
|
+
['inner', new Set([
|
|
388
|
+
new Map([['deep', 'value']])
|
|
389
|
+
])]
|
|
390
|
+
])
|
|
391
|
+
}
|
|
392
|
+
const result = decode(encode(obj))
|
|
393
|
+
|
|
394
|
+
// Plain objects stay as objects, tagged Maps become Maps
|
|
395
|
+
assert.ok(!(result instanceof Map))
|
|
396
|
+
assert.ok(result.outer instanceof Map)
|
|
397
|
+
const inner = result.outer.get('inner')
|
|
398
|
+
assert.ok(inner instanceof Set)
|
|
399
|
+
const deepMap = [...inner][0]
|
|
400
|
+
assert.ok(deepMap instanceof Map)
|
|
401
|
+
assert.strictEqual(deepMap.get('deep'), 'value')
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
it('array of dates', () => {
|
|
405
|
+
const dates = [
|
|
406
|
+
new Date('2024-01-01'),
|
|
407
|
+
new Date('2024-06-15'),
|
|
408
|
+
new Date('2024-12-31')
|
|
409
|
+
]
|
|
410
|
+
const result = decode(encode(dates))
|
|
411
|
+
|
|
412
|
+
assert.strictEqual(result.length, 3)
|
|
413
|
+
assert.ok(result[0] instanceof Date)
|
|
414
|
+
assert.ok(result[1] instanceof Date)
|
|
415
|
+
assert.ok(result[2] instanceof Date)
|
|
416
|
+
assert.strictEqual(result[0].getTime(), dates[0].getTime())
|
|
417
|
+
assert.strictEqual(result[1].getTime(), dates[1].getTime())
|
|
418
|
+
assert.strictEqual(result[2].getTime(), dates[2].getTime())
|
|
419
|
+
})
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
describe('undefined and null', () => {
|
|
423
|
+
it('preserves undefined in objects', () => {
|
|
424
|
+
const obj = { a: undefined, b: null, c: 1 }
|
|
425
|
+
const result = decode(encode(obj))
|
|
426
|
+
// Plain objects stay as objects
|
|
427
|
+
assert.ok(!(result instanceof Map))
|
|
428
|
+
assert.strictEqual(result.a, undefined)
|
|
429
|
+
assert.strictEqual(result.b, null)
|
|
430
|
+
assert.strictEqual(result.c, 1)
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
it('preserves null values', () => {
|
|
434
|
+
const result = decode(encode(null))
|
|
435
|
+
assert.strictEqual(result, null)
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
it('preserves undefined values', () => {
|
|
439
|
+
const result = decode(encode(undefined))
|
|
440
|
+
assert.strictEqual(result, undefined)
|
|
441
|
+
})
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
describe('compatibility', () => {
|
|
445
|
+
it('plain objects decode as objects (useMaps: false is default)', () => {
|
|
446
|
+
const obj = { a: 1, b: [2, 3], c: 'hello' }
|
|
447
|
+
const result = decode(encode(obj))
|
|
448
|
+
// Plain objects stay as objects
|
|
449
|
+
assert.ok(!(result instanceof Map))
|
|
450
|
+
assert.strictEqual(result.a, 1)
|
|
451
|
+
assert.deepStrictEqual(result.b, [2, 3])
|
|
452
|
+
assert.strictEqual(result.c, 'hello')
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
it('plain objects decode as Maps with useMaps: true', () => {
|
|
456
|
+
const obj = { a: 1, b: [2, 3], c: 'hello' }
|
|
457
|
+
const result = decode(encode(obj), { useMaps: true })
|
|
458
|
+
assert.ok(result instanceof Map)
|
|
459
|
+
assert.strictEqual(result.get('a'), 1)
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
it('plain arrays still work', () => {
|
|
463
|
+
const arr = [1, 'two', { three: 3 }]
|
|
464
|
+
const result = decode(encode(arr))
|
|
465
|
+
assert.ok(Array.isArray(result))
|
|
466
|
+
assert.strictEqual(result[0], 1)
|
|
467
|
+
assert.strictEqual(result[1], 'two')
|
|
468
|
+
// Nested objects stay as objects
|
|
469
|
+
assert.ok(!(result[2] instanceof Map))
|
|
470
|
+
assert.strictEqual(result[2].three, 3)
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
it('can decode standard cborg output', () => {
|
|
474
|
+
const obj = { a: 1, b: 'hello' }
|
|
475
|
+
const standardCbor = cborgEncode(obj)
|
|
476
|
+
const result = decode(standardCbor)
|
|
477
|
+
// Plain objects stay as objects
|
|
478
|
+
assert.ok(!(result instanceof Map))
|
|
479
|
+
assert.strictEqual(result.a, 1)
|
|
480
|
+
assert.strictEqual(result.b, 'hello')
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
it('primitives round-trip correctly', () => {
|
|
484
|
+
assert.strictEqual(decode(encode(true)), true)
|
|
485
|
+
assert.strictEqual(decode(encode(false)), false)
|
|
486
|
+
assert.strictEqual(decode(encode(42)), 42)
|
|
487
|
+
assert.strictEqual(decode(encode(-42)), -42)
|
|
488
|
+
assert.strictEqual(decode(encode(3.14)), 3.14)
|
|
489
|
+
assert.strictEqual(decode(encode('hello')), 'hello')
|
|
490
|
+
assert.strictEqual(decode(encode('')), '')
|
|
491
|
+
})
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
describe('edge cases', () => {
|
|
495
|
+
it('empty object', () => {
|
|
496
|
+
const result = decode(encode({}))
|
|
497
|
+
// Plain objects stay as objects
|
|
498
|
+
assert.ok(!(result instanceof Map))
|
|
499
|
+
assert.deepStrictEqual(result, {})
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
it('empty array', () => {
|
|
503
|
+
const result = decode(encode([]))
|
|
504
|
+
assert.deepStrictEqual(result, [])
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
it('object with numeric string keys', () => {
|
|
508
|
+
const obj = { 1: 'one', 2: 'two' }
|
|
509
|
+
const result = decode(encode(obj))
|
|
510
|
+
// Plain objects stay as objects
|
|
511
|
+
// Note: JS object keys are strings, so { 1: 'one' } has key '1' not 1
|
|
512
|
+
assert.ok(!(result instanceof Map))
|
|
513
|
+
assert.strictEqual(result['1'], 'one')
|
|
514
|
+
assert.strictEqual(result['2'], 'two')
|
|
515
|
+
})
|
|
516
|
+
|
|
517
|
+
it('very long string', () => {
|
|
518
|
+
const str = 'x'.repeat(10000)
|
|
519
|
+
const result = decode(encode(str))
|
|
520
|
+
assert.strictEqual(result, str)
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
it('special float values', () => {
|
|
524
|
+
assert.strictEqual(decode(encode(Infinity)), Infinity)
|
|
525
|
+
assert.strictEqual(decode(encode(-Infinity)), -Infinity)
|
|
526
|
+
assert.ok(Number.isNaN(decode(encode(NaN))))
|
|
527
|
+
})
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
describe('insertion order preservation', () => {
|
|
531
|
+
it('preserves Map key insertion order', () => {
|
|
532
|
+
// Keys intentionally not in alphabetical or length-first order
|
|
533
|
+
const map = new Map([
|
|
534
|
+
['zebra', 1],
|
|
535
|
+
['a', 2],
|
|
536
|
+
['mango', 3],
|
|
537
|
+
['b', 4]
|
|
538
|
+
])
|
|
539
|
+
const result = decode(encode(map))
|
|
540
|
+
const keys = [...result.keys()]
|
|
541
|
+
assert.deepStrictEqual(keys, ['zebra', 'a', 'mango', 'b'])
|
|
542
|
+
})
|
|
543
|
+
|
|
544
|
+
it('preserves Map key insertion order with mixed key types', () => {
|
|
545
|
+
const map = new Map([
|
|
546
|
+
[100, 'hundred'],
|
|
547
|
+
['first', 1],
|
|
548
|
+
[1, 'one'],
|
|
549
|
+
['zzz', 'last']
|
|
550
|
+
])
|
|
551
|
+
const result = decode(encode(map))
|
|
552
|
+
const keys = [...result.keys()]
|
|
553
|
+
assert.deepStrictEqual(keys, [100, 'first', 1, 'zzz'])
|
|
554
|
+
})
|
|
555
|
+
|
|
556
|
+
it('preserves object key insertion order', () => {
|
|
557
|
+
// Keys intentionally not in alphabetical or length-first order
|
|
558
|
+
const obj = {
|
|
559
|
+
zebra: 1,
|
|
560
|
+
a: 2,
|
|
561
|
+
mango: 3,
|
|
562
|
+
b: 4
|
|
563
|
+
}
|
|
564
|
+
const result = decode(encode(obj))
|
|
565
|
+
const keys = Object.keys(result)
|
|
566
|
+
assert.deepStrictEqual(keys, ['zebra', 'a', 'mango', 'b'])
|
|
567
|
+
})
|
|
568
|
+
|
|
569
|
+
it('preserves nested Map and object insertion order', () => {
|
|
570
|
+
const data = {
|
|
571
|
+
zz: 'first',
|
|
572
|
+
aa: new Map([
|
|
573
|
+
['zzz', 1],
|
|
574
|
+
['aaa', 2]
|
|
575
|
+
]),
|
|
576
|
+
mm: 'last'
|
|
577
|
+
}
|
|
578
|
+
const result = decode(encode(data))
|
|
579
|
+
// Object key order preserved
|
|
580
|
+
assert.deepStrictEqual(Object.keys(result), ['zz', 'aa', 'mm'])
|
|
581
|
+
// Nested Map key order preserved
|
|
582
|
+
assert.deepStrictEqual([...result.aa.keys()], ['zzz', 'aaa'])
|
|
583
|
+
})
|
|
584
|
+
})
|
|
585
|
+
|
|
586
|
+
describe('Error types', () => {
|
|
587
|
+
it('round-trips Error', () => {
|
|
588
|
+
const err = new Error('test error')
|
|
589
|
+
const result = decode(encode(err))
|
|
590
|
+
assert.ok(result instanceof Error)
|
|
591
|
+
assert.strictEqual(result.message, 'test error')
|
|
592
|
+
assert.strictEqual(result.name, 'Error')
|
|
593
|
+
})
|
|
594
|
+
|
|
595
|
+
it('round-trips TypeError', () => {
|
|
596
|
+
const err = new TypeError('type mismatch')
|
|
597
|
+
const result = decode(encode(err))
|
|
598
|
+
assert.ok(result instanceof TypeError)
|
|
599
|
+
assert.strictEqual(result.message, 'type mismatch')
|
|
600
|
+
assert.strictEqual(result.name, 'TypeError')
|
|
601
|
+
})
|
|
602
|
+
|
|
603
|
+
it('round-trips RangeError', () => {
|
|
604
|
+
const err = new RangeError('out of range')
|
|
605
|
+
const result = decode(encode(err))
|
|
606
|
+
assert.ok(result instanceof RangeError)
|
|
607
|
+
assert.strictEqual(result.message, 'out of range')
|
|
608
|
+
assert.strictEqual(result.name, 'RangeError')
|
|
609
|
+
})
|
|
610
|
+
|
|
611
|
+
it('round-trips SyntaxError', () => {
|
|
612
|
+
const err = new SyntaxError('bad syntax')
|
|
613
|
+
const result = decode(encode(err))
|
|
614
|
+
assert.ok(result instanceof SyntaxError)
|
|
615
|
+
assert.strictEqual(result.message, 'bad syntax')
|
|
616
|
+
})
|
|
617
|
+
|
|
618
|
+
it('round-trips ReferenceError', () => {
|
|
619
|
+
const err = new ReferenceError('not defined')
|
|
620
|
+
const result = decode(encode(err))
|
|
621
|
+
assert.ok(result instanceof ReferenceError)
|
|
622
|
+
assert.strictEqual(result.message, 'not defined')
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
it('round-trips EvalError', () => {
|
|
626
|
+
const err = new EvalError('eval failed')
|
|
627
|
+
const result = decode(encode(err))
|
|
628
|
+
assert.ok(result instanceof EvalError)
|
|
629
|
+
assert.strictEqual(result.message, 'eval failed')
|
|
630
|
+
})
|
|
631
|
+
|
|
632
|
+
it('round-trips URIError', () => {
|
|
633
|
+
const err = new URIError('bad URI')
|
|
634
|
+
const result = decode(encode(err))
|
|
635
|
+
assert.ok(result instanceof URIError)
|
|
636
|
+
assert.strictEqual(result.message, 'bad URI')
|
|
637
|
+
})
|
|
638
|
+
|
|
639
|
+
it('round-trips Error with empty message', () => {
|
|
640
|
+
const err = new Error()
|
|
641
|
+
const result = decode(encode(err))
|
|
642
|
+
assert.ok(result instanceof Error)
|
|
643
|
+
assert.strictEqual(result.message, '')
|
|
644
|
+
})
|
|
645
|
+
|
|
646
|
+
it('round-trips Error in object', () => {
|
|
647
|
+
const obj = { error: new TypeError('failed'), code: 500 }
|
|
648
|
+
const result = decode(encode(obj))
|
|
649
|
+
assert.ok(result.error instanceof TypeError)
|
|
650
|
+
assert.strictEqual(result.error.message, 'failed')
|
|
651
|
+
assert.strictEqual(result.code, 500)
|
|
652
|
+
})
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
describe('negative zero', () => {
|
|
656
|
+
it('round-trips -0', () => {
|
|
657
|
+
const result = decode(encode(-0))
|
|
658
|
+
assert.ok(Object.is(result, -0))
|
|
659
|
+
})
|
|
660
|
+
|
|
661
|
+
it('distinguishes -0 from 0', () => {
|
|
662
|
+
const negZero = decode(encode(-0))
|
|
663
|
+
const posZero = decode(encode(0))
|
|
664
|
+
assert.ok(Object.is(negZero, -0))
|
|
665
|
+
assert.ok(Object.is(posZero, 0))
|
|
666
|
+
assert.ok(!Object.is(negZero, posZero))
|
|
667
|
+
})
|
|
668
|
+
|
|
669
|
+
it('round-trips -0 in object', () => {
|
|
670
|
+
const obj = { neg: -0, pos: 0 }
|
|
671
|
+
const result = decode(encode(obj))
|
|
672
|
+
assert.ok(Object.is(result.neg, -0))
|
|
673
|
+
assert.ok(Object.is(result.pos, 0))
|
|
674
|
+
})
|
|
675
|
+
|
|
676
|
+
it('round-trips -0 in array', () => {
|
|
677
|
+
const arr = [-0, 0, -0]
|
|
678
|
+
const result = decode(encode(arr))
|
|
679
|
+
assert.ok(Object.is(result[0], -0))
|
|
680
|
+
assert.ok(Object.is(result[1], 0))
|
|
681
|
+
assert.ok(Object.is(result[2], -0))
|
|
682
|
+
})
|
|
683
|
+
})
|
|
684
|
+
})
|