@bcts/dcbor-parse 1.0.0-alpha.13

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.
@@ -0,0 +1,1142 @@
1
+ var bctsDcborParse = (function(exports, _bcts_dcbor, _bcts_known_values, _bcts_uniform_resources) {
2
+
3
+
4
+ //#region src/error.ts
5
+ /**
6
+ * Creates a span with the given start and end positions.
7
+ */
8
+ function span(start, end) {
9
+ return {
10
+ start,
11
+ end
12
+ };
13
+ }
14
+ /**
15
+ * Creates a default (empty) span.
16
+ */
17
+ function defaultSpan() {
18
+ return {
19
+ start: 0,
20
+ end: 0
21
+ };
22
+ }
23
+ const parseError = {
24
+ emptyInput() {
25
+ return { type: "EmptyInput" };
26
+ },
27
+ unexpectedEndOfInput() {
28
+ return { type: "UnexpectedEndOfInput" };
29
+ },
30
+ extraData(span$1) {
31
+ return {
32
+ type: "ExtraData",
33
+ span: span$1
34
+ };
35
+ },
36
+ unexpectedToken(token$1, span$1) {
37
+ return {
38
+ type: "UnexpectedToken",
39
+ token: token$1,
40
+ span: span$1
41
+ };
42
+ },
43
+ unrecognizedToken(span$1) {
44
+ return {
45
+ type: "UnrecognizedToken",
46
+ span: span$1
47
+ };
48
+ },
49
+ expectedComma(span$1) {
50
+ return {
51
+ type: "ExpectedComma",
52
+ span: span$1
53
+ };
54
+ },
55
+ expectedColon(span$1) {
56
+ return {
57
+ type: "ExpectedColon",
58
+ span: span$1
59
+ };
60
+ },
61
+ unmatchedParentheses(span$1) {
62
+ return {
63
+ type: "UnmatchedParentheses",
64
+ span: span$1
65
+ };
66
+ },
67
+ unmatchedBraces(span$1) {
68
+ return {
69
+ type: "UnmatchedBraces",
70
+ span: span$1
71
+ };
72
+ },
73
+ expectedMapKey(span$1) {
74
+ return {
75
+ type: "ExpectedMapKey",
76
+ span: span$1
77
+ };
78
+ },
79
+ invalidTagValue(value, span$1) {
80
+ return {
81
+ type: "InvalidTagValue",
82
+ value,
83
+ span: span$1
84
+ };
85
+ },
86
+ unknownTagName(name, span$1) {
87
+ return {
88
+ type: "UnknownTagName",
89
+ name,
90
+ span: span$1
91
+ };
92
+ },
93
+ invalidHexString(span$1) {
94
+ return {
95
+ type: "InvalidHexString",
96
+ span: span$1
97
+ };
98
+ },
99
+ invalidBase64String(span$1) {
100
+ return {
101
+ type: "InvalidBase64String",
102
+ span: span$1
103
+ };
104
+ },
105
+ unknownUrType(urType, span$1) {
106
+ return {
107
+ type: "UnknownUrType",
108
+ urType,
109
+ span: span$1
110
+ };
111
+ },
112
+ invalidUr(message, span$1) {
113
+ return {
114
+ type: "InvalidUr",
115
+ message,
116
+ span: span$1
117
+ };
118
+ },
119
+ invalidKnownValue(value, span$1) {
120
+ return {
121
+ type: "InvalidKnownValue",
122
+ value,
123
+ span: span$1
124
+ };
125
+ },
126
+ unknownKnownValueName(name, span$1) {
127
+ return {
128
+ type: "UnknownKnownValueName",
129
+ name,
130
+ span: span$1
131
+ };
132
+ },
133
+ invalidDateString(dateString, span$1) {
134
+ return {
135
+ type: "InvalidDateString",
136
+ dateString,
137
+ span: span$1
138
+ };
139
+ },
140
+ duplicateMapKey(span$1) {
141
+ return {
142
+ type: "DuplicateMapKey",
143
+ span: span$1
144
+ };
145
+ }
146
+ };
147
+ /**
148
+ * Checks if an error is the default unrecognized token error.
149
+ *
150
+ * Corresponds to Rust `Error::is_default()`
151
+ */
152
+ function isDefaultError(error) {
153
+ return error.type === "UnrecognizedToken";
154
+ }
155
+ /**
156
+ * Gets the error message for a parse error.
157
+ *
158
+ * Corresponds to Rust's `Display` implementation for `Error`
159
+ */
160
+ function errorMessage(error) {
161
+ switch (error.type) {
162
+ case "EmptyInput": return "Empty input";
163
+ case "UnexpectedEndOfInput": return "Unexpected end of input";
164
+ case "ExtraData": return "Extra data at end of input";
165
+ case "UnexpectedToken": return `Unexpected token ${tokenDebugString(error.token)}`;
166
+ case "UnrecognizedToken": return "Unrecognized token";
167
+ case "ExpectedComma": return "Expected comma";
168
+ case "ExpectedColon": return "Expected colon";
169
+ case "UnmatchedParentheses": return "Unmatched parentheses";
170
+ case "UnmatchedBraces": return "Unmatched braces";
171
+ case "ExpectedMapKey": return "Expected map key";
172
+ case "InvalidTagValue": return `Invalid tag value '${error.value}'`;
173
+ case "UnknownTagName": return `Unknown tag name '${error.name}'`;
174
+ case "InvalidHexString": return "Invalid hex string";
175
+ case "InvalidBase64String": return "Invalid base64 string";
176
+ case "UnknownUrType": return `Unknown UR type '${error.urType}'`;
177
+ case "InvalidUr": return `Invalid UR '${error.message}'`;
178
+ case "InvalidKnownValue": return `Invalid known value '${error.value}'`;
179
+ case "UnknownKnownValueName": return `Unknown known value name '${error.name}'`;
180
+ case "InvalidDateString": return `Invalid date string '${error.dateString}'`;
181
+ case "DuplicateMapKey": return "Duplicate map key";
182
+ }
183
+ }
184
+ /**
185
+ * Gets the span for a parse error, if applicable.
186
+ */
187
+ function errorSpan(error) {
188
+ switch (error.type) {
189
+ case "EmptyInput":
190
+ case "UnexpectedEndOfInput": return;
191
+ case "ExtraData":
192
+ case "UnexpectedToken":
193
+ case "UnrecognizedToken":
194
+ case "ExpectedComma":
195
+ case "ExpectedColon":
196
+ case "UnmatchedParentheses":
197
+ case "UnmatchedBraces":
198
+ case "ExpectedMapKey":
199
+ case "InvalidTagValue":
200
+ case "UnknownTagName":
201
+ case "InvalidHexString":
202
+ case "InvalidBase64String":
203
+ case "UnknownUrType":
204
+ case "InvalidUr":
205
+ case "InvalidKnownValue":
206
+ case "UnknownKnownValueName":
207
+ case "InvalidDateString":
208
+ case "DuplicateMapKey": return error.span;
209
+ }
210
+ }
211
+ /**
212
+ * Formats an error message with source context, line number, and caret.
213
+ *
214
+ * Corresponds to Rust `Error::format_message()`
215
+ */
216
+ function formatMessage(message, source, range) {
217
+ const start = range.start;
218
+ const end = range.end;
219
+ let lineNumber = 1;
220
+ let lineStart = 0;
221
+ for (let idx = 0; idx < source.length && idx < start; idx++) if (source[idx] === "\n") {
222
+ lineNumber++;
223
+ lineStart = idx + 1;
224
+ }
225
+ const line = source.split("\n")[lineNumber - 1] ?? "";
226
+ const column = Math.max(0, start - lineStart);
227
+ const underlineLen = Math.max(1, end - start);
228
+ const caret = " ".repeat(column) + "^".repeat(underlineLen);
229
+ return `line ${lineNumber}: ${message}\n${line}\n${caret}`;
230
+ }
231
+ /**
232
+ * Gets the full error message with source context.
233
+ *
234
+ * Corresponds to Rust `Error::full_message()`
235
+ */
236
+ function fullErrorMessage(error, source) {
237
+ const message = errorMessage(error);
238
+ switch (error.type) {
239
+ case "EmptyInput": return formatMessage(message, source, defaultSpan());
240
+ case "UnexpectedEndOfInput": return formatMessage(message, source, span(source.length, source.length));
241
+ case "ExtraData":
242
+ case "UnexpectedToken":
243
+ case "UnrecognizedToken":
244
+ case "ExpectedComma":
245
+ case "ExpectedColon":
246
+ case "UnmatchedParentheses":
247
+ case "UnmatchedBraces":
248
+ case "ExpectedMapKey":
249
+ case "InvalidTagValue":
250
+ case "UnknownTagName":
251
+ case "InvalidHexString":
252
+ case "InvalidBase64String":
253
+ case "UnknownUrType":
254
+ case "InvalidUr":
255
+ case "InvalidKnownValue":
256
+ case "UnknownKnownValueName":
257
+ case "InvalidDateString":
258
+ case "DuplicateMapKey": return formatMessage(message, source, error.span);
259
+ }
260
+ }
261
+ /**
262
+ * Creates a default parse error (UnrecognizedToken with empty span).
263
+ *
264
+ * Corresponds to Rust `Error::default()`
265
+ */
266
+ function defaultParseError() {
267
+ return parseError.unrecognizedToken(defaultSpan());
268
+ }
269
+ /**
270
+ * Creates a successful result.
271
+ */
272
+ function ok(value) {
273
+ return {
274
+ ok: true,
275
+ value
276
+ };
277
+ }
278
+ /**
279
+ * Creates an error result.
280
+ */
281
+ function err(error) {
282
+ return {
283
+ ok: false,
284
+ error
285
+ };
286
+ }
287
+ /**
288
+ * Checks if a result is successful.
289
+ */
290
+ function isOk(result) {
291
+ return result.ok;
292
+ }
293
+ /**
294
+ * Checks if a result is an error.
295
+ */
296
+ function isErr(result) {
297
+ return !result.ok;
298
+ }
299
+ /**
300
+ * Unwraps a result, throwing if it's an error.
301
+ */
302
+ function unwrap(result) {
303
+ if (result.ok) return result.value;
304
+ throw new Error(errorMessage(result.error));
305
+ }
306
+ /**
307
+ * Unwraps a result error, throwing if it's successful.
308
+ */
309
+ function unwrapErr(result) {
310
+ if (!result.ok) return result.error;
311
+ throw new Error("Called unwrapErr on an Ok result");
312
+ }
313
+ function tokenDebugString(token$1) {
314
+ return JSON.stringify(token$1);
315
+ }
316
+
317
+ //#endregion
318
+ //#region src/token.ts
319
+ /**
320
+ * @bcts/dcbor-parse - Token types and Lexer
321
+ *
322
+ * This is a 1:1 TypeScript port of bc-dcbor-parse-rust token.rs
323
+ *
324
+ * @module dcbor-parse/token
325
+ */
326
+ const token = {
327
+ bool(value) {
328
+ return {
329
+ type: "Bool",
330
+ value
331
+ };
332
+ },
333
+ braceOpen() {
334
+ return { type: "BraceOpen" };
335
+ },
336
+ braceClose() {
337
+ return { type: "BraceClose" };
338
+ },
339
+ bracketOpen() {
340
+ return { type: "BracketOpen" };
341
+ },
342
+ bracketClose() {
343
+ return { type: "BracketClose" };
344
+ },
345
+ parenthesisOpen() {
346
+ return { type: "ParenthesisOpen" };
347
+ },
348
+ parenthesisClose() {
349
+ return { type: "ParenthesisClose" };
350
+ },
351
+ colon() {
352
+ return { type: "Colon" };
353
+ },
354
+ comma() {
355
+ return { type: "Comma" };
356
+ },
357
+ null() {
358
+ return { type: "Null" };
359
+ },
360
+ nan() {
361
+ return { type: "NaN" };
362
+ },
363
+ infinity() {
364
+ return { type: "Infinity" };
365
+ },
366
+ negInfinity() {
367
+ return { type: "NegInfinity" };
368
+ },
369
+ byteStringHex(value) {
370
+ return {
371
+ type: "ByteStringHex",
372
+ value
373
+ };
374
+ },
375
+ byteStringBase64(value) {
376
+ return {
377
+ type: "ByteStringBase64",
378
+ value
379
+ };
380
+ },
381
+ dateLiteral(value) {
382
+ return {
383
+ type: "DateLiteral",
384
+ value
385
+ };
386
+ },
387
+ number(value) {
388
+ return {
389
+ type: "Number",
390
+ value
391
+ };
392
+ },
393
+ string(value) {
394
+ return {
395
+ type: "String",
396
+ value
397
+ };
398
+ },
399
+ tagValue(value) {
400
+ return {
401
+ type: "TagValue",
402
+ value
403
+ };
404
+ },
405
+ tagName(value) {
406
+ return {
407
+ type: "TagName",
408
+ value
409
+ };
410
+ },
411
+ knownValueNumber(value) {
412
+ return {
413
+ type: "KnownValueNumber",
414
+ value
415
+ };
416
+ },
417
+ knownValueName(value) {
418
+ return {
419
+ type: "KnownValueName",
420
+ value
421
+ };
422
+ },
423
+ unit() {
424
+ return { type: "Unit" };
425
+ },
426
+ ur(value) {
427
+ return {
428
+ type: "UR",
429
+ value
430
+ };
431
+ }
432
+ };
433
+ /**
434
+ * Lexer for dCBOR diagnostic notation.
435
+ *
436
+ * Corresponds to the Rust `logos::Lexer` used in parse.rs
437
+ */
438
+ var Lexer = class {
439
+ #source;
440
+ #position;
441
+ #tokenStart;
442
+ #tokenEnd;
443
+ constructor(source) {
444
+ this.#source = source;
445
+ this.#position = 0;
446
+ this.#tokenStart = 0;
447
+ this.#tokenEnd = 0;
448
+ }
449
+ /**
450
+ * Gets the current span (position range of the last token).
451
+ */
452
+ span() {
453
+ return span(this.#tokenStart, this.#tokenEnd);
454
+ }
455
+ /**
456
+ * Gets the slice of source corresponding to the last token.
457
+ */
458
+ slice() {
459
+ return this.#source.slice(this.#tokenStart, this.#tokenEnd);
460
+ }
461
+ /**
462
+ * Gets the next token, or undefined if at end of input.
463
+ * Returns a Result to handle lexing errors.
464
+ */
465
+ next() {
466
+ this.#skipWhitespaceAndComments();
467
+ if (this.#position >= this.#source.length) return;
468
+ this.#tokenStart = this.#position;
469
+ const result = this.#tryMatchKeyword() ?? this.#tryMatchDateLiteral() ?? this.#tryMatchTagValueOrNumber() ?? this.#tryMatchTagName() ?? this.#tryMatchString() ?? this.#tryMatchByteStringHex() ?? this.#tryMatchByteStringBase64() ?? this.#tryMatchKnownValue() ?? this.#tryMatchUR() ?? this.#tryMatchPunctuation();
470
+ if (result === void 0) {
471
+ this.#position++;
472
+ this.#tokenEnd = this.#position;
473
+ return err(parseError.unrecognizedToken(this.span()));
474
+ }
475
+ return result;
476
+ }
477
+ #skipWhitespaceAndComments() {
478
+ while (this.#position < this.#source.length) {
479
+ const ch = this.#source[this.#position];
480
+ if (ch === " " || ch === " " || ch === "\r" || ch === "\n" || ch === "\f") {
481
+ this.#position++;
482
+ continue;
483
+ }
484
+ if (ch === "/" && this.#position + 1 < this.#source.length && this.#source[this.#position + 1] !== "/") {
485
+ this.#position++;
486
+ while (this.#position < this.#source.length && this.#source[this.#position] !== "/") this.#position++;
487
+ if (this.#position < this.#source.length) this.#position++;
488
+ continue;
489
+ }
490
+ if (ch === "#") {
491
+ while (this.#position < this.#source.length && this.#source[this.#position] !== "\n") this.#position++;
492
+ continue;
493
+ }
494
+ break;
495
+ }
496
+ }
497
+ #tryMatchKeyword() {
498
+ const keywords = [
499
+ ["true", token.bool(true)],
500
+ ["false", token.bool(false)],
501
+ ["null", token.null()],
502
+ ["NaN", token.nan()],
503
+ ["Infinity", token.infinity()],
504
+ ["-Infinity", token.negInfinity()],
505
+ ["Unit", token.unit()]
506
+ ];
507
+ for (const [keyword, tok] of keywords) if (this.#matchLiteral(keyword)) {
508
+ const nextChar = this.#source[this.#position];
509
+ if (nextChar === void 0 || !this.#isIdentifierChar(nextChar)) {
510
+ this.#tokenEnd = this.#position;
511
+ return ok(tok);
512
+ }
513
+ this.#position = this.#tokenStart;
514
+ }
515
+ }
516
+ #tryMatchDateLiteral() {
517
+ const dateRegex = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?/;
518
+ const remaining = this.#source.slice(this.#position);
519
+ const match = dateRegex.exec(remaining);
520
+ if (match !== null) {
521
+ const dateStr = match[0];
522
+ this.#position += dateStr.length;
523
+ this.#tokenEnd = this.#position;
524
+ if (!isValidDateString(dateStr)) return err(parseError.invalidDateString(dateStr, this.span()));
525
+ try {
526
+ const date = _bcts_dcbor.CborDate.fromString(dateStr);
527
+ return ok(token.dateLiteral(date));
528
+ } catch {
529
+ return err(parseError.invalidDateString(dateStr, this.span()));
530
+ }
531
+ }
532
+ }
533
+ #tryMatchTagValueOrNumber() {
534
+ const numberRegex = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/;
535
+ const remaining = this.#source.slice(this.#position);
536
+ const match = numberRegex.exec(remaining);
537
+ if (match !== null) {
538
+ const numStr = match[0];
539
+ if (this.#source[this.#position + numStr.length] === "(" && !numStr.includes(".") && !numStr.includes("e") && !numStr.includes("E") && !numStr.startsWith("-")) {
540
+ this.#position += numStr.length + 1;
541
+ this.#tokenEnd = this.#position;
542
+ const tagValue = parseInt(numStr, 10);
543
+ if (!Number.isSafeInteger(tagValue) || tagValue < 0) return err(parseError.invalidTagValue(numStr, span(this.#tokenStart, this.#tokenStart + numStr.length)));
544
+ return ok(token.tagValue(tagValue));
545
+ }
546
+ this.#position += numStr.length;
547
+ this.#tokenEnd = this.#position;
548
+ const num = parseFloat(numStr);
549
+ return ok(token.number(num));
550
+ }
551
+ }
552
+ #tryMatchTagName() {
553
+ const tagNameRegex = /^[a-zA-Z_][a-zA-Z0-9_-]*\(/;
554
+ const remaining = this.#source.slice(this.#position);
555
+ const match = tagNameRegex.exec(remaining);
556
+ if (match !== null) {
557
+ const fullMatch = match[0];
558
+ const name = fullMatch.slice(0, -1);
559
+ this.#position += fullMatch.length;
560
+ this.#tokenEnd = this.#position;
561
+ return ok(token.tagName(name));
562
+ }
563
+ }
564
+ #tryMatchString() {
565
+ if (this.#source[this.#position] !== "\"") return;
566
+ const stringRegex = /^"([^"\\\x00-\x1F]|\\(["\\bnfrt/]|u[a-fA-F0-9]{4}))*"/;
567
+ const remaining = this.#source.slice(this.#position);
568
+ const match = stringRegex.exec(remaining);
569
+ if (match !== null) {
570
+ const fullMatch = match[0];
571
+ this.#position += fullMatch.length;
572
+ this.#tokenEnd = this.#position;
573
+ return ok(token.string(fullMatch));
574
+ }
575
+ this.#position++;
576
+ while (this.#position < this.#source.length) {
577
+ const ch = this.#source[this.#position];
578
+ if (ch === "\"" || ch === "\n") {
579
+ if (ch === "\"") this.#position++;
580
+ break;
581
+ }
582
+ if (ch === "\\") this.#position += 2;
583
+ else this.#position++;
584
+ }
585
+ this.#tokenEnd = this.#position;
586
+ return err(parseError.unrecognizedToken(this.span()));
587
+ }
588
+ #tryMatchByteStringHex() {
589
+ if (!this.#matchLiteral("h'")) return;
590
+ const hexRegex = /^[0-9a-fA-F]*/;
591
+ const remaining = this.#source.slice(this.#position);
592
+ const match = hexRegex.exec(remaining);
593
+ const hexPart = match !== null ? match[0] : "";
594
+ this.#position += hexPart.length;
595
+ if (this.#source[this.#position] !== "'") {
596
+ this.#tokenEnd = this.#position;
597
+ return err(parseError.invalidHexString(this.span()));
598
+ }
599
+ this.#position++;
600
+ this.#tokenEnd = this.#position;
601
+ if (hexPart.length % 2 !== 0) return err(parseError.invalidHexString(this.span()));
602
+ const bytes = hexToBytes(hexPart);
603
+ return ok(token.byteStringHex(bytes));
604
+ }
605
+ #tryMatchByteStringBase64() {
606
+ if (!this.#matchLiteral("b64'")) return;
607
+ const base64Regex = /^[A-Za-z0-9+/=]*/;
608
+ const remaining = this.#source.slice(this.#position);
609
+ const match = base64Regex.exec(remaining);
610
+ const base64Part = match !== null ? match[0] : "";
611
+ this.#position += base64Part.length;
612
+ if (this.#source[this.#position] !== "'") {
613
+ this.#tokenEnd = this.#position;
614
+ return err(parseError.invalidBase64String(this.span()));
615
+ }
616
+ this.#position++;
617
+ this.#tokenEnd = this.#position;
618
+ if (base64Part.length < 2) return err(parseError.invalidBase64String(this.span()));
619
+ try {
620
+ const bytes = base64ToBytes(base64Part);
621
+ return ok(token.byteStringBase64(bytes));
622
+ } catch {
623
+ return err(parseError.invalidBase64String(this.span()));
624
+ }
625
+ }
626
+ #tryMatchKnownValue() {
627
+ if (this.#source[this.#position] !== "'") return;
628
+ if (this.#source[this.#position + 1] === "'") {
629
+ this.#position += 2;
630
+ this.#tokenEnd = this.#position;
631
+ return ok(token.knownValueName(""));
632
+ }
633
+ const numericRegex = /^'(0|[1-9][0-9]*)'/;
634
+ const remaining = this.#source.slice(this.#position);
635
+ let match = numericRegex.exec(remaining);
636
+ if (match !== null) {
637
+ const fullMatch = match[0];
638
+ const numStr = match[1];
639
+ this.#position += fullMatch.length;
640
+ this.#tokenEnd = this.#position;
641
+ const value = parseInt(numStr, 10);
642
+ if (!Number.isSafeInteger(value) || value < 0) return err(parseError.invalidKnownValue(numStr, span(this.#tokenStart + 1, this.#tokenEnd - 1)));
643
+ return ok(token.knownValueNumber(value));
644
+ }
645
+ match = /^'([a-zA-Z_][a-zA-Z0-9_-]*)'/.exec(remaining);
646
+ if (match !== null) {
647
+ const fullMatch = match[0];
648
+ const name = match[1];
649
+ this.#position += fullMatch.length;
650
+ this.#tokenEnd = this.#position;
651
+ return ok(token.knownValueName(name));
652
+ }
653
+ this.#position++;
654
+ while (this.#position < this.#source.length && this.#source[this.#position] !== "'") this.#position++;
655
+ if (this.#position < this.#source.length) this.#position++;
656
+ this.#tokenEnd = this.#position;
657
+ return err(parseError.unrecognizedToken(this.span()));
658
+ }
659
+ #tryMatchUR() {
660
+ const urRegex = /^ur:([a-zA-Z0-9][a-zA-Z0-9-]*)\/([a-zA-Z]{8,})/;
661
+ const remaining = this.#source.slice(this.#position);
662
+ const match = urRegex.exec(remaining);
663
+ if (match !== null) {
664
+ const fullMatch = match[0];
665
+ this.#position += fullMatch.length;
666
+ this.#tokenEnd = this.#position;
667
+ try {
668
+ const ur = _bcts_uniform_resources.UR.fromURString(fullMatch);
669
+ return ok(token.ur(ur));
670
+ } catch (e) {
671
+ const errorMsg = e instanceof Error ? e.message : String(e);
672
+ return err(parseError.invalidUr(errorMsg, this.span()));
673
+ }
674
+ }
675
+ }
676
+ #tryMatchPunctuation() {
677
+ const ch = this.#source[this.#position];
678
+ const matched = {
679
+ "{": token.braceOpen(),
680
+ "}": token.braceClose(),
681
+ "[": token.bracketOpen(),
682
+ "]": token.bracketClose(),
683
+ "(": token.parenthesisOpen(),
684
+ ")": token.parenthesisClose(),
685
+ ":": token.colon(),
686
+ ",": token.comma()
687
+ }[ch];
688
+ if (matched !== void 0) {
689
+ this.#position++;
690
+ this.#tokenEnd = this.#position;
691
+ return ok(matched);
692
+ }
693
+ }
694
+ #matchLiteral(literal) {
695
+ if (this.#source.slice(this.#position, this.#position + literal.length) === literal) {
696
+ this.#position += literal.length;
697
+ return true;
698
+ }
699
+ return false;
700
+ }
701
+ #isIdentifierChar(ch) {
702
+ return /[a-zA-Z0-9_-]/.test(ch);
703
+ }
704
+ };
705
+ /**
706
+ * Converts a hex string to bytes.
707
+ */
708
+ function hexToBytes(hex) {
709
+ const bytes = new Uint8Array(hex.length / 2);
710
+ for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
711
+ return bytes;
712
+ }
713
+ /**
714
+ * Converts a base64 string to bytes with strict validation.
715
+ * Rejects base64 strings with invalid padding (matches Rust's base64 crate behavior).
716
+ */
717
+ function base64ToBytes(base64) {
718
+ const expectedPadding = (4 - base64.replace(/=/g, "").length % 4) % 4;
719
+ const paddingMatch = /=+$/.exec(base64);
720
+ if (expectedPadding !== (paddingMatch !== null ? paddingMatch[0].length : 0)) throw new Error("Invalid base64 padding");
721
+ const binaryString = atob(base64);
722
+ const bytes = new Uint8Array(binaryString.length);
723
+ for (let i = 0; i < binaryString.length; i++) bytes[i] = binaryString.charCodeAt(i);
724
+ return bytes;
725
+ }
726
+ /**
727
+ * Validates a date string has valid month/day values.
728
+ * JavaScript Date is lenient and accepts invalid dates like 2023-02-30,
729
+ * but Rust's Date::from_string rejects them.
730
+ */
731
+ function isValidDateString(dateStr) {
732
+ const dateMatch = /^(\d{4})-(\d{2})-(\d{2})/.exec(dateStr);
733
+ if (dateMatch === null) return false;
734
+ const year = parseInt(dateMatch[1], 10);
735
+ const month = parseInt(dateMatch[2], 10);
736
+ const day = parseInt(dateMatch[3], 10);
737
+ if (month < 1 || month > 12) return false;
738
+ if (day < 1) return false;
739
+ const daysInMonth = [
740
+ 31,
741
+ 28,
742
+ 31,
743
+ 30,
744
+ 31,
745
+ 30,
746
+ 31,
747
+ 31,
748
+ 30,
749
+ 31,
750
+ 30,
751
+ 31
752
+ ];
753
+ if ((year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) && month === 2) {
754
+ if (day > 29) return false;
755
+ } else if (day > daysInMonth[month - 1]) return false;
756
+ const timeMatch = /T(\d{2}):(\d{2}):(\d{2})/.exec(dateStr);
757
+ if (timeMatch !== null) {
758
+ const hour = parseInt(timeMatch[1], 10);
759
+ const minute = parseInt(timeMatch[2], 10);
760
+ const second = parseInt(timeMatch[3], 10);
761
+ if (hour < 0 || hour > 23) return false;
762
+ if (minute < 0 || minute > 59) return false;
763
+ if (second < 0 || second > 59) return false;
764
+ }
765
+ return true;
766
+ }
767
+
768
+ //#endregion
769
+ //#region src/parse.ts
770
+ /**
771
+ * @bcts/dcbor-parse - Parse module
772
+ *
773
+ * This is a 1:1 TypeScript port of bc-dcbor-parse-rust parse.rs
774
+ *
775
+ * @module dcbor-parse/parse
776
+ */
777
+ /**
778
+ * Parses a dCBOR item from a string input.
779
+ *
780
+ * This function takes a string slice containing a dCBOR diagnostic notation
781
+ * encoded value and attempts to parse it into a `Cbor` object. If the input
782
+ * contains extra tokens after a valid item, an error is returned.
783
+ *
784
+ * @param src - A string containing the dCBOR-encoded data.
785
+ * @returns `Ok(Cbor)` if parsing is successful and the input contains exactly one
786
+ * valid dCBOR item, which itself might be an atomic value like a number or
787
+ * string, or a complex value like an array or map.
788
+ * `Err(ParseError)` if parsing fails or if extra tokens are found after the item.
789
+ *
790
+ * @example
791
+ * ```typescript
792
+ * const result = parseDcborItem("[1, 2, 3]");
793
+ * if (result.ok) {
794
+ * console.log(result.value.toDiagnostic()); // "[1, 2, 3]"
795
+ * }
796
+ * ```
797
+ */
798
+ function parseDcborItem(src) {
799
+ const lexer = new Lexer(src);
800
+ const firstTokenResult = expectToken(lexer);
801
+ if (!firstTokenResult.ok) {
802
+ if (firstTokenResult.error.type === "UnexpectedEndOfInput") return err(parseError.emptyInput());
803
+ return firstTokenResult;
804
+ }
805
+ const parseResult = parseItemToken(firstTokenResult.value, lexer);
806
+ if (!parseResult.ok) return parseResult;
807
+ if (lexer.next() !== void 0) return err(parseError.extraData(lexer.span()));
808
+ return parseResult;
809
+ }
810
+ /**
811
+ * Parses a dCBOR item from the beginning of a string and returns the parsed
812
+ * `Cbor` along with the number of bytes consumed.
813
+ *
814
+ * Unlike `parseDcborItem`, this function succeeds even if additional
815
+ * characters follow the first item. The returned index points to the first
816
+ * unparsed character after skipping any trailing whitespace or comments.
817
+ *
818
+ * @param src - A string containing the dCBOR-encoded data.
819
+ * @returns `Ok([Cbor, number])` with the parsed item and bytes consumed.
820
+ *
821
+ * @example
822
+ * ```typescript
823
+ * const result = parseDcborItemPartial("true )");
824
+ * if (result.ok) {
825
+ * const [cbor, used] = result.value;
826
+ * console.log(cbor.toDiagnostic()); // "true"
827
+ * console.log(used); // 5
828
+ * }
829
+ * ```
830
+ */
831
+ function parseDcborItemPartial(src) {
832
+ const lexer = new Lexer(src);
833
+ const firstTokenResult = expectToken(lexer);
834
+ if (!firstTokenResult.ok) {
835
+ if (firstTokenResult.error.type === "UnexpectedEndOfInput") return err(parseError.emptyInput());
836
+ return firstTokenResult;
837
+ }
838
+ const parseResult = parseItemToken(firstTokenResult.value, lexer);
839
+ if (!parseResult.ok) return parseResult;
840
+ const consumed = lexer.next() !== void 0 ? lexer.span().start : src.length;
841
+ return ok([parseResult.value, consumed]);
842
+ }
843
+ function parseItem(lexer) {
844
+ const tokenResult = expectToken(lexer);
845
+ if (!tokenResult.ok) return tokenResult;
846
+ return parseItemToken(tokenResult.value, lexer);
847
+ }
848
+ function expectToken(lexer) {
849
+ const spanBefore = lexer.span();
850
+ const result = lexer.next();
851
+ if (result === void 0) return err(parseError.unexpectedEndOfInput());
852
+ if (!result.ok) {
853
+ if (isDefaultError(result.error)) return err(parseError.unrecognizedToken(spanBefore));
854
+ return result;
855
+ }
856
+ return result;
857
+ }
858
+ function parseItemToken(token$1, lexer) {
859
+ switch (token$1.type) {
860
+ case "Bool": return ok((0, _bcts_dcbor.cbor)(token$1.value));
861
+ case "Null": return ok((0, _bcts_dcbor.cbor)(null));
862
+ case "ByteStringHex": return ok((0, _bcts_dcbor.cbor)(token$1.value));
863
+ case "ByteStringBase64": return ok((0, _bcts_dcbor.cbor)(token$1.value));
864
+ case "DateLiteral": return ok((0, _bcts_dcbor.cbor)(token$1.value));
865
+ case "Number": return ok((0, _bcts_dcbor.cbor)(token$1.value));
866
+ case "NaN": return ok((0, _bcts_dcbor.cbor)(NaN));
867
+ case "Infinity": return ok((0, _bcts_dcbor.cbor)(Number.POSITIVE_INFINITY));
868
+ case "NegInfinity": return ok((0, _bcts_dcbor.cbor)(Number.NEGATIVE_INFINITY));
869
+ case "String": return parseString(token$1.value, lexer.span());
870
+ case "UR": return parseUr(token$1.value, lexer.span());
871
+ case "TagValue": return parseNumberTag(token$1.value, lexer);
872
+ case "TagName": return parseNameTag(token$1.value, lexer);
873
+ case "KnownValueNumber": return ok(new _bcts_known_values.KnownValue(token$1.value).taggedCbor());
874
+ case "KnownValueName": {
875
+ if (token$1.value === "") return ok(new _bcts_known_values.KnownValue(0).taggedCbor());
876
+ const knownValue = knownValueForName(token$1.value);
877
+ if (knownValue !== void 0) return ok(knownValue.taggedCbor());
878
+ const tokenSpan = lexer.span();
879
+ return err(parseError.unknownKnownValueName(token$1.value, span(tokenSpan.start + 1, tokenSpan.end - 1)));
880
+ }
881
+ case "Unit": return ok(new _bcts_known_values.KnownValue(0).taggedCbor());
882
+ case "BracketOpen": return parseArray(lexer);
883
+ case "BraceOpen": return parseMap(lexer);
884
+ case "BraceClose":
885
+ case "BracketClose":
886
+ case "ParenthesisOpen":
887
+ case "ParenthesisClose":
888
+ case "Colon":
889
+ case "Comma": return err(parseError.unexpectedToken(token$1, lexer.span()));
890
+ }
891
+ }
892
+ function parseString(s, tokenSpan) {
893
+ if (s.startsWith("\"") && s.endsWith("\"")) return ok((0, _bcts_dcbor.cbor)(s.slice(1, -1)));
894
+ return err(parseError.unrecognizedToken(tokenSpan));
895
+ }
896
+ function tagForName(name) {
897
+ return (0, _bcts_dcbor.getGlobalTagsStore)().tagForName(name)?.value;
898
+ }
899
+ function knownValueForName(name) {
900
+ return _bcts_known_values.KNOWN_VALUES.get().knownValueNamed(name);
901
+ }
902
+ function parseUr(ur, tokenSpan) {
903
+ const urType = ur.urTypeStr();
904
+ const tag = tagForName(urType);
905
+ if (tag !== void 0) return ok((0, _bcts_dcbor.cbor)({
906
+ tag,
907
+ value: ur.cbor()
908
+ }));
909
+ return err(parseError.unknownUrType(urType, span(tokenSpan.start + 3, tokenSpan.start + 3 + urType.length)));
910
+ }
911
+ function parseNumberTag(tagValue, lexer) {
912
+ const itemResult = parseItem(lexer);
913
+ if (!itemResult.ok) return itemResult;
914
+ const closeResult = expectToken(lexer);
915
+ if (!closeResult.ok) {
916
+ if (closeResult.error.type === "UnexpectedEndOfInput") return err(parseError.unmatchedParentheses(lexer.span()));
917
+ return closeResult;
918
+ }
919
+ if (closeResult.value.type === "ParenthesisClose") return ok((0, _bcts_dcbor.cbor)({
920
+ tag: tagValue,
921
+ value: itemResult.value
922
+ }));
923
+ return err(parseError.unmatchedParentheses(lexer.span()));
924
+ }
925
+ function parseNameTag(name, lexer) {
926
+ const tagSpan = span(lexer.span().start, lexer.span().end - 1);
927
+ const itemResult = parseItem(lexer);
928
+ if (!itemResult.ok) return itemResult;
929
+ const closeResult = expectToken(lexer);
930
+ if (!closeResult.ok) return closeResult;
931
+ if (closeResult.value.type === "ParenthesisClose") {
932
+ const tag = tagForName(name);
933
+ if (tag !== void 0) return ok((0, _bcts_dcbor.cbor)({
934
+ tag,
935
+ value: itemResult.value
936
+ }));
937
+ return err(parseError.unknownTagName(name, tagSpan));
938
+ }
939
+ return err(parseError.unmatchedParentheses(lexer.span()));
940
+ }
941
+ function parseArray(lexer) {
942
+ const items = [];
943
+ let awaitsComma = false;
944
+ let awaitsItem = false;
945
+ while (true) {
946
+ const tokenResult = expectToken(lexer);
947
+ if (!tokenResult.ok) return tokenResult;
948
+ const token$1 = tokenResult.value;
949
+ if (token$1.type === "BracketClose" && !awaitsItem) return ok((0, _bcts_dcbor.cbor)(items));
950
+ if (token$1.type === "Comma" && awaitsComma) {
951
+ awaitsItem = true;
952
+ awaitsComma = false;
953
+ continue;
954
+ }
955
+ if (awaitsComma) return err(parseError.expectedComma(lexer.span()));
956
+ const itemResult = parseItemToken(token$1, lexer);
957
+ if (!itemResult.ok) return itemResult;
958
+ items.push(itemResult.value);
959
+ awaitsItem = false;
960
+ awaitsComma = true;
961
+ }
962
+ }
963
+ function parseMap(lexer) {
964
+ const map = new _bcts_dcbor.CborMap();
965
+ let awaitsComma = false;
966
+ let awaitsKey = false;
967
+ while (true) {
968
+ const tokenResult = expectToken(lexer);
969
+ if (!tokenResult.ok) {
970
+ if (tokenResult.error.type === "UnexpectedEndOfInput") return err(parseError.unmatchedBraces(lexer.span()));
971
+ return tokenResult;
972
+ }
973
+ const token$1 = tokenResult.value;
974
+ if (token$1.type === "BraceClose" && !awaitsKey) return ok((0, _bcts_dcbor.cbor)(map));
975
+ if (token$1.type === "Comma" && awaitsComma) {
976
+ awaitsKey = true;
977
+ awaitsComma = false;
978
+ continue;
979
+ }
980
+ if (awaitsComma) return err(parseError.expectedComma(lexer.span()));
981
+ const keyResult = parseItemToken(token$1, lexer);
982
+ if (!keyResult.ok) return keyResult;
983
+ const key = keyResult.value;
984
+ const keySpan = lexer.span();
985
+ if (map.has(key)) return err(parseError.duplicateMapKey(keySpan));
986
+ const colonResult = expectToken(lexer);
987
+ if (!colonResult.ok) return colonResult;
988
+ if (colonResult.value.type !== "Colon") return err(parseError.expectedColon(lexer.span()));
989
+ const valueResult = parseItem(lexer);
990
+ if (!valueResult.ok) {
991
+ if (valueResult.error.type === "UnexpectedToken") {
992
+ if (valueResult.error.token.type === "BraceClose") return err(parseError.expectedMapKey(lexer.span()));
993
+ }
994
+ return valueResult;
995
+ }
996
+ map.set(key, valueResult.value);
997
+ awaitsKey = false;
998
+ awaitsComma = true;
999
+ }
1000
+ }
1001
+
1002
+ //#endregion
1003
+ //#region src/compose.ts
1004
+ /**
1005
+ * @bcts/dcbor-parse - Compose module
1006
+ *
1007
+ * This is a 1:1 TypeScript port of bc-dcbor-parse-rust compose.rs
1008
+ *
1009
+ * @module dcbor-parse/compose
1010
+ */
1011
+ const composeError = {
1012
+ oddMapLength() {
1013
+ return { type: "OddMapLength" };
1014
+ },
1015
+ duplicateMapKey() {
1016
+ return { type: "DuplicateMapKey" };
1017
+ },
1018
+ parseError(error) {
1019
+ return {
1020
+ type: "ParseError",
1021
+ error
1022
+ };
1023
+ }
1024
+ };
1025
+ /**
1026
+ * Gets the error message for a compose error.
1027
+ */
1028
+ function composeErrorMessage(error) {
1029
+ switch (error.type) {
1030
+ case "OddMapLength": return "Invalid odd map length";
1031
+ case "DuplicateMapKey": return "Duplicate map key";
1032
+ case "ParseError": return `Invalid CBOR item: ${error.error.type}`;
1033
+ }
1034
+ }
1035
+ /**
1036
+ * Creates a successful compose result.
1037
+ */
1038
+ function composeOk(value) {
1039
+ return {
1040
+ ok: true,
1041
+ value
1042
+ };
1043
+ }
1044
+ /**
1045
+ * Creates an error compose result.
1046
+ */
1047
+ function composeErr(error) {
1048
+ return {
1049
+ ok: false,
1050
+ error
1051
+ };
1052
+ }
1053
+ /**
1054
+ * Composes a dCBOR array from a slice of string slices, and returns a CBOR
1055
+ * object representing the array.
1056
+ *
1057
+ * Each string slice is parsed as a dCBOR item.
1058
+ *
1059
+ * @param array - Array of strings, each representing a dCBOR item
1060
+ * @returns A CBOR array containing all parsed items
1061
+ *
1062
+ * @example
1063
+ * ```typescript
1064
+ * const result = composeDcborArray(["1", "2", "3"]);
1065
+ * if (result.ok) {
1066
+ * console.log(result.value.toDiagnostic()); // "[1, 2, 3]"
1067
+ * }
1068
+ * ```
1069
+ */
1070
+ function composeDcborArray(array) {
1071
+ const result = [];
1072
+ for (const item of array) {
1073
+ const parseResult = parseDcborItem(item);
1074
+ if (!parseResult.ok) return composeErr(composeError.parseError(parseResult.error));
1075
+ result.push(parseResult.value);
1076
+ }
1077
+ return composeOk((0, _bcts_dcbor.cbor)(result));
1078
+ }
1079
+ /**
1080
+ * Composes a dCBOR map from a slice of string slices, and returns a CBOR
1081
+ * object representing the map.
1082
+ *
1083
+ * The length of the slice must be even, as each key must have a corresponding
1084
+ * value.
1085
+ *
1086
+ * Each string slice is parsed as a dCBOR item.
1087
+ *
1088
+ * @param array - Array of strings representing key-value pairs in alternating order
1089
+ * @returns A CBOR map containing all parsed key-value pairs
1090
+ *
1091
+ * @example
1092
+ * ```typescript
1093
+ * const result = composeDcborMap(["1", "2", "3", "4"]);
1094
+ * if (result.ok) {
1095
+ * console.log(result.value.toDiagnostic()); // "{1: 2, 3: 4}"
1096
+ * }
1097
+ * ```
1098
+ */
1099
+ function composeDcborMap(array) {
1100
+ if (array.length % 2 !== 0) return composeErr(composeError.oddMapLength());
1101
+ const map = new _bcts_dcbor.CborMap();
1102
+ for (let i = 0; i < array.length; i += 2) {
1103
+ const keyStr = array[i];
1104
+ const valueStr = array[i + 1];
1105
+ const keyResult = parseDcborItem(keyStr);
1106
+ if (!keyResult.ok) return composeErr(composeError.parseError(keyResult.error));
1107
+ const valueResult = parseDcborItem(valueStr);
1108
+ if (!valueResult.ok) return composeErr(composeError.parseError(valueResult.error));
1109
+ if (map.has(keyResult.value)) return composeErr(composeError.duplicateMapKey());
1110
+ map.set(keyResult.value, valueResult.value);
1111
+ }
1112
+ return composeOk((0, _bcts_dcbor.cbor)(map));
1113
+ }
1114
+
1115
+ //#endregion
1116
+ exports.Lexer = Lexer;
1117
+ exports.composeDcborArray = composeDcborArray;
1118
+ exports.composeDcborMap = composeDcborMap;
1119
+ exports.composeErr = composeErr;
1120
+ exports.composeError = composeError;
1121
+ exports.composeErrorMessage = composeErrorMessage;
1122
+ exports.composeOk = composeOk;
1123
+ exports.defaultParseError = defaultParseError;
1124
+ exports.defaultSpan = defaultSpan;
1125
+ exports.err = err;
1126
+ exports.errorMessage = errorMessage;
1127
+ exports.errorSpan = errorSpan;
1128
+ exports.fullErrorMessage = fullErrorMessage;
1129
+ exports.isDefaultError = isDefaultError;
1130
+ exports.isErr = isErr;
1131
+ exports.isOk = isOk;
1132
+ exports.ok = ok;
1133
+ exports.parseDcborItem = parseDcborItem;
1134
+ exports.parseDcborItemPartial = parseDcborItemPartial;
1135
+ exports.parseError = parseError;
1136
+ exports.span = span;
1137
+ exports.token = token;
1138
+ exports.unwrap = unwrap;
1139
+ exports.unwrapErr = unwrapErr;
1140
+ return exports;
1141
+ })({}, bctsDcbor, bctsKnownValues, bctsUniformResources);
1142
+ //# sourceMappingURL=index.iife.js.map