@cbortech/cbor 0.26.5 → 0.26.7

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 (52) hide show
  1. package/README.ja.md +239 -34
  2. package/README.md +242 -38
  3. package/dist/ast/CborByteString.d.ts +14 -0
  4. package/dist/ast/CborEllipsis.d.ts +94 -2
  5. package/dist/ast/CborFloat.d.ts +10 -0
  6. package/dist/ast/CborItem.d.ts +93 -4
  7. package/dist/ast/CborNint.d.ts +9 -0
  8. package/dist/ast/CborSimple.d.ts +11 -2
  9. package/dist/ast/CborTag.d.ts +8 -0
  10. package/dist/ast/CborTextString.d.ts +20 -0
  11. package/dist/ast/CborUint.d.ts +8 -0
  12. package/dist/ast/index.cjs +1 -1
  13. package/dist/ast/index.js +2 -2
  14. package/dist/cbor.d.ts +8 -0
  15. package/dist/cddl/ast.d.ts +196 -0
  16. package/dist/cddl/controls.d.ts +28 -0
  17. package/dist/cddl/equal.d.ts +19 -0
  18. package/dist/cddl/errors.d.ts +91 -0
  19. package/dist/cddl/index.cjs +3 -0
  20. package/dist/cddl/index.cjs.map +1 -0
  21. package/dist/cddl/index.d.ts +52 -0
  22. package/dist/cddl/index.js +67 -0
  23. package/dist/cddl/index.js.map +1 -0
  24. package/dist/cddl/parser.d.ts +13 -0
  25. package/dist/cddl/position.d.ts +13 -0
  26. package/dist/cddl/prelude.d.ts +5 -0
  27. package/dist/cddl/schema.d.ts +90 -0
  28. package/dist/cddl/tokenizer.d.ts +138 -0
  29. package/dist/cddl/validator.d.ts +30 -0
  30. package/dist/cddl/writer.d.ts +23 -0
  31. package/dist/cdn/serialize-utils.d.ts +186 -8
  32. package/dist/extensions/dt.d.ts +3 -0
  33. package/dist/extensions/types.d.ts +31 -9
  34. package/dist/index.cjs +4 -4
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.js +97 -107
  38. package/dist/index.js.map +1 -1
  39. package/dist/mapEntries-D2NyeCX3.cjs +17 -0
  40. package/dist/mapEntries-D2NyeCX3.cjs.map +1 -0
  41. package/dist/{mapEntries-Clr-oNtQ.js → mapEntries-DxrDre2P.js} +1498 -991
  42. package/dist/mapEntries-DxrDre2P.js.map +1 -0
  43. package/dist/schema-Bofmsptw.js +1977 -0
  44. package/dist/schema-Bofmsptw.js.map +1 -0
  45. package/dist/schema-t_bdPk8_.cjs +63 -0
  46. package/dist/schema-t_bdPk8_.cjs.map +1 -0
  47. package/dist/types.d.ts +258 -7
  48. package/dist/utils/base64.d.ts +12 -0
  49. package/package.json +24 -7
  50. package/dist/mapEntries-6hy7UgeN.cjs +0 -15
  51. package/dist/mapEntries-6hy7UgeN.cjs.map +0 -1
  52. package/dist/mapEntries-Clr-oNtQ.js.map +0 -1
