@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
package/src/date.ts ADDED
@@ -0,0 +1,553 @@
1
+ /**
2
+ * Date/time support for CBOR with tag(1) encoding.
3
+ *
4
+ * A CBOR-friendly representation of a date and time.
5
+ *
6
+ * The `CborDate` type provides a wrapper around JavaScript's native `Date` that
7
+ * supports encoding and decoding to/from CBOR with tag 1, following the CBOR
8
+ * date/time standard specified in RFC 8949.
9
+ *
10
+ * When encoded to CBOR, dates are represented as tag 1 followed by a numeric
11
+ * value representing the number of seconds since (or before) the Unix epoch
12
+ * (1970-01-01T00:00:00Z). The numeric value can be a positive or negative
13
+ * integer, or a floating-point value for dates with fractional seconds.
14
+ *
15
+ * @module date
16
+ */
17
+
18
+ import { type Cbor, MajorType } from "./cbor";
19
+ import { cbor } from "./cbor";
20
+ import { createTag, type Tag } from "./tag";
21
+ import { TAG_EPOCH_DATE_TIME } from "./tags";
22
+ import {
23
+ type CborTaggedEncodable,
24
+ type CborTaggedDecodable,
25
+ type CborTagged,
26
+ createTaggedCbor,
27
+ validateTag,
28
+ extractTaggedContent,
29
+ } from "./cbor-tagged";
30
+ import { CborError } from "./error";
31
+
32
+ /**
33
+ * A CBOR-friendly representation of a date and time.
34
+ *
35
+ * The `CborDate` type provides a wrapper around JavaScript's native `Date` that
36
+ * supports encoding and decoding to/from CBOR with tag 1, following the CBOR
37
+ * date/time standard specified in RFC 8949.
38
+ *
39
+ * When encoded to CBOR, dates are represented as tag 1 followed by a numeric
40
+ * value representing the number of seconds since (or before) the Unix epoch
41
+ * (1970-01-01T00:00:00Z). The numeric value can be a positive or negative
42
+ * integer, or a floating-point value for dates with fractional seconds.
43
+ *
44
+ * # Features
45
+ *
46
+ * - Supports UTC dates with optional fractional seconds
47
+ * - Provides convenient constructors for common date creation patterns
48
+ * - Implements the `CborTagged`, `CborTaggedEncodable`, and
49
+ * `CborTaggedDecodable` interfaces
50
+ * - Supports arithmetic operations with durations and between dates
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * import { CborDate } from './date';
55
+ *
56
+ * // Create a date from a timestamp (seconds since Unix epoch)
57
+ * const date = CborDate.fromTimestamp(1675854714.0);
58
+ *
59
+ * // Create a date from year, month, day
60
+ * const date2 = CborDate.fromYmd(2023, 2, 8);
61
+ *
62
+ * // Convert to CBOR
63
+ * const cborValue = date.taggedCbor();
64
+ *
65
+ * // Decode from CBOR
66
+ * const decoded = CborDate.fromTaggedCbor(cborValue);
67
+ * ```
68
+ */
69
+ export class CborDate implements CborTagged, CborTaggedEncodable, CborTaggedDecodable<CborDate> {
70
+ #datetime: Date;
71
+
72
+ /**
73
+ * Creates a new `CborDate` from the given JavaScript `Date`.
74
+ *
75
+ * This method creates a new `CborDate` instance by wrapping a
76
+ * JavaScript `Date`.
77
+ *
78
+ * @param dateTime - A `Date` instance to wrap
79
+ *
80
+ * @returns A new `CborDate` instance
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * const datetime = new Date();
85
+ * const date = CborDate.fromDatetime(datetime);
86
+ * ```
87
+ */
88
+ static fromDatetime(dateTime: Date): CborDate {
89
+ const instance = new CborDate();
90
+ instance.#datetime = new Date(dateTime);
91
+ return instance;
92
+ }
93
+
94
+ /**
95
+ * Creates a new `CborDate` from year, month, and day components.
96
+ *
97
+ * This method creates a new `CborDate` with the time set to 00:00:00 UTC.
98
+ *
99
+ * @param year - The year component (e.g., 2023)
100
+ * @param month - The month component (1-12)
101
+ * @param day - The day component (1-31)
102
+ *
103
+ * @returns A new `CborDate` instance
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * // Create February 8, 2023
108
+ * const date = CborDate.fromYmd(2023, 2, 8);
109
+ * ```
110
+ *
111
+ * @throws Error if the provided components do not form a valid date.
112
+ */
113
+ static fromYmd(year: number, month: number, day: number): CborDate {
114
+ const dt = new Date(Date.UTC(year, month - 1, day, 0, 0, 0, 0));
115
+ return CborDate.fromDatetime(dt);
116
+ }
117
+
118
+ /**
119
+ * Creates a new `CborDate` from year, month, day, hour, minute, and second
120
+ * components.
121
+ *
122
+ * @param year - The year component (e.g., 2023)
123
+ * @param month - The month component (1-12)
124
+ * @param day - The day component (1-31)
125
+ * @param hour - The hour component (0-23)
126
+ * @param minute - The minute component (0-59)
127
+ * @param second - The second component (0-59)
128
+ *
129
+ * @returns A new `CborDate` instance
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * // Create February 8, 2023, 15:30:45 UTC
134
+ * const date = CborDate.fromYmdHms(2023, 2, 8, 15, 30, 45);
135
+ * ```
136
+ *
137
+ * @throws Error if the provided components do not form a valid date and time.
138
+ */
139
+ static fromYmdHms(
140
+ year: number,
141
+ month: number,
142
+ day: number,
143
+ hour: number,
144
+ minute: number,
145
+ second: number,
146
+ ): CborDate {
147
+ const dt = new Date(Date.UTC(year, month - 1, day, hour, minute, second, 0));
148
+ return CborDate.fromDatetime(dt);
149
+ }
150
+
151
+ /**
152
+ * Creates a new `CborDate` from seconds since (or before) the Unix epoch.
153
+ *
154
+ * This method creates a new `CborDate` representing the specified number of
155
+ * seconds since the Unix epoch (1970-01-01T00:00:00Z). Negative values
156
+ * represent times before the epoch.
157
+ *
158
+ * @param secondsSinceUnixEpoch - Seconds from the Unix epoch (positive or
159
+ * negative), which can include a fractional part for sub-second
160
+ * precision
161
+ *
162
+ * @returns A new `CborDate` instance
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * // Create a date from a timestamp
167
+ * const date = CborDate.fromTimestamp(1675854714.0);
168
+ *
169
+ * // Create a date one second before the Unix epoch
170
+ * const beforeEpoch = CborDate.fromTimestamp(-1.0);
171
+ *
172
+ * // Create a date with fractional seconds
173
+ * const withFraction = CborDate.fromTimestamp(1675854714.5);
174
+ * ```
175
+ */
176
+ static fromTimestamp(secondsSinceUnixEpoch: number): CborDate {
177
+ const wholeSecondsSinceUnixEpoch = Math.trunc(secondsSinceUnixEpoch);
178
+ const fractionalSeconds = secondsSinceUnixEpoch - wholeSecondsSinceUnixEpoch;
179
+ const milliseconds = wholeSecondsSinceUnixEpoch * 1000 + fractionalSeconds * 1000;
180
+ return CborDate.fromDatetime(new Date(milliseconds));
181
+ }
182
+
183
+ /**
184
+ * Creates a new `CborDate` from a string containing an ISO-8601 (RFC-3339)
185
+ * date (with or without time).
186
+ *
187
+ * This method parses a string representation of a date or date-time in
188
+ * ISO-8601/RFC-3339 format and creates a new `CborDate` instance. It
189
+ * supports both full date-time strings (e.g., "2023-02-08T15:30:45Z")
190
+ * and date-only strings (e.g., "2023-02-08").
191
+ *
192
+ * @param value - A string containing a date or date-time in ISO-8601/RFC-3339
193
+ * format
194
+ *
195
+ * @returns A new `CborDate` instance if parsing succeeds
196
+ *
197
+ * @throws Error if the string cannot be parsed as a valid date or date-time
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * // Parse a date-time string
202
+ * const date = CborDate.fromString("2023-02-08T15:30:45Z");
203
+ *
204
+ * // Parse a date-only string (time will be set to 00:00:00)
205
+ * const date2 = CborDate.fromString("2023-02-08");
206
+ * ```
207
+ */
208
+ static fromString(value: string): CborDate {
209
+ // Try parsing as ISO 8601 date string
210
+ const dt = new Date(value);
211
+ if (isNaN(dt.getTime())) {
212
+ throw new CborError({ type: "InvalidDate", message: `Invalid date string: ${value}` });
213
+ }
214
+ return CborDate.fromDatetime(dt);
215
+ }
216
+
217
+ /**
218
+ * Creates a new `CborDate` containing the current date and time.
219
+ *
220
+ * @returns A new `CborDate` instance representing the current UTC date and time
221
+ *
222
+ * @example
223
+ * ```typescript
224
+ * const now = CborDate.now();
225
+ * ```
226
+ */
227
+ static now(): CborDate {
228
+ return CborDate.fromDatetime(new Date());
229
+ }
230
+
231
+ /**
232
+ * Creates a new `CborDate` containing the current date and time plus the given
233
+ * duration.
234
+ *
235
+ * @param durationMs - The duration in milliseconds to add to the current time
236
+ *
237
+ * @returns A new `CborDate` instance representing the current UTC date and time plus
238
+ * the duration
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * // Get a date 1 hour from now
243
+ * const oneHourLater = CborDate.withDurationFromNow(3600 * 1000);
244
+ * ```
245
+ */
246
+ static withDurationFromNow(durationMs: number): CborDate {
247
+ const now = new Date();
248
+ const future = new Date(now.getTime() + durationMs);
249
+ return CborDate.fromDatetime(future);
250
+ }
251
+
252
+ /**
253
+ * Returns the underlying JavaScript `Date` object.
254
+ *
255
+ * This method provides access to the wrapped JavaScript `Date`
256
+ * instance.
257
+ *
258
+ * @returns The wrapped `Date` instance
259
+ *
260
+ * @example
261
+ * ```typescript
262
+ * const date = CborDate.now();
263
+ * const datetime = date.datetime();
264
+ * const year = datetime.getFullYear();
265
+ * ```
266
+ */
267
+ datetime(): Date {
268
+ return new Date(this.#datetime);
269
+ }
270
+
271
+ /**
272
+ * Returns the `CborDate` as the number of seconds since the Unix epoch.
273
+ *
274
+ * This method converts the date to a floating-point number representing
275
+ * the number of seconds since the Unix epoch (1970-01-01T00:00:00Z).
276
+ * Negative values represent times before the epoch. The fractional
277
+ * part represents sub-second precision.
278
+ *
279
+ * @returns Seconds since the Unix epoch as a `number`
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * const date = CborDate.fromYmd(2023, 2, 8);
284
+ * const timestamp = date.timestamp();
285
+ * ```
286
+ */
287
+ timestamp(): number {
288
+ const wholeSecondsSinceUnixEpoch = Math.trunc(this.#datetime.getTime() / 1000);
289
+ const msecs = this.#datetime.getTime() % 1000;
290
+ return wholeSecondsSinceUnixEpoch + msecs / 1000.0;
291
+ }
292
+
293
+ /**
294
+ * Add seconds to this date.
295
+ *
296
+ * @param seconds - Seconds to add (can be fractional)
297
+ * @returns New CborDate instance
298
+ *
299
+ * @example
300
+ * ```typescript
301
+ * const date = CborDate.fromYmd(2022, 3, 21);
302
+ * const tomorrow = date.add(24 * 60 * 60);
303
+ * ```
304
+ */
305
+ add(seconds: number): CborDate {
306
+ return CborDate.fromTimestamp(this.timestamp() + seconds);
307
+ }
308
+
309
+ /**
310
+ * Subtract seconds from this date.
311
+ *
312
+ * @param seconds - Seconds to subtract (can be fractional)
313
+ * @returns New CborDate instance
314
+ *
315
+ * @example
316
+ * ```typescript
317
+ * const date = CborDate.fromYmd(2022, 3, 21);
318
+ * const yesterday = date.subtract(24 * 60 * 60);
319
+ * ```
320
+ */
321
+ subtract(seconds: number): CborDate {
322
+ return CborDate.fromTimestamp(this.timestamp() - seconds);
323
+ }
324
+
325
+ /**
326
+ * Get the difference in seconds between this date and another.
327
+ *
328
+ * @param other - Other CborDate to compare with
329
+ * @returns Difference in seconds (this - other)
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * const date1 = CborDate.fromYmd(2022, 3, 22);
334
+ * const date2 = CborDate.fromYmd(2022, 3, 21);
335
+ * const diff = date1.difference(date2);
336
+ * // Returns 86400 (one day in seconds)
337
+ * ```
338
+ */
339
+ difference(other: CborDate): number {
340
+ return this.timestamp() - other.timestamp();
341
+ }
342
+
343
+ /**
344
+ * Implementation of the `CborTagged` interface for `CborDate`.
345
+ *
346
+ * This implementation specifies that `CborDate` values are tagged with CBOR tag 1,
347
+ * which is the standard CBOR tag for date/time values represented as seconds
348
+ * since the Unix epoch per RFC 8949.
349
+ *
350
+ * @returns A vector containing tag 1
351
+ */
352
+ cborTags(): Tag[] {
353
+ return [createTag(TAG_EPOCH_DATE_TIME, "date")];
354
+ }
355
+
356
+ /**
357
+ * Implementation of the `CborTaggedEncodable` interface for `CborDate`.
358
+ *
359
+ * Converts this `CborDate` to an untagged CBOR value.
360
+ *
361
+ * The date is converted to a numeric value representing the number of
362
+ * seconds since the Unix epoch. This value may be an integer or a
363
+ * floating-point number, depending on whether the date has fractional
364
+ * seconds.
365
+ *
366
+ * @returns A CBOR value representing the timestamp
367
+ */
368
+ untaggedCbor(): Cbor {
369
+ return cbor(this.timestamp());
370
+ }
371
+
372
+ /**
373
+ * Converts this `CborDate` to a tagged CBOR value with tag 1.
374
+ *
375
+ * @returns Tagged CBOR value
376
+ */
377
+ taggedCbor(): Cbor {
378
+ return createTaggedCbor(this);
379
+ }
380
+
381
+ /**
382
+ * Implementation of the `CborTaggedDecodable` interface for `CborDate`.
383
+ *
384
+ * Creates a `CborDate` from an untagged CBOR value.
385
+ *
386
+ * The CBOR value must be a numeric value (integer or floating-point)
387
+ * representing the number of seconds since the Unix epoch.
388
+ *
389
+ * @param cbor - The untagged CBOR value
390
+ *
391
+ * @returns This CborDate instance (mutated)
392
+ *
393
+ * @throws Error if the CBOR value is not a valid timestamp
394
+ */
395
+ fromUntaggedCbor(cbor: Cbor): CborDate {
396
+ let timestamp: number;
397
+
398
+ // Only handle numeric types (Unsigned, Negative, Float); others are invalid for dates
399
+ // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
400
+ switch (cbor.type) {
401
+ case MajorType.Unsigned:
402
+ timestamp = typeof cbor.value === "number" ? cbor.value : Number(cbor.value);
403
+ break;
404
+
405
+ case MajorType.Negative:
406
+ // Convert stored magnitude back to actual negative value
407
+ if (typeof cbor.value === "bigint") {
408
+ timestamp = Number(-cbor.value - 1n);
409
+ } else {
410
+ timestamp = -cbor.value - 1;
411
+ }
412
+ break;
413
+
414
+ case MajorType.Simple:
415
+ if (cbor.value.type === "Float") {
416
+ timestamp = cbor.value.value;
417
+ } else {
418
+ throw new CborError({
419
+ type: "Custom",
420
+ message: "Invalid date CBOR: expected numeric value",
421
+ });
422
+ }
423
+ break;
424
+
425
+ default:
426
+ throw new CborError({
427
+ type: "Custom",
428
+ message: "Invalid date CBOR: expected numeric value",
429
+ });
430
+ }
431
+
432
+ const date = CborDate.fromTimestamp(timestamp);
433
+ this.#datetime = date.#datetime;
434
+ return this;
435
+ }
436
+
437
+ /**
438
+ * Creates a `CborDate` from a tagged CBOR value with tag 1.
439
+ *
440
+ * @param cbor - Tagged CBOR value
441
+ *
442
+ * @returns This CborDate instance (mutated)
443
+ *
444
+ * @throws Error if the CBOR value has the wrong tag or cannot be decoded
445
+ */
446
+ fromTaggedCbor(cbor: Cbor): CborDate {
447
+ const expectedTags = this.cborTags();
448
+ validateTag(cbor, expectedTags);
449
+ const content = extractTaggedContent(cbor);
450
+ return this.fromUntaggedCbor(content);
451
+ }
452
+
453
+ /**
454
+ * Static method to create a CborDate from tagged CBOR.
455
+ *
456
+ * @param cbor - Tagged CBOR value
457
+ * @returns New CborDate instance
458
+ */
459
+ static fromTaggedCbor(cbor: Cbor): CborDate {
460
+ const instance = new CborDate();
461
+ return instance.fromTaggedCbor(cbor);
462
+ }
463
+
464
+ /**
465
+ * Static method to create a CborDate from untagged CBOR.
466
+ *
467
+ * @param cbor - Untagged CBOR value
468
+ * @returns New CborDate instance
469
+ */
470
+ static fromUntaggedCbor(cbor: Cbor): CborDate {
471
+ const instance = new CborDate();
472
+ return instance.fromUntaggedCbor(cbor);
473
+ }
474
+
475
+ /**
476
+ * Implementation of the `toString` method for `CborDate`.
477
+ *
478
+ * This implementation provides a string representation of a `CborDate` in ISO-8601
479
+ * format. For dates with time exactly at midnight (00:00:00), only the date
480
+ * part is shown. For other times, a full date-time string is shown.
481
+ *
482
+ * @returns String representation in ISO-8601 format
483
+ *
484
+ * @example
485
+ * ```typescript
486
+ * // A date at midnight will display as just the date
487
+ * const date = CborDate.fromYmd(2023, 2, 8);
488
+ * // Returns "2023-02-08"
489
+ * console.log(date.toString());
490
+ *
491
+ * // A date with time will display as date and time
492
+ * const date2 = CborDate.fromYmdHms(2023, 2, 8, 15, 30, 45);
493
+ * // Returns "2023-02-08T15:30:45.000Z"
494
+ * console.log(date2.toString());
495
+ * ```
496
+ */
497
+ toString(): string {
498
+ const dt = this.#datetime;
499
+ // Check only hours, minutes, and seconds (not milliseconds) to match Rust behavior
500
+ const hasTime = dt.getUTCHours() !== 0 || dt.getUTCMinutes() !== 0 || dt.getUTCSeconds() !== 0;
501
+
502
+ if (!hasTime) {
503
+ // Midnight (with possible subsecond precision) - show only date
504
+ const datePart = dt.toISOString().split("T")[0];
505
+ if (datePart === undefined) {
506
+ throw new CborError({ type: "Custom", message: "Invalid ISO string format" });
507
+ }
508
+ return datePart;
509
+ } else {
510
+ // Show full ISO datetime without milliseconds (matches Rust's SecondsFormat::Secs)
511
+ const iso = dt.toISOString();
512
+ // Remove milliseconds: "2023-02-08T15:30:45.000Z" -> "2023-02-08T15:30:45Z"
513
+ return iso.replace(/\.\d{3}Z$/, "Z");
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Compare two dates for equality.
519
+ *
520
+ * @param other - Other CborDate to compare
521
+ * @returns true if dates represent the same moment in time
522
+ */
523
+ equals(other: CborDate): boolean {
524
+ return this.#datetime.getTime() === other.#datetime.getTime();
525
+ }
526
+
527
+ /**
528
+ * Compare two dates.
529
+ *
530
+ * @param other - Other CborDate to compare
531
+ * @returns -1 if this < other, 0 if equal, 1 if this > other
532
+ */
533
+ compare(other: CborDate): number {
534
+ const thisTime = this.#datetime.getTime();
535
+ const otherTime = other.#datetime.getTime();
536
+ if (thisTime < otherTime) return -1;
537
+ if (thisTime > otherTime) return 1;
538
+ return 0;
539
+ }
540
+
541
+ /**
542
+ * Convert to JSON (returns ISO 8601 string).
543
+ *
544
+ * @returns ISO 8601 string
545
+ */
546
+ toJSON(): string {
547
+ return this.toString();
548
+ }
549
+
550
+ private constructor() {
551
+ this.#datetime = new Date();
552
+ }
553
+ }