@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/tags-store.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
|
* Tag registry and management system.
|
|
3
7
|
*
|
|
4
8
|
* The TagsStore provides a centralized registry for CBOR tags,
|
|
@@ -9,17 +13,26 @@
|
|
|
9
13
|
|
|
10
14
|
import type { Cbor, CborNumber } from "./cbor";
|
|
11
15
|
import type { Tag } from "./tag";
|
|
16
|
+
import type { Error as CborErrorType } from "./error";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Result type for summarizer functions, matching Rust's Result<String, Error>.
|
|
20
|
+
*/
|
|
21
|
+
export type SummarizerResult =
|
|
22
|
+
| { readonly ok: true; readonly value: string }
|
|
23
|
+
| { readonly ok: false; readonly error: CborErrorType };
|
|
12
24
|
|
|
13
25
|
/**
|
|
14
26
|
* Function type for custom CBOR value summarizers.
|
|
15
27
|
*
|
|
16
28
|
* Summarizers provide custom string representations for tagged values.
|
|
29
|
+
* Returns a Result type matching Rust's `Result<String, Error>`.
|
|
17
30
|
*
|
|
18
31
|
* @param cbor - The CBOR value to summarize
|
|
19
32
|
* @param flat - If true, produce single-line output
|
|
20
|
-
* @returns
|
|
33
|
+
* @returns Result with summary string on success, or error on failure
|
|
21
34
|
*/
|
|
22
|
-
export type CborSummarizer = (cbor: Cbor, flat: boolean) =>
|
|
35
|
+
export type CborSummarizer = (cbor: Cbor, flat: boolean) => SummarizerResult;
|
|
23
36
|
|
|
24
37
|
/**
|
|
25
38
|
* Interface for tag store operations.
|
|
@@ -80,9 +93,9 @@ export interface TagsStoreTrait {
|
|
|
80
93
|
* Stores tags with their names and optional summarizer functions.
|
|
81
94
|
*/
|
|
82
95
|
export class TagsStore implements TagsStoreTrait {
|
|
83
|
-
readonly
|
|
84
|
-
readonly
|
|
85
|
-
readonly
|
|
96
|
+
private readonly _tagsByValue = new Map<string, Tag>();
|
|
97
|
+
private readonly _tagsByName = new Map<string, Tag>();
|
|
98
|
+
private readonly _summarizers = new Map<string, CborSummarizer>();
|
|
86
99
|
|
|
87
100
|
constructor() {
|
|
88
101
|
// Start with empty store, matching Rust's Default implementation
|
|
@@ -92,7 +105,13 @@ export class TagsStore implements TagsStoreTrait {
|
|
|
92
105
|
/**
|
|
93
106
|
* Insert a tag into the registry.
|
|
94
107
|
*
|
|
95
|
-
*
|
|
108
|
+
* Matches Rust's TagsStore::insert() behavior:
|
|
109
|
+
* - Throws if the tag name is undefined or empty
|
|
110
|
+
* - Throws if a tag with the same value exists with a different name
|
|
111
|
+
* - Allows re-registering the same tag value with the same name
|
|
112
|
+
*
|
|
113
|
+
* @param tag - The tag to register (must have a non-empty name)
|
|
114
|
+
* @throws Error if tag has no name, empty name, or conflicts with existing registration
|
|
96
115
|
*
|
|
97
116
|
* @example
|
|
98
117
|
* ```typescript
|
|
@@ -101,11 +120,25 @@ export class TagsStore implements TagsStoreTrait {
|
|
|
101
120
|
* ```
|
|
102
121
|
*/
|
|
103
122
|
insert(tag: Tag): void {
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
123
|
+
const name = tag.name;
|
|
124
|
+
|
|
125
|
+
// Rust: let name = tag.name().unwrap(); assert!(!name.is_empty());
|
|
126
|
+
if (name === undefined || name === "") {
|
|
127
|
+
throw new Error(`Tag ${tag.value} must have a non-empty name`);
|
|
108
128
|
}
|
|
129
|
+
|
|
130
|
+
const key = this._valueKey(tag.value);
|
|
131
|
+
const existing = this._tagsByValue.get(key);
|
|
132
|
+
|
|
133
|
+
// Rust: if old_name != name { panic!(...) }
|
|
134
|
+
if (existing?.name !== undefined && existing.name !== name) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`Attempt to register tag: ${tag.value} '${existing.name}' with different name: '${name}'`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
this._tagsByValue.set(key, tag);
|
|
141
|
+
this._tagsByName.set(name, tag);
|
|
109
142
|
}
|
|
110
143
|
|
|
111
144
|
/**
|
|
@@ -144,34 +177,13 @@ export class TagsStore implements TagsStoreTrait {
|
|
|
144
177
|
* ```
|
|
145
178
|
*/
|
|
146
179
|
setSummarizer(tagValue: CborNumber, summarizer: CborSummarizer): void {
|
|
147
|
-
const key = this
|
|
148
|
-
this
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Remove a tag from the registry.
|
|
153
|
-
*
|
|
154
|
-
* @param tagValue - The numeric tag value to remove
|
|
155
|
-
* @returns true if a tag was removed, false otherwise
|
|
156
|
-
*/
|
|
157
|
-
remove(tagValue: CborNumber): boolean {
|
|
158
|
-
const key = this.#valueKey(tagValue);
|
|
159
|
-
const tag = this.#tagsByValue.get(key);
|
|
160
|
-
if (tag === undefined) {
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
this.#tagsByValue.delete(key);
|
|
165
|
-
if (tag.name !== undefined) {
|
|
166
|
-
this.#tagsByName.delete(tag.name);
|
|
167
|
-
}
|
|
168
|
-
this.#summarizers.delete(key);
|
|
169
|
-
return true;
|
|
180
|
+
const key = this._valueKey(tagValue);
|
|
181
|
+
this._summarizers.set(key, summarizer);
|
|
170
182
|
}
|
|
171
183
|
|
|
172
184
|
assignedNameForTag(tag: Tag): string | undefined {
|
|
173
|
-
const key = this
|
|
174
|
-
const stored = this
|
|
185
|
+
const key = this._valueKey(tag.value);
|
|
186
|
+
const stored = this._tagsByValue.get(key);
|
|
175
187
|
return stored?.name;
|
|
176
188
|
}
|
|
177
189
|
|
|
@@ -180,12 +192,12 @@ export class TagsStore implements TagsStoreTrait {
|
|
|
180
192
|
}
|
|
181
193
|
|
|
182
194
|
tagForValue(value: CborNumber): Tag | undefined {
|
|
183
|
-
const key = this
|
|
184
|
-
return this
|
|
195
|
+
const key = this._valueKey(value);
|
|
196
|
+
return this._tagsByValue.get(key);
|
|
185
197
|
}
|
|
186
198
|
|
|
187
199
|
tagForName(name: string): Tag | undefined {
|
|
188
|
-
return this
|
|
200
|
+
return this._tagsByName.get(name);
|
|
189
201
|
}
|
|
190
202
|
|
|
191
203
|
nameForValue(value: CborNumber): string {
|
|
@@ -194,35 +206,8 @@ export class TagsStore implements TagsStoreTrait {
|
|
|
194
206
|
}
|
|
195
207
|
|
|
196
208
|
summarizer(tag: CborNumber): CborSummarizer | undefined {
|
|
197
|
-
const key = this
|
|
198
|
-
return this
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Get all registered tags.
|
|
203
|
-
*
|
|
204
|
-
* @returns Array of all registered tags
|
|
205
|
-
*/
|
|
206
|
-
getAllTags(): Tag[] {
|
|
207
|
-
return Array.from(this.#tagsByValue.values());
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Clear all registered tags and summarizers.
|
|
212
|
-
*/
|
|
213
|
-
clear(): void {
|
|
214
|
-
this.#tagsByValue.clear();
|
|
215
|
-
this.#tagsByName.clear();
|
|
216
|
-
this.#summarizers.clear();
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Get the number of registered tags.
|
|
221
|
-
*
|
|
222
|
-
* @returns Number of tags in the registry
|
|
223
|
-
*/
|
|
224
|
-
get size(): number {
|
|
225
|
-
return this.#tagsByValue.size;
|
|
209
|
+
const key = this._valueKey(tag);
|
|
210
|
+
return this._summarizers.get(key);
|
|
226
211
|
}
|
|
227
212
|
|
|
228
213
|
/**
|
|
@@ -231,7 +216,7 @@ export class TagsStore implements TagsStoreTrait {
|
|
|
231
216
|
*
|
|
232
217
|
* @private
|
|
233
218
|
*/
|
|
234
|
-
|
|
219
|
+
private _valueKey(value: CborNumber): string {
|
|
235
220
|
return value.toString();
|
|
236
221
|
}
|
|
237
222
|
}
|
package/src/tags.ts
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*
|
|
2
6
|
* Standard CBOR tag definitions from the IANA registry.
|
|
3
7
|
*
|
|
8
|
+
* **Cross-package note (parity with Rust).** `bc-dcbor-rust/src/tags.rs`
|
|
9
|
+
* intentionally only defines the three tags dcbor itself needs:
|
|
10
|
+
* `TAG_DATE` (1), `TAG_POSITIVE_BIGNUM` (2), `TAG_NEGATIVE_BIGNUM` (3).
|
|
11
|
+
* Every other Blockchain Commons tag — UR types, IANA semantic tags, the
|
|
12
|
+
* Envelope/XID family — lives in the dedicated `bc-tags-rust` crate
|
|
13
|
+
* (mirrored as `@bcts/tags` in TypeScript). This file additionally
|
|
14
|
+
* re-exports many of those constants for ergonomic in-package use; they
|
|
15
|
+
* are duplicate convenience definitions, not authoritative. Prefer
|
|
16
|
+
* importing from `@bcts/tags` in new code; treat the extras here as
|
|
17
|
+
* deprecated aliases that may be relocated in a future release.
|
|
18
|
+
*
|
|
4
19
|
* @module tags
|
|
5
20
|
* @see https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
|
|
6
21
|
*/
|
|
@@ -40,6 +55,18 @@ export const TAG_POSITIVE_BIGNUM = 2;
|
|
|
40
55
|
*/
|
|
41
56
|
export const TAG_NEGATIVE_BIGNUM = 3;
|
|
42
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Name for tag 2 (positive bignum).
|
|
60
|
+
* Matches Rust's `TAG_NAME_POSITIVE_BIGNUM`.
|
|
61
|
+
*/
|
|
62
|
+
export const TAG_NAME_POSITIVE_BIGNUM = "positive-bignum";
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Name for tag 3 (negative bignum).
|
|
66
|
+
* Matches Rust's `TAG_NAME_NEGATIVE_BIGNUM`.
|
|
67
|
+
*/
|
|
68
|
+
export const TAG_NAME_NEGATIVE_BIGNUM = "negative-bignum";
|
|
69
|
+
|
|
43
70
|
/**
|
|
44
71
|
* Tag 4: Decimal fraction [exponent, mantissa]
|
|
45
72
|
*/
|
|
@@ -148,11 +175,11 @@ export const TAG_SELF_DESCRIBE_CBOR = 55799;
|
|
|
148
175
|
// Matches Rust's register_tags() functionality
|
|
149
176
|
// ============================================================================
|
|
150
177
|
|
|
151
|
-
import type { TagsStore } from "./tags-store";
|
|
178
|
+
import type { TagsStore, SummarizerResult } from "./tags-store";
|
|
152
179
|
import { getGlobalTagsStore } from "./tags-store";
|
|
153
180
|
import { CborDate } from "./date";
|
|
154
181
|
import type { Cbor } from "./cbor";
|
|
155
|
-
import {
|
|
182
|
+
import { biguintFromUntaggedCbor, bigintFromNegativeUntaggedCbor } from "./bignum";
|
|
156
183
|
|
|
157
184
|
// Tag constants matching Rust
|
|
158
185
|
export const TAG_DATE = 1;
|
|
@@ -169,13 +196,48 @@ export const registerTagsIn = (tagsStore: TagsStore): void => {
|
|
|
169
196
|
tagsStore.insertAll(tags);
|
|
170
197
|
|
|
171
198
|
// Set summarizer for date tag
|
|
172
|
-
tagsStore.setSummarizer(TAG_DATE, (untaggedCbor: Cbor, _flat: boolean):
|
|
199
|
+
tagsStore.setSummarizer(TAG_DATE, (untaggedCbor: Cbor, _flat: boolean): SummarizerResult => {
|
|
173
200
|
try {
|
|
174
|
-
return CborDate.fromUntaggedCbor(untaggedCbor).toString();
|
|
175
|
-
} catch {
|
|
176
|
-
return
|
|
201
|
+
return { ok: true, value: CborDate.fromUntaggedCbor(untaggedCbor).toString() };
|
|
202
|
+
} catch (e) {
|
|
203
|
+
// On error, return error result matching Rust's Result::Err
|
|
204
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
205
|
+
return { ok: false, error: { type: "Custom", message } };
|
|
177
206
|
}
|
|
178
207
|
});
|
|
208
|
+
|
|
209
|
+
// Register bignum tags (matching Rust's #[cfg(feature = "num-bigint")] block)
|
|
210
|
+
const biguintTag = createTag(TAG_POSITIVE_BIGNUM, TAG_NAME_POSITIVE_BIGNUM);
|
|
211
|
+
const bigintTag = createTag(TAG_NEGATIVE_BIGNUM, TAG_NAME_NEGATIVE_BIGNUM);
|
|
212
|
+
tagsStore.insertAll([biguintTag, bigintTag]);
|
|
213
|
+
|
|
214
|
+
// Summarizer for tag 2 (positive bignum)
|
|
215
|
+
tagsStore.setSummarizer(
|
|
216
|
+
TAG_POSITIVE_BIGNUM,
|
|
217
|
+
(untaggedCbor: Cbor, _flat: boolean): SummarizerResult => {
|
|
218
|
+
try {
|
|
219
|
+
const value = biguintFromUntaggedCbor(untaggedCbor);
|
|
220
|
+
return { ok: true, value: `bignum(${value})` };
|
|
221
|
+
} catch (e) {
|
|
222
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
223
|
+
return { ok: false, error: { type: "Custom", message } };
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// Summarizer for tag 3 (negative bignum)
|
|
229
|
+
tagsStore.setSummarizer(
|
|
230
|
+
TAG_NEGATIVE_BIGNUM,
|
|
231
|
+
(untaggedCbor: Cbor, _flat: boolean): SummarizerResult => {
|
|
232
|
+
try {
|
|
233
|
+
const value = bigintFromNegativeUntaggedCbor(untaggedCbor);
|
|
234
|
+
return { ok: true, value: `bignum(${value})` };
|
|
235
|
+
} catch (e) {
|
|
236
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
237
|
+
return { ok: false, error: { type: "Custom", message } };
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
);
|
|
179
241
|
};
|
|
180
242
|
|
|
181
243
|
/**
|
package/src/varint.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright © 2023-2026 Blockchain Commons, LLC
|
|
3
|
+
* Copyright © 2025-2026 Parity Technologies
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import { type CborNumber, isCborNumber, type MajorType } from "./cbor";
|
|
2
8
|
import { hasFractionalPart } from "./float";
|
|
3
9
|
import { CborError } from "./error";
|
|
@@ -47,21 +53,20 @@ export const encodeVarInt = (value: CborNumber, majorType: MajorType): Uint8Arra
|
|
|
47
53
|
return new Uint8Array(buffer);
|
|
48
54
|
}
|
|
49
55
|
} else {
|
|
50
|
-
value
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
// Bigint branch — value is strictly greater than `Number.MAX_SAFE_INTEGER`,
|
|
57
|
+
// therefore strictly greater than `0xffffffff`. The CBOR encoding rule
|
|
58
|
+
// collapses to: 9 bytes total (header `0x1b | type` + 8 big-endian bytes)
|
|
59
|
+
// if the value fits in u64, otherwise `OutOfRange`. We must NOT use
|
|
60
|
+
// `Math.log2(Number(value))` here — `Number(value)` is lossy past 2^53
|
|
61
|
+
// and would mis-pick the encoding length for values near u64::MAX.
|
|
62
|
+
const big = BigInt(value);
|
|
63
|
+
if (big > 0xffffffffffffffffn) {
|
|
53
64
|
throw new CborError({ type: "OutOfRange" });
|
|
54
65
|
}
|
|
55
|
-
const
|
|
56
|
-
const buffer = new ArrayBuffer(length);
|
|
66
|
+
const buffer = new ArrayBuffer(9);
|
|
57
67
|
const view = new DataView(buffer);
|
|
58
|
-
let i = length - 1;
|
|
59
|
-
while (value > 0) {
|
|
60
|
-
view.setUint8(i, Number(value & 0xffn));
|
|
61
|
-
value >>= 8n;
|
|
62
|
-
i--;
|
|
63
|
-
}
|
|
64
68
|
view.setUint8(0, 0x1b | type);
|
|
69
|
+
view.setBigUint64(1, big);
|
|
65
70
|
return new Uint8Array(buffer);
|
|
66
71
|
}
|
|
67
72
|
};
|