@bcts/dcbor 1.0.0-alpha.5

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.
Files changed (45) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +13 -0
  3. package/dist/index.cjs +9151 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +3107 -0
  6. package/dist/index.d.cts.map +1 -0
  7. package/dist/index.d.mts +3107 -0
  8. package/dist/index.d.mts.map +1 -0
  9. package/dist/index.iife.js +9155 -0
  10. package/dist/index.iife.js.map +1 -0
  11. package/dist/index.mjs +9027 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/package.json +80 -0
  14. package/src/.claude-flow/metrics/agent-metrics.json +1 -0
  15. package/src/.claude-flow/metrics/performance.json +87 -0
  16. package/src/.claude-flow/metrics/task-metrics.json +10 -0
  17. package/src/byte-string.ts +300 -0
  18. package/src/cbor-codable.ts +170 -0
  19. package/src/cbor-tagged-codable.ts +72 -0
  20. package/src/cbor-tagged-decodable.ts +184 -0
  21. package/src/cbor-tagged-encodable.ts +138 -0
  22. package/src/cbor-tagged.ts +104 -0
  23. package/src/cbor.ts +869 -0
  24. package/src/conveniences.ts +840 -0
  25. package/src/date.ts +553 -0
  26. package/src/decode.ts +276 -0
  27. package/src/diag.ts +462 -0
  28. package/src/dump.ts +277 -0
  29. package/src/error.ts +259 -0
  30. package/src/exact.ts +714 -0
  31. package/src/float.ts +279 -0
  32. package/src/global.d.ts +34 -0
  33. package/src/globals.d.ts +0 -0
  34. package/src/index.ts +180 -0
  35. package/src/map.ts +308 -0
  36. package/src/prelude.ts +70 -0
  37. package/src/set.ts +515 -0
  38. package/src/simple.ts +153 -0
  39. package/src/stdlib.ts +55 -0
  40. package/src/string-util.ts +55 -0
  41. package/src/tag.ts +53 -0
  42. package/src/tags-store.ts +294 -0
  43. package/src/tags.ts +231 -0
  44. package/src/varint.ts +124 -0
  45. package/src/walk.ts +516 -0
