@bcts/dcbor 1.0.0-alpha.8 → 1.0.0-beta.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/LICENSE +3 -2
- package/README.md +1 -1
- package/dist/index.cjs +1941 -1497
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +613 -327
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +613 -327
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +2149 -1754
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +1932 -1511
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -16
- package/src/bignum.ts +347 -0
- package/src/byte-string.ts +21 -17
- package/src/cbor-codable.ts +4 -0
- package/src/cbor-tagged-codable.ts +4 -0
- package/src/cbor-tagged-decodable.ts +33 -5
- package/src/cbor-tagged-encodable.ts +15 -0
- package/src/cbor-tagged.ts +11 -1
- package/src/cbor.ts +73 -18
- package/src/conveniences.ts +23 -5
- package/src/date.ts +35 -28
- package/src/decode.ts +13 -0
- package/src/diag.ts +233 -196
- package/src/dump.ts +18 -7
- package/src/error.ts +4 -0
- package/src/exact.ts +4 -0
- package/src/float.ts +25 -6
- package/src/global.d.ts +4 -0
- package/src/globals.d.ts +5 -0
- package/src/index.ts +65 -13
- package/src/map.ts +27 -23
- package/src/prelude.ts +12 -2
- package/src/set.ts +37 -10
- package/src/simple.ts +15 -2
- package/src/sortable.ts +70 -0
- package/src/stdlib.ts +4 -0
- package/src/string-util.ts +4 -0
- package/src/tag.ts +51 -2
- package/src/tags-store.ts +53 -68
- package/src/tags.ts +68 -6
- package/src/varint.ts +16 -11
- package/src/walk.ts +77 -281
package/src/global.d.ts
CHANGED
package/src/globals.d.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
2
6
|
* BC-DCBOR TypeScript Library
|
|
3
7
|
*
|
|
4
8
|
* A TypeScript implementation of Blockchain Commons' Deterministic CBOR (dCBOR).
|
|
@@ -27,31 +31,52 @@ export {
|
|
|
27
31
|
export { type Simple, simpleName, isNaN } from "./simple";
|
|
28
32
|
|
|
29
33
|
// Encoding/Decoding
|
|
30
|
-
export { cbor, cborData } from "./cbor";
|
|
34
|
+
export { cbor, cborData, cborEquals } from "./cbor";
|
|
31
35
|
export { decodeCbor } from "./decode";
|
|
32
36
|
|
|
33
|
-
// Factory functions (static creators)
|
|
34
|
-
|
|
37
|
+
// Factory functions (static creators).
|
|
38
|
+
// `cborFalse`, `cborTrue`, `cborNull`, `cborNaN` mirror Rust
|
|
39
|
+
// `CBOR::r#false()`, `CBOR::r#true()`, `CBOR::null()`, `CBOR::nan()`.
|
|
40
|
+
export {
|
|
41
|
+
toByteString,
|
|
42
|
+
toByteStringFromHex,
|
|
43
|
+
toTaggedValue,
|
|
44
|
+
cborFalse,
|
|
45
|
+
cborTrue,
|
|
46
|
+
cborNull,
|
|
47
|
+
cborNaN,
|
|
48
|
+
} from "./cbor";
|
|
35
49
|
|
|
36
50
|
// Map and Set
|
|
37
51
|
export { CborMap, type MapEntry } from "./map";
|
|
38
52
|
export { CborSet } from "./set";
|
|
39
53
|
|
|
40
54
|
// Tags and Tagged values
|
|
41
|
-
export {
|
|
55
|
+
export {
|
|
56
|
+
type Tag,
|
|
57
|
+
type TagValue,
|
|
58
|
+
createTag,
|
|
59
|
+
tagWithValue,
|
|
60
|
+
tagWithStaticName,
|
|
61
|
+
tagsEqual,
|
|
62
|
+
} from "./tag";
|
|
42
63
|
export {
|
|
43
64
|
type CborTagged,
|
|
44
65
|
type CborTaggedEncodable,
|
|
45
66
|
type CborTaggedDecodable,
|
|
46
67
|
type CborTaggedCodable,
|
|
47
68
|
createTaggedCbor,
|
|
69
|
+
taggedCborData,
|
|
48
70
|
validateTag,
|
|
49
71
|
extractTaggedContent,
|
|
72
|
+
fromTaggedCborData,
|
|
73
|
+
fromUntaggedCborData,
|
|
50
74
|
} from "./cbor-tagged";
|
|
51
75
|
export {
|
|
52
76
|
TagsStore,
|
|
53
77
|
type TagsStoreTrait,
|
|
54
78
|
type CborSummarizer,
|
|
79
|
+
type SummarizerResult,
|
|
55
80
|
getGlobalTagsStore,
|
|
56
81
|
} from "./tags-store";
|
|
57
82
|
export * from "./tags";
|
|
@@ -61,10 +86,17 @@ export { registerTags, registerTagsIn, tagsForValues } from "./tags";
|
|
|
61
86
|
export { CborDate } from "./date";
|
|
62
87
|
|
|
63
88
|
// Diagnostic formatting
|
|
64
|
-
export {
|
|
89
|
+
export {
|
|
90
|
+
diagnostic,
|
|
91
|
+
diagnosticAnnotated,
|
|
92
|
+
diagnosticFlat,
|
|
93
|
+
diagnosticOpt,
|
|
94
|
+
summary,
|
|
95
|
+
type DiagFormatOpts,
|
|
96
|
+
} from "./diag";
|
|
65
97
|
|
|
66
98
|
// Hex formatting
|
|
67
|
-
export { hexOpt, hexToBytes, bytesToHex, type HexFormatOpts } from "./dump";
|
|
99
|
+
export { hex, hexOpt, hexAnnotated, hexToBytes, bytesToHex, type HexFormatOpts } from "./dump";
|
|
68
100
|
|
|
69
101
|
// Walk/Traversal functionality
|
|
70
102
|
export {
|
|
@@ -72,15 +104,10 @@ export {
|
|
|
72
104
|
type EdgeTypeVariant,
|
|
73
105
|
type WalkElement,
|
|
74
106
|
type Visitor,
|
|
107
|
+
walk,
|
|
75
108
|
asSingle,
|
|
76
109
|
asKeyValue,
|
|
77
110
|
edgeLabel,
|
|
78
|
-
// Helper functions
|
|
79
|
-
countElements,
|
|
80
|
-
collectAtLevel,
|
|
81
|
-
findFirst,
|
|
82
|
-
collectAllText,
|
|
83
|
-
maxDepth,
|
|
84
111
|
} from "./walk";
|
|
85
112
|
|
|
86
113
|
// Codable interfaces
|
|
@@ -92,6 +119,19 @@ export { type Error, type Result, Ok, Err, errorMsg, errorToString, CborError }
|
|
|
92
119
|
// Note: conveniences.ts is an internal module (not exported in Rust either)
|
|
93
120
|
// The main convenience functions are exported from cbor.ts above
|
|
94
121
|
|
|
122
|
+
// BigNum support (CBOR tags 2/3, RFC 8949 §3.4.3)
|
|
123
|
+
export {
|
|
124
|
+
biguintToCbor,
|
|
125
|
+
bigintToCbor,
|
|
126
|
+
cborToBiguint,
|
|
127
|
+
cborToBigint,
|
|
128
|
+
biguintFromUntaggedCbor,
|
|
129
|
+
bigintFromNegativeUntaggedCbor,
|
|
130
|
+
} from "./bignum";
|
|
131
|
+
|
|
132
|
+
// CBOR-encoding-based array sorting (Rust `array.rs::CBORSortable`).
|
|
133
|
+
export { sortArrayByCborEncoding, arraySortable, setSortable, type CBORSortable } from "./sortable";
|
|
134
|
+
|
|
95
135
|
// Float utilities
|
|
96
136
|
export { hasFractionalPart } from "./float";
|
|
97
137
|
|
|
@@ -169,7 +209,19 @@ export {
|
|
|
169
209
|
// Extract native JavaScript value from CBOR
|
|
170
210
|
export { extractCbor } from "./conveniences";
|
|
171
211
|
|
|
172
|
-
//
|
|
212
|
+
// =============================================================================
|
|
213
|
+
// Envelope compatibility helpers (TS-only, no Rust counterpart)
|
|
214
|
+
//
|
|
215
|
+
// These wrappers expose `dcbor` types to `@bcts/envelope`'s decode pipeline
|
|
216
|
+
// in the shape it expects — mostly thin renames around the convenience
|
|
217
|
+
// functions above. They have no equivalents in `bc-dcbor-rust` (Rust
|
|
218
|
+
// envelope decoding goes through `TryFrom<CBOR>` directly).
|
|
219
|
+
//
|
|
220
|
+
// Down-stream callers wanting strict Rust parity should prefer the
|
|
221
|
+
// canonical names (`asTagged` / `asBytes` / `asArray` / `asMap`,
|
|
222
|
+
// `isNumber` is just a type guard). Kept here for backwards compatibility
|
|
223
|
+
// with older Envelope code; do not extend.
|
|
224
|
+
// =============================================================================
|
|
173
225
|
export {
|
|
174
226
|
asTaggedValue,
|
|
175
227
|
asByteString,
|
package/src/map.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
2
6
|
* Map Support in dCBOR
|
|
3
7
|
*
|
|
4
8
|
* A deterministic CBOR map implementation that ensures maps with the same
|
|
@@ -44,14 +48,14 @@ export interface MapEntry {
|
|
|
44
48
|
* encoded CBOR representation, ensuring deterministic encoding.
|
|
45
49
|
*/
|
|
46
50
|
export class CborMap {
|
|
47
|
-
|
|
51
|
+
private _dict: SortedMap<MapKey, MapEntry>;
|
|
48
52
|
|
|
49
53
|
/**
|
|
50
54
|
* Creates a new, empty CBOR Map.
|
|
51
55
|
* Optionally initializes from a JavaScript Map.
|
|
52
56
|
*/
|
|
53
57
|
constructor(map?: Map<unknown, unknown>) {
|
|
54
|
-
this
|
|
58
|
+
this._dict = new SortedMap(null, areBytesEqual, lexicographicallyCompareBytes);
|
|
55
59
|
|
|
56
60
|
if (map !== undefined) {
|
|
57
61
|
for (const [key, value] of map.entries()) {
|
|
@@ -76,7 +80,7 @@ export class CborMap {
|
|
|
76
80
|
const keyCbor = cbor(key);
|
|
77
81
|
const valueCbor = cbor(value);
|
|
78
82
|
const keyData = cborData(keyCbor);
|
|
79
|
-
this
|
|
83
|
+
this._dict.set(keyData, { key: keyCbor, value: valueCbor });
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
/**
|
|
@@ -86,7 +90,7 @@ export class CborMap {
|
|
|
86
90
|
this.set(key, value);
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
|
|
93
|
+
private _makeKey<K extends CborInput>(key: K): MapKey {
|
|
90
94
|
const keyCbor = cbor(key);
|
|
91
95
|
return cborData(keyCbor);
|
|
92
96
|
}
|
|
@@ -97,8 +101,8 @@ export class CborMap {
|
|
|
97
101
|
* Matches Rust's Map::get().
|
|
98
102
|
*/
|
|
99
103
|
get<K extends CborInput, V>(key: K): V | undefined {
|
|
100
|
-
const keyData = this
|
|
101
|
-
const value = this
|
|
104
|
+
const keyData = this._makeKey(key);
|
|
105
|
+
const value = this._dict.get(keyData);
|
|
102
106
|
if (value === undefined) {
|
|
103
107
|
return undefined;
|
|
104
108
|
}
|
|
@@ -124,24 +128,24 @@ export class CborMap {
|
|
|
124
128
|
* Matches Rust's Map::contains_key().
|
|
125
129
|
*/
|
|
126
130
|
containsKey<K extends CborInput>(key: K): boolean {
|
|
127
|
-
const keyData = this
|
|
128
|
-
return this
|
|
131
|
+
const keyData = this._makeKey(key);
|
|
132
|
+
return this._dict.has(keyData);
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
delete<K extends CborInput>(key: K): boolean {
|
|
132
|
-
const keyData = this
|
|
133
|
-
const existed = this
|
|
134
|
-
this
|
|
136
|
+
const keyData = this._makeKey(key);
|
|
137
|
+
const existed = this._dict.has(keyData);
|
|
138
|
+
this._dict.delete(keyData);
|
|
135
139
|
return existed;
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
has<K extends CborInput>(key: K): boolean {
|
|
139
|
-
const keyData = this
|
|
140
|
-
return this
|
|
143
|
+
const keyData = this._makeKey(key);
|
|
144
|
+
return this._dict.has(keyData);
|
|
141
145
|
}
|
|
142
146
|
|
|
143
147
|
clear(): void {
|
|
144
|
-
this
|
|
148
|
+
this._dict = new SortedMap(null, areBytesEqual, lexicographicallyCompareBytes);
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
/**
|
|
@@ -149,7 +153,7 @@ export class CborMap {
|
|
|
149
153
|
* Matches Rust's Map::len().
|
|
150
154
|
*/
|
|
151
155
|
get length(): number {
|
|
152
|
-
return this
|
|
156
|
+
return this._dict.length;
|
|
153
157
|
}
|
|
154
158
|
|
|
155
159
|
/**
|
|
@@ -157,7 +161,7 @@ export class CborMap {
|
|
|
157
161
|
* Also matches Rust's Map::len().
|
|
158
162
|
*/
|
|
159
163
|
get size(): number {
|
|
160
|
-
return this
|
|
164
|
+
return this._dict.length;
|
|
161
165
|
}
|
|
162
166
|
|
|
163
167
|
/**
|
|
@@ -165,7 +169,7 @@ export class CborMap {
|
|
|
165
169
|
* Matches Rust's Map::len().
|
|
166
170
|
*/
|
|
167
171
|
len(): number {
|
|
168
|
-
return this
|
|
172
|
+
return this._dict.length;
|
|
169
173
|
}
|
|
170
174
|
|
|
171
175
|
/**
|
|
@@ -173,7 +177,7 @@ export class CborMap {
|
|
|
173
177
|
* Matches Rust's Map::is_empty().
|
|
174
178
|
*/
|
|
175
179
|
isEmpty(): boolean {
|
|
176
|
-
return this
|
|
180
|
+
return this._dict.length === 0;
|
|
177
181
|
}
|
|
178
182
|
|
|
179
183
|
/**
|
|
@@ -181,7 +185,7 @@ export class CborMap {
|
|
|
181
185
|
* Keys are sorted in lexicographic order of their encoded CBOR bytes.
|
|
182
186
|
*/
|
|
183
187
|
get entriesArray(): MapEntry[] {
|
|
184
|
-
return this
|
|
188
|
+
return this._dict.map((value: MapEntry, _key: MapKey) => ({
|
|
185
189
|
key: value.key,
|
|
186
190
|
value: value.value,
|
|
187
191
|
}));
|
|
@@ -213,21 +217,21 @@ export class CborMap {
|
|
|
213
217
|
* Matches Rust's Map::insert_next().
|
|
214
218
|
*/
|
|
215
219
|
setNext<K extends CborInput, V extends CborInput>(key: K, value: V): void {
|
|
216
|
-
const lastEntry = this
|
|
220
|
+
const lastEntry = this._dict.max();
|
|
217
221
|
if (lastEntry === undefined) {
|
|
218
222
|
this.set(key, value);
|
|
219
223
|
return;
|
|
220
224
|
}
|
|
221
225
|
const keyCbor = cbor(key);
|
|
222
226
|
const newKey = cborData(keyCbor);
|
|
223
|
-
if (this
|
|
227
|
+
if (this._dict.has(newKey)) {
|
|
224
228
|
throw new CborError({ type: "DuplicateMapKey" });
|
|
225
229
|
}
|
|
226
|
-
const lastEntryKey = this
|
|
230
|
+
const lastEntryKey = this._makeKey(lastEntry.key);
|
|
227
231
|
if (lexicographicallyCompareBytes(newKey, lastEntryKey) <= 0) {
|
|
228
232
|
throw new CborError({ type: "MisorderedMapKey" });
|
|
229
233
|
}
|
|
230
|
-
this
|
|
234
|
+
this._dict.set(newKey, { key: keyCbor, value: cbor(value) });
|
|
231
235
|
}
|
|
232
236
|
|
|
233
237
|
get debug(): string {
|
package/src/prelude.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
2
6
|
* Prelude module - Re-exports commonly used types and classes.
|
|
3
7
|
*
|
|
4
8
|
* This module provides a curated set of imports matching Rust's prelude.rs.
|
|
@@ -51,8 +55,8 @@ export { ByteString } from "./byte-string";
|
|
|
51
55
|
export { CborDate } from "./date";
|
|
52
56
|
|
|
53
57
|
// Tag handling
|
|
54
|
-
export type { Tag } from "./tag";
|
|
55
|
-
export { createTag } from "./tag";
|
|
58
|
+
export type { Tag, TagValue } from "./tag";
|
|
59
|
+
export { createTag, tagWithValue, tagWithStaticName, tagsEqual } from "./tag";
|
|
56
60
|
export { TagsStore, getGlobalTagsStore, withTags, withTagsMut } from "./tags-store";
|
|
57
61
|
export type { TagsStoreTrait } from "./tags-store";
|
|
58
62
|
export { tagsForValues } from "./tags";
|
|
@@ -65,6 +69,12 @@ export type { HexFormatOpts } from "./dump";
|
|
|
65
69
|
export { EdgeType } from "./walk";
|
|
66
70
|
export type { WalkElement, EdgeTypeVariant, Visitor } from "./walk";
|
|
67
71
|
|
|
72
|
+
// BigNum support
|
|
73
|
+
export { biguintToCbor, bigintToCbor, cborToBiguint, cborToBigint } from "./bignum";
|
|
74
|
+
|
|
75
|
+
// CBOR-encoding-based array sorting (Rust `prelude.rs::CBORSortable`).
|
|
76
|
+
export { sortArrayByCborEncoding, arraySortable, setSortable, type CBORSortable } from "./sortable";
|
|
77
|
+
|
|
68
78
|
// Error handling
|
|
69
79
|
export type { Error, Result } from "./error";
|
|
70
80
|
export { Ok, Err, errorMsg, errorToString, CborError } from "./error";
|
package/src/set.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
2
6
|
* Set data structure for CBOR with tag(258) encoding.
|
|
3
7
|
*
|
|
4
8
|
* A Set is encoded as an array with no duplicate elements,
|
|
@@ -47,10 +51,10 @@ import { CborError } from "./error";
|
|
|
47
51
|
* ```
|
|
48
52
|
*/
|
|
49
53
|
export class CborSet implements CborTaggedEncodable, CborTaggedDecodable<CborSet> {
|
|
50
|
-
readonly
|
|
54
|
+
private readonly _map: CborMap;
|
|
51
55
|
|
|
52
56
|
constructor() {
|
|
53
|
-
this
|
|
57
|
+
this._map = new CborMap();
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
// =========================================================================
|
|
@@ -127,7 +131,27 @@ export class CborSet implements CborTaggedEncodable, CborTaggedDecodable<CborSet
|
|
|
127
131
|
insert(value: CborInput): void {
|
|
128
132
|
const cborValue = encodeCborValue(value);
|
|
129
133
|
// In a set, key and value are the same
|
|
130
|
-
this
|
|
134
|
+
this._map.set(cborValue, cborValue);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Insert an element into the set, requiring it to be strictly greater
|
|
139
|
+
* (in canonical CBOR-encoded byte order) than every previously-inserted
|
|
140
|
+
* element. Used by the decoder to reject misordered or duplicate
|
|
141
|
+
* elements in tag-258 set encodings.
|
|
142
|
+
*
|
|
143
|
+
* Mirrors Rust `Set::insert_next` (`pub(crate)`); exposed here because
|
|
144
|
+
* TypeScript doesn't have a crate-private visibility level.
|
|
145
|
+
*
|
|
146
|
+
* @throws CborError of type `MisorderedMap` if `value` would not preserve
|
|
147
|
+
* strict ascending CBOR-byte order, or `DuplicateMapKey` for an exact
|
|
148
|
+
* repeat.
|
|
149
|
+
*/
|
|
150
|
+
insertNext(value: CborInput): void {
|
|
151
|
+
const cborValue = encodeCborValue(value);
|
|
152
|
+
// Set is `Map<key=value, value>` in Rust. `Map::insert_next` enforces
|
|
153
|
+
// strict ascending order on the encoded key bytes.
|
|
154
|
+
this._map.setNext(cborValue, cborValue);
|
|
131
155
|
}
|
|
132
156
|
|
|
133
157
|
/**
|
|
@@ -145,7 +169,7 @@ export class CborSet implements CborTaggedEncodable, CborTaggedDecodable<CborSet
|
|
|
145
169
|
*/
|
|
146
170
|
contains(value: CborInput): boolean {
|
|
147
171
|
const cborValue = encodeCborValue(value);
|
|
148
|
-
return this
|
|
172
|
+
return this._map.has(cborValue);
|
|
149
173
|
}
|
|
150
174
|
|
|
151
175
|
/**
|
|
@@ -163,14 +187,14 @@ export class CborSet implements CborTaggedEncodable, CborTaggedDecodable<CborSet
|
|
|
163
187
|
*/
|
|
164
188
|
delete(value: CborInput): boolean {
|
|
165
189
|
const cborValue = encodeCborValue(value);
|
|
166
|
-
return this
|
|
190
|
+
return this._map.delete(cborValue);
|
|
167
191
|
}
|
|
168
192
|
|
|
169
193
|
/**
|
|
170
194
|
* Remove all elements from the set.
|
|
171
195
|
*/
|
|
172
196
|
clear(): void {
|
|
173
|
-
this
|
|
197
|
+
this._map.clear();
|
|
174
198
|
}
|
|
175
199
|
|
|
176
200
|
/**
|
|
@@ -179,7 +203,7 @@ export class CborSet implements CborTaggedEncodable, CborTaggedDecodable<CborSet
|
|
|
179
203
|
* @returns Number of elements
|
|
180
204
|
*/
|
|
181
205
|
get size(): number {
|
|
182
|
-
return this
|
|
206
|
+
return this._map.size;
|
|
183
207
|
}
|
|
184
208
|
|
|
185
209
|
/**
|
|
@@ -188,7 +212,7 @@ export class CborSet implements CborTaggedEncodable, CborTaggedDecodable<CborSet
|
|
|
188
212
|
* @returns true if set has no elements
|
|
189
213
|
*/
|
|
190
214
|
isEmpty(): boolean {
|
|
191
|
-
return this
|
|
215
|
+
return this._map.size === 0;
|
|
192
216
|
}
|
|
193
217
|
|
|
194
218
|
// =========================================================================
|
|
@@ -313,7 +337,7 @@ export class CborSet implements CborTaggedEncodable, CborTaggedDecodable<CborSet
|
|
|
313
337
|
* ```
|
|
314
338
|
*/
|
|
315
339
|
*[Symbol.iterator](): Iterator<Cbor> {
|
|
316
|
-
for (const [_, value] of this
|
|
340
|
+
for (const [_, value] of this._map) {
|
|
317
341
|
yield value;
|
|
318
342
|
}
|
|
319
343
|
}
|
|
@@ -376,8 +400,11 @@ export class CborSet implements CborTaggedEncodable, CborTaggedDecodable<CborSet
|
|
|
376
400
|
}
|
|
377
401
|
|
|
378
402
|
this.clear();
|
|
403
|
+
// Mirrors Rust `Set::try_from_vec` which calls `insert_next` per item:
|
|
404
|
+
// a tag-258 wire encoding must already be in strict ascending CBOR-byte
|
|
405
|
+
// order with no duplicates.
|
|
379
406
|
for (const value of c.value) {
|
|
380
|
-
this.
|
|
407
|
+
this.insertNext(extractCbor(value) as CborInput);
|
|
381
408
|
}
|
|
382
409
|
|
|
383
410
|
return this;
|
package/src/simple.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
2
6
|
* CBOR Simple Values (Major Type 7).
|
|
3
7
|
*
|
|
4
8
|
* @module simple
|
|
@@ -120,10 +124,19 @@ export const simpleEquals = (a: Simple, b: Simple): boolean => {
|
|
|
120
124
|
/**
|
|
121
125
|
* Hash a Simple value.
|
|
122
126
|
*
|
|
123
|
-
*
|
|
127
|
+
* **Cross-language note.** Rust derives `Hash` on `Simple` via
|
|
128
|
+
* `f64::to_bits().hash(state)` through `DefaultHasher` (SipHash). JS
|
|
129
|
+
* doesn't expose SipHash, so this implementation uses FNV-1a — a fast
|
|
130
|
+
* non-cryptographic hash. The hash codes therefore **differ between
|
|
131
|
+
* Rust and TS at runtime**; this is intentional and harmless because
|
|
132
|
+
* `Hash` is only used to drive per-runtime hash tables (e.g. dedup in
|
|
133
|
+
* `HashSet<Simple>`). It is **not** part of the deterministic CBOR wire
|
|
134
|
+
* format, which uses bytewise lex on the encoded CBOR (see
|
|
135
|
+
* `lexicographicallyCompareBytes`). Do not rely on these hash values
|
|
136
|
+
* across implementations.
|
|
124
137
|
*/
|
|
125
138
|
export const simpleHash = (simple: Simple): number => {
|
|
126
|
-
//
|
|
139
|
+
// FNV-1a hash. (See doc-comment above for why this differs from Rust.)
|
|
127
140
|
let hash = 2166136261;
|
|
128
141
|
|
|
129
142
|
switch (simple.type) {
|
package/src/sortable.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
6
|
+
* CBOR-encoding-based array sorting helpers.
|
|
7
|
+
*
|
|
8
|
+
* Ported from `bc-dcbor-rust/src/array.rs` (`sort_array_by_cbor_encoding` +
|
|
9
|
+
* `CBORSortable<T>` trait). dCBOR / CDE deterministic ordering is bytewise
|
|
10
|
+
* lexicographic over the CBOR encoding of each element — this is the same
|
|
11
|
+
* comparator that `Map`/`Set` use internally for key ordering.
|
|
12
|
+
*
|
|
13
|
+
* @module sortable
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { type CborInput, cbor } from "./cbor";
|
|
17
|
+
import { lexicographicallyCompareBytes } from "./stdlib";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Return a new array sorted by the bytewise lexicographic order of each
|
|
21
|
+
* element's CBOR encoding.
|
|
22
|
+
*
|
|
23
|
+
* Mirrors Rust `pub fn sort_array_by_cbor_encoding<T>(array)`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const sorted = sortArrayByCborEncoding([3, 1, 2]); // [1, 2, 3]
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function sortArrayByCborEncoding<T extends CborInput>(array: readonly T[]): T[] {
|
|
31
|
+
const annotated: { encoding: Uint8Array; item: T }[] = array.map((item) => ({
|
|
32
|
+
encoding: cbor(item).toData(),
|
|
33
|
+
item,
|
|
34
|
+
}));
|
|
35
|
+
annotated.sort((a, b) => lexicographicallyCompareBytes(a.encoding, b.encoding));
|
|
36
|
+
return annotated.map((entry) => entry.item);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Sortable-by-CBOR-encoding trait shape.
|
|
41
|
+
*
|
|
42
|
+
* Mirrors Rust `pub trait CBORSortable<T> { fn sort_by_cbor_encoding(&self)
|
|
43
|
+
* -> Vec<T>; }` with blanket implementations for `Vec<T>`, `&[T]`,
|
|
44
|
+
* `HashSet<T>`. In TypeScript we expose a narrow interface plus
|
|
45
|
+
* `arraySortable` / `setSortable` helpers that wrap any iterable into a
|
|
46
|
+
* `CBORSortable` view.
|
|
47
|
+
*/
|
|
48
|
+
export interface CBORSortable<T extends CborInput> {
|
|
49
|
+
sortByCborEncoding(): T[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Wrap a readonly array as a {@link CBORSortable}. Equivalent to Rust's
|
|
54
|
+
* blanket `impl CBORSortable<T> for Vec<T>` / `for &[T]`.
|
|
55
|
+
*/
|
|
56
|
+
export function arraySortable<T extends CborInput>(array: readonly T[]): CBORSortable<T> {
|
|
57
|
+
return {
|
|
58
|
+
sortByCborEncoding: () => sortArrayByCborEncoding(array),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Wrap a `Set<T>` as a {@link CBORSortable}. Equivalent to Rust's
|
|
64
|
+
* `impl CBORSortable<T> for HashSet<T>`.
|
|
65
|
+
*/
|
|
66
|
+
export function setSortable<T extends CborInput>(set: ReadonlySet<T>): CBORSortable<T> {
|
|
67
|
+
return {
|
|
68
|
+
sortByCborEncoding: () => sortArrayByCborEncoding(Array.from(set)),
|
|
69
|
+
};
|
|
70
|
+
}
|
package/src/stdlib.ts
CHANGED
package/src/string-util.ts
CHANGED
package/src/tag.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
2
6
|
* CBOR Tag support for semantic tagging of values.
|
|
3
7
|
*
|
|
4
8
|
* Tags provide semantic information about CBOR data items.
|
|
@@ -9,14 +13,29 @@
|
|
|
9
13
|
|
|
10
14
|
import type { CborNumber } from "./cbor";
|
|
11
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Numeric tag value type alias.
|
|
18
|
+
*
|
|
19
|
+
* Mirrors Rust `pub type TagValue = u64`. Where Rust narrows to u64, TS
|
|
20
|
+
* accepts the broader `CborNumber` (`number | bigint`) since JavaScript
|
|
21
|
+
* has no native u64 — the runtime guards in `encodeVarInt` enforce the
|
|
22
|
+
* 0..=2^64-1 range.
|
|
23
|
+
*/
|
|
24
|
+
export type TagValue = CborNumber;
|
|
25
|
+
|
|
12
26
|
/**
|
|
13
27
|
* A CBOR tag with an optional name.
|
|
14
28
|
*
|
|
15
29
|
* Tags consist of a numeric value and an optional human-readable name.
|
|
30
|
+
*
|
|
31
|
+
* Note on equality: Rust derives `PartialEq` on `Tag` keyed on `value`
|
|
32
|
+
* only — two tags with the same value but different names compare equal.
|
|
33
|
+
* Use {@link tagsEqual} to mirror that behaviour from TypeScript; raw
|
|
34
|
+
* `===` on `Tag` objects compares by reference.
|
|
16
35
|
*/
|
|
17
36
|
export interface Tag {
|
|
18
37
|
/** The numeric tag value */
|
|
19
|
-
readonly value:
|
|
38
|
+
readonly value: TagValue;
|
|
20
39
|
/** Optional human-readable name for the tag */
|
|
21
40
|
readonly name?: string;
|
|
22
41
|
}
|
|
@@ -34,13 +53,43 @@ export interface Tag {
|
|
|
34
53
|
* const customTag = createTag(12345, 'myCustomTag');
|
|
35
54
|
* ```
|
|
36
55
|
*/
|
|
37
|
-
export const createTag = (value:
|
|
56
|
+
export const createTag = (value: TagValue, name?: string): Tag => {
|
|
38
57
|
if (name !== undefined) {
|
|
39
58
|
return { value, name };
|
|
40
59
|
}
|
|
41
60
|
return { value };
|
|
42
61
|
};
|
|
43
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Create a Tag from just its numeric value, no name attached.
|
|
65
|
+
*
|
|
66
|
+
* Mirrors Rust's `Tag::with_value(v: TagValue)`.
|
|
67
|
+
*/
|
|
68
|
+
export const tagWithValue = (value: TagValue): Tag => ({ value });
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create a Tag with a static (string) name.
|
|
72
|
+
*
|
|
73
|
+
* In Rust this distinguishes `Tag::Static(&'static str)` from
|
|
74
|
+
* `Tag::Dynamic(String)`. TypeScript has no compile-time equivalent — both
|
|
75
|
+
* variants collapse to the same `{ value, name }` object — but the
|
|
76
|
+
* function name is preserved for API parity.
|
|
77
|
+
*
|
|
78
|
+
* Mirrors Rust's `Tag::with_static_name(v, name)`.
|
|
79
|
+
*/
|
|
80
|
+
export const tagWithStaticName = (value: TagValue, name: string): Tag => ({ value, name });
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Compare two tags for equality. Mirrors Rust's `PartialEq for Tag`, which
|
|
84
|
+
* compares by `value` only and ignores the optional `name`.
|
|
85
|
+
*/
|
|
86
|
+
export const tagsEqual = (a: Tag, b: Tag): boolean => {
|
|
87
|
+
if (typeof a.value === "bigint" || typeof b.value === "bigint") {
|
|
88
|
+
return BigInt(a.value) === BigInt(b.value);
|
|
89
|
+
}
|
|
90
|
+
return a.value === b.value;
|
|
91
|
+
};
|
|
92
|
+
|
|
44
93
|
/**
|
|
45
94
|
* Get the string representation of a tag.
|
|
46
95
|
* Internal function used for error messages.
|