@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.
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
@@ -0,0 +1,55 @@
1
+ /**
2
+ * String utilities for dCBOR, including Unicode normalization.
3
+ *
4
+ * @module string-util
5
+ */
6
+
7
+ /**
8
+ * Flank a string with left and right strings.
9
+ *
10
+ * @param s - String to flank
11
+ * @param left - Left flanking string
12
+ * @param right - Right flanking string
13
+ * @returns Flanked string
14
+ */
15
+ export const flanked = (s: string, left: string, right: string): string => left + s + right;
16
+
17
+ /**
18
+ * Check if a character is printable.
19
+ *
20
+ * @param c - Character to check
21
+ * @returns True if printable
22
+ */
23
+ export const isPrintable = (c: string): boolean => {
24
+ if (c.length !== 1) return false;
25
+ const code = c.charCodeAt(0);
26
+ // Non-ASCII or ASCII printable (32-126)
27
+ return code > 127 || (code >= 32 && code <= 126);
28
+ };
29
+
30
+ /**
31
+ * Sanitize a string by replacing non-printable characters with dots.
32
+ * Returns None if the string has no printable characters.
33
+ *
34
+ * @param str - String to sanitize
35
+ * @returns Sanitized string or undefined if no printable characters
36
+ */
37
+ export const sanitized = (str: string): string | undefined => {
38
+ let hasPrintable = false;
39
+ const chars: string[] = [];
40
+
41
+ for (const c of str) {
42
+ if (isPrintable(c)) {
43
+ hasPrintable = true;
44
+ chars.push(c);
45
+ } else {
46
+ chars.push(".");
47
+ }
48
+ }
49
+
50
+ if (!hasPrintable) {
51
+ return undefined;
52
+ }
53
+
54
+ return chars.join("");
55
+ };
package/src/tag.ts ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * CBOR Tag support for semantic tagging of values.
3
+ *
4
+ * Tags provide semantic information about CBOR data items.
5
+ * For example, tag 1 indicates a date/time value.
6
+ *
7
+ * @module tag
8
+ */
9
+
10
+ import type { CborNumber } from "./cbor";
11
+
12
+ /**
13
+ * A CBOR tag with an optional name.
14
+ *
15
+ * Tags consist of a numeric value and an optional human-readable name.
16
+ */
17
+ export interface Tag {
18
+ /** The numeric tag value */
19
+ readonly value: CborNumber;
20
+ /** Optional human-readable name for the tag */
21
+ readonly name?: string;
22
+ }
23
+
24
+ /**
25
+ * Create a new Tag.
26
+ *
27
+ * @param value - The numeric tag value
28
+ * @param name - Optional human-readable name
29
+ * @returns A new Tag object
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const dateTag = createTag(1, 'date');
34
+ * const customTag = createTag(12345, 'myCustomTag');
35
+ * ```
36
+ */
37
+ export const createTag = (value: CborNumber, name?: string): Tag => {
38
+ if (name !== undefined) {
39
+ return { value, name };
40
+ }
41
+ return { value };
42
+ };
43
+
44
+ /**
45
+ * Get the string representation of a tag.
46
+ * Internal function used for error messages.
47
+ *
48
+ * @param tag - The tag to represent
49
+ * @returns String representation (name if available, otherwise value)
50
+ *
51
+ * @internal
52
+ */
53
+ export const tagToString = (tag: Tag): string => tag.name ?? tag.value.toString();
@@ -0,0 +1,294 @@
1
+ /**
2
+ * Tag registry and management system.
3
+ *
4
+ * The TagsStore provides a centralized registry for CBOR tags,
5
+ * including name resolution and custom summarizer functions.
6
+ *
7
+ * @module tags-store
8
+ */
9
+
10
+ import type { Cbor, CborNumber } from "./cbor";
11
+ import type { Tag } from "./tag";
12
+
13
+ /**
14
+ * Function type for custom CBOR value summarizers.
15
+ *
16
+ * Summarizers provide custom string representations for tagged values.
17
+ *
18
+ * @param cbor - The CBOR value to summarize
19
+ * @param flat - If true, produce single-line output
20
+ * @returns String summary of the value
21
+ */
22
+ export type CborSummarizer = (cbor: Cbor, flat: boolean) => string;
23
+
24
+ /**
25
+ * Interface for tag store operations.
26
+ */
27
+ export interface TagsStoreTrait {
28
+ /**
29
+ * Get the assigned name for a tag, if any.
30
+ *
31
+ * @param tag - The tag to look up
32
+ * @returns The assigned name, or undefined if no name is registered
33
+ */
34
+ assignedNameForTag(tag: Tag): string | undefined;
35
+
36
+ /**
37
+ * Get a display name for a tag.
38
+ *
39
+ * @param tag - The tag to get a name for
40
+ * @returns The assigned name if available, otherwise the tag value as a string
41
+ */
42
+ nameForTag(tag: Tag): string;
43
+
44
+ /**
45
+ * Look up a tag by its numeric value.
46
+ *
47
+ * @param value - The numeric tag value
48
+ * @returns The Tag object if found, undefined otherwise
49
+ */
50
+ tagForValue(value: CborNumber): Tag | undefined;
51
+
52
+ /**
53
+ * Look up a tag by its name.
54
+ *
55
+ * @param name - The tag name
56
+ * @returns The Tag object if found, undefined otherwise
57
+ */
58
+ tagForName(name: string): Tag | undefined;
59
+
60
+ /**
61
+ * Get a display name for a tag value.
62
+ *
63
+ * @param value - The numeric tag value
64
+ * @returns The tag name if registered, otherwise the value as a string
65
+ */
66
+ nameForValue(value: CborNumber): string;
67
+
68
+ /**
69
+ * Get a custom summarizer function for a tag, if registered.
70
+ *
71
+ * @param tag - The numeric tag value
72
+ * @returns The summarizer function if registered, undefined otherwise
73
+ */
74
+ summarizer(tag: CborNumber): CborSummarizer | undefined;
75
+ }
76
+
77
+ /**
78
+ * Tag registry implementation.
79
+ *
80
+ * Stores tags with their names and optional summarizer functions.
81
+ */
82
+ export class TagsStore implements TagsStoreTrait {
83
+ readonly #tagsByValue = new Map<string, Tag>();
84
+ readonly #tagsByName = new Map<string, Tag>();
85
+ readonly #summarizers = new Map<string, CborSummarizer>();
86
+
87
+ constructor() {
88
+ // Start with empty store, matching Rust's Default implementation
89
+ // Tags must be explicitly registered using insert() or registerTags()
90
+ }
91
+
92
+ /**
93
+ * Insert a tag into the registry.
94
+ *
95
+ * @param tag - The tag to register
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const store = new TagsStore();
100
+ * store.insert(createTag(12345, 'myCustomTag'));
101
+ * ```
102
+ */
103
+ insert(tag: Tag): void {
104
+ const key = this.#valueKey(tag.value);
105
+ this.#tagsByValue.set(key, tag);
106
+ if (tag.name !== undefined) {
107
+ this.#tagsByName.set(tag.name, tag);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Insert multiple tags into the registry.
113
+ * Matches Rust's insert_all() method.
114
+ *
115
+ * @param tags - Array of tags to register
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * const store = new TagsStore();
120
+ * store.insertAll([
121
+ * createTag(1, 'date'),
122
+ * createTag(100, 'custom')
123
+ * ]);
124
+ * ```
125
+ */
126
+ insertAll(tags: Tag[]): void {
127
+ for (const tag of tags) {
128
+ this.insert(tag);
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Register a custom summarizer function for a tag.
134
+ *
135
+ * @param tagValue - The numeric tag value
136
+ * @param summarizer - The summarizer function
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * store.setSummarizer(1, (cbor, flat) => {
141
+ * // Custom date formatting
142
+ * return `Date(${extractCbor(cbor)})`;
143
+ * });
144
+ * ```
145
+ */
146
+ setSummarizer(tagValue: CborNumber, summarizer: CborSummarizer): void {
147
+ const key = this.#valueKey(tagValue);
148
+ this.#summarizers.set(key, summarizer);
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;
170
+ }
171
+
172
+ assignedNameForTag(tag: Tag): string | undefined {
173
+ const key = this.#valueKey(tag.value);
174
+ const stored = this.#tagsByValue.get(key);
175
+ return stored?.name;
176
+ }
177
+
178
+ nameForTag(tag: Tag): string {
179
+ return this.assignedNameForTag(tag) ?? tag.value.toString();
180
+ }
181
+
182
+ tagForValue(value: CborNumber): Tag | undefined {
183
+ const key = this.#valueKey(value);
184
+ return this.#tagsByValue.get(key);
185
+ }
186
+
187
+ tagForName(name: string): Tag | undefined {
188
+ return this.#tagsByName.get(name);
189
+ }
190
+
191
+ nameForValue(value: CborNumber): string {
192
+ const tag = this.tagForValue(value);
193
+ return tag !== undefined ? this.nameForTag(tag) : value.toString();
194
+ }
195
+
196
+ summarizer(tag: CborNumber): CborSummarizer | undefined {
197
+ const key = this.#valueKey(tag);
198
+ return this.#summarizers.get(key);
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;
226
+ }
227
+
228
+ /**
229
+ * Create a string key for a numeric tag value.
230
+ * Handles both number and bigint types.
231
+ *
232
+ * @private
233
+ */
234
+ #valueKey(value: CborNumber): string {
235
+ return value.toString();
236
+ }
237
+ }
238
+
239
+ // ============================================================================
240
+ // Global Tags Store Singleton
241
+ // ============================================================================
242
+
243
+ /**
244
+ * Global singleton instance of the tags store.
245
+ */
246
+ let globalTagsStore: TagsStore | undefined;
247
+
248
+ /**
249
+ * Get the global tags store instance.
250
+ *
251
+ * Creates the instance on first access.
252
+ *
253
+ * @returns The global TagsStore instance
254
+ *
255
+ * @example
256
+ * ```typescript
257
+ * const store = getGlobalTagsStore();
258
+ * store.insert(createTag(999, 'myTag'));
259
+ * ```
260
+ */
261
+ export const getGlobalTagsStore = (): TagsStore => {
262
+ globalTagsStore ??= new TagsStore();
263
+ return globalTagsStore;
264
+ };
265
+
266
+ /**
267
+ * Execute a function with access to the global tags store.
268
+ *
269
+ * @template T - Return type of the action function
270
+ * @param action - Function to execute with the tags store
271
+ * @returns Result of the action function
272
+ *
273
+ * @example
274
+ * ```typescript
275
+ * const tagName = withTags(store => store.nameForValue(1));
276
+ * console.log(tagName); // 'date'
277
+ * ```
278
+ */
279
+ export const withTags = <T>(action: (tags: TagsStore) => T): T => {
280
+ return action(getGlobalTagsStore());
281
+ };
282
+
283
+ /**
284
+ * Execute a function with mutable access to the global tags store.
285
+ *
286
+ * This is an alias for withTags() for consistency with Rust API.
287
+ *
288
+ * @template T - Return type of the action function
289
+ * @param action - Function to execute with the tags store
290
+ * @returns Result of the action function
291
+ */
292
+ export const withTagsMut = <T>(action: (tags: TagsStore) => T): T => {
293
+ return action(getGlobalTagsStore());
294
+ };
package/src/tags.ts ADDED
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Standard CBOR tag definitions from the IANA registry.
3
+ *
4
+ * @module tags
5
+ * @see https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
6
+ */
7
+
8
+ import { type Tag, createTag } from "./tag";
9
+
10
+ // ============================================================================
11
+ // Standard Date/Time Tags
12
+ // ============================================================================
13
+
14
+ /**
15
+ * Tag 0: Standard date/time string (RFC 3339)
16
+ */
17
+ export const TAG_DATE_TIME_STRING = 0;
18
+
19
+ /**
20
+ * Tag 1: Epoch-based date/time (seconds since 1970-01-01T00:00:00Z)
21
+ */
22
+ export const TAG_EPOCH_DATE_TIME = 1;
23
+
24
+ /**
25
+ * Tag 100: Epoch-based date (days since 1970-01-01)
26
+ */
27
+ export const TAG_EPOCH_DATE = 100;
28
+
29
+ // ============================================================================
30
+ // Numeric Tags
31
+ // ============================================================================
32
+
33
+ /**
34
+ * Tag 2: Positive bignum (unsigned arbitrary-precision integer)
35
+ */
36
+ export const TAG_POSITIVE_BIGNUM = 2;
37
+
38
+ /**
39
+ * Tag 3: Negative bignum (signed arbitrary-precision integer)
40
+ */
41
+ export const TAG_NEGATIVE_BIGNUM = 3;
42
+
43
+ /**
44
+ * Tag 4: Decimal fraction [exponent, mantissa]
45
+ */
46
+ export const TAG_DECIMAL_FRACTION = 4;
47
+
48
+ /**
49
+ * Tag 5: Bigfloat [exponent, mantissa]
50
+ */
51
+ export const TAG_BIGFLOAT = 5;
52
+
53
+ // ============================================================================
54
+ // Encoding Hints
55
+ // ============================================================================
56
+
57
+ /**
58
+ * Tag 21: Expected conversion to base64url encoding
59
+ */
60
+ export const TAG_BASE64URL = 21;
61
+
62
+ /**
63
+ * Tag 22: Expected conversion to base64 encoding
64
+ */
65
+ export const TAG_BASE64 = 22;
66
+
67
+ /**
68
+ * Tag 23: Expected conversion to base16 encoding
69
+ */
70
+ export const TAG_BASE16 = 23;
71
+
72
+ /**
73
+ * Tag 24: Encoded CBOR data item
74
+ */
75
+ export const TAG_ENCODED_CBOR = 24;
76
+
77
+ // ============================================================================
78
+ // URI and Network Tags
79
+ // ============================================================================
80
+
81
+ /**
82
+ * Tag 32: URI (text string)
83
+ */
84
+ export const TAG_URI = 32;
85
+
86
+ /**
87
+ * Tag 33: base64url-encoded text
88
+ */
89
+ export const TAG_BASE64URL_TEXT = 33;
90
+
91
+ /**
92
+ * Tag 34: base64-encoded text
93
+ */
94
+ export const TAG_BASE64_TEXT = 34;
95
+
96
+ /**
97
+ * Tag 35: Regular expression (PCRE/ECMA262)
98
+ */
99
+ export const TAG_REGEXP = 35;
100
+
101
+ /**
102
+ * Tag 36: MIME message
103
+ */
104
+ export const TAG_MIME_MESSAGE = 36;
105
+
106
+ /**
107
+ * Tag 37: Binary UUID
108
+ */
109
+ export const TAG_UUID = 37;
110
+
111
+ // ============================================================================
112
+ // Cryptography Tags
113
+ // ============================================================================
114
+
115
+ /**
116
+ * Tag 256: string reference (namespace)
117
+ */
118
+ export const TAG_STRING_REF_NAMESPACE = 256;
119
+
120
+ /**
121
+ * Tag 257: binary UUID reference
122
+ */
123
+ export const TAG_BINARY_UUID = 257;
124
+
125
+ /**
126
+ * Tag 258: Set of values (array with no duplicates)
127
+ */
128
+ export const TAG_SET = 258;
129
+
130
+ // ============================================================================
131
+ // NOTE: Blockchain Commons envelope and extension tags (TAG_ENVELOPE, TAG_LEAF,
132
+ // TAG_KNOWN_VALUE, TAG_COMPRESSED, etc.) are defined in the @bcts/tags
133
+ // package, NOT in dcbor. This matches the Rust architecture where bc-dcbor-rust
134
+ // only defines TAG_DATE, and bc-tags-rust defines all envelope-related tags.
135
+ // ============================================================================
136
+
137
+ // ============================================================================
138
+ // Self-describing CBOR
139
+ // ============================================================================
140
+
141
+ /**
142
+ * Tag 55799: Self-describe CBOR (magic number 0xd9d9f7)
143
+ */
144
+ export const TAG_SELF_DESCRIBE_CBOR = 55799;
145
+
146
+ // ============================================================================
147
+ // Global Tags Store Registration
148
+ // Matches Rust's register_tags() functionality
149
+ // ============================================================================
150
+
151
+ import type { TagsStore } from "./tags-store";
152
+ import { getGlobalTagsStore } from "./tags-store";
153
+ import { CborDate } from "./date";
154
+ import type { Cbor } from "./cbor";
155
+ import { diagnostic } from "./diag";
156
+
157
+ // Tag constants matching Rust
158
+ export const TAG_DATE = 1;
159
+ export const TAG_NAME_DATE = "date";
160
+
161
+ /**
162
+ * Register standard tags in a specific tags store.
163
+ * Matches Rust's register_tags_in() function.
164
+ *
165
+ * @param tagsStore - The tags store to register tags into
166
+ */
167
+ export const registerTagsIn = (tagsStore: TagsStore): void => {
168
+ const tags = [createTag(TAG_DATE, TAG_NAME_DATE)];
169
+ tagsStore.insertAll(tags);
170
+
171
+ // Set summarizer for date tag
172
+ tagsStore.setSummarizer(TAG_DATE, (untaggedCbor: Cbor, _flat: boolean): string => {
173
+ try {
174
+ return CborDate.fromUntaggedCbor(untaggedCbor).toString();
175
+ } catch {
176
+ return diagnostic(untaggedCbor);
177
+ }
178
+ });
179
+ };
180
+
181
+ /**
182
+ * Register standard tags in the global tags store.
183
+ * Matches Rust's register_tags() function.
184
+ *
185
+ * This function is idempotent - calling it multiple times is safe.
186
+ */
187
+ export const registerTags = (): void => {
188
+ const globalStore = getGlobalTagsStore();
189
+ registerTagsIn(globalStore);
190
+ };
191
+
192
+ /**
193
+ * Converts an array of tag values to their corresponding Tag objects.
194
+ * Matches Rust's tags_for_values() function.
195
+ *
196
+ * This function looks up each tag value in the global tag registry and returns
197
+ * an array of complete Tag objects. For any tag values that aren't
198
+ * registered in the global registry, it creates a basic Tag with just the
199
+ * value (no name).
200
+ *
201
+ * @param values - Array of numeric tag values to convert
202
+ * @returns Array of Tag objects corresponding to the input values
203
+ *
204
+ * @example
205
+ * ```typescript
206
+ * // Register some tags first
207
+ * registerTags();
208
+ *
209
+ * // Convert tag values to Tag objects
210
+ * const tags = tagsForValues([1, 42, 999]);
211
+ *
212
+ * // The first tag (value 1) should be registered as "date"
213
+ * console.log(tags[0].value); // 1
214
+ * console.log(tags[0].name); // "date"
215
+ *
216
+ * // Unregistered tags will have a value but no name
217
+ * console.log(tags[1].value); // 42
218
+ * console.log(tags[2].value); // 999
219
+ * ```
220
+ */
221
+ export const tagsForValues = (values: (number | bigint)[]): Tag[] => {
222
+ const globalStore = getGlobalTagsStore();
223
+ return values.map((value) => {
224
+ const tag = globalStore.tagForValue(value);
225
+ if (tag !== undefined) {
226
+ return tag;
227
+ }
228
+ // Create basic tag with just the value
229
+ return createTag(value);
230
+ });
231
+ };