package/src/cbor.ts ADDED
@@ -0,0 +1,869 @@
1
+ import { CborMap } from "./map";
2
+ import type { Simple } from "./simple";
3
+ import { simpleCborData, isFloat as isSimpleFloat } from "./simple";
4
+ import { hasFractionalPart } from "./float";
5
+ import { encodeVarInt } from "./varint";
6
+ import { concatBytes } from "./stdlib";
7
+ import { bytesToHex, hexOpt } from "./dump";
8
+ import { hexToBytes } from "./dump";
9
+ import type { Tag } from "./tag";
10
+ import type { ByteString } from "./byte-string";
11
+ import type { CborDate } from "./date";
12
+ import { diagnosticOpt } from "./diag";
13
+ import { decodeCbor } from "./decode";
14
+ import type { TagsStore } from "./tags-store";
15
+ import { getGlobalTagsStore } from "./tags-store";
16
+ import type { Visitor } from "./walk";
17
+ import { walk } from "./walk";
18
+ import { CborError } from "./error";
19
+
20
+ export type { Simple };
21
+
22
+ export const MajorType = {
23
+ Unsigned: 0,
24
+ Negative: 1,
25
+ ByteString: 2,
26
+ Text: 3,
27
+ Array: 4,
28
+ Map: 5,
29
+ Tagged: 6,
30
+ Simple: 7,
31
+ } as const;
32
+
33
+ // eslint-disable-next-line no-redeclare -- Intentionally using same name for value and type
34
+ export type MajorType = (typeof MajorType)[keyof typeof MajorType];
35
+
36
+ // Helper to get MajorType name from value (replaces enum reverse mapping)
37
+ const MajorTypeNames: Record<MajorType, string> = {
38
+ [MajorType.Unsigned]: "Unsigned",
39
+ [MajorType.Negative]: "Negative",
40
+ [MajorType.ByteString]: "ByteString",
41
+ [MajorType.Text]: "Text",
42
+ [MajorType.Array]: "Array",
43
+ [MajorType.Map]: "Map",
44
+ [MajorType.Tagged]: "Tagged",
45
+ [MajorType.Simple]: "Simple",
46
+ };
47
+
48
+ const getMajorTypeName = (type: MajorType): string => MajorTypeNames[type];
49
+
50
+ /**
51
+ * Numeric type that can be encoded in CBOR.
52
+ *
53
+ * Supports both standard JavaScript numbers and BigInt for large integers.
54
+ * Numbers are automatically encoded as either unsigned or negative integers
55
+ * depending on their value, following dCBOR canonical encoding rules.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const smallNum: CborNumber = 42;
60
+ * const largeNum: CborNumber = 9007199254740992n;
61
+ * ```
62
+ */
63
+ export type CborNumber = number | bigint;
64
+
65
+ /**
66
+ * Type for values that can be converted to CBOR.
67
+ *
68
+ * This is a comprehensive union type representing all values that can be encoded
69
+ * as CBOR using the `cbor()` function. It includes:
70
+ * - Already-encoded CBOR values (`Cbor`)
71
+ * - Primitive types: numbers, bigints, strings, booleans, null, undefined
72
+ * - Binary data: `Uint8Array`, `ByteString`
73
+ * - Dates: `CborDate`
74
+ * - Collections: `CborMap`, arrays, JavaScript `Map`, JavaScript `Set`
75
+ * - Objects: Plain objects are converted to CBOR maps
76
+ *
77
+ * Matches Rust's `From<T>` trait implementations for CBOR.
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * cbor(42); // number
82
+ * cbor("hello"); // string
83
+ * cbor([1, 2, 3]); // array
84
+ * cbor(new Map([["key", "value"]])); // Map
85
+ * cbor({ name: "Alice", age: 30 }); // plain object -> CborMap
86
+ * ```
87
+ */
88
+ export type CborInput =
89
+ | Cbor
90
+ | CborNumber
91
+ | string
92
+ | boolean
93
+ | null
94
+ | undefined
95
+ | Uint8Array
96
+ | ByteString
97
+ | CborDate
98
+ | CborMap
99
+ | CborInput[]
100
+ | Map<unknown, unknown>
101
+ | Set<unknown>
102
+ | Record<string, unknown>;
103
+
104
+ export const isCborNumber = (value: unknown): value is CborNumber => {
105
+ return typeof value === "number" || typeof value === "bigint";
106
+ };
107
+
108
+ export const isCbor = (value: unknown): value is Cbor => {
109
+ return value !== null && typeof value === "object" && "isCbor" in value && value.isCbor === true;
110
+ };
111
+
112
+ export interface CborUnsignedType {
113
+ readonly isCbor: true;
114
+ readonly type: typeof MajorType.Unsigned;
115
+ readonly value: CborNumber;
116
+ }
117
+ export interface CborNegativeType {
118
+ readonly isCbor: true;
119
+ readonly type: typeof MajorType.Negative;
120
+ readonly value: CborNumber;
121
+ }
122
+ export interface CborByteStringType {
123
+ readonly isCbor: true;
124
+ readonly type: typeof MajorType.ByteString;
125
+ readonly value: Uint8Array;
126
+ }
127
+ export interface CborTextType {
128
+ readonly isCbor: true;
129
+ readonly type: typeof MajorType.Text;
130
+ readonly value: string;
131
+ }
132
+ export interface CborArrayType {
133
+ readonly isCbor: true;
134
+ readonly type: typeof MajorType.Array;
135
+ readonly value: readonly Cbor[];
136
+ }
137
+ export interface CborMapType {
138
+ readonly isCbor: true;
139
+ readonly type: typeof MajorType.Map;
140
+ readonly value: CborMap;
141
+ }
142
+ export interface CborTaggedType {
143
+ readonly isCbor: true;
144
+ readonly type: typeof MajorType.Tagged;
145
+ readonly tag: CborNumber;
146
+ readonly value: Cbor;
147
+ }
148
+ export interface CborSimpleType {
149
+ readonly isCbor: true;
150
+ readonly type: typeof MajorType.Simple;
151
+ readonly value: Simple;
152
+ }
153
+
154
+ // Instance methods interface
155
+ export interface CborMethods {
156
+ // Universal encoding/formatting
157
+ toData(): Uint8Array;
158
+ toHex(): string;
159
+ toHexAnnotated(tagsStore?: TagsStore): string;
160
+ toString(): string;
161
+ toDebugString(): string;
162
+ toDiagnostic(): string;
163
+ toDiagnosticAnnotated(): string;
164
+
165
+ // Type checking
166
+ isByteString(): boolean;
167
+ isText(): boolean;
168
+ isArray(): boolean;
169
+ isMap(): boolean;
170
+ isTagged(): boolean;
171
+ isSimple(): boolean;
172
+ isBool(): boolean;
173
+ isTrue(): boolean;
174
+ isFalse(): boolean;
175
+ isNull(): boolean;
176
+ isNumber(): boolean;
177
+ isInteger(): boolean;
178
+ isUnsigned(): boolean;
179
+ isNegative(): boolean;
180
+ isNaN(): boolean;
181
+ isFloat(): boolean;
182
+
183
+ // Safe conversion (returns undefined on mismatch)
184
+ asByteString(): Uint8Array | undefined;
185
+ asText(): string | undefined;
186
+ asArray(): readonly Cbor[] | undefined;
187
+ asMap(): CborMap | undefined;
188
+ asTagged(): [Tag, Cbor] | undefined;
189
+ asBool(): boolean | undefined;
190
+ asInteger(): (number | bigint) | undefined;
191
+ asNumber(): (number | bigint) | undefined;
192
+ asSimpleValue(): Simple | undefined;
193
+
194
+ // Throwing conversion (throws on mismatch)
195
+ /**
196
+ * Convert to byte string, throwing if type doesn't match.
197
+ * @throws {TypeError} If value is not a byte string type
198
+ */
199
+ toByteString(): Uint8Array;
200
+ /**
201
+ * Convert to text string, throwing if type doesn't match.
202
+ * @throws {TypeError} If value is not a text string type
203
+ */
204
+ toText(): string;
205
+ /**
206
+ * Convert to array, throwing if type doesn't match.
207
+ * @throws {TypeError} If value is not an array type
208
+ */
209
+ toArray(): readonly Cbor[];
210
+ /**
211
+ * Convert to map, throwing if type doesn't match.
212
+ * @throws {TypeError} If value is not a map type
213
+ */
214
+ toMap(): CborMap;
215
+ /**
216
+ * Convert to tagged value, throwing if type doesn't match.
217
+ * @throws {TypeError} If value is not a tagged type
218
+ */
219
+ toTagged(): [Tag, Cbor];
220
+ /**
221
+ * Convert to boolean, throwing if type doesn't match.
222
+ * @throws {TypeError} If value is not a boolean (True/False) type
223
+ */
224
+ toBool(): boolean;
225
+ /**
226
+ * Convert to integer, throwing if type doesn't match.
227
+ * @throws {TypeError} If value is not an integer (Unsigned or Negative) type
228
+ */
229
+ toInteger(): number | bigint;
230
+ /**
231
+ * Convert to number, throwing if type doesn't match.
232
+ * @throws {TypeError} If value is not a numeric (Unsigned, Negative, or Float) type
233
+ */
234
+ toNumber(): number | bigint;
235
+ /**
236
+ * Convert to simple value, throwing if type doesn't match.
237
+ * @throws {TypeError} If value is not a simple type
238
+ */
239
+ toSimpleValue(): Simple;
240
+ /**
241
+ * Expect specific tag and return content, throwing if tag doesn't match.
242
+ * @param tag - Expected tag value
243
+ * @throws {CborError} With type 'WrongType' if value is not tagged, or 'Custom' if tag doesn't match
244
+ */
245
+ expectTag(tag: CborNumber | Tag): Cbor;
246
+
247
+ // Advanced operations
248
+ /**
249
+ * Walk the CBOR structure with a visitor function.
250
+ * @param initialState - Initial state for the visitor
251
+ * @param visitor - Visitor function called for each element
252
+ */
253
+ walk<State>(initialState: State, visitor: Visitor<State>): State;
254
+ /**
255
+ * Validate that value has one of the expected tags.
256
+ * @param expectedTags - Array of expected tag values
257
+ * @throws {CborError} With type 'WrongType' if value is not tagged, or 'Custom' if tag doesn't match any expected value
258
+ */
259
+ validateTag(expectedTags: Tag[]): Tag;
260
+ /**
261
+ * Remove one level of tagging, returning the inner content.
262
+ */
263
+ untagged(): Cbor;
264
+ }
265
+
266
+ export type Cbor = (
267
+ | CborUnsignedType
268
+ | CborNegativeType
269
+ | CborByteStringType
270
+ | CborTextType
271
+ | CborArrayType
272
+ | CborMapType
273
+ | CborTaggedType
274
+ | CborSimpleType
275
+ ) &
276
+ CborMethods;
277
+
278
+ // ============================================================================
279
+ // Encoding Functions (matches Rust CBOR conversion logic)
280
+ // ============================================================================
281
+
282
+ export interface ToCbor {
283
+ toCbor(): Cbor;
284
+ }
285
+
286
+ export interface TaggedCborEncodable {
287
+ taggedCbor(): Cbor;
288
+ }
289
+
290
+ /**
291
+ * Type guard to check if value has taggedCbor method.
292
+ */
293
+ const hasTaggedCbor = (value: unknown): value is TaggedCborEncodable => {
294
+ return (
295
+ typeof value === "object" &&
296
+ value !== null &&
297
+ "taggedCbor" in value &&
298
+ typeof (value as TaggedCborEncodable).taggedCbor === "function"
299
+ );
300
+ };
301
+
302
+ /**
303
+ * Type guard to check if value has toCbor method.
304
+ */
305
+ const hasToCbor = (value: unknown): value is ToCbor => {
306
+ return (
307
+ typeof value === "object" &&
308
+ value !== null &&
309
+ "toCbor" in value &&
310
+ typeof (value as ToCbor).toCbor === "function"
311
+ );
312
+ };
313
+
314
+ /**
315
+ * Convert any value to a CBOR representation.
316
+ * Matches Rust's `From` trait implementations for CBOR.
317
+ */
318
+ export const cbor = (value: CborInput): Cbor => {
319
+ // If already CBOR and has methods, return as-is
320
+ if (isCbor(value) && "toData" in value) {
321
+ return value;
322
+ }
323
+
324
+ // If CBOR but no methods, attach them
325
+ if (isCbor(value)) {
326
+ return attachMethods(value as Omit<Cbor, keyof CborMethods>) as Cbor;
327
+ }
328
+
329
+ let result: Omit<Cbor, keyof CborMethods>;
330
+
331
+ if (isCborNumber(value)) {
332
+ if (typeof value === "number" && Number.isNaN(value)) {
333
+ result = { isCbor: true, type: MajorType.Simple, value: { type: "Float", value: NaN } };
334
+ } else if (typeof value === "number" && hasFractionalPart(value)) {
335
+ result = { isCbor: true, type: MajorType.Simple, value: { type: "Float", value: value } };
336
+ } else if (value == Infinity) {
337
+ result = { isCbor: true, type: MajorType.Simple, value: { type: "Float", value: Infinity } };
338
+ } else if (value == -Infinity) {
339
+ result = { isCbor: true, type: MajorType.Simple, value: { type: "Float", value: -Infinity } };
340
+ } else if (value < 0) {
341
+ // Store the magnitude to encode, matching Rust's representation
342
+ // For a negative value n, CBOR encodes it as -1-n, so we store -n-1
343
+ if (typeof value === "bigint") {
344
+ result = { isCbor: true, type: MajorType.Negative, value: -value - 1n };
345
+ } else {
346
+ result = { isCbor: true, type: MajorType.Negative, value: -value - 1 };
347
+ }
348
+ } else {
349
+ result = { isCbor: true, type: MajorType.Unsigned, value: value };
350
+ }
351
+ } else if (typeof value === "string") {
352
+ // dCBOR requires all text strings to be in Unicode Normalization Form C (NFC)
353
+ // This ensures deterministic encoding regardless of how the string was composed
354
+ const normalized = value.normalize("NFC");
355
+ result = { isCbor: true, type: MajorType.Text, value: normalized };
356
+ } else if (value === null || value === undefined) {
357
+ result = { isCbor: true, type: MajorType.Simple, value: { type: "Null" } };
358
+ } else if (value === true) {
359
+ result = { isCbor: true, type: MajorType.Simple, value: { type: "True" } };
360
+ } else if (value === false) {
361
+ result = { isCbor: true, type: MajorType.Simple, value: { type: "False" } };
362
+ } else if (Array.isArray(value)) {
363
+ result = { isCbor: true, type: MajorType.Array, value: value.map(cbor) };
364
+ } else if (value instanceof Uint8Array) {
365
+ result = { isCbor: true, type: MajorType.ByteString, value: value };
366
+ } else if (value instanceof CborMap) {
367
+ result = { isCbor: true, type: MajorType.Map, value: value };
368
+ } else if (value instanceof Map) {
369
+ result = { isCbor: true, type: MajorType.Map, value: new CborMap(value) };
370
+ } else if (value instanceof Set) {
371
+ result = {
372
+ isCbor: true,
373
+ type: MajorType.Array,
374
+ value: Array.from(value).map((v) => cbor(v as CborInput)),
375
+ };
376
+ } else if (hasTaggedCbor(value)) {
377
+ return value.taggedCbor();
378
+ } else if (hasToCbor(value)) {
379
+ return value.toCbor();
380
+ } else if (typeof value === "object" && value !== null && "tag" in value && "value" in value) {
381
+ // Handle plain tagged value format: { tag: number, value: unknown }
382
+ const keys = Object.keys(value);
383
+ const objValue = value as { tag: unknown; value: unknown; [key: string]: unknown };
384
+ if (keys.length === 2 && keys.includes("tag") && keys.includes("value")) {
385
+ return taggedCbor(objValue.tag, objValue.value as CborInput);
386
+ }
387
+ // Not a tagged value, fall through to map handling
388
+ const map = new CborMap();
389
+ for (const [key, val] of Object.entries(value)) {
390
+ map.set(cbor(key as CborInput), cbor(val as CborInput));
391
+ }
392
+ result = { isCbor: true, type: MajorType.Map, value: map };
393
+ } else if (typeof value === "object" && value !== null) {
394
+ // Handle plain objects by converting to CborMap
395
+ const map = new CborMap();
396
+ for (const [key, val] of Object.entries(value)) {
397
+ map.set(cbor(key as CborInput), cbor(val as CborInput));
398
+ }
399
+ result = { isCbor: true, type: MajorType.Map, value: map };
400
+ } else {
401
+ throw new CborError({ type: "Custom", message: "Unsupported type for CBOR encoding" });
402
+ }
403
+
404
+ return attachMethods(result) as Cbor;
405
+ };
406
+
407
+ export const cborHex = (value: CborInput): string => {
408
+ return bytesToHex(cborData(value));
409
+ };
410
+
411
+ /**
412
+ * Encode a CBOR value to binary data.
413
+ * Matches Rust's `CBOR::to_cbor_data()` method.
414
+ */
415
+ export const cborData = (value: CborInput): Uint8Array => {
416
+ const c = cbor(value);
417
+ switch (c.type) {
418
+ case MajorType.Unsigned: {
419
+ return encodeVarInt(c.value, MajorType.Unsigned);
420
+ }
421
+ case MajorType.Negative: {
422
+ // Value is already stored as the magnitude to encode (matching Rust)
423
+ return encodeVarInt(c.value, MajorType.Negative);
424
+ }
425
+ case MajorType.ByteString: {
426
+ if (c.value instanceof Uint8Array) {
427
+ const lengthBytes = encodeVarInt(c.value.length, MajorType.ByteString);
428
+ return new Uint8Array([...lengthBytes, ...c.value]);
429
+ }
430
+ break;
431
+ }
432
+ case MajorType.Text: {
433
+ if (typeof c.value === "string") {
434
+ const utf8Bytes = new TextEncoder().encode(c.value);
435
+ const lengthBytes = encodeVarInt(utf8Bytes.length, MajorType.Text);
436
+ return new Uint8Array([...lengthBytes, ...utf8Bytes]);
437
+ }
438
+ break;
439
+ }
440
+ case MajorType.Tagged: {
441
+ if (typeof c.tag === "bigint" || typeof c.tag === "number") {
442
+ const tagBytes = encodeVarInt(c.tag, MajorType.Tagged);
443
+ const valueBytes = cborData(c.value);
444
+ return new Uint8Array([...tagBytes, ...valueBytes]);
445
+ }
446
+ break;
447
+ }
448
+ case MajorType.Simple: {
449
+ // Use the simpleCborData function from simple.ts
450
+ return simpleCborData(c.value);
451
+ }
452
+ case MajorType.Array: {
453
+ const arrayBytes = c.value.map(cborData);
454
+ const flatArrayBytes = concatBytes(arrayBytes);
455
+ const lengthBytes = encodeVarInt(c.value.length, MajorType.Array);
456
+ return new Uint8Array([...lengthBytes, ...flatArrayBytes]);
457
+ }
458
+ case MajorType.Map: {
459
+ const entries = c.value.entriesArray;
460
+ const arrayBytes = entries.map(({ key, value }) =>
461
+ concatBytes([cborData(key), cborData(value)]),
462
+ );
463
+ const flatArrayBytes = concatBytes(arrayBytes);
464
+ const lengthBytes = encodeVarInt(entries.length, MajorType.Map);
465
+ return new Uint8Array([...lengthBytes, ...flatArrayBytes]);
466
+ }
467
+ }
468
+ throw new CborError({ type: "WrongType" });
469
+ };
470
+
471
+ export const encodeCbor = (value: CborInput): Uint8Array => {
472
+ return cborData(cbor(value));
473
+ };
474
+
475
+ export const taggedCbor = (tag: unknown, value: CborInput): Cbor => {
476
+ // Validate and convert tag to CborNumber
477
+ const tagNumber: CborNumber =
478
+ typeof tag === "number" || typeof tag === "bigint" ? tag : Number(tag);
479
+ return attachMethods({
480
+ isCbor: true,
481
+ type: MajorType.Tagged,
482
+ tag: tagNumber,
483
+ value: cbor(value),
484
+ });
485
+ };
486
+
487
+ // ============================================================================
488
+ // Static Factory Functions
489
+ // (Keep only essential creation functions)
490
+ // ============================================================================
491
+
492
+ export const toByteString = (data: Uint8Array): Cbor => {
493
+ return cbor(data);
494
+ };
495
+
496
+ export const toByteStringFromHex = (hex: string): Cbor => {
497
+ return toByteString(hexToBytes(hex));
498
+ };
499
+
500
+ export const toTaggedValue = (tag: CborNumber | Tag, item: CborInput): Cbor => {
501
+ const tagValue = typeof tag === "object" && "value" in tag ? tag.value : tag;
502
+ return attachMethods({
503
+ isCbor: true,
504
+ type: MajorType.Tagged,
505
+ tag: tagValue,
506
+ value: cbor(item),
507
+ });
508
+ };
509
+
510
+ export const cborFalse = (): Cbor => {
511
+ return attachMethods({ isCbor: true, type: MajorType.Simple, value: { type: "False" } });
512
+ };
513
+
514
+ export const cborTrue = (): Cbor => {
515
+ return attachMethods({ isCbor: true, type: MajorType.Simple, value: { type: "True" } });
516
+ };
517
+
518
+ export const cborNull = (): Cbor => {
519
+ return attachMethods({ isCbor: true, type: MajorType.Simple, value: { type: "Null" } });
520
+ };
521
+
522
+ export const cborNaN = (): Cbor => {
523
+ return attachMethods({
524
+ isCbor: true,
525
+ type: MajorType.Simple,
526
+ value: { type: "Float", value: NaN },
527
+ });
528
+ };
529
+
530
+ // ============================================================================
531
+ // Method Attachment System
532
+ // ============================================================================
533
+
534
+ /**
535
+ * Attaches instance methods to a CBOR value.
536
+ * This enables method chaining like cbor.toHex() instead of Cbor.toHex(cbor).
537
+ * @internal
538
+ */
539
+ export const attachMethods = <T extends Omit<Cbor, keyof CborMethods>>(obj: T): T & CborMethods => {
540
+ return Object.assign(obj, {
541
+ // Universal encoding/formatting
542
+ toData(this: Cbor): Uint8Array {
543
+ return cborData(this);
544
+ },
545
+ toHex(this: Cbor): string {
546
+ return bytesToHex(cborData(this));
547
+ },
548
+ toHexAnnotated(this: Cbor, tagsStore?: TagsStore): string {
549
+ tagsStore = tagsStore ?? getGlobalTagsStore();
550
+ return hexOpt(this, { annotate: true, tagsStore });
551
+ },
552
+ toString(this: Cbor): string {
553
+ return diagnosticOpt(this, { flat: true });
554
+ },
555
+ toDebugString(this: Cbor): string {
556
+ return diagnosticOpt(this, { flat: false });
557
+ },
558
+ toDiagnostic(this: Cbor): string {
559
+ return diagnosticOpt(this, { flat: false });
560
+ },
561
+ toDiagnosticAnnotated(this: Cbor): string {
562
+ return diagnosticOpt(this, { annotate: true });
563
+ },
564
+
565
+ // Type checking
566
+ isByteString(this: Cbor): boolean {
567
+ return this.type === MajorType.ByteString;
568
+ },
569
+ isText(this: Cbor): boolean {
570
+ return this.type === MajorType.Text;
571
+ },
572
+ isArray(this: Cbor): boolean {
573
+ return this.type === MajorType.Array;
574
+ },
575
+ isMap(this: Cbor): boolean {
576
+ return this.type === MajorType.Map;
577
+ },
578
+ isTagged(this: Cbor): boolean {
579
+ return this.type === MajorType.Tagged;
580
+ },
581
+ isSimple(this: Cbor): boolean {
582
+ return this.type === MajorType.Simple;
583
+ },
584
+ isBool(this: Cbor): boolean {
585
+ return (
586
+ this.type === MajorType.Simple &&
587
+ (this.value.type === "True" || this.value.type === "False")
588
+ );
589
+ },
590
+ isTrue(this: Cbor): boolean {
591
+ return this.type === MajorType.Simple && this.value.type === "True";
592
+ },
593
+ isFalse(this: Cbor): boolean {
594
+ return this.type === MajorType.Simple && this.value.type === "False";
595
+ },
596
+ isNull(this: Cbor): boolean {
597
+ return this.type === MajorType.Simple && this.value.type === "Null";
598
+ },
599
+ isNumber(this: Cbor): boolean {
600
+ if (this.type === MajorType.Unsigned || this.type === MajorType.Negative) {
601
+ return true;
602
+ }
603
+ if (this.type === MajorType.Simple) {
604
+ return isSimpleFloat(this.value);
605
+ }
606
+ return false;
607
+ },
608
+ isInteger(this: Cbor): boolean {
609
+ return this.type === MajorType.Unsigned || this.type === MajorType.Negative;
610
+ },
611
+ isUnsigned(this: Cbor): boolean {
612
+ return this.type === MajorType.Unsigned;
613
+ },
614
+ isNegative(this: Cbor): boolean {
615
+ return this.type === MajorType.Negative;
616
+ },
617
+ isNaN(this: Cbor): boolean {
618
+ return (
619
+ this.type === MajorType.Simple &&
620
+ this.value.type === "Float" &&
621
+ Number.isNaN(this.value.value)
622
+ );
623
+ },
624
+ isFloat(this: Cbor): boolean {
625
+ return this.type === MajorType.Simple && isSimpleFloat(this.value);
626
+ },
627
+
628
+ // Safe conversion (returns undefined on mismatch)
629
+ asByteString(this: Cbor): Uint8Array | undefined {
630
+ return this.type === MajorType.ByteString ? this.value : undefined;
631
+ },
632
+ asText(this: Cbor): string | undefined {
633
+ return this.type === MajorType.Text ? this.value : undefined;
634
+ },
635
+ asArray(this: Cbor): readonly Cbor[] | undefined {
636
+ return this.type === MajorType.Array ? this.value : undefined;
637
+ },
638
+ asMap(this: Cbor): CborMap | undefined {
639
+ return this.type === MajorType.Map ? this.value : undefined;
640
+ },
641
+ asTagged(this: Cbor): [Tag, Cbor] | undefined {
642
+ if (this.type !== MajorType.Tagged) {
643
+ return undefined;
644
+ }
645
+ const tag: Tag = { value: this.tag, name: `tag-${this.tag}` };
646
+ return [tag, this.value];
647
+ },
648
+ asBool(this: Cbor): boolean | undefined {
649
+ if (this.type !== MajorType.Simple) return undefined;
650
+ if (this.value.type === "True") return true;
651
+ if (this.value.type === "False") return false;
652
+ return undefined;
653
+ },
654
+ asInteger(this: Cbor): (number | bigint) | undefined {
655
+ if (this.type === MajorType.Unsigned) {
656
+ return this.value;
657
+ } else if (this.type === MajorType.Negative) {
658
+ if (typeof this.value === "bigint") {
659
+ return -this.value - 1n;
660
+ } else {
661
+ return -this.value - 1;
662
+ }
663
+ }
664
+ return undefined;
665
+ },
666
+ asNumber(this: Cbor): (number | bigint) | undefined {
667
+ if (this.type === MajorType.Unsigned) {
668
+ return this.value;
669
+ } else if (this.type === MajorType.Negative) {
670
+ if (typeof this.value === "bigint") {
671
+ return -this.value - 1n;
672
+ } else {
673
+ return -this.value - 1;
674
+ }
675
+ } else if (this.type === MajorType.Simple && isSimpleFloat(this.value)) {
676
+ return this.value.value;
677
+ }
678
+ return undefined;
679
+ },
680
+ asSimpleValue(this: Cbor): Simple | undefined {
681
+ return this.type === MajorType.Simple ? this.value : undefined;
682
+ },
683
+
684
+ // Throwing conversion (throws on mismatch)
685
+ toByteString(this: Cbor): Uint8Array {
686
+ if (this.type !== MajorType.ByteString) {
687
+ throw new TypeError(
688
+ `Cannot convert CBOR to ByteString: expected ByteString type, got ${getMajorTypeName(this.type)}`,
689
+ );
690
+ }
691
+ return this.value;
692
+ },
693
+ toText(this: Cbor): string {
694
+ if (this.type !== MajorType.Text) {
695
+ throw new TypeError(
696
+ `Cannot convert CBOR to Text: expected Text type, got ${getMajorTypeName(this.type)}`,
697
+ );
698
+ }
699
+ return this.value;
700
+ },
701
+ toArray(this: Cbor): readonly Cbor[] {
702
+ if (this.type !== MajorType.Array) {
703
+ throw new TypeError(
704
+ `Cannot convert CBOR to Array: expected Array type, got ${getMajorTypeName(this.type)}`,
705
+ );
706
+ }
707
+ return this.value;
708
+ },
709
+ toMap(this: Cbor): CborMap {
710
+ if (this.type !== MajorType.Map) {
711
+ throw new TypeError(
712
+ `Cannot convert CBOR to Map: expected Map type, got ${getMajorTypeName(this.type)}`,
713
+ );
714
+ }
715
+ return this.value;
716
+ },
717
+ toTagged(this: Cbor): [Tag, Cbor] {
718
+ if (this.type !== MajorType.Tagged) {
719
+ throw new TypeError(
720
+ `Cannot convert CBOR to Tagged: expected Tagged type, got ${getMajorTypeName(this.type)}`,
721
+ );
722
+ }
723
+ const tag: Tag = { value: this.tag, name: `tag-${this.tag}` };
724
+ return [tag, this.value];
725
+ },
726
+ toBool(this: Cbor): boolean {
727
+ const result = this.asBool();
728
+ if (result === undefined) {
729
+ throw new TypeError(
730
+ `Cannot convert CBOR to boolean: expected Simple(True/False) type, got ${getMajorTypeName(this.type)}`,
731
+ );
732
+ }
733
+ return result;
734
+ },
735
+ toInteger(this: Cbor): number | bigint {
736
+ const result = this.asInteger();
737
+ if (result === undefined) {
738
+ throw new TypeError(
739
+ `Cannot convert CBOR to integer: expected Unsigned or Negative type, got ${getMajorTypeName(this.type)}`,
740
+ );
741
+ }
742
+ return result;
743
+ },
744
+ toNumber(this: Cbor): number | bigint {
745
+ const result = this.asNumber();
746
+ if (result === undefined) {
747
+ throw new TypeError(
748
+ `Cannot convert CBOR to number: expected Unsigned, Negative, or Float type, got ${getMajorTypeName(this.type)}`,
749
+ );
750
+ }
751
+ return result;
752
+ },
753
+ toSimpleValue(this: Cbor): Simple {
754
+ if (this.type !== MajorType.Simple) {
755
+ throw new TypeError(
756
+ `Cannot convert CBOR to Simple: expected Simple type, got ${getMajorTypeName(this.type)}`,
757
+ );
758
+ }
759
+ return this.value;
760
+ },
761
+ expectTag(this: Cbor, expectedTag: CborNumber | Tag): Cbor {
762
+ if (this.type !== MajorType.Tagged) {
763
+ throw new CborError({ type: "WrongType" });
764
+ }
765
+ const expectedValue =
766
+ typeof expectedTag === "object" && "value" in expectedTag ? expectedTag.value : expectedTag;
767
+ if (this.tag !== expectedValue) {
768
+ throw new CborError({
769
+ type: "Custom",
770
+ message: `Wrong tag: expected ${expectedValue}, got ${this.tag}`,
771
+ });
772
+ }
773
+ return this.value;
774
+ },
775
+
776
+ // Advanced operations
777
+ walk<State>(this: Cbor, initialState: State, visitor: Visitor<State>): State {
778
+ return walk(this, initialState, visitor);
779
+ },
780
+ validateTag(this: Cbor, expectedTags: Tag[]): Tag {
781
+ if (this.type !== MajorType.Tagged) {
782
+ throw new CborError({ type: "WrongType" });
783
+ }
784
+ const expectedValues = expectedTags.map((t) => t.value);
785
+ const tagValue = this.tag;
786
+ const matchingTag = expectedTags.find((t) => t.value === tagValue);
787
+ if (matchingTag === undefined) {
788
+ const expectedStr = expectedValues.join(" or ");
789
+ throw new CborError({
790
+ type: "Custom",
791
+ message: `Wrong tag: expected ${expectedStr}, got ${tagValue}`,
792
+ });
793
+ }
794
+ return matchingTag;
795
+ },
796
+ untagged(this: Cbor): Cbor {
797
+ if (this.type !== MajorType.Tagged) {
798
+ throw new CborError({ type: "WrongType" });
799
+ }
800
+ return this.value;
801
+ },
802
+ });
803
+ };
804
+
805
+ // ============================================================================
806
+ // Cbor Namespace - Static Constants and Factory Methods
807
+ // ============================================================================
808
+
809
+ /**
810
+ * CBOR constants and helper methods.
811
+ *
812
+ * Provides constants for common simple values (False, True, Null) and static methods
813
+ * matching the Rust CBOR API for encoding/decoding.
814
+ */
815
+ // eslint-disable-next-line no-redeclare
816
+ export const Cbor = {
817
+ // Static CBOR simple values (matching Rust naming) - with methods attached
818
+ False: attachMethods({ isCbor: true, type: MajorType.Simple, value: { type: "False" } }),
819
+ True: attachMethods({ isCbor: true, type: MajorType.Simple, value: { type: "True" } }),
820
+ Null: attachMethods({ isCbor: true, type: MajorType.Simple, value: { type: "Null" } }),
821
+ NaN: attachMethods({
822
+ isCbor: true,
823
+ type: MajorType.Simple,
824
+ value: { type: "Float", value: NaN },
825
+ }),
826
+
827
+ // ============================================================================
828
+ // Static Factory/Decoding Methods (matches Rust CBOR static methods)
829
+ // ============================================================================
830
+
831
+ /**
832
+ * Creates a CBOR value from any JavaScript value.
833
+ *
834
+ * Matches Rust's `CBOR::from()` behavior for various types.
835
+ *
836
+ * @param value - Any JavaScript value (number, string, boolean, null, array, object, etc.)
837
+ * @returns A CBOR symbolic representation with instance methods
838
+ */
839
+ from(value: CborInput): Cbor {
840
+ return cbor(value);
841
+ },
842
+
843
+ /**
844
+ * Decodes binary data into CBOR symbolic representation.
845
+ *
846
+ * Matches Rust's `CBOR::try_from_data()` method.
847
+ *
848
+ * @param data - The binary data to decode
849
+ * @returns A CBOR value with instance methods
850
+ * @throws Error if the data is not valid CBOR or violates dCBOR encoding rules
851
+ */
852
+ tryFromData(data: Uint8Array): Cbor {
853
+ return decodeCbor(data);
854
+ },
855
+
856
+ /**
857
+ * Decodes a hexadecimal string into CBOR symbolic representation.
858
+ *
859
+ * Matches Rust's `CBOR::try_from_hex()` method.
860
+ *
861
+ * @param hex - A string containing hexadecimal characters
862
+ * @returns A CBOR value with instance methods
863
+ * @throws Error if the hex string is invalid or the resulting data is not valid dCBOR
864
+ */
865
+ tryFromHex(hex: string): Cbor {
866
+ const data = hexToBytes(hex);
867
+ return this.tryFromData(data);
868
+ },
869
+ };