cborg 4.3.2 → 4.4.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.
- package/.github/workflows/test-and-release.yml +7 -3
- package/CHANGELOG.md +18 -0
- package/README.md +2 -1
- package/example-bytestrings.js +1 -1
- package/interface.ts +5 -0
- package/lib/2bytes.js +1 -1
- package/lib/byte-utils.js +3 -0
- package/lib/decode.js +4 -4
- package/lib/encode.js +33 -14
- package/lib/json/encode.js +4 -4
- package/lib/token.js +12 -0
- package/package.json +2 -2
- package/test/test-3string.js +18 -0
- package/test/test-encode-undefined.js +112 -0
- package/test/test-type-equals.js +87 -0
- package/types/interface.d.ts +5 -0
- package/types/interface.d.ts.map +1 -1
- package/types/lib/byte-utils.d.ts.map +1 -1
- package/types/lib/encode.d.ts.map +1 -1
- package/types/lib/token.d.ts +9 -0
- package/types/lib/token.d.ts.map +1 -1
|
@@ -12,7 +12,7 @@ jobs:
|
|
|
12
12
|
- name: Checkout Repository
|
|
13
13
|
uses: actions/checkout@v6
|
|
14
14
|
- name: Use Node.js ${{ matrix.node }}
|
|
15
|
-
uses: actions/setup-node@v6.
|
|
15
|
+
uses: actions/setup-node@v6.2.0
|
|
16
16
|
with:
|
|
17
17
|
node-version: ${{ matrix.node }}
|
|
18
18
|
- name: Install Dependencies
|
|
@@ -29,13 +29,18 @@ jobs:
|
|
|
29
29
|
needs: test
|
|
30
30
|
runs-on: ubuntu-latest
|
|
31
31
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
32
|
+
permissions:
|
|
33
|
+
contents: write
|
|
34
|
+
issues: write
|
|
35
|
+
pull-requests: write
|
|
36
|
+
id-token: write
|
|
32
37
|
steps:
|
|
33
38
|
- name: Checkout
|
|
34
39
|
uses: actions/checkout@v6
|
|
35
40
|
with:
|
|
36
41
|
fetch-depth: 0
|
|
37
42
|
- name: Setup Node.js
|
|
38
|
-
uses: actions/setup-node@v6.
|
|
43
|
+
uses: actions/setup-node@v6.2.0
|
|
39
44
|
with:
|
|
40
45
|
node-version: lts/*
|
|
41
46
|
- name: Install dependencies
|
|
@@ -47,6 +52,5 @@ jobs:
|
|
|
47
52
|
- name: Release
|
|
48
53
|
env:
|
|
49
54
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
50
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
51
55
|
run: npx semantic-release
|
|
52
56
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## [4.4.0](https://github.com/rvagg/cborg/compare/v4.3.2...v4.4.0) (2026-01-19)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **encode:** Allow ignoring `undefined` plain object properties ([a0db184](https://github.com/rvagg/cborg/commit/a0db18472e0a459b23affa60c20224f571968aa8)), closes [#155](https://github.com/rvagg/cborg/issues/155)
|
|
6
|
+
* **token:** add Type.equals() for bundler-safe type comparison ([#159](https://github.com/rvagg/cborg/issues/159)) ([b3e7cf4](https://github.com/rvagg/cborg/commit/b3e7cf48122e512050ff04bd826cca59ba1fd2d2)), closes [#136](https://github.com/rvagg/cborg/issues/136)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **encode:** replace unpaired surrogates with U+FFFD in utf8ToBytes ([#157](https://github.com/rvagg/cborg/issues/157)) ([362856c](https://github.com/rvagg/cborg/commit/362856c6bf0cc85b946af9ef87f47831fe3d98f5))
|
|
11
|
+
|
|
12
|
+
### Trivial Changes
|
|
13
|
+
|
|
14
|
+
* **ci:** OIDC publishing ([743ecc3](https://github.com/rvagg/cborg/commit/743ecc3eed0a6dce904636a3ce0a46747418b06b))
|
|
15
|
+
* **deps-dev:** bump @types/node from 24.10.3 to 25.0.0 ([#153](https://github.com/rvagg/cborg/issues/153)) ([20f9e4f](https://github.com/rvagg/cborg/commit/20f9e4f59d18382492b2f7546df2e1e17eaa3cd2))
|
|
16
|
+
* **deps:** bump actions/setup-node from 6.1.0 to 6.2.0 ([#158](https://github.com/rvagg/cborg/issues/158)) ([feafeb2](https://github.com/rvagg/cborg/commit/feafeb2e74b266424cf97072a9dbfb73ea6ac3ee))
|
|
17
|
+
* minor optimisation & test additions for ignoreUndefinedProperties ([55f653c](https://github.com/rvagg/cborg/commit/55f653c74eff0e7f4b766f3b6e81c90ba7e06a39))
|
|
18
|
+
|
|
1
19
|
## [4.3.2](https://github.com/rvagg/cborg/compare/v4.3.1...v4.3.2) (2025-12-04)
|
|
2
20
|
|
|
3
21
|
### Trivial Changes
|
package/README.md
CHANGED
|
@@ -219,6 +219,7 @@ Encode a JavaScript object and return a `Uint8Array` with the CBOR byte represen
|
|
|
219
219
|
* `float64` (boolean, default `false`): do not attempt to store floats as their smallest possible form, store all floats as 64-bit
|
|
220
220
|
* `typeEncoders` (object): a mapping of type name to function that can encode that type into cborg tokens. This may also be used to reject or transform types as objects are dissected for encoding. See the [Type encoders](#type-encoders) section below for more information.
|
|
221
221
|
* `mapSorter` (function): a function taking two arguments, where each argument is a `Token`, or an array of `Token`s representing the keys of a map being encoded. Similar to other JavaScript compare functions, a `-1`, `1` or `0` (which shouldn't be possible) should be returned depending on the sorting order of the keys. See the source code for the default sorting order which uses the length-first rule recommendation from [RFC 7049](https://tools.ietf.org/html/rfc7049).
|
|
222
|
+
* `ignoreUndefinedProperties` (boolean, default `false`): when encoding a plain object, properties with `undefined` values will be omitted. Does not apply to `Map`s or arrays.
|
|
222
223
|
|
|
223
224
|
### `decode(data[, options])`
|
|
224
225
|
|
|
@@ -410,7 +411,7 @@ import { decode, Tokenizer, Type } from 'cborg'
|
|
|
410
411
|
class CustomTokeniser extends Tokenizer {
|
|
411
412
|
next () {
|
|
412
413
|
const nextToken = super.next()
|
|
413
|
-
if (nextToken.type
|
|
414
|
+
if (Type.equals(nextToken.type, Type.bytes)) {
|
|
414
415
|
throw new Error('Unsupported type: bytes')
|
|
415
416
|
}
|
|
416
417
|
return nextToken
|
package/example-bytestrings.js
CHANGED
|
@@ -92,7 +92,7 @@ tags[tagUint64Array] = uint64ArrayDecoder
|
|
|
92
92
|
class ArrayBufferTransformingTokeniser extends Tokenizer {
|
|
93
93
|
next () {
|
|
94
94
|
const nextToken = super.next()
|
|
95
|
-
if (nextToken.type
|
|
95
|
+
if (Type.equals(nextToken.type, Type.bytes)) {
|
|
96
96
|
// Transform the (assumed) Uint8Array value to an ArrayBuffer of the same bytes, note though
|
|
97
97
|
// that all tags we care about are going to be <tag><bytes>, so we're also transforming those
|
|
98
98
|
// into ArrayBuffers, so our tag decoders need to also assume they are getting ArrayBuffers
|
package/interface.ts
CHANGED
|
@@ -53,4 +53,9 @@ export interface EncodeOptions {
|
|
|
53
53
|
mapSorter?: MapSorter,
|
|
54
54
|
quickEncodeToken?: QuickEncodeToken,
|
|
55
55
|
typeEncoders?: { [typeName: string]: OptionalTypeEncoder }
|
|
56
|
+
/**
|
|
57
|
+
* If true, plain object properties with `undefined` values are ignored during
|
|
58
|
+
* encoding.
|
|
59
|
+
*/
|
|
60
|
+
ignoreUndefinedProperties?: boolean,
|
|
56
61
|
}
|
package/lib/2bytes.js
CHANGED
|
@@ -89,7 +89,7 @@ export function decodeBytes64 (data, pos, _minor, options) {
|
|
|
89
89
|
*/
|
|
90
90
|
function tokenBytes (token) {
|
|
91
91
|
if (token.encodedBytes === undefined) {
|
|
92
|
-
token.encodedBytes = token.type
|
|
92
|
+
token.encodedBytes = Type.equals(token.type, Type.string) ? fromString(token.value) : token.value
|
|
93
93
|
}
|
|
94
94
|
// @ts-ignore c'mon
|
|
95
95
|
return token.encodedBytes
|
package/lib/byte-utils.js
CHANGED
|
@@ -302,6 +302,9 @@ function utf8ToBytes (str) {
|
|
|
302
302
|
out[p++] = ((c >> 6) & 63) | 128
|
|
303
303
|
out[p++] = (c & 63) | 128
|
|
304
304
|
} else {
|
|
305
|
+
if ((c >= 0xD800) && (c <= 0xDFFF)) {
|
|
306
|
+
c = 0xFFFD // Unpaired Surrogate
|
|
307
|
+
}
|
|
305
308
|
out[p++] = (c >> 12) | 224
|
|
306
309
|
out[p++] = ((c >> 6) & 63) | 128
|
|
307
310
|
out[p++] = (c & 63) | 128
|
package/lib/decode.js
CHANGED
|
@@ -145,7 +145,7 @@ function tokensToObject (tokeniser, options) {
|
|
|
145
145
|
|
|
146
146
|
const token = tokeniser.next()
|
|
147
147
|
|
|
148
|
-
if (token.type
|
|
148
|
+
if (Type.equals(token.type, Type.break)) {
|
|
149
149
|
return BREAK
|
|
150
150
|
}
|
|
151
151
|
|
|
@@ -153,15 +153,15 @@ function tokensToObject (tokeniser, options) {
|
|
|
153
153
|
return token.value
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
if (token.type
|
|
156
|
+
if (Type.equals(token.type, Type.array)) {
|
|
157
157
|
return tokenToArray(token, tokeniser, options)
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
if (token.type
|
|
160
|
+
if (Type.equals(token.type, Type.map)) {
|
|
161
161
|
return tokenToMap(token, tokeniser, options)
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
if (token.type
|
|
164
|
+
if (Type.equals(token.type, Type.tag)) {
|
|
165
165
|
if (options.tags && typeof options.tags[token.value] === 'function') {
|
|
166
166
|
const tagged = tokensToObject(tokeniser, options)
|
|
167
167
|
return options.tags[token.value](tagged)
|
package/lib/encode.js
CHANGED
|
@@ -252,28 +252,47 @@ const typeEncoders = {
|
|
|
252
252
|
const isMap = typ !== 'Object'
|
|
253
253
|
// it's slightly quicker to use Object.keys() than Object.entries()
|
|
254
254
|
const keys = isMap ? obj.keys() : Object.keys(obj)
|
|
255
|
-
const
|
|
256
|
-
|
|
255
|
+
const maxLength = isMap ? obj.size : keys.length
|
|
256
|
+
|
|
257
|
+
/** @type {undefined | [TokenOrNestedTokens, TokenOrNestedTokens][]} */
|
|
258
|
+
let entries
|
|
259
|
+
|
|
260
|
+
if (maxLength) {
|
|
261
|
+
// Pre-allocate the array with the expected size
|
|
262
|
+
entries = new Array(maxLength)
|
|
263
|
+
refStack = Ref.createCheck(refStack, obj)
|
|
264
|
+
const skipUndefined = !isMap && options.ignoreUndefinedProperties
|
|
265
|
+
|
|
266
|
+
let i = 0
|
|
267
|
+
for (const key of keys) {
|
|
268
|
+
const value = isMap ? obj.get(key) : obj[key]
|
|
269
|
+
if (skipUndefined && value === undefined) {
|
|
270
|
+
continue
|
|
271
|
+
}
|
|
272
|
+
entries[i++] = [
|
|
273
|
+
objectToTokens(key, options, refStack),
|
|
274
|
+
objectToTokens(value, options, refStack)
|
|
275
|
+
]
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Truncate only if properties were skipped
|
|
279
|
+
if (i < maxLength) {
|
|
280
|
+
entries.length = i
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (!entries?.length) {
|
|
257
285
|
if (options.addBreakTokens === true) {
|
|
258
286
|
return [simpleTokens.emptyMap, new Token(Type.break)]
|
|
259
287
|
}
|
|
260
288
|
return simpleTokens.emptyMap
|
|
261
289
|
}
|
|
262
|
-
|
|
263
|
-
/** @type {TokenOrNestedTokens[]} */
|
|
264
|
-
const entries = []
|
|
265
|
-
let i = 0
|
|
266
|
-
for (const key of keys) {
|
|
267
|
-
entries[i++] = [
|
|
268
|
-
objectToTokens(key, options, refStack),
|
|
269
|
-
objectToTokens(isMap ? obj.get(key) : obj[key], options, refStack)
|
|
270
|
-
]
|
|
271
|
-
}
|
|
290
|
+
|
|
272
291
|
sortMapEntries(entries, options)
|
|
273
292
|
if (options.addBreakTokens) {
|
|
274
|
-
return [new Token(Type.map, length), entries, new Token(Type.break)]
|
|
293
|
+
return [new Token(Type.map, entries.length), entries, new Token(Type.break)]
|
|
275
294
|
}
|
|
276
|
-
return [new Token(Type.map, length), entries]
|
|
295
|
+
return [new Token(Type.map, entries.length), entries]
|
|
277
296
|
}
|
|
278
297
|
}
|
|
279
298
|
|
package/lib/json/encode.js
CHANGED
|
@@ -22,13 +22,13 @@ class JSONEncoder extends Array {
|
|
|
22
22
|
prefix (buf) {
|
|
23
23
|
const recurs = this.inRecursive[this.inRecursive.length - 1]
|
|
24
24
|
if (recurs) {
|
|
25
|
-
if (recurs.type
|
|
25
|
+
if (Type.equals(recurs.type, Type.array)) {
|
|
26
26
|
recurs.elements++
|
|
27
27
|
if (recurs.elements !== 1) { // >first
|
|
28
28
|
buf.push([44]) // ','
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
if (recurs.type
|
|
31
|
+
if (Type.equals(recurs.type, Type.map)) {
|
|
32
32
|
recurs.elements++
|
|
33
33
|
if (recurs.elements !== 1) { // >first
|
|
34
34
|
if (recurs.elements % 2 === 1) { // key
|
|
@@ -119,9 +119,9 @@ class JSONEncoder extends Array {
|
|
|
119
119
|
if (token.type.name === 'break') {
|
|
120
120
|
const recurs = this.inRecursive.pop()
|
|
121
121
|
if (recurs) {
|
|
122
|
-
if (recurs.type
|
|
122
|
+
if (Type.equals(recurs.type, Type.array)) {
|
|
123
123
|
buf.push([93]) // ']'
|
|
124
|
-
} else if (recurs.type
|
|
124
|
+
} else if (Type.equals(recurs.type, Type.map)) {
|
|
125
125
|
buf.push([125]) // '}'
|
|
126
126
|
/* c8 ignore next 3 */
|
|
127
127
|
} else {
|
package/lib/token.js
CHANGED
|
@@ -24,6 +24,18 @@ class Type {
|
|
|
24
24
|
/* c8 ignore next 1 */
|
|
25
25
|
return this.major < typ.major ? -1 : this.major > typ.major ? 1 : 0
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check equality between two Type instances. Safe to use across different
|
|
30
|
+
* copies of the Type class (e.g., when bundlers duplicate the module).
|
|
31
|
+
* (major, name) uniquely identifies a Type; terminal is implied by these.
|
|
32
|
+
* @param {Type} a
|
|
33
|
+
* @param {Type} b
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
36
|
+
static equals (a, b) {
|
|
37
|
+
return a === b || (a.major === b.major && a.name === b.name)
|
|
38
|
+
}
|
|
27
39
|
}
|
|
28
40
|
|
|
29
41
|
// convert to static fields when better supported
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cborg",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"description": "Fast CBOR with a focus on strictness",
|
|
5
5
|
"main": "cborg.js",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@semantic-release/release-notes-generator": "^14.0.1",
|
|
37
37
|
"@types/chai": "^5.0.0",
|
|
38
38
|
"@types/mocha": "^10.0.8",
|
|
39
|
-
"@types/node": "^
|
|
39
|
+
"@types/node": "^25.0.0",
|
|
40
40
|
"c8": "^10.1.2",
|
|
41
41
|
"chai": "^6.0.1",
|
|
42
42
|
"conventional-changelog-conventionalcommits": "^9.0.0",
|
package/test/test-3string.js
CHANGED
|
@@ -109,6 +109,10 @@ describe('string', () => {
|
|
|
109
109
|
() => decode(fromHex('7ba5f702b3a5f702b34c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e6720656c69742e20446f6e6563206d692074656c6c75732c20696163756c6973206e656320766573746962756c756d20717569732c206665726d656e74756d206e6f6e2066656c69732e204d616563656e6173207574206a7573746f20706f73756572652e')),
|
|
110
110
|
/CBOR decode error: 64-bit integer string lengths not supported/)
|
|
111
111
|
})
|
|
112
|
+
|
|
113
|
+
it('decodes unpaired surrogates as U+FFFD', () => {
|
|
114
|
+
assert.strictEqual(decode(fromHex('63edb88e')), '\uFFFD\uFFFD\uFFFD')
|
|
115
|
+
})
|
|
112
116
|
}
|
|
113
117
|
})
|
|
114
118
|
|
|
@@ -131,6 +135,20 @@ describe('string', () => {
|
|
|
131
135
|
assert.strictEqual(toHex(encode(data)), expectedHex, `encode ${fixture.type}`)
|
|
132
136
|
}
|
|
133
137
|
})
|
|
138
|
+
|
|
139
|
+
it('should encode unpaired surrogates as U+FFFD', () => {
|
|
140
|
+
// short strings
|
|
141
|
+
assert.strictEqual(
|
|
142
|
+
toHex(encode('😎'.slice(1))),
|
|
143
|
+
toHex(encode('\uFFFD'))
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
// long strings (>64)
|
|
147
|
+
assert.strictEqual(
|
|
148
|
+
toHex(encode('A'.repeat(65) + '😎'.slice(1))),
|
|
149
|
+
toHex(encode('A'.repeat(65) + '\uFFFD'))
|
|
150
|
+
)
|
|
151
|
+
})
|
|
134
152
|
}
|
|
135
153
|
})
|
|
136
154
|
})
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
3
|
+
import * as chai from 'chai'
|
|
4
|
+
|
|
5
|
+
import { encode, decode } from '../cborg.js'
|
|
6
|
+
|
|
7
|
+
const { assert } = chai
|
|
8
|
+
|
|
9
|
+
describe('ignoreUndefinedProperties option', () => {
|
|
10
|
+
it('should include undefined properties when option is not set', () => {
|
|
11
|
+
const obj = {
|
|
12
|
+
a: 1,
|
|
13
|
+
b: undefined,
|
|
14
|
+
c: 3,
|
|
15
|
+
d: undefined
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const encoded = encode(obj)
|
|
19
|
+
const decoded = decode(encoded)
|
|
20
|
+
|
|
21
|
+
assert.deepStrictEqual(decoded, { a: 1, b: undefined, c: 3, d: undefined })
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should ignore undefined plain object properties when option is set', () => {
|
|
25
|
+
const obj = {
|
|
26
|
+
a: 1,
|
|
27
|
+
b: undefined,
|
|
28
|
+
c: 3,
|
|
29
|
+
d: undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const encoded = encode(obj, { ignoreUndefinedProperties: true })
|
|
33
|
+
const decoded = decode(encoded)
|
|
34
|
+
|
|
35
|
+
assert.deepStrictEqual(decoded, { a: 1, c: 3 })
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should include undefined Map values when option is set', () => {
|
|
39
|
+
const obj = new Map()
|
|
40
|
+
obj.set('a', 1)
|
|
41
|
+
obj.set('b', undefined)
|
|
42
|
+
obj.set('c', 3)
|
|
43
|
+
obj.set('d', undefined)
|
|
44
|
+
|
|
45
|
+
const encoded = encode(obj, { ignoreUndefinedProperties: true })
|
|
46
|
+
const decoded = decode(encoded)
|
|
47
|
+
|
|
48
|
+
assert.deepStrictEqual(decoded, { a: 1, b: undefined, c: 3, d: undefined })
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should return empty object when all properties are undefined', () => {
|
|
52
|
+
const obj = {
|
|
53
|
+
a: undefined,
|
|
54
|
+
b: undefined,
|
|
55
|
+
c: undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const encoded = encode(obj, { ignoreUndefinedProperties: true })
|
|
59
|
+
const decoded = decode(encoded)
|
|
60
|
+
|
|
61
|
+
assert.deepStrictEqual(decoded, {})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should handle nested objects with undefined properties', () => {
|
|
65
|
+
const obj = {
|
|
66
|
+
a: 1,
|
|
67
|
+
b: undefined,
|
|
68
|
+
c: {
|
|
69
|
+
d: 2,
|
|
70
|
+
e: undefined,
|
|
71
|
+
f: {
|
|
72
|
+
g: 3,
|
|
73
|
+
h: undefined
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const encoded = encode(obj, { ignoreUndefinedProperties: true })
|
|
79
|
+
const decoded = decode(encoded)
|
|
80
|
+
|
|
81
|
+
assert.deepStrictEqual(decoded, {
|
|
82
|
+
a: 1,
|
|
83
|
+
c: {
|
|
84
|
+
d: 2,
|
|
85
|
+
f: {
|
|
86
|
+
g: 3
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('should handle empty object', () => {
|
|
93
|
+
const obj = {}
|
|
94
|
+
|
|
95
|
+
const encoded = encode(obj, { ignoreUndefinedProperties: true })
|
|
96
|
+
const decoded = decode(encoded)
|
|
97
|
+
|
|
98
|
+
assert.deepStrictEqual(decoded, {})
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should not affect undefined values in arrays', () => {
|
|
102
|
+
const obj = {
|
|
103
|
+
a: [1, undefined, 3],
|
|
104
|
+
b: undefined
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const encoded = encode(obj, { ignoreUndefinedProperties: true })
|
|
108
|
+
const decoded = decode(encoded)
|
|
109
|
+
|
|
110
|
+
assert.deepStrictEqual(decoded, { a: [1, undefined, 3] })
|
|
111
|
+
})
|
|
112
|
+
})
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
3
|
+
import * as chai from 'chai'
|
|
4
|
+
|
|
5
|
+
import { Type } from '../lib/token.js'
|
|
6
|
+
import { Tokeniser } from '../lib/decode.js'
|
|
7
|
+
import { encode } from '../cborg.js'
|
|
8
|
+
|
|
9
|
+
const { assert } = chai
|
|
10
|
+
|
|
11
|
+
describe('Type.equals', () => {
|
|
12
|
+
describe('same instance', () => {
|
|
13
|
+
it('should return true for identical references', () => {
|
|
14
|
+
assert.strictEqual(Type.equals(Type.map, Type.map), true)
|
|
15
|
+
assert.strictEqual(Type.equals(Type.array, Type.array), true)
|
|
16
|
+
assert.strictEqual(Type.equals(Type.uint, Type.uint), true)
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe('duplicate instances (bundler simulation)', () => {
|
|
21
|
+
// Simulate what happens when a bundler creates duplicate Type instances
|
|
22
|
+
// These have the same properties but are different objects
|
|
23
|
+
const duplicateTypes = {
|
|
24
|
+
uint: { major: 0, name: 'uint', terminal: true },
|
|
25
|
+
negint: { major: 1, name: 'negint', terminal: true },
|
|
26
|
+
bytes: { major: 2, name: 'bytes', terminal: true },
|
|
27
|
+
string: { major: 3, name: 'string', terminal: true },
|
|
28
|
+
array: { major: 4, name: 'array', terminal: false },
|
|
29
|
+
map: { major: 5, name: 'map', terminal: false },
|
|
30
|
+
tag: { major: 6, name: 'tag', terminal: false },
|
|
31
|
+
float: { major: 7, name: 'float', terminal: true },
|
|
32
|
+
false: { major: 7, name: 'false', terminal: true },
|
|
33
|
+
true: { major: 7, name: 'true', terminal: true },
|
|
34
|
+
null: { major: 7, name: 'null', terminal: true },
|
|
35
|
+
undefined: { major: 7, name: 'undefined', terminal: true },
|
|
36
|
+
break: { major: 7, name: 'break', terminal: true }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
it('should return true for duplicate instances with same major/name', () => {
|
|
40
|
+
assert.strictEqual(Type.equals(duplicateTypes.map, Type.map), true)
|
|
41
|
+
assert.strictEqual(Type.equals(Type.map, duplicateTypes.map), true)
|
|
42
|
+
assert.strictEqual(Type.equals(duplicateTypes.array, Type.array), true)
|
|
43
|
+
assert.strictEqual(Type.equals(duplicateTypes.uint, Type.uint), true)
|
|
44
|
+
assert.strictEqual(Type.equals(duplicateTypes.string, Type.string), true)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should correctly distinguish major-7 types', () => {
|
|
48
|
+
// All these share major 7, must be distinguished by name
|
|
49
|
+
assert.strictEqual(Type.equals(duplicateTypes.float, Type.float), true)
|
|
50
|
+
assert.strictEqual(Type.equals(duplicateTypes.false, Type.false), true)
|
|
51
|
+
assert.strictEqual(Type.equals(duplicateTypes.true, Type.true), true)
|
|
52
|
+
assert.strictEqual(Type.equals(duplicateTypes.null, Type.null), true)
|
|
53
|
+
assert.strictEqual(Type.equals(duplicateTypes.undefined, Type.undefined), true)
|
|
54
|
+
assert.strictEqual(Type.equals(duplicateTypes.break, Type.break), true)
|
|
55
|
+
|
|
56
|
+
// Cross-checks: same major, different name
|
|
57
|
+
assert.strictEqual(Type.equals(duplicateTypes.float, Type.null), false)
|
|
58
|
+
assert.strictEqual(Type.equals(duplicateTypes.true, Type.false), false)
|
|
59
|
+
assert.strictEqual(Type.equals(duplicateTypes.break, Type.undefined), false)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should return false for different types', () => {
|
|
63
|
+
assert.strictEqual(Type.equals(duplicateTypes.map, Type.array), false)
|
|
64
|
+
assert.strictEqual(Type.equals(duplicateTypes.uint, Type.negint), false)
|
|
65
|
+
assert.strictEqual(Type.equals(duplicateTypes.string, Type.bytes), false)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('decode with simulated duplicate types', () => {
|
|
70
|
+
it('should handle tokens with duplicate Type instances', () => {
|
|
71
|
+
// Create a token with a "duplicate" Type (simulating bundler scenario)
|
|
72
|
+
const duplicateMapType = { major: 5, name: 'map', terminal: false }
|
|
73
|
+
|
|
74
|
+
// Encode a simple object
|
|
75
|
+
const encoded = encode({ foo: 'bar' })
|
|
76
|
+
|
|
77
|
+
// Decode using standard tokeniser
|
|
78
|
+
const tokeniser = new Tokeniser(encoded, {})
|
|
79
|
+
const firstToken = tokeniser.next()
|
|
80
|
+
|
|
81
|
+
// The token should have our real Type.map
|
|
82
|
+
assert.strictEqual(Type.equals(firstToken.type, Type.map), true)
|
|
83
|
+
// And should also match a "duplicate" type
|
|
84
|
+
assert.strictEqual(Type.equals(firstToken.type, duplicateMapType), true)
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
})
|
package/types/interface.d.ts
CHANGED
|
@@ -43,5 +43,10 @@ export interface EncodeOptions {
|
|
|
43
43
|
typeEncoders?: {
|
|
44
44
|
[typeName: string]: OptionalTypeEncoder;
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* If true, plain object properties with `undefined` values are ignored during
|
|
48
|
+
* encoding.
|
|
49
|
+
*/
|
|
50
|
+
ignoreUndefinedProperties?: boolean;
|
|
46
51
|
}
|
|
47
52
|
//# sourceMappingURL=interface.d.ts.map
|
package/types/interface.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,EAAE,EAAE,MAAM,UAAU,CAAA;AAE7B,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,KAAK,EAAE,GAAG,mBAAmB,EAAE,CAAA;AAEzE,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,SAAS,GAAG,SAAS,CAAA;IAC7B,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,CAAA;IACnB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAA;CACvC;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,SAAS,KAAK,mBAAmB,GAAG,IAAI,CAAA;AAEtI,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,SAAS,KAAK,mBAAmB,CAAA;AAE7H,MAAM,MAAM,gBAAgB,GAAG;IAC7B,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACvD,aAAa,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,MAAM,CAAC;IAE5C,WAAW,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;CAC7D,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,EAAE,KAAK,MAAM,CAAA;AAEpF,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,UAAU,GAAG,SAAS,CAAA;AAEvE,MAAM,WAAW,eAAe;IAC9B,IAAI,IAAI,OAAO,CAAC;IAChB,IAAI,IAAI,KAAK,CAAC;IACd,GAAG,IAAI,MAAM,CAAC;CACf;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;AAE5C,MAAM,WAAW,aAAa;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,IAAI,CAAC,EAAE,UAAU,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,eAAe,CAAA;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,YAAY,CAAC,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAAA;KAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,EAAE,EAAE,MAAM,UAAU,CAAA;AAE7B,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,KAAK,EAAE,GAAG,mBAAmB,EAAE,CAAA;AAEzE,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,SAAS,GAAG,SAAS,CAAA;IAC7B,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,CAAA;IACnB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAA;CACvC;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,SAAS,KAAK,mBAAmB,GAAG,IAAI,CAAA;AAEtI,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,SAAS,KAAK,mBAAmB,CAAA;AAE7H,MAAM,MAAM,gBAAgB,GAAG;IAC7B,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACvD,aAAa,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,GAAG,MAAM,CAAC;IAE5C,WAAW,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;CAC7D,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,EAAE,KAAK,MAAM,CAAA;AAEpF,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,UAAU,GAAG,SAAS,CAAA;AAEvE,MAAM,WAAW,eAAe;IAC9B,IAAI,IAAI,OAAO,CAAC;IAChB,IAAI,IAAI,KAAK,CAAC;IACd,GAAG,IAAI,MAAM,CAAC;CACf;AAED,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;AAE5C,MAAM,WAAW,aAAa;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,IAAI,CAAC,EAAE,UAAU,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,eAAe,CAAA;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,YAAY,CAAC,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAAA;KAAE,CAAA;IAC1D;;;OAGG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;CACrC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"byte-utils.d.ts","sourceRoot":"","sources":["../../lib/byte-utils.js"],"names":[],"mappings":"AAwBA;;;GAGG;AACH,2BAHW,UAAU,GAAC,MAAM,EAAE,GACjB,UAAU,CAQtB;AA8ND;;;;GAIG;AACH,4BAJW,UAAU,MACV,UAAU,GACR,MAAM,CAgBlB;
|
|
1
|
+
{"version":3,"file":"byte-utils.d.ts","sourceRoot":"","sources":["../../lib/byte-utils.js"],"names":[],"mappings":"AAwBA;;;GAGG;AACH,2BAHW,UAAU,GAAC,MAAM,EAAE,GACjB,UAAU,CAQtB;AA8ND;;;;GAIG;AACH,4BAJW,UAAU,MACV,UAAU,GACR,MAAM,CAgBlB;AA4HD;;;GAGG;AACH,kDAHW,MAAM,EAAE,GACN,MAAM,CAkBlB;AA/ZD,gCAMkD;AA4B9C;;;;GAIG;AACH,gCAJW,UAAU,SACV,MAAM,OACN,MAAM,UAQhB;AAcL,mCAGe,MAAM,iDAYN,MAAM,yCAIhB;AAOE,+BAHI,MAAM,EAAE,GACN,UAAU,CAItB;AAIG;;;;GAIG;AACH,6BAJW,UAAU,SACV,MAAM,OACN,MAAM,2BAOhB;AAcD;;;;GAIG;AACH,+BAJW,UAAU,EAAE,UACZ,MAAM,GACJ,UAAU,CActB;AAwBD;;;GAGG;AACH,4BAHW,MAAM,GACJ,UAAU,CAMtB;AAaD;;;GAGG;AACH,yBAHW,UAAU,GACR,MAAM,CAQlB;AAiBH;;;GAGG;AACD,6BAHS,MAAM,GAAC,UAAU,GACf,UAAU,CAQpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../lib/encode.js"],"names":[],"mappings":"AAuCA,oCAAoC;AACpC,oCADc,gBAAgB,EAAE,CAY/B;AAnBD,4BAA4B;AAC5B,mCADW,aAAa,CAKtB;
|
|
1
|
+
{"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../../lib/encode.js"],"names":[],"mappings":"AAuCA,oCAAoC;AACpC,oCADc,gBAAgB,EAAE,CAY/B;AAnBD,4BAA4B;AAC5B,mCADW,aAAa,CAKtB;sBA+XW,KAAK,GAAG;IAAE,SAAS,CAAC,EAAE,UAAU,CAAA;CAAE;4BAnZlC,OAAO,cAAc,EAAE,aAAa;kCACpC,OAAO,cAAc,EAAE,mBAAmB;wBAC1C,OAAO,cAAc,EAAE,SAAS;gCAChC,OAAO,cAAc,EAAE,iBAAiB;+BACxC,OAAO,cAAc,EAAE,gBAAgB;kCACvC,OAAO,cAAc,EAAE,mBAAmB;AA0RvD;;;;;GAKG;AACH,oCALW,GAAG,YACH,aAAa,aACb,SAAS,GACP,mBAAmB,CAgB/B;AAgLD;;;;GAIG;AACH,6BAJW,GAAG,YACH,aAAa,GACX,UAAU,CAKtB;AAvCD;;;;;GAKG;AACH,mCALW,GAAG,YACH,gBAAgB,EAAE,WAClB,aAAa,GACX,UAAU,CAyBtB;AAzbD,8BAA8B;AAC9B,4BADiB,SAAS;IA0BxB;;;;OAIG;IACH,0BAJW,SAAS,GAAC,SAAS,OACnB,MAAM,GAAC,GAAG,EAAE,GACV,SAAS,CAOrB;IAlCD;;;OAGG;IACH,iBAHW,MAAM,GAAC,GAAG,EAAE,UACZ,SAAS,GAAC,SAAS,EAK7B;IAFC,oBAAc;IACd,qDAAoB;IAGtB;;;OAGG;IACH,cAHW,MAAM,GAAC,GAAG,EAAE,GACV,OAAO,CAWnB;CAaF;sBA7F2B,YAAY"}
|
package/types/lib/token.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
export class Type {
|
|
2
|
+
/**
|
|
3
|
+
* Check equality between two Type instances. Safe to use across different
|
|
4
|
+
* copies of the Type class (e.g., when bundlers duplicate the module).
|
|
5
|
+
* (major, name) uniquely identifies a Type; terminal is implied by these.
|
|
6
|
+
* @param {Type} a
|
|
7
|
+
* @param {Type} b
|
|
8
|
+
* @returns {boolean}
|
|
9
|
+
*/
|
|
10
|
+
static equals(a: Type, b: Type): boolean;
|
|
2
11
|
/**
|
|
3
12
|
* @param {number} major
|
|
4
13
|
* @param {string} name
|
package/types/lib/token.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../lib/token.js"],"names":[],"mappings":"AAAA;
|
|
1
|
+
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../lib/token.js"],"names":[],"mappings":"AAAA;IA2BE;;;;;;;OAOG;IACH,iBAJW,IAAI,KACJ,IAAI,GACF,OAAO,CAInB;IApCD;;;;OAIG;IACH,mBAJW,MAAM,QACN,MAAM,YACN,OAAO,EAOjB;IAJC,cAAkB;IAClB,qBAA8B;IAC9B,aAAgB;IAChB,kBAAwB;IAI1B,mBAEC;IAED;;;OAGG;IACH,aAHW,IAAI,GACF,MAAM,CAKlB;CAaF;;;;;;;;;;;;;;;;;;;;AAkBD;IACE;;;;OAIG;IACH,kBAJW,IAAI,UACJ,GAAG,kBACH,MAAM,EAUhB;IAPC,WAAgB;IAChB,WAAkB;IAClB,kCAAkC;IAClC,mCAAmC;IACnC,cADW,UAAU,GAAC,SAAS,CACF;IAC7B,mCAAmC;IACnC,WADW,UAAU,GAAC,SAAS,CACL;IAI5B,mBAEC;CACF"}
|