package/README.ja.md CHANGED
@@ -6,13 +6,13 @@
6
6
  [![license](https://img.shields.io/npm/l/%40cbortech%2Fcbor)](./LICENSE)
7
7
  ![platform](https://img.shields.io/badge/platform-Node.js%20%7C%20Browser-blue)
8
8
 
9
- [CBOR](#準拠している仕様)、[CDN (CBOR-EDN)](#準拠している仕様)、JavaScript 値を相互変換するための TypeScript ライブラリです。
10
-
11
- ![CBOR、CDN、JavaScript 値の関係図](./assets/cbor-cdn-js.png)
9
+ [CBOR](#準拠している仕様)、[CDN (CBOR-EDN)](#準拠している仕様)、JavaScript 値の相互変換に加え、[CDDL](#準拠している仕様) スキーマのパース・整形・検証に対応する TypeScript ライブラリです。
12
10
 
13
11
  プレイグラウンドを **https://cbor.tech/cbor/** で公開しています。
14
12
 
15
- このパッケージは `CBOR` ファサードに加えて、extension の実装に必要な CBOR AST ノードクラス用の entrypoint を公開します。
13
+ ![CBOR、CDN、JavaScript 値の関係図](./assets/cbor-cdn-js.png)
14
+
15
+ このパッケージは CBOR ファサードに加えて、tooling や extension 向けに CDN、CDDL、AST の各 entrypoint を公開します。
16
16
  低レベルのパーサー、エンコーダー内部は、ドキュメント上の公開 API には含めていません。
17
17
 
18
18
  ## インストール
@@ -72,6 +72,20 @@ console.log(value);
72
72
  // { hello: 'world', n: 42 }
73
73
  ```
74
74
 
75
+ ### CDDL で検証する
76
+
77
+ CDDLスキーマをコンパイルし、CBOR バイト列、CDN テキスト、`CborItem` AST を検証できます。
78
+
79
+ ```ts
80
+ import { CDDL } from '@cbortech/cbor/cddl';
81
+
82
+ const schema = CDDL.compile('point = { x: int, y: int }');
83
+ const result = schema.validate('{"x": 12, "y": -3}');
84
+
85
+ console.log(result.valid);
86
+ // true
87
+ ```
88
+
75
89
  ### CBOR Sequence から JavaScript へ
76
90
 
77
91
  `decodeSeq` は連結された CBOR item を CBOR Sequence として読み取り、各 item を JavaScript 値として yield します。
@@ -316,6 +330,46 @@ CBOR.format('`\\d+`', { preserveRawString: true });
316
330
  // '`\\d+`'
317
331
  ```
318
332
 
333
+ ### ダブルクォート文字列の表記を保持する
334
+
335
+ デフォルトでは、`CBOR.format()` はダブルクォートのテキスト文字列をデコード後の
336
+ 値から再エスケープします。そのため `\uXXXX` エスケープはリテラル文字に変換され
337
+ ます。`preserveTextString` を指定すると、連結されていない `"..."` リテラルを
338
+ 元のソース表記のまま再出力します。(バッククォートの raw 文字列(`` `...` ``)
339
+ はこのオプションではなく `preserveRawString` の対象です。`+` 連結を経由した
340
+ 文字列は、このオプションを指定していても通常どおり正規化されます。)
341
+
342
+ ```ts
343
+ import { CBOR } from '@cbortech/cbor';
344
+
345
+ CBOR.format('"caf\\u00e9"');
346
+ // '"café"'
347
+
348
+ CBOR.format('"caf\\u00e9"', { preserveTextString: true });
349
+ // '"caf\\u00e9"'
350
+ ```
351
+
352
+ ### 数値リテラルの表記を保持する
353
+
354
+ デフォルトでは、`CBOR.format()` は整数・浮動小数点数リテラルを正規化します。
355
+ 16進数・8進数・2進数の整数(`0xff`、`0o377`、`0b101`)は10進数に変換され、
356
+ 末尾のゼロや冗長なエンコーディング指標のサフィックス(`1.50`、`1.5_1`)は
357
+ 省略されます。`preserveNumberFormat` を指定すると、これらのリテラルを元の
358
+ CDN ソース表記のまま再出力します。`intFormat` / `floatFormat` より優先され
359
+ ます。CDN テキストからパースされたリテラルにのみ効果があり、`CBOR.from()`
360
+ で構築した値や CBOR バイト列からデコードした値には効果がありません(通常
361
+ どおりの整形になります)。
362
+
363
+ ```ts
364
+ import { CBOR } from '@cbortech/cbor';
365
+
366
+ CBOR.format('{"a": 0xff, "b": 1.50}');
367
+ // '{"a":255,"b":1.5}'
368
+
369
+ CBOR.format('{"a": 0xff, "b": 1.50}', { preserveNumberFormat: true });
370
+ // '{"a":0xff,"b":1.50}'
371
+ ```
372
+
319
373
  ### `+` による文字列連結を保持する
320
374
 
321
375
  注意: `+` による文字列連結構文は draft-26 で削除されました。この節は legacy
@@ -350,6 +404,110 @@ CBOR.format("h'68' + b64'aQ'", {
350
404
  // b64'aQ'
351
405
  ```
352
406
 
407
+ ### application-string / -sequence 記法を保持する
408
+
409
+ 一部の組み込み拡張(`dt`/`DT`、`ip`/`IP`)は同じ値に対して `prefix'...'`
410
+ (application string)、`` prefix`...` ``(backtick application string)、
411
+ `prefix<<...>>`(application sequence)、生のタグリテラル(`N(...)`)の
412
+ いずれの記法もサポートしていますが、デフォルトでは `CBOR.format()` を呼ぶ
413
+ たびに解決済みの値から `prefix'...'` を再生成します。そのため
414
+ `` DT`1969-07-21T02:56:16Z` `` も `DT<<'1969-07-21T02:56:16Z'>>` も、生の
415
+ タグ記法 `1(1749772800)` さえも、すべて `DT'...'` 記法に正規化され、さら
416
+ に非正規な `DT'...'` の表記(例えば `Z` の代わりに `+00:00` を使った場合
417
+ など)も書き換えられます。`preserveAppSequence` を指定すると、実際に使わ
418
+ れていた表記のまま保持します。`appStrings: false` を同時に指定した場合は
419
+ 効果がありません(どちらにせよ元の表記に関わらず生のタグ記法になるため)。
420
+ これらの記法からパースされていない値にも効果はありません。
421
+
422
+ ```ts
423
+ import { CBOR } from '@cbortech/cbor';
424
+
425
+ CBOR.format('1(1749772800)');
426
+ // "DT'2025-06-13T00:00:00Z'"
427
+
428
+ CBOR.format('1(1749772800)', { preserveAppSequence: true });
429
+ // "1(1749772800)"
430
+
431
+ CBOR.format("DT<<'1969-07-21T02:56:16Z'>>", { preserveAppSequence: true });
432
+ // "DT<<'1969-07-21T02:56:16Z'>>"
433
+
434
+ CBOR.format('DT`1969-07-21T02:56:16Z`', { preserveAppSequence: true });
435
+ // "DT`1969-07-21T02:56:16Z`"
436
+ ```
437
+
438
+ ### 空行を保持する
439
+
440
+ デフォルトでは、`CBOR.format()` は配列・マップの要素間(および `(_ ...)`
441
+ のチャンク間)の空行を再シリアライズ時に取り除きます。`preserveBlankLines`
442
+ を指定すると、元のソースでその要素の前のどこかに空行があった場合、要素の
443
+ 直前に空行を 1 行だけ再出力します。要素をパラグラフのようにまとめる元の
444
+ 見た目を、フォーマット後も保てます。元の空行が何行連続していても、出力
445
+ されるのは常に 1 行だけです。判定は要素の位置だけに基づいており、
446
+ `preserveComments` は不要で、コメントを出力するかどうかにも影響されません。
447
+ `indent` を指定して整形出力する場合のみ効果があり、空行を保持する
448
+ コンテナは `inlineLeafContainers` が有効でも常に 1 要素 1 行で出力されます。
449
+ `preserveAll` にも含まれます。
450
+
451
+ ```ts
452
+ import { CBOR } from '@cbortech/cbor';
453
+
454
+ const src = `[
455
+ 1,
456
+ 2,
457
+
458
+ 3
459
+ ]`;
460
+
461
+ CBOR.format(src, { indent: 2 });
462
+ // [
463
+ // 1,
464
+ // 2,
465
+ // 3
466
+ // ]
467
+
468
+ CBOR.format(src, { indent: 2, preserveBlankLines: true });
469
+ // [
470
+ // 1,
471
+ // 2,
472
+ //
473
+ // 3
474
+ // ]
475
+ ```
476
+
477
+ ### 変更を最小限にとどめてフォーマットする
478
+
479
+ `preserveAll` を指定すると、すべての `preserve*` 系オプションが一括で
480
+ 有効になります。たとえばエディタの保存時フォーマットのように、空白・
481
+ インデントだけを変更し、ほとんどのリテラルの元の表記には手を加えずに
482
+ CDN テキストを整形できます(bignum だけは例外です。上記の
483
+ `preserveNumberFormat` の説明を参照してください)。個別のオプションを
484
+ 明示的に指定した場合(`false` も含む)は、そちらが `preserveAll` より
485
+ 優先されます。
486
+
487
+ ```ts
488
+ import { CBOR } from '@cbortech/cbor';
489
+
490
+ CBOR.format('{"a":0xff,"b":1.5_1,"c":b64\'aGk=\'}', {
491
+ indent: 2,
492
+ preserveAll: true,
493
+ });
494
+ // {
495
+ // "a": 0xff,
496
+ // "b": 1.5_1,
497
+ // "c": b64'aGk='
498
+ // }
499
+ ```
500
+
501
+ `CBOR.format()` は内部で `fromCDN()` と `toCDN()` の両方に同じオプション
502
+ を渡すため、このオプション一つで済みます。両者を別々に呼び出す場合は、
503
+ コメントはパース時に取り込んでおく必要があるため、`fromCDN()` 側にも
504
+ `preserveAll`(または `preserveComments`)を指定してください。
505
+
506
+ ```ts
507
+ const item = CBOR.fromCDN(text, { preserveAll: true });
508
+ item.toCDN({ preserveAll: true, indent: 2 });
509
+ ```
510
+
353
511
  ### CBOR / CDN / hex dump のバリデーション
354
512
 
355
513
  `validate` は入力の well-formedness と validity を、例外を投げずにチェックします。
@@ -865,12 +1023,63 @@ const lenient = tokenizeLenient('[1, "ab');
865
1023
  インスタンスで、`offset`・`line`・`column`、判明している場合は `endOffset` を
866
1024
  保持します。
867
1025
 
1026
+ ## CDDL
1027
+
1028
+ `@cbortech/cbor/cddl` サブパスには、CDDL のパーサ・コンパイラ・バリデータが
1029
+ 入っています。CDDL は CBOR データ構造を記述するスキーマ言語です。
1030
+ コンパイル済みスキーマで、CBOR バイト列・CDN テキスト・`CborItem` AST を
1031
+ 検証できます。
1032
+
1033
+ ```ts
1034
+ import { CDDL } from '@cbortech/cbor/cddl';
1035
+
1036
+ const schema = CDDL.compile('point = { x: int, y: int }');
1037
+
1038
+ console.log(schema.validate('{"x": 12, "y": -3}').valid);
1039
+ // true
1040
+ ```
1041
+
1042
+ 検証は throw せず結果オブジェクトを返し、失敗時は入力内のパスと入力・
1043
+ スキーマ双方のソース位置を報告します。既定では最初のルールを使い、`rule`
1044
+ で別の非 generic 型ルールを指定できます。このほか `features`、`maxDepth`、
1045
+ `maxSteps` を指定できます。
1046
+
1047
+ [RFC 8610](https://www.rfc-editor.org/rfc/rfc8610) の全 control operator と、
1048
+ [RFC 9165](https://www.rfc-editor.org/rfc/rfc9165) の `.plus`、`.cat`、
1049
+ `.feature` を実装しています。`.feature` の許可名は `features` オプションで
1050
+ 指定します。`.abnf` などの未対応演算子は `result.warnings` に報告され、
1051
+ 制約なしで判定されます。
1052
+
1053
+ メインの `CBOR` facade でも、`cddl` オプションにコンパイル済みスキーマ
1054
+ または CDDL ソーステキストを渡して検証できます。
1055
+
1056
+ ```ts
1057
+ import { CBOR } from '@cbortech/cbor';
1058
+
1059
+ const value = CBOR.parse('{"x": 12, "y": -3}', {
1060
+ cddl: 'point = { x: int, y: int }',
1061
+ });
1062
+ // { x: 12, y: -3 }
1063
+ ```
1064
+
1065
+ `parse`・`decode`・`encode` などは不一致時に `CddlMismatchError` を throw
1066
+ します。`CBOR.validate()` は代わりに `result.cddlErrors` へ収集します。
1067
+ 検証オプションは `cddlValidationOptions` で渡せます。`cddl` は
1068
+ `new CBOR({ cddl: … })` のようにインスタンスのデフォルトにもできます。
1069
+
1070
+ `CDDL.compile()` は `CddlSyntaxError` または `CddlSemanticError` を throw
1071
+ します。`{ strict: false }` を指定すると、意味上の問題を
1072
+ `schema.warnings` に収集できます。コンパイル済みスキーマは
1073
+ `schema.format()` で整形できます。同じサブパスから `tokenize`、
1074
+ `tokenizeLenient`、`schema.ast`、`schema.rules` も利用できます。
1075
+
868
1076
  ## 公開 API
869
1077
 
870
1078
  ドキュメント化している公開 export は次のとおりです。
871
1079
 
872
1080
  - `CBOR`
873
1081
  - `CdnSyntaxError`
1082
+ - `CddlMismatchError`(`cddl` オプションが throw。[CDDL](#cddl) 節を参照)
874
1083
 
875
1084
  `CBOR` ファサードからは次にもアクセスできます。
876
1085
 
@@ -884,38 +1093,34 @@ const lenient = tokenizeLenient('[1, "ab');
884
1093
  (`tokenize`, `tokenizeLenient`, `Token`, `TokenType`, `EdnComment`)に、
885
1094
  AST ノードクラスは `@cbortech/cbor/ast` にあります。
886
1095
 
887
- ## 準拠している仕様
888
-
889
- このライブラリは次の仕様を対象にしています。
890
-
891
- - [CBOR, RFC 8949](https://www.rfc-editor.org/rfc/rfc8949)
892
- - [Concise Diagnostic Notation (CDN), draft-ietf-cbor-edn-literals-25](https://datatracker.ietf.org/doc/draft-ietf-cbor-edn-literals/25/)
1096
+ CDDL コンパイラは `@cbortech/cbor/cddl`
1097
+ (`CDDL`, `CddlSchema`, `CddlSyntaxError`, `CddlSemanticError`,
1098
+ `CddlMismatchError`, `tokenize`, `tokenizeLenient`, CDDL AST 型)に
1099
+ あります。
893
1100
 
894
- draft -25 をベースに、
895
- [draft -26](https://datatracker.ietf.org/doc/draft-ietf-cbor-edn-literals/26/)
896
- の一部仕様も先行して取り込んでいます。
897
-
898
- - 文字列連結 extension `t1` / `b1`(§3.4)
899
- - 不定長文字列 extension `ilbs` / `ilts`(§3.5)
900
- - `float` extension のデフォルト有効化(§3.7)
901
- - draft-26 の raw string 規則(§2.5.4): 終端デリミタは開始デリミタと
902
- 同数のバッククォートに限られ、スペース除去規則がすべてのデリミタ長に
903
- 適用されます
904
-
905
- draft -26 で削除された `+` による文字列連結構文と、非推奨となった
906
- `(_ ...)` streamstring 構文は、引き続き受理します。CDN は Internet-Draft
907
- として策定中の仕様であり、今後も変更される可能性がある点に注意して
908
- ください(たとえば extension 名 `t1` / `b1` は暫定とされています)。
909
-
910
- CDN は、CBOR データを人間が読み書きしやすいテキストとして表現するための記法です。
911
- サンプル、テストベクター、デバッグ、fixture、設定ファイルに近い用途など、CBOR のバイト列をそのまま扱うと読みにくい場面で役立ちます。
912
-
913
- 通常の配列、マップ、文字列、数値、真偽値、null は JSON に近い見た目で書けます。
914
- 一方で、CBOR 固有の byte string、tag、simple value、不定長 item、文字列以外の map key、
915
- `dt'2026-05-06T00:00:00Z'` のような application literal も表現できます。
1101
+ ## 準拠している仕様
916
1102
 
917
- CDN は JSON / JSONC の上位互換なので、通常の JSON データやコメント付きの JSON 風データも、
918
- 特別な変換なしに CDN としてパース・整形できます。
1103
+ - CBOR
1104
+ - [RFC 8949](https://www.rfc-editor.org/rfc/rfc8949)
1105
+ - CDN (CBOR-EDN)
1106
+ - [draft-ietf-cbor-edn-literals-25](https://datatracker.ietf.org/doc/draft-ietf-cbor-edn-literals/25/)
1107
+ - [draft-ietf-cbor-edn-literals-26](https://datatracker.ietf.org/doc/draft-ietf-cbor-edn-literals/26/)
1108
+ - CDDL
1109
+ - [RFC 8610](https://www.rfc-editor.org/rfc/rfc8610)
1110
+ - [RFC 9682](https://www.rfc-editor.org/rfc/rfc9682)
1111
+ - [RFC 9165](https://www.rfc-editor.org/rfc/rfc9165)
1112
+
1113
+ 補足:
1114
+
1115
+ - CDN は draft-26 に準拠しつつ、draft-25 の `(_ ...)` streamstring 構文と
1116
+ `+` による文字列連結構文も引き続きサポートしています。
1117
+ - CDDL は RFC 8610 のすべての control operator と、RFC 9165 の `.plus`、
1118
+ `.cat`、`.feature` をサポートしています。
1119
+ - RFC 9682 の更新内容である文字列リテラル文法(`\u{...}` を含む)、構文上の
1120
+ 空データモデル(ルールなしのモデルはコンパイル時に意味エラー)、非リテラルの
1121
+ `#6.<type>` / `#7.<type>` head number に対応しています。コメントの `PCHAR`
1122
+ 検証、単独 CR の改行、EOF で終わるコメントは、collected ABNF よりも意図的に
1123
+ 寛容に受理します。
919
1124
 
920
1125
  ## ライセンス
921
1126
 
package/README.md CHANGED
@@ -7,14 +7,15 @@
7
7
  ![platform](https://img.shields.io/badge/platform-Node.js%20%7C%20Browser-blue)
8
8
 
9
9
  TypeScript library for converting between [CBOR](#specifications),
10
- [CDN (CBOR-EDN)](#specifications), and JavaScript values.
11
-
12
- ![Relationship between CBOR, CDN, and JavaScript values](./assets/cbor-cdn-js.png)
10
+ [CDN (CBOR-EDN)](#specifications), and JavaScript values, plus parsing,
11
+ formatting, and validation for [CDDL](#specifications) schemas.
13
12
 
14
13
  A live playground is available at **https://cbor.tech/cbor/**.
15
14
 
16
- This package exposes the `CBOR` facade plus a separate AST entrypoint for the
17
- CBOR node classes needed by extensions. Lower-level parser and encoder internals
15
+ ![Relationship between CBOR, CDN, and JavaScript values](./assets/cbor-cdn-js.png)
16
+
17
+ This package exposes the CBOR facade plus separate CDN, CDDL, and AST
18
+ entrypoints for tooling and extensions. Lower-level parser and encoder internals
18
19
  are not part of the documented public API.
19
20
 
20
21
  ## Install
@@ -75,6 +76,21 @@ console.log(value);
75
76
  // { hello: 'world', n: 42 }
76
77
  ```
77
78
 
79
+ ### Validate with CDDL
80
+
81
+ Compile a CDDL schema and validate CBOR bytes, CDN text, or a `CborItem` AST
82
+ against it:
83
+
84
+ ```ts
85
+ import { CDDL } from '@cbortech/cbor/cddl';
86
+
87
+ const schema = CDDL.compile('point = { x: int, y: int }');
88
+ const result = schema.validate('{"x": 12, "y": -3}');
89
+
90
+ console.log(result.valid);
91
+ // true
92
+ ```
93
+
78
94
  ### CBOR Sequence to JavaScript values
79
95
 
80
96
  `decodeSeq` reads concatenated CBOR items as a CBOR Sequence and yields each item as a JavaScript value.
@@ -320,6 +336,45 @@ CBOR.format('`\\d+`', { preserveRawString: true });
320
336
  // '`\\d+`'
321
337
  ```
322
338
 
339
+ ### Preserve double-quoted string spelling
340
+
341
+ By default, `CBOR.format()` re-escapes double-quoted text strings from their
342
+ decoded value, so e.g. a `\uXXXX` escape becomes the literal character.
343
+ `preserveTextString` re-emits a non-concatenated `"..."` literal using its
344
+ original source spelling instead. (Raw backtick literals such as `` `...` ``
345
+ are covered by `preserveRawString`, not this option; a string reached via
346
+ `+` concatenation is normalised as usual regardless of this option.)
347
+
348
+ ```ts
349
+ import { CBOR } from '@cbortech/cbor';
350
+
351
+ CBOR.format('"caf\\u00e9"');
352
+ // '"café"'
353
+
354
+ CBOR.format('"caf\\u00e9"', { preserveTextString: true });
355
+ // '"caf\\u00e9"'
356
+ ```
357
+
358
+ ### Preserve number literal spelling
359
+
360
+ By default, `CBOR.format()` normalizes integer and floating-point literals:
361
+ hex/octal/binary integers (`0xff`, `0o377`, `0b101`) become decimal, trailing
362
+ zeros and redundant encoding-indicator suffixes (`1.50`, `1.5_1`) are
363
+ dropped. `preserveNumberFormat` re-emits these literals using their original
364
+ CDN source spelling instead, taking precedence over `intFormat` /
365
+ `floatFormat`. It only affects literals parsed from CDN text — values built
366
+ with `CBOR.from()` or decoded from CBOR bytes always use normal formatting.
367
+
368
+ ```ts
369
+ import { CBOR } from '@cbortech/cbor';
370
+
371
+ CBOR.format('{"a": 0xff, "b": 1.50}');
372
+ // '{"a":255,"b":1.5}'
373
+
374
+ CBOR.format('{"a": 0xff, "b": 1.50}', { preserveNumberFormat: true });
375
+ // '{"a":0xff,"b":1.50}'
376
+ ```
377
+
323
378
  ### Preserve `+` string concatenation
324
379
 
325
380
  Note: `+` string concatenation was removed in draft-26. This section is for
@@ -356,6 +411,110 @@ CBOR.format("h'68' + b64'aQ'", {
356
411
  // b64'aQ'
357
412
  ```
358
413
 
414
+ ### Preserve application-string/-sequence notation
415
+
416
+ Some built-in extensions (`dt`/`DT`, `ip`/`IP`) support `prefix'...'`
417
+ (application string), `` prefix`...` `` (backtick application string),
418
+ `prefix<<...>>` (application sequence), and a raw tag literal (`N(...)`)
419
+ notation for the same value, and by default regenerate `prefix'...'` from
420
+ the resolved value on every `CBOR.format()` call — so
421
+ `` DT`1969-07-21T02:56:16Z` ``, `DT<<'1969-07-21T02:56:16Z'>>`, and even the
422
+ raw tag form `1(1749772800)` all normalize to `DT'...'` notation, and a
423
+ non-canonical `DT'...'` spelling (e.g. a `+00:00` offset instead of `Z`)
424
+ gets rewritten too. `preserveAppSequence` keeps the original spelling
425
+ instead — whichever form was used. It has no effect when
426
+ `appStrings: false` is also set (raw tag notation is used either way
427
+ regardless of the original spelling), or on values not parsed from one of
428
+ these forms.
429
+
430
+ ```ts
431
+ import { CBOR } from '@cbortech/cbor';
432
+
433
+ CBOR.format('1(1749772800)');
434
+ // "DT'2025-06-13T00:00:00Z'"
435
+
436
+ CBOR.format('1(1749772800)', { preserveAppSequence: true });
437
+ // "1(1749772800)"
438
+
439
+ CBOR.format("DT<<'1969-07-21T02:56:16Z'>>", { preserveAppSequence: true });
440
+ // "DT<<'1969-07-21T02:56:16Z'>>"
441
+
442
+ CBOR.format('DT`1969-07-21T02:56:16Z`', { preserveAppSequence: true });
443
+ // "DT`1969-07-21T02:56:16Z`"
444
+ ```
445
+
446
+ ### Preserve blank lines
447
+
448
+ By default, `CBOR.format()` drops blank lines between array/map entries (and
449
+ `(_ ...)` chunks) when re-serializing. `preserveBlankLines` re-emits a single
450
+ blank line above an entry that had one anywhere before it in the source, so
451
+ paragraph-like groupings of entries survive a reformat — at most one blank
452
+ line per gap, regardless of how many were originally there. Detection is
453
+ based on entry positions alone: it does not require `preserveComments` and is
454
+ unaffected by whether comments are emitted. Only effective when `indent`
455
+ enables pretty-printing; a container with a preserved blank line is always
456
+ rendered one entry per line, even under `inlineLeafContainers`. Included in
457
+ `preserveAll`.
458
+
459
+ ```ts
460
+ import { CBOR } from '@cbortech/cbor';
461
+
462
+ const src = `[
463
+ 1,
464
+ 2,
465
+
466
+ 3
467
+ ]`;
468
+
469
+ CBOR.format(src, { indent: 2 });
470
+ // [
471
+ // 1,
472
+ // 2,
473
+ // 3
474
+ // ]
475
+
476
+ CBOR.format(src, { indent: 2, preserveBlankLines: true });
477
+ // [
478
+ // 1,
479
+ // 2,
480
+ //
481
+ // 3
482
+ // ]
483
+ ```
484
+
485
+ ### Format with minimal changes
486
+
487
+ `preserveAll` turns on every `preserve*` option at once, to reformat CDN
488
+ text — e.g. when reformatting on save in an editor — touching only
489
+ whitespace/indentation and leaving most literals' original spelling
490
+ untouched (bignums are the one exception; see `preserveNumberFormat`
491
+ above). An explicitly-set individual option (including `false`) still wins
492
+ over `preserveAll`.
493
+
494
+ ```ts
495
+ import { CBOR } from '@cbortech/cbor';
496
+
497
+ CBOR.format('{"a":0xff,"b":1.5_1,"c":b64\'aGk=\'}', {
498
+ indent: 2,
499
+ preserveAll: true,
500
+ });
501
+ // {
502
+ // "a": 0xff,
503
+ // "b": 1.5_1,
504
+ // "c": b64'aGk='
505
+ // }
506
+ ```
507
+
508
+ `CBOR.format()` passes the same options to both `fromCDN()` and `toCDN()`
509
+ internally, so this one option is enough. Calling them separately needs
510
+ `preserveAll` (or `preserveComments`) on the `fromCDN()` side too, since
511
+ comments must be captured while parsing to be re-emittable later:
512
+
513
+ ```ts
514
+ const item = CBOR.fromCDN(text, { preserveAll: true });
515
+ item.toCDN({ preserveAll: true, indent: 2 });
516
+ ```
517
+
359
518
  ### Validate CBOR / CDN / hex dump
360
519
 
361
520
  `validate` checks input for well-formedness and validity without throwing.
@@ -874,12 +1033,63 @@ Syntax errors thrown by `fromCDN`/`parse`/`tokenize` are `CdnSyntaxError`
874
1033
  instances (a `SyntaxError` subclass, also exported from the main entry) and
875
1034
  carry `offset`, `line`, `column`, and — where known — `endOffset`.
876
1035
 
1036
+ ## CDDL
1037
+
1038
+ The `@cbortech/cbor/cddl` subpath contains a parser, compiler, and validator for
1039
+ CDDL, the schema language for describing CBOR data structures. A compiled schema
1040
+ validates CBOR bytes, CDN text, or a `CborItem` AST:
1041
+
1042
+ ```ts
1043
+ import { CDDL } from '@cbortech/cbor/cddl';
1044
+
1045
+ const schema = CDDL.compile('point = { x: int, y: int }');
1046
+
1047
+ console.log(schema.validate('{"x": 12, "y": -3}').valid);
1048
+ // true
1049
+ ```
1050
+
1051
+ Validation returns a result object rather than throwing. Failures include the
1052
+ instance path and source offsets for both the input and schema. Validation uses
1053
+ the first rule by default; pass `rule` to select another non-generic type rule.
1054
+ Options also include `features`, `maxDepth`, and `maxSteps`.
1055
+
1056
+ All control operators from
1057
+ [RFC 8610](https://www.rfc-editor.org/rfc/rfc8610) are implemented, along with
1058
+ [RFC 9165](https://www.rfc-editor.org/rfc/rfc9165)'s `.plus`, `.cat`, and
1059
+ `.feature`. Enable `.feature` names with the `features` validation option.
1060
+ Unsupported operators such as `.abnf` are reported in `result.warnings` and
1061
+ matched without their constraint.
1062
+
1063
+ The main `CBOR` facade also accepts a compiled schema or CDDL source text through
1064
+ the `cddl` option:
1065
+
1066
+ ```ts
1067
+ import { CBOR } from '@cbortech/cbor';
1068
+
1069
+ const value = CBOR.parse('{"x": 12, "y": -3}', {
1070
+ cddl: 'point = { x: int, y: int }',
1071
+ });
1072
+ // { x: 12, y: -3 }
1073
+ ```
1074
+
1075
+ Throwing methods such as `parse`, `decode`, and `encode` throw
1076
+ `CddlMismatchError` on a mismatch; `CBOR.validate()` instead collects failures
1077
+ in `result.cddlErrors`. Pass validator options through `cddlValidationOptions`,
1078
+ or set `cddl` as an instance default with `new CBOR({ cddl: … })`.
1079
+
1080
+ `CDDL.compile()` throws `CddlSyntaxError` or `CddlSemanticError`; use
1081
+ `{ strict: false }` to collect semantic issues in `schema.warnings` instead.
1082
+ Compiled schemas can be formatted with `schema.format()`. The subpath also
1083
+ exports `tokenize`, `tokenizeLenient`, and a typed rule AST through `schema.ast`
1084
+ and `schema.rules`.
1085
+
877
1086
  ## Public API
878
1087
 
879
1088
  The documented public exports are:
880
1089
 
881
1090
  - `CBOR`
882
1091
  - `CdnSyntaxError`
1092
+ - `CddlMismatchError` (thrown by the `cddl` option; see [CDDL](#cddl))
883
1093
 
884
1094
  The `CBOR` facade also exposes:
885
1095
 
@@ -893,41 +1103,35 @@ Lower-level CDN tokenization lives in `@cbortech/cbor/cdn`
893
1103
  (`tokenize`, `tokenizeLenient`, `Token`, `TokenType`, `EdnComment`),
894
1104
  and AST node classes in `@cbortech/cbor/ast`.
895
1105
 
896
- ## Specifications
897
-
898
- This library targets:
899
-
900
- - [CBOR, RFC 8949](https://www.rfc-editor.org/rfc/rfc8949)
901
- - [Concise Diagnostic Notation (CDN), draft-ietf-cbor-edn-literals-25](https://datatracker.ietf.org/doc/draft-ietf-cbor-edn-literals/25/)
1106
+ The CDDL compiler lives in `@cbortech/cbor/cddl`
1107
+ (`CDDL`, `CddlSchema`, `CddlSyntaxError`, `CddlSemanticError`,
1108
+ `CddlMismatchError`, `tokenize`, `tokenizeLenient`, and the CDDL AST
1109
+ types).
902
1110
 
903
- On top of draft -25, this library already incorporates parts of
904
- [draft -26](https://datatracker.ietf.org/doc/draft-ietf-cbor-edn-literals/26/):
905
-
906
- - the `t1` / `b1` string-concatenation extensions (§3.4)
907
- - the `ilbs` / `ilts` indefinite-length string extensions (§3.5)
908
- - the `float` extension as a default extension (§3.7)
909
- - the draft-26 raw-string delimiter and trimming rules (§2.5.4): the closing
910
- delimiter must have exactly as many backquotes as the opening one, and the
911
- space-trimming rule applies to all delimiter lengths
912
-
913
- The legacy `+` string-concatenation syntax (removed in draft -26) and the
914
- `(_ ...)` streamstring syntax (deprecated in draft -26) are still accepted.
915
- Note that the CDN specification is still an Internet-Draft and may continue
916
- to change (for example, the extension names `t1` and `b1` are explicitly
917
- provisional).
918
-
919
- CDN is a human-readable text notation for CBOR data. It is useful for
920
- examples, test vectors, debugging, fixtures, and configuration-like files where
921
- raw CBOR bytes would be hard to read.
922
-
923
- It looks similar to JSON for ordinary arrays, maps, strings, numbers, booleans,
924
- and null values, but it can also represent CBOR-specific features such as byte
925
- strings, tags, simple values, indefinite-length items, non-string map keys, and
926
- application literals like `dt'2026-05-06T00:00:00Z'`.
1111
+ ## Specifications
927
1112
 
928
- CDN is a superset of JSON and JSONC, so ordinary JSON data and
929
- commented JSON-style data can be parsed and formatted as CDN without
930
- special handling.
1113
+ - CBOR
1114
+ - [RFC 8949](https://www.rfc-editor.org/rfc/rfc8949)
1115
+ - CDN (CBOR-EDN)
1116
+ - [draft-ietf-cbor-edn-literals-25](https://datatracker.ietf.org/doc/draft-ietf-cbor-edn-literals/25/)
1117
+ - [draft-ietf-cbor-edn-literals-26](https://datatracker.ietf.org/doc/draft-ietf-cbor-edn-literals/26/)
1118
+ - CDDL
1119
+ - [RFC 8610](https://www.rfc-editor.org/rfc/rfc8610)
1120
+ - [RFC 9682](https://www.rfc-editor.org/rfc/rfc9682)
1121
+ - [RFC 9165](https://www.rfc-editor.org/rfc/rfc9165)
1122
+
1123
+ Implementation notes:
1124
+
1125
+ - CDN follows draft-26 while retaining draft-25's `(_ ...)` streamstring syntax
1126
+ and `+` string-concatenation syntax.
1127
+ - CDDL implements every RFC 8610 control operator, plus RFC 9165's `.plus`,
1128
+ `.cat`, and `.feature`.
1129
+ - The RFC 9682 updates are implemented: its string-literal grammar (including
1130
+ `\u{...}`), empty data models at the syntax layer (a model with no rules is
1131
+ still a semantic error when compiled), and non-literal `#6.<type>` /
1132
+ `#7.<type>` head numbers. Comment `PCHAR` validation, bare CR line endings,
1133
+ and comments ending at EOF are intentionally accepted more leniently than the
1134
+ collected ABNF.
931
1135
 
932
1136
  ## License
933
1137