@bcts/dcbor 1.0.0-alpha.10
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 +48 -0
- package/README.md +13 -0
- package/dist/index.cjs +9151 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3107 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +3107 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.iife.js +9155 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.mjs +9027 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +80 -0
- package/src/.claude-flow/metrics/agent-metrics.json +1 -0
- package/src/.claude-flow/metrics/performance.json +87 -0
- package/src/.claude-flow/metrics/task-metrics.json +10 -0
- package/src/byte-string.ts +300 -0
- package/src/cbor-codable.ts +170 -0
- package/src/cbor-tagged-codable.ts +72 -0
- package/src/cbor-tagged-decodable.ts +184 -0
- package/src/cbor-tagged-encodable.ts +138 -0
- package/src/cbor-tagged.ts +104 -0
- package/src/cbor.ts +869 -0
- package/src/conveniences.ts +840 -0
- package/src/date.ts +553 -0
- package/src/decode.ts +276 -0
- package/src/diag.ts +462 -0
- package/src/dump.ts +277 -0
- package/src/error.ts +259 -0
- package/src/exact.ts +714 -0
- package/src/float.ts +279 -0
- package/src/global.d.ts +34 -0
- package/src/globals.d.ts +0 -0
- package/src/index.ts +180 -0
- package/src/map.ts +308 -0
- package/src/prelude.ts +70 -0
- package/src/set.ts +515 -0
- package/src/simple.ts +153 -0
- package/src/stdlib.ts +55 -0
- package/src/string-util.ts +55 -0
- package/src/tag.ts +53 -0
- package/src/tags-store.ts +294 -0
- package/src/tags.ts +231 -0
- package/src/varint.ts +124 -0
- package/src/walk.ts +516 -0
package/src/map.ts
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map Support in dCBOR
|
|
3
|
+
*
|
|
4
|
+
* A deterministic CBOR map implementation that ensures maps with the same
|
|
5
|
+
* content always produce identical binary encodings, regardless of insertion
|
|
6
|
+
* order.
|
|
7
|
+
*
|
|
8
|
+
* ## Deterministic Map Representation
|
|
9
|
+
*
|
|
10
|
+
* The `CborMap` type follows strict deterministic encoding rules as specified by
|
|
11
|
+
* dCBOR:
|
|
12
|
+
*
|
|
13
|
+
* - Map keys are always sorted in lexicographic order of their encoded CBOR bytes
|
|
14
|
+
* - Duplicate keys are not allowed (enforced by the implementation)
|
|
15
|
+
* - Keys and values can be any type that can be converted to CBOR
|
|
16
|
+
* - Numeric reduction is applied (e.g., 3.0 is stored as integer 3)
|
|
17
|
+
*
|
|
18
|
+
* This deterministic encoding ensures that equivalent maps always produce
|
|
19
|
+
* identical byte representations, which is crucial for applications that rely
|
|
20
|
+
* on consistent hashing, digital signatures, or other cryptographic operations.
|
|
21
|
+
*
|
|
22
|
+
* @module map
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { SortedMap } from "collections/sorted-map";
|
|
26
|
+
import { type Cbor, type CborInput, MajorType } from "./cbor";
|
|
27
|
+
import { cbor, cborData, encodeCbor } from "./cbor";
|
|
28
|
+
import { areBytesEqual, lexicographicallyCompareBytes } from "./stdlib";
|
|
29
|
+
import { bytesToHex } from "./dump";
|
|
30
|
+
import { diagnostic } from "./diag";
|
|
31
|
+
import { extractCbor } from "./conveniences";
|
|
32
|
+
import { CborError } from "./error";
|
|
33
|
+
|
|
34
|
+
type MapKey = Uint8Array;
|
|
35
|
+
export interface MapEntry {
|
|
36
|
+
readonly key: Cbor;
|
|
37
|
+
readonly value: Cbor;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A deterministic CBOR map implementation.
|
|
42
|
+
*
|
|
43
|
+
* Maps are always encoded with keys sorted lexicographically by their
|
|
44
|
+
* encoded CBOR representation, ensuring deterministic encoding.
|
|
45
|
+
*/
|
|
46
|
+
export class CborMap {
|
|
47
|
+
#dict: SortedMap<MapKey, MapEntry>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates a new, empty CBOR Map.
|
|
51
|
+
* Optionally initializes from a JavaScript Map.
|
|
52
|
+
*/
|
|
53
|
+
constructor(map?: Map<unknown, unknown>) {
|
|
54
|
+
this.#dict = new SortedMap(null, areBytesEqual, lexicographicallyCompareBytes);
|
|
55
|
+
|
|
56
|
+
if (map !== undefined) {
|
|
57
|
+
for (const [key, value] of map.entries()) {
|
|
58
|
+
this.set(key as CborInput, value as CborInput);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Creates a new, empty CBOR Map.
|
|
65
|
+
* Matches Rust's Map::new().
|
|
66
|
+
*/
|
|
67
|
+
static new(): CborMap {
|
|
68
|
+
return new CborMap();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Inserts a key-value pair into the map.
|
|
73
|
+
* Matches Rust's Map::insert().
|
|
74
|
+
*/
|
|
75
|
+
set<K extends CborInput, V extends CborInput>(key: K, value: V): void {
|
|
76
|
+
const keyCbor = cbor(key);
|
|
77
|
+
const valueCbor = cbor(value);
|
|
78
|
+
const keyData = cborData(keyCbor);
|
|
79
|
+
this.#dict.set(keyData, { key: keyCbor, value: valueCbor });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Alias for set() to match Rust's insert() method.
|
|
84
|
+
*/
|
|
85
|
+
insert<K extends CborInput, V extends CborInput>(key: K, value: V): void {
|
|
86
|
+
this.set(key, value);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#makeKey<K extends CborInput>(key: K): MapKey {
|
|
90
|
+
const keyCbor = cbor(key);
|
|
91
|
+
return cborData(keyCbor);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get a value from the map, given a key.
|
|
96
|
+
* Returns undefined if the key is not present in the map.
|
|
97
|
+
* Matches Rust's Map::get().
|
|
98
|
+
*/
|
|
99
|
+
get<K extends CborInput, V>(key: K): V | undefined {
|
|
100
|
+
const keyData = this.#makeKey(key);
|
|
101
|
+
const value = this.#dict.get(keyData);
|
|
102
|
+
if (value === undefined) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
// Extract CBOR value: primitives become native types, maps/arrays preserve structure
|
|
106
|
+
return extractCbor(value.value) as V;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get a value from the map, given a key.
|
|
111
|
+
* Throws an error if the key is not present.
|
|
112
|
+
* Matches Rust's Map::extract().
|
|
113
|
+
*/
|
|
114
|
+
extract<K extends CborInput, V>(key: K): V {
|
|
115
|
+
const value = this.get<K, V>(key);
|
|
116
|
+
if (value === undefined) {
|
|
117
|
+
throw new CborError({ type: "MissingMapKey" });
|
|
118
|
+
}
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Tests if the map contains a key.
|
|
124
|
+
* Matches Rust's Map::contains_key().
|
|
125
|
+
*/
|
|
126
|
+
containsKey<K extends CborInput>(key: K): boolean {
|
|
127
|
+
const keyData = this.#makeKey(key);
|
|
128
|
+
return this.#dict.has(keyData);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
delete<K extends CborInput>(key: K): boolean {
|
|
132
|
+
const keyData = this.#makeKey(key);
|
|
133
|
+
const existed = this.#dict.has(keyData);
|
|
134
|
+
this.#dict.delete(keyData);
|
|
135
|
+
return existed;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
has<K extends CborInput>(key: K): boolean {
|
|
139
|
+
const keyData = this.#makeKey(key);
|
|
140
|
+
return this.#dict.has(keyData);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
clear(): void {
|
|
144
|
+
this.#dict = new SortedMap(null, areBytesEqual, lexicographicallyCompareBytes);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Returns the number of entries in the map.
|
|
149
|
+
* Matches Rust's Map::len().
|
|
150
|
+
*/
|
|
151
|
+
get length(): number {
|
|
152
|
+
return this.#dict.length;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Alias for length to match JavaScript Map API.
|
|
157
|
+
* Also matches Rust's Map::len().
|
|
158
|
+
*/
|
|
159
|
+
get size(): number {
|
|
160
|
+
return this.#dict.length;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Returns the number of entries in the map.
|
|
165
|
+
* Matches Rust's Map::len().
|
|
166
|
+
*/
|
|
167
|
+
len(): number {
|
|
168
|
+
return this.#dict.length;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Checks if the map is empty.
|
|
173
|
+
* Matches Rust's Map::is_empty().
|
|
174
|
+
*/
|
|
175
|
+
isEmpty(): boolean {
|
|
176
|
+
return this.#dict.length === 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get the entries of the map as an array.
|
|
181
|
+
* Keys are sorted in lexicographic order of their encoded CBOR bytes.
|
|
182
|
+
*/
|
|
183
|
+
get entriesArray(): MapEntry[] {
|
|
184
|
+
return this.#dict.map((value: MapEntry, _key: MapKey) => ({
|
|
185
|
+
key: value.key,
|
|
186
|
+
value: value.value,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Gets an iterator over the entries of the CBOR map, sorted by key.
|
|
192
|
+
* Key sorting order is lexicographic by the key's binary-encoded CBOR.
|
|
193
|
+
* Matches Rust's Map::iter().
|
|
194
|
+
*/
|
|
195
|
+
iter(): MapEntry[] {
|
|
196
|
+
return this.entriesArray;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Returns an iterator of [key, value] tuples for JavaScript Map API compatibility.
|
|
201
|
+
* This matches the standard JavaScript Map.entries() method behavior.
|
|
202
|
+
*/
|
|
203
|
+
*entries(): IterableIterator<[Cbor, Cbor]> {
|
|
204
|
+
for (const entry of this.entriesArray) {
|
|
205
|
+
yield [entry.key, entry.value];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Inserts the next key-value pair into the map during decoding.
|
|
211
|
+
* This is used for efficient map building during CBOR decoding.
|
|
212
|
+
* Throws if the key is not in ascending order or is a duplicate.
|
|
213
|
+
* Matches Rust's Map::insert_next().
|
|
214
|
+
*/
|
|
215
|
+
setNext<K extends CborInput, V extends CborInput>(key: K, value: V): void {
|
|
216
|
+
const lastEntry = this.#dict.max();
|
|
217
|
+
if (lastEntry === undefined) {
|
|
218
|
+
this.set(key, value);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const keyCbor = cbor(key);
|
|
222
|
+
const newKey = cborData(keyCbor);
|
|
223
|
+
if (this.#dict.has(newKey)) {
|
|
224
|
+
throw new CborError({ type: "DuplicateMapKey" });
|
|
225
|
+
}
|
|
226
|
+
const lastEntryKey = this.#makeKey(lastEntry.key);
|
|
227
|
+
if (lexicographicallyCompareBytes(newKey, lastEntryKey) <= 0) {
|
|
228
|
+
throw new CborError({ type: "MisorderedMapKey" });
|
|
229
|
+
}
|
|
230
|
+
this.#dict.set(newKey, { key: keyCbor, value: cbor(value) });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
get debug(): string {
|
|
234
|
+
return `map({${this.entriesArray.map(CborMap.entryDebug).join(", ")}})`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
get diagnostic(): string {
|
|
238
|
+
return `{${this.entriesArray.map(CborMap.entryDiagnostic).join(", ")}}`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private static entryDebug(this: void, entry: MapEntry): string {
|
|
242
|
+
// Format with full type information for debug output
|
|
243
|
+
const keyDebug = CborMap.formatDebug(entry.key);
|
|
244
|
+
const valueDebug = CborMap.formatDebug(entry.value);
|
|
245
|
+
return `0x${bytesToHex(encodeCbor(entry.key))}: (${keyDebug}, ${valueDebug})`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private static formatDebug(this: void, cbor: Cbor): string {
|
|
249
|
+
switch (cbor.type) {
|
|
250
|
+
case MajorType.Unsigned:
|
|
251
|
+
return `unsigned(${cbor.value})`;
|
|
252
|
+
case MajorType.Negative: {
|
|
253
|
+
const negValue = typeof cbor.value === "bigint" ? -cbor.value - 1n : -cbor.value - 1;
|
|
254
|
+
return `negative(${negValue})`;
|
|
255
|
+
}
|
|
256
|
+
case MajorType.ByteString: {
|
|
257
|
+
return `bytes(${bytesToHex(cbor.value)})`;
|
|
258
|
+
}
|
|
259
|
+
case MajorType.Text:
|
|
260
|
+
return `text("${cbor.value}")`;
|
|
261
|
+
case MajorType.Array: {
|
|
262
|
+
const items = cbor.value.map(CborMap.formatDebug);
|
|
263
|
+
return `array([${items.join(", ")}])`;
|
|
264
|
+
}
|
|
265
|
+
case MajorType.Map: {
|
|
266
|
+
return cbor.value.debug;
|
|
267
|
+
}
|
|
268
|
+
case MajorType.Tagged:
|
|
269
|
+
return `tagged(${cbor.tag}, ${CborMap.formatDebug(cbor.value)})`;
|
|
270
|
+
case MajorType.Simple: {
|
|
271
|
+
const simple = cbor.value;
|
|
272
|
+
if (typeof simple === "object" && simple !== null && "type" in simple) {
|
|
273
|
+
switch (simple.type) {
|
|
274
|
+
case "True":
|
|
275
|
+
return "simple(true)";
|
|
276
|
+
case "False":
|
|
277
|
+
return "simple(false)";
|
|
278
|
+
case "Null":
|
|
279
|
+
return "simple(null)";
|
|
280
|
+
case "Float":
|
|
281
|
+
return `simple(${simple.value})`;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return "simple";
|
|
285
|
+
}
|
|
286
|
+
default:
|
|
287
|
+
return diagnostic(cbor);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private static entryDiagnostic(this: void, entry: MapEntry): string {
|
|
292
|
+
return `${diagnostic(entry.key)}: ${diagnostic(entry.value)}`;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
*[Symbol.iterator](): Iterator<[Cbor, Cbor]> {
|
|
296
|
+
for (const entry of this.entriesArray) {
|
|
297
|
+
yield [entry.key, entry.value];
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
toMap<K, V>(): Map<K, V> {
|
|
302
|
+
const map = new Map<K, V>();
|
|
303
|
+
for (const entry of this.entriesArray) {
|
|
304
|
+
map.set(extractCbor(entry.key) as K, extractCbor(entry.value) as V);
|
|
305
|
+
}
|
|
306
|
+
return map;
|
|
307
|
+
}
|
|
308
|
+
}
|
package/src/prelude.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prelude module - Re-exports commonly used types and classes.
|
|
3
|
+
*
|
|
4
|
+
* This module provides a curated set of imports matching Rust's prelude.rs.
|
|
5
|
+
* Exports only types, interfaces, and core classes - not convenience functions.
|
|
6
|
+
*
|
|
7
|
+
* Equivalent to Rust's prelude.rs
|
|
8
|
+
*
|
|
9
|
+
* @module prelude
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { Cbor, CborMap, ByteString, Tag } from './prelude';
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Core types
|
|
18
|
+
export { Cbor, MajorType } from "./cbor";
|
|
19
|
+
export type { Simple } from "./simple";
|
|
20
|
+
export type {
|
|
21
|
+
CborUnsignedType,
|
|
22
|
+
CborNegativeType,
|
|
23
|
+
CborByteStringType,
|
|
24
|
+
CborTextType,
|
|
25
|
+
CborArrayType,
|
|
26
|
+
CborMapType,
|
|
27
|
+
CborTaggedType,
|
|
28
|
+
CborSimpleType,
|
|
29
|
+
CborNumber,
|
|
30
|
+
} from "./cbor";
|
|
31
|
+
|
|
32
|
+
// Codable interfaces
|
|
33
|
+
export type { CborEncodable, CborDecodable, CborCodable } from "./cbor-codable";
|
|
34
|
+
|
|
35
|
+
// Tagged value interfaces
|
|
36
|
+
export type {
|
|
37
|
+
CborTagged,
|
|
38
|
+
CborTaggedEncodable,
|
|
39
|
+
CborTaggedDecodable,
|
|
40
|
+
CborTaggedCodable,
|
|
41
|
+
} from "./cbor-tagged";
|
|
42
|
+
|
|
43
|
+
// Map and Set classes
|
|
44
|
+
export { CborMap } from "./map";
|
|
45
|
+
export { CborSet } from "./set";
|
|
46
|
+
|
|
47
|
+
// ByteString class
|
|
48
|
+
export { ByteString } from "./byte-string";
|
|
49
|
+
|
|
50
|
+
// Date class
|
|
51
|
+
export { CborDate } from "./date";
|
|
52
|
+
|
|
53
|
+
// Tag handling
|
|
54
|
+
export type { Tag } from "./tag";
|
|
55
|
+
export { createTag } from "./tag";
|
|
56
|
+
export { TagsStore, getGlobalTagsStore, withTags, withTagsMut } from "./tags-store";
|
|
57
|
+
export type { TagsStoreTrait } from "./tags-store";
|
|
58
|
+
export { tagsForValues } from "./tags";
|
|
59
|
+
|
|
60
|
+
// Format options
|
|
61
|
+
export type { DiagFormatOpts } from "./diag";
|
|
62
|
+
export type { HexFormatOpts } from "./dump";
|
|
63
|
+
|
|
64
|
+
// Walk/traversal
|
|
65
|
+
export { EdgeType } from "./walk";
|
|
66
|
+
export type { WalkElement, EdgeTypeVariant, Visitor } from "./walk";
|
|
67
|
+
|
|
68
|
+
// Error handling
|
|
69
|
+
export type { Error, Result } from "./error";
|
|
70
|
+
export { Ok, Err, errorMsg, errorToString, CborError } from "./error";
|