@marcuspuchalla/nachos 0.1.3 → 0.2.0
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.
- package/CHANGELOG.md +75 -0
- package/dist/{chunk-PTWN7K3Y.cjs → chunk-3Z45RBZP.cjs} +469 -244
- package/dist/chunk-3Z45RBZP.cjs.map +1 -0
- package/dist/{chunk-2MTLSQ7E.js → chunk-EDXZTSIA.js} +224 -166
- package/dist/chunk-EDXZTSIA.js.map +1 -0
- package/dist/{chunk-R62CQQNI.cjs → chunk-HMUA5KLG.cjs} +239 -181
- package/dist/chunk-HMUA5KLG.cjs.map +1 -0
- package/dist/{chunk-ZDZ2B5PE.js → chunk-JESIF5IF.js} +7 -3
- package/dist/chunk-JESIF5IF.js.map +1 -0
- package/dist/{chunk-5A5T56JB.js → chunk-LWNWC2O7.js} +442 -217
- package/dist/chunk-LWNWC2O7.js.map +1 -0
- package/dist/{chunk-PD72MVTX.cjs → chunk-P6A2OOIY.cjs} +7 -3
- package/dist/chunk-P6A2OOIY.cjs.map +1 -0
- package/dist/encoder/index.cjs +14 -14
- package/dist/encoder/index.d.cts +5 -4
- package/dist/encoder/index.d.ts +5 -4
- package/dist/encoder/index.js +2 -2
- package/dist/index.cjs +58 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -21
- package/dist/index.d.ts +40 -21
- package/dist/index.js +37 -17
- package/dist/index.js.map +1 -1
- package/dist/metafile-cjs.json +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/parser/index.cjs +21 -21
- package/dist/parser/index.d.cts +4 -2
- package/dist/parser/index.d.ts +4 -2
- package/dist/parser/index.js +2 -2
- package/dist/{types-DvNlfbKB.d.cts → types-eG2qalpr.d.cts} +27 -1
- package/dist/{types-DvNlfbKB.d.ts → types-eG2qalpr.d.ts} +27 -1
- package/dist/{useCborSimpleEncoder-TVxzNJ_9.d.ts → useCborSimpleEncoder-CamvS-_N.d.ts} +7 -3
- package/dist/{useCborSimpleEncoder-ButVU988.d.cts → useCborSimpleEncoder-DXgPx62d.d.cts} +7 -3
- package/dist/{useCborTag-xV2Pz2VY.d.ts → useCborTag-D4d7xG3-.d.cts} +9 -4
- package/dist/{useCborTag-Cs1CZuXZ.d.cts → useCborTag-TYst1KR6.d.ts} +9 -4
- package/package.json +1 -1
- package/src/__tests__/audit-fixes.test.ts +141 -0
- package/src/__tests__/public-api.test.ts +153 -0
- package/src/__tests__/roundtrip.test.ts +5 -6
- package/src/encoder/__tests__/cbor-collection-encoder.test.ts +103 -5
- package/src/encoder/__tests__/cbor-encoder-errors.test.ts +40 -5
- package/src/encoder/__tests__/cbor-simple-encoder.test.ts +126 -0
- package/src/encoder/composables/useCborCollectionEncoder.ts +30 -26
- package/src/encoder/composables/useCborEncoder.ts +40 -0
- package/src/encoder/composables/useCborSimpleEncoder.ts +40 -9
- package/src/encoder/types.ts +9 -4
- package/src/encoder/utils.ts +33 -1
- package/src/index.ts +39 -20
- package/src/parser/__tests__/buffer-native-parsing.test.ts +338 -0
- package/src/parser/__tests__/cbor-map-duplicate-keys.test.ts +97 -7
- package/src/parser/__tests__/cbor-security-dos-protection.test.ts +164 -31
- package/src/parser/__tests__/cbor-standard-tags.test.ts +75 -7
- package/src/parser/__tests__/cbor-tag-reparse-fix.test.ts +268 -0
- package/src/parser/__tests__/utils-errors.test.ts +11 -3
- package/src/parser/composables/useCborCollection.ts +51 -45
- package/src/parser/composables/useCborDiagnostic.ts +28 -0
- package/src/parser/composables/useCborFloat.ts +2 -1
- package/src/parser/composables/useCborInteger.ts +24 -10
- package/src/parser/composables/useCborParser.ts +448 -208
- package/src/parser/composables/useCborTag.ts +53 -38
- package/src/parser/types.ts +32 -1
- package/src/parser/utils.ts +52 -0
- package/dist/chunk-2MTLSQ7E.js.map +0 -1
- package/dist/chunk-5A5T56JB.js.map +0 -1
- package/dist/chunk-PD72MVTX.cjs.map +0 -1
- package/dist/chunk-PTWN7K3Y.cjs.map +0 -1
- package/dist/chunk-R62CQQNI.cjs.map +0 -1
- package/dist/chunk-ZDZ2B5PE.js.map +0 -1
package/dist/parser/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { c as useCborCollection, d as useCborFloat, a as useCborInteger, u as useCborParser, b as useCborString, e as useCborTag } from '../useCborTag-
|
|
2
|
-
export { o as CborAdditionalInfo, C as CborContext, n as CborMajorType, f as CborMap, p as CborSimpleValue, q as CborTag, e as CborValue, m as DEFAULT_LIMITS, D as DEFAULT_OPTIONS, r as DupMapKeyMode, c as ParseError, P as ParseOptions, a as ParseResult, b as ParseResultWithMap, d as ParserLimits, l as PlutusBytes, h as PlutusConstr, g as PlutusData, k as PlutusInt, j as PlutusList, i as PlutusMap, R as Result, S as SourceMapEntry, T as TaggedValue } from '../types-
|
|
1
|
+
export { c as useCborCollection, d as useCborFloat, a as useCborInteger, u as useCborParser, b as useCborString, e as useCborTag } from '../useCborTag-D4d7xG3-.cjs';
|
|
2
|
+
export { o as CborAdditionalInfo, C as CborContext, n as CborMajorType, f as CborMap, p as CborSimpleValue, q as CborTag, e as CborValue, m as DEFAULT_LIMITS, D as DEFAULT_OPTIONS, r as DupMapKeyMode, c as ParseError, P as ParseOptions, a as ParseResult, b as ParseResultWithMap, d as ParserLimits, l as PlutusBytes, h as PlutusConstr, g as PlutusData, k as PlutusInt, j as PlutusList, i as PlutusMap, R as Result, S as SourceMapEntry, T as TaggedValue } from '../types-eG2qalpr.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Common utility functions for CBOR parsing
|
|
@@ -7,6 +7,8 @@ export { o as CborAdditionalInfo, C as CborContext, n as CborMajorType, f as Cbo
|
|
|
7
7
|
/**
|
|
8
8
|
* Converts hex string to Uint8Array
|
|
9
9
|
*
|
|
10
|
+
* Validates even length and hex-only characters.
|
|
11
|
+
*
|
|
10
12
|
* @param hex - Hex string (e.g., "1864")
|
|
11
13
|
* @returns Byte array
|
|
12
14
|
*/
|
package/dist/parser/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { c as useCborCollection, d as useCborFloat, a as useCborInteger, u as useCborParser, b as useCborString, e as useCborTag } from '../useCborTag-
|
|
2
|
-
export { o as CborAdditionalInfo, C as CborContext, n as CborMajorType, f as CborMap, p as CborSimpleValue, q as CborTag, e as CborValue, m as DEFAULT_LIMITS, D as DEFAULT_OPTIONS, r as DupMapKeyMode, c as ParseError, P as ParseOptions, a as ParseResult, b as ParseResultWithMap, d as ParserLimits, l as PlutusBytes, h as PlutusConstr, g as PlutusData, k as PlutusInt, j as PlutusList, i as PlutusMap, R as Result, S as SourceMapEntry, T as TaggedValue } from '../types-
|
|
1
|
+
export { c as useCborCollection, d as useCborFloat, a as useCborInteger, u as useCborParser, b as useCborString, e as useCborTag } from '../useCborTag-TYst1KR6.js';
|
|
2
|
+
export { o as CborAdditionalInfo, C as CborContext, n as CborMajorType, f as CborMap, p as CborSimpleValue, q as CborTag, e as CborValue, m as DEFAULT_LIMITS, D as DEFAULT_OPTIONS, r as DupMapKeyMode, c as ParseError, P as ParseOptions, a as ParseResult, b as ParseResultWithMap, d as ParserLimits, l as PlutusBytes, h as PlutusConstr, g as PlutusData, k as PlutusInt, j as PlutusList, i as PlutusMap, R as Result, S as SourceMapEntry, T as TaggedValue } from '../types-eG2qalpr.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Common utility functions for CBOR parsing
|
|
@@ -7,6 +7,8 @@ export { o as CborAdditionalInfo, C as CborContext, n as CborMajorType, f as Cbo
|
|
|
7
7
|
/**
|
|
8
8
|
* Converts hex string to Uint8Array
|
|
9
9
|
*
|
|
10
|
+
* Validates even length and hex-only characters.
|
|
11
|
+
*
|
|
10
12
|
* @param hex - Hex string (e.g., "1864")
|
|
11
13
|
* @returns Byte array
|
|
12
14
|
*/
|
package/dist/parser/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { bytesToHex, extractCborHeader, hexToBytes, readBigUint, readByte, readUint, useCborCollection, useCborFloat, useCborInteger, useCborParser, useCborString, useCborTag, validateUtf8Strict } from '../chunk-
|
|
2
|
-
export { CborAdditionalInfo, CborMajorType, CborSimpleValue, CborTag, DEFAULT_LIMITS, DEFAULT_OPTIONS } from '../chunk-
|
|
1
|
+
export { bytesToHex, extractCborHeader, hexToBytes, readBigUint, readByte, readUint, useCborCollection, useCborFloat, useCborInteger, useCborParser, useCborString, useCborTag, validateUtf8Strict } from '../chunk-LWNWC2O7.js';
|
|
2
|
+
export { CborAdditionalInfo, CborMajorType, CborSimpleValue, CborTag, DEFAULT_LIMITS, DEFAULT_OPTIONS } from '../chunk-JESIF5IF.js';
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
4
4
|
//# sourceMappingURL=index.js.map
|
|
@@ -30,6 +30,19 @@ interface ParserLimits {
|
|
|
30
30
|
* Following RFC 8949 Section 5.6 guidance
|
|
31
31
|
*/
|
|
32
32
|
type DupMapKeyMode = 'allow' | 'warn' | 'reject';
|
|
33
|
+
/**
|
|
34
|
+
* Map key ordering for canonical/deterministic encoding and validation.
|
|
35
|
+
*
|
|
36
|
+
* - 'length-first': RFC 7049 Section 3.9 "Old Canonical CBOR" — shorter encoded
|
|
37
|
+
* keys sort first, ties broken bytewise. This is what Cardano CIP-21 mandates
|
|
38
|
+
* for transaction serialization, and is the default in this library.
|
|
39
|
+
* - 'bytewise': RFC 8949 Section 4.2.1 "Core Deterministic Encoding" — pure
|
|
40
|
+
* bytewise lexicographic order of the encoded keys (the modern generic default).
|
|
41
|
+
*
|
|
42
|
+
* @see https://cips.cardano.org/cip/CIP-21
|
|
43
|
+
* @see https://www.rfc-editor.org/rfc/rfc8949.html#section-4.2.1
|
|
44
|
+
*/
|
|
45
|
+
type MapKeyOrder = 'length-first' | 'bytewise';
|
|
33
46
|
/**
|
|
34
47
|
* Parser options for controlling behavior
|
|
35
48
|
*/
|
|
@@ -55,6 +68,19 @@ interface ParseOptions {
|
|
|
55
68
|
validateTagSemantics?: boolean;
|
|
56
69
|
/** Validate Plutus constructor semantics (Tags 102, 121-127, 1280-1400) */
|
|
57
70
|
validatePlutusSemantics?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Map key ordering enforced when validateCanonical is set.
|
|
73
|
+
* Defaults to 'length-first' (Cardano CIP-21 / RFC 7049 Section 3.9).
|
|
74
|
+
* Use 'bytewise' for RFC 8949 Section 4.2.1 core deterministic order.
|
|
75
|
+
*/
|
|
76
|
+
mapKeyOrder?: MapKeyOrder;
|
|
77
|
+
/**
|
|
78
|
+
* Reject trailing bytes after the top-level data item (well-formedness).
|
|
79
|
+
* Defaults to true for backward compatibility (decode returns bytesRead so
|
|
80
|
+
* callers can detect leftover data); automatically false-tightened, i.e. set
|
|
81
|
+
* to reject, in strict mode. Set explicitly to override.
|
|
82
|
+
*/
|
|
83
|
+
allowTrailingData?: boolean;
|
|
58
84
|
/** Resource limits */
|
|
59
85
|
limits?: ParserLimits;
|
|
60
86
|
}
|
|
@@ -298,4 +324,4 @@ type Result<T, E> = {
|
|
|
298
324
|
error: E;
|
|
299
325
|
};
|
|
300
326
|
|
|
301
|
-
export { type CborContext as C, DEFAULT_OPTIONS as D, type ParseOptions as P, type Result as R, type SourceMapEntry as S, type TaggedValue as T, type ParseResult as a, type ParseResultWithMap as b, type ParseError as c, type ParserLimits as d, type CborValue as e, type CborMap as f, type PlutusData as g, type PlutusConstr as h, type PlutusMap as i, type PlutusList as j, type PlutusInt as k, type PlutusBytes as l, DEFAULT_LIMITS as m, CborMajorType as n, CborAdditionalInfo as o, CborSimpleValue as p, CborTag as q, type DupMapKeyMode as r, type CborByteString as s, type CborTextString as t };
|
|
327
|
+
export { type CborContext as C, DEFAULT_OPTIONS as D, type MapKeyOrder as M, type ParseOptions as P, type Result as R, type SourceMapEntry as S, type TaggedValue as T, type ParseResult as a, type ParseResultWithMap as b, type ParseError as c, type ParserLimits as d, type CborValue as e, type CborMap as f, type PlutusData as g, type PlutusConstr as h, type PlutusMap as i, type PlutusList as j, type PlutusInt as k, type PlutusBytes as l, DEFAULT_LIMITS as m, CborMajorType as n, CborAdditionalInfo as o, CborSimpleValue as p, CborTag as q, type DupMapKeyMode as r, type CborByteString as s, type CborTextString as t };
|
|
@@ -30,6 +30,19 @@ interface ParserLimits {
|
|
|
30
30
|
* Following RFC 8949 Section 5.6 guidance
|
|
31
31
|
*/
|
|
32
32
|
type DupMapKeyMode = 'allow' | 'warn' | 'reject';
|
|
33
|
+
/**
|
|
34
|
+
* Map key ordering for canonical/deterministic encoding and validation.
|
|
35
|
+
*
|
|
36
|
+
* - 'length-first': RFC 7049 Section 3.9 "Old Canonical CBOR" — shorter encoded
|
|
37
|
+
* keys sort first, ties broken bytewise. This is what Cardano CIP-21 mandates
|
|
38
|
+
* for transaction serialization, and is the default in this library.
|
|
39
|
+
* - 'bytewise': RFC 8949 Section 4.2.1 "Core Deterministic Encoding" — pure
|
|
40
|
+
* bytewise lexicographic order of the encoded keys (the modern generic default).
|
|
41
|
+
*
|
|
42
|
+
* @see https://cips.cardano.org/cip/CIP-21
|
|
43
|
+
* @see https://www.rfc-editor.org/rfc/rfc8949.html#section-4.2.1
|
|
44
|
+
*/
|
|
45
|
+
type MapKeyOrder = 'length-first' | 'bytewise';
|
|
33
46
|
/**
|
|
34
47
|
* Parser options for controlling behavior
|
|
35
48
|
*/
|
|
@@ -55,6 +68,19 @@ interface ParseOptions {
|
|
|
55
68
|
validateTagSemantics?: boolean;
|
|
56
69
|
/** Validate Plutus constructor semantics (Tags 102, 121-127, 1280-1400) */
|
|
57
70
|
validatePlutusSemantics?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Map key ordering enforced when validateCanonical is set.
|
|
73
|
+
* Defaults to 'length-first' (Cardano CIP-21 / RFC 7049 Section 3.9).
|
|
74
|
+
* Use 'bytewise' for RFC 8949 Section 4.2.1 core deterministic order.
|
|
75
|
+
*/
|
|
76
|
+
mapKeyOrder?: MapKeyOrder;
|
|
77
|
+
/**
|
|
78
|
+
* Reject trailing bytes after the top-level data item (well-formedness).
|
|
79
|
+
* Defaults to true for backward compatibility (decode returns bytesRead so
|
|
80
|
+
* callers can detect leftover data); automatically false-tightened, i.e. set
|
|
81
|
+
* to reject, in strict mode. Set explicitly to override.
|
|
82
|
+
*/
|
|
83
|
+
allowTrailingData?: boolean;
|
|
58
84
|
/** Resource limits */
|
|
59
85
|
limits?: ParserLimits;
|
|
60
86
|
}
|
|
@@ -298,4 +324,4 @@ type Result<T, E> = {
|
|
|
298
324
|
error: E;
|
|
299
325
|
};
|
|
300
326
|
|
|
301
|
-
export { type CborContext as C, DEFAULT_OPTIONS as D, type ParseOptions as P, type Result as R, type SourceMapEntry as S, type TaggedValue as T, type ParseResult as a, type ParseResultWithMap as b, type ParseError as c, type ParserLimits as d, type CborValue as e, type CborMap as f, type PlutusData as g, type PlutusConstr as h, type PlutusMap as i, type PlutusList as j, type PlutusInt as k, type PlutusBytes as l, DEFAULT_LIMITS as m, CborMajorType as n, CborAdditionalInfo as o, CborSimpleValue as p, CborTag as q, type DupMapKeyMode as r, type CborByteString as s, type CborTextString as t };
|
|
327
|
+
export { type CborContext as C, DEFAULT_OPTIONS as D, type MapKeyOrder as M, type ParseOptions as P, type Result as R, type SourceMapEntry as S, type TaggedValue as T, type ParseResult as a, type ParseResultWithMap as b, type ParseError as c, type ParserLimits as d, type CborValue as e, type CborMap as f, type PlutusData as g, type PlutusConstr as h, type PlutusMap as i, type PlutusList as j, type PlutusInt as k, type PlutusBytes as l, DEFAULT_LIMITS as m, CborMajorType as n, CborAdditionalInfo as o, CborSimpleValue as p, CborTag as q, type DupMapKeyMode as r, type CborByteString as s, type CborTextString as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h as PlutusConstr, s as CborByteString, t as CborTextString } from './types-
|
|
1
|
+
import { h as PlutusConstr, M as MapKeyOrder, s as CborByteString, t as CborTextString } from './types-eG2qalpr.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* CBOR Encoder Type Definitions
|
|
@@ -15,6 +15,12 @@ interface EncodeOptions {
|
|
|
15
15
|
allowIndefinite?: boolean;
|
|
16
16
|
/** Reject duplicate map keys */
|
|
17
17
|
rejectDuplicateKeys?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Map key ordering used in canonical mode.
|
|
20
|
+
* Defaults to 'length-first' (Cardano CIP-21 / RFC 7049 §3.9).
|
|
21
|
+
* Use 'bytewise' for RFC 8949 §4.2.1 core deterministic order.
|
|
22
|
+
*/
|
|
23
|
+
mapKeyOrder?: MapKeyOrder;
|
|
18
24
|
/** Maximum nesting depth */
|
|
19
25
|
maxDepth?: number;
|
|
20
26
|
/** Maximum output size in bytes */
|
|
@@ -56,8 +62,6 @@ interface TaggedValue {
|
|
|
56
62
|
interface EncodeContext {
|
|
57
63
|
/** Current nesting depth */
|
|
58
64
|
depth: number;
|
|
59
|
-
/** Bytes written so far */
|
|
60
|
-
bytesWritten: number;
|
|
61
65
|
/** Encoder options */
|
|
62
66
|
options: Required<EncodeOptions>;
|
|
63
67
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { h as PlutusConstr, s as CborByteString, t as CborTextString } from './types-
|
|
1
|
+
import { h as PlutusConstr, M as MapKeyOrder, s as CborByteString, t as CborTextString } from './types-eG2qalpr.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* CBOR Encoder Type Definitions
|
|
@@ -15,6 +15,12 @@ interface EncodeOptions {
|
|
|
15
15
|
allowIndefinite?: boolean;
|
|
16
16
|
/** Reject duplicate map keys */
|
|
17
17
|
rejectDuplicateKeys?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Map key ordering used in canonical mode.
|
|
20
|
+
* Defaults to 'length-first' (Cardano CIP-21 / RFC 7049 §3.9).
|
|
21
|
+
* Use 'bytewise' for RFC 8949 §4.2.1 core deterministic order.
|
|
22
|
+
*/
|
|
23
|
+
mapKeyOrder?: MapKeyOrder;
|
|
18
24
|
/** Maximum nesting depth */
|
|
19
25
|
maxDepth?: number;
|
|
20
26
|
/** Maximum output size in bytes */
|
|
@@ -56,8 +62,6 @@ interface TaggedValue {
|
|
|
56
62
|
interface EncodeContext {
|
|
57
63
|
/** Current nesting depth */
|
|
58
64
|
depth: number;
|
|
59
|
-
/** Bytes written so far */
|
|
60
|
-
bytesWritten: number;
|
|
61
65
|
/** Encoder options */
|
|
62
66
|
options: Required<EncodeOptions>;
|
|
63
67
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { P as ParseOptions, a as ParseResult, b as ParseResultWithMap, e as CborValue, S as SourceMapEntry } from './types-
|
|
1
|
+
import { P as ParseOptions, a as ParseResult, b as ParseResultWithMap, e as CborValue, S as SourceMapEntry, h as PlutusConstr } from './types-eG2qalpr.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* CBOR Main Parser Composable
|
|
@@ -19,9 +19,9 @@ import { P as ParseOptions, a as ParseResult, b as ParseResultWithMap, e as Cbor
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
declare function useCborParser(): {
|
|
22
|
-
parse: (
|
|
23
|
-
parseWithSourceMap: (
|
|
24
|
-
parseSequence: (
|
|
22
|
+
parse: (input: string | Uint8Array, options?: ParseOptions) => ParseResult;
|
|
23
|
+
parseWithSourceMap: (input: string | Uint8Array, options?: ParseOptions) => ParseResultWithMap;
|
|
24
|
+
parseSequence: (input: string | Uint8Array, options?: ParseOptions) => CborValue[];
|
|
25
25
|
parseSequenceWithSourceMap: (hexString: string, options?: ParseOptions) => {
|
|
26
26
|
values: CborValue[];
|
|
27
27
|
sourceMaps: SourceMapEntry[][];
|
|
@@ -47,6 +47,7 @@ declare function useCborParser(): {
|
|
|
47
47
|
*/
|
|
48
48
|
declare function useCborInteger(): {
|
|
49
49
|
parseInteger: (hexString: string, options?: ParseOptions) => ParseResult;
|
|
50
|
+
parseIntegerFromBuffer: (buffer: Uint8Array, offset: number, options?: ParseOptions) => ParseResult;
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
/**
|
|
@@ -115,6 +116,7 @@ declare function useCborFloat(): {
|
|
|
115
116
|
parse: (hexString: string, options?: ParseOptions) => ParseResult;
|
|
116
117
|
parseFloat: (hexString: string, options?: ParseOptions) => ParseResult;
|
|
117
118
|
parseSimple: (hexString: string, _options?: ParseOptions) => ParseResult;
|
|
119
|
+
parseFromBuffer: (buffer: Uint8Array, offset: number, options?: ParseOptions) => ParseResult;
|
|
118
120
|
};
|
|
119
121
|
|
|
120
122
|
/**
|
|
@@ -137,6 +139,9 @@ declare function useCborFloat(): {
|
|
|
137
139
|
declare function useCborTag(): {
|
|
138
140
|
parseTag: (hexString: string, options?: ParseOptions) => ParseResult;
|
|
139
141
|
parse: (hexString: string, options?: ParseOptions) => ParseResult;
|
|
142
|
+
parseTagFromBuffer: (buffer: Uint8Array, offset: number, options?: ParseOptions, tagDepth?: number) => ParseResult;
|
|
143
|
+
validateTagSemantics: (tagNumber: number, value: CborValue, options?: ParseOptions) => void;
|
|
144
|
+
decodePlutusConstructor: (tagNumber: number, value: CborValue) => PlutusConstr | null;
|
|
140
145
|
};
|
|
141
146
|
|
|
142
147
|
export { useCborInteger as a, useCborString as b, useCborCollection as c, useCborFloat as d, useCborTag as e, useCborParser as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { P as ParseOptions, a as ParseResult, b as ParseResultWithMap, e as CborValue, S as SourceMapEntry } from './types-
|
|
1
|
+
import { P as ParseOptions, a as ParseResult, b as ParseResultWithMap, e as CborValue, S as SourceMapEntry, h as PlutusConstr } from './types-eG2qalpr.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* CBOR Main Parser Composable
|
|
@@ -19,9 +19,9 @@ import { P as ParseOptions, a as ParseResult, b as ParseResultWithMap, e as Cbor
|
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
21
|
declare function useCborParser(): {
|
|
22
|
-
parse: (
|
|
23
|
-
parseWithSourceMap: (
|
|
24
|
-
parseSequence: (
|
|
22
|
+
parse: (input: string | Uint8Array, options?: ParseOptions) => ParseResult;
|
|
23
|
+
parseWithSourceMap: (input: string | Uint8Array, options?: ParseOptions) => ParseResultWithMap;
|
|
24
|
+
parseSequence: (input: string | Uint8Array, options?: ParseOptions) => CborValue[];
|
|
25
25
|
parseSequenceWithSourceMap: (hexString: string, options?: ParseOptions) => {
|
|
26
26
|
values: CborValue[];
|
|
27
27
|
sourceMaps: SourceMapEntry[][];
|
|
@@ -47,6 +47,7 @@ declare function useCborParser(): {
|
|
|
47
47
|
*/
|
|
48
48
|
declare function useCborInteger(): {
|
|
49
49
|
parseInteger: (hexString: string, options?: ParseOptions) => ParseResult;
|
|
50
|
+
parseIntegerFromBuffer: (buffer: Uint8Array, offset: number, options?: ParseOptions) => ParseResult;
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
/**
|
|
@@ -115,6 +116,7 @@ declare function useCborFloat(): {
|
|
|
115
116
|
parse: (hexString: string, options?: ParseOptions) => ParseResult;
|
|
116
117
|
parseFloat: (hexString: string, options?: ParseOptions) => ParseResult;
|
|
117
118
|
parseSimple: (hexString: string, _options?: ParseOptions) => ParseResult;
|
|
119
|
+
parseFromBuffer: (buffer: Uint8Array, offset: number, options?: ParseOptions) => ParseResult;
|
|
118
120
|
};
|
|
119
121
|
|
|
120
122
|
/**
|
|
@@ -137,6 +139,9 @@ declare function useCborFloat(): {
|
|
|
137
139
|
declare function useCborTag(): {
|
|
138
140
|
parseTag: (hexString: string, options?: ParseOptions) => ParseResult;
|
|
139
141
|
parse: (hexString: string, options?: ParseOptions) => ParseResult;
|
|
142
|
+
parseTagFromBuffer: (buffer: Uint8Array, offset: number, options?: ParseOptions, tagDepth?: number) => ParseResult;
|
|
143
|
+
validateTagSemantics: (tagNumber: number, value: CborValue, options?: ParseOptions) => void;
|
|
144
|
+
decodePlutusConstructor: (tagNumber: number, value: CborValue) => PlutusConstr | null;
|
|
140
145
|
};
|
|
141
146
|
|
|
142
147
|
export { useCborInteger as a, useCborString as b, useCborCollection as c, useCborFloat as d, useCborTag as e, useCborParser as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marcuspuchalla/nachos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "NACHOS - Not Another CBOR Handling Object System. RFC 8949 CBOR encoder/decoder with full source map support for interactive debugging. Zero dependencies, TypeScript, works in Node.js and browsers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nachos",
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit Remediation Regression Tests
|
|
3
|
+
*
|
|
4
|
+
* Locks in the fixes from the June 2026 RFC 8949 conformance/security audit.
|
|
5
|
+
* Each test maps to a finding ID (H1, H2, M1-M5, L1, L3, L5).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect } from 'vitest'
|
|
9
|
+
import { decode, decodeWithSourceMap, encode, encodeToHex, toDiagnostic } from '../index'
|
|
10
|
+
|
|
11
|
+
describe('Audit fixes', () => {
|
|
12
|
+
describe('H1 — source-map parse path enforces tag-depth limit', () => {
|
|
13
|
+
const deepTag = 'c2'.repeat(60000) + '00'
|
|
14
|
+
|
|
15
|
+
it('decode() rejects deeply nested tags cleanly', () => {
|
|
16
|
+
expect(() => decode(deepTag, { limits: { maxTagDepth: 100 } }))
|
|
17
|
+
.toThrow(/Tag nesting depth/)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('decodeWithSourceMap() rejects deeply nested tags cleanly (no stack overflow)', () => {
|
|
21
|
+
let err: Error | null = null
|
|
22
|
+
try { decodeWithSourceMap(deepTag, { limits: { maxTagDepth: 100 } }) }
|
|
23
|
+
catch (e) { err = e as Error }
|
|
24
|
+
expect(err).not.toBeNull()
|
|
25
|
+
expect(err!.constructor.name).toBe('Error') // NOT RangeError
|
|
26
|
+
expect(err!.message).toMatch(/Tag nesting depth/)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('still decodes legitimately nested tags', () => {
|
|
30
|
+
const r = decode('c1c0' + '60', { limits: { maxTagDepth: 100 } }) // tag1(tag0(""))
|
|
31
|
+
expect((r.value as any).tag).toBe(1)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('H2 — map key ordering (CIP-21 length-first default, bytewise option)', () => {
|
|
36
|
+
// key "aa" -> 62 61 61 (len 3); key 1000000 -> 1a 00 0f 42 40 (len 5)
|
|
37
|
+
const m = new Map<any, any>([['aa', 1], [1000000, 2]])
|
|
38
|
+
|
|
39
|
+
it('canonical default is length-first (Cardano CIP-21)', () => {
|
|
40
|
+
expect(encodeToHex(m, { canonical: true })).toBe('a2' + '626161' + '01' + '1a000f4240' + '02')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('mapKeyOrder:bytewise yields RFC 8949 §4.2.1 order', () => {
|
|
44
|
+
expect(encodeToHex(m, { canonical: true, mapKeyOrder: 'bytewise' }))
|
|
45
|
+
.toBe('a2' + '1a000f4240' + '02' + '626161' + '01')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const lenFirst = 'a2' + '626161' + '01' + '1a000f4240' + '02'
|
|
49
|
+
const byteWise = 'a2' + '1a000f4240' + '02' + '626161' + '01'
|
|
50
|
+
|
|
51
|
+
it('decoder (default) accepts length-first order', () => {
|
|
52
|
+
expect(() => decode(lenFirst, { validateCanonical: true })).not.toThrow()
|
|
53
|
+
})
|
|
54
|
+
it('decoder (bytewise) accepts bytewise order', () => {
|
|
55
|
+
expect(() => decode(byteWise, { validateCanonical: true, mapKeyOrder: 'bytewise' })).not.toThrow()
|
|
56
|
+
})
|
|
57
|
+
it('decoder (bytewise) rejects length-first order', () => {
|
|
58
|
+
expect(() => decode(lenFirst, { validateCanonical: true, mapKeyOrder: 'bytewise' }))
|
|
59
|
+
.toThrow(/not in canonical order/)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('encoder output round-trips through its own canonical decoder (both orders)', () => {
|
|
63
|
+
const lf = encodeToHex(m, { canonical: true })
|
|
64
|
+
const bw = encodeToHex(m, { canonical: true, mapKeyOrder: 'bytewise' })
|
|
65
|
+
expect(() => decode(lf, { validateCanonical: true })).not.toThrow()
|
|
66
|
+
expect(() => decode(bw, { validateCanonical: true, mapKeyOrder: 'bytewise' })).not.toThrow()
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('M1 — trailing data well-formedness', () => {
|
|
71
|
+
it('default lenient: reports bytesRead and ignores trailing', () => {
|
|
72
|
+
expect(decode('000102').bytesRead).toBe(1)
|
|
73
|
+
})
|
|
74
|
+
it('allowTrailingData:false rejects trailing bytes', () => {
|
|
75
|
+
expect(() => decode('000102', { allowTrailingData: false })).toThrow(/Trailing data/)
|
|
76
|
+
})
|
|
77
|
+
it('strict mode rejects trailing bytes', () => {
|
|
78
|
+
expect(() => decode('000102', { strict: true })).toThrow(/Trailing data/)
|
|
79
|
+
})
|
|
80
|
+
it('strict mode accepts an exact single item', () => {
|
|
81
|
+
expect(decode('00', { strict: true }).value).toBe(0)
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('M2 — encoder maxDepth enforced across tag boundary', () => {
|
|
86
|
+
it('rejects deeply nested tagged values', () => {
|
|
87
|
+
let v: any = 0
|
|
88
|
+
for (let i = 0; i < 300; i++) v = { tag: 6, value: v }
|
|
89
|
+
expect(() => encode(v)).toThrow(/Maximum nesting depth/)
|
|
90
|
+
})
|
|
91
|
+
it('allows shallow nested tagged values', () => {
|
|
92
|
+
let v: any = 0
|
|
93
|
+
for (let i = 0; i < 10; i++) v = { tag: 6, value: v }
|
|
94
|
+
expect(() => encode(v)).not.toThrow()
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
describe('M4 — canonical mode enforces shortest-form tag numbers', () => {
|
|
99
|
+
it('rejects non-shortest tag number (d80100)', () => {
|
|
100
|
+
expect(() => decode('d80100', { validateCanonical: true }))
|
|
101
|
+
.toThrow(/Non-canonical/)
|
|
102
|
+
})
|
|
103
|
+
it('accepts shortest tag number (c100)', () => {
|
|
104
|
+
expect(() => decode('c100', { validateCanonical: true })).not.toThrow()
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe('M5 — float16 subnormals encode to shortest form and stay self-canonical', () => {
|
|
109
|
+
it('encode(2^-24) uses float16, not float32', () => {
|
|
110
|
+
expect(encodeToHex(5.960464477539063e-8)).toMatch(/^f9/)
|
|
111
|
+
})
|
|
112
|
+
it('encoder float output passes its own canonical validation', () => {
|
|
113
|
+
for (const v of [1.5, 65504, 5.960464477539063e-8, 0.1]) {
|
|
114
|
+
const h = encodeToHex(v)
|
|
115
|
+
expect(() => decode(h, { validateCanonical: true }), `value ${v} -> ${h}`).not.toThrow()
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
describe('L3 — readUint refuses values above MAX_SAFE_INTEGER (via decode)', () => {
|
|
121
|
+
it('8-byte integer above 2^53 decodes as BigInt without loss', () => {
|
|
122
|
+
// 0x1b + 0102030405060708 -> tag-free uint, larger than MAX_SAFE_INTEGER
|
|
123
|
+
const r = decode('1b0102030405060708')
|
|
124
|
+
expect(typeof r.value).toBe('bigint')
|
|
125
|
+
expect(r.value).toBe(0x0102030405060708n)
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
describe('L5 — diagnostic notation', () => {
|
|
130
|
+
it('renders indefinite-length arrays', () => {
|
|
131
|
+
expect(toDiagnostic(decode('9f0102ff').value)).toBe('[_ 1, 2]')
|
|
132
|
+
})
|
|
133
|
+
it('renders unassigned simple values', () => {
|
|
134
|
+
expect(toDiagnostic(decode('f820').value)).toBe('simple(32)')
|
|
135
|
+
})
|
|
136
|
+
it('renders indefinite-length text strings', () => {
|
|
137
|
+
const v = decode('7f626162ff').value // (_ "ab") from chunks "ab"
|
|
138
|
+
expect(toDiagnostic(v)).toContain('_')
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
})
|
|
@@ -123,6 +123,14 @@ describe('Public API - Functional Encode', () => {
|
|
|
123
123
|
expect(result.hex).toBe('010203')
|
|
124
124
|
expect(result.bytes).toEqual(new Uint8Array([0x01, 0x02, 0x03]))
|
|
125
125
|
})
|
|
126
|
+
|
|
127
|
+
it('encode() should preserve -0', () => {
|
|
128
|
+
const result = encode(-0)
|
|
129
|
+
expect(result.hex).toBe('f98000')
|
|
130
|
+
|
|
131
|
+
const decoded = decode(result.hex)
|
|
132
|
+
expect(Object.is(decoded.value, -0)).toBe(true)
|
|
133
|
+
})
|
|
126
134
|
})
|
|
127
135
|
|
|
128
136
|
describe('Public API - Diagnostic Notation', () => {
|
|
@@ -277,6 +285,151 @@ describe('Public API - Utilities', () => {
|
|
|
277
285
|
})
|
|
278
286
|
})
|
|
279
287
|
|
|
288
|
+
describe('Public API - Uint8Array Decode', () => {
|
|
289
|
+
it('decode() should accept Uint8Array input for integers', () => {
|
|
290
|
+
const result = decode(new Uint8Array([0x18, 0x64]))
|
|
291
|
+
expect(result.value).toBe(100)
|
|
292
|
+
expect(result.bytesRead).toBe(2)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it('decode() should accept Uint8Array for small integers', () => {
|
|
296
|
+
const result = decode(new Uint8Array([0x01]))
|
|
297
|
+
expect(result.value).toBe(1)
|
|
298
|
+
expect(result.bytesRead).toBe(1)
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
it('decode(Uint8Array) should produce identical results to decode(hexString)', () => {
|
|
302
|
+
// Integer 100
|
|
303
|
+
const hexResult = decode('1864')
|
|
304
|
+
const bytesResult = decode(new Uint8Array([0x18, 0x64]))
|
|
305
|
+
expect(bytesResult.value).toEqual(hexResult.value)
|
|
306
|
+
expect(bytesResult.bytesRead).toEqual(hexResult.bytesRead)
|
|
307
|
+
|
|
308
|
+
// String "IETF"
|
|
309
|
+
const hexStr = decode('6449455446')
|
|
310
|
+
const bytesStr = decode(new Uint8Array([0x64, 0x49, 0x45, 0x54, 0x46]))
|
|
311
|
+
expect(bytesStr.value).toEqual(hexStr.value)
|
|
312
|
+
expect(bytesStr.bytesRead).toEqual(hexStr.bytesRead)
|
|
313
|
+
|
|
314
|
+
// Array [1, 2, 3]
|
|
315
|
+
const hexArr = decode('83010203')
|
|
316
|
+
const bytesArr = decode(new Uint8Array([0x83, 0x01, 0x02, 0x03]))
|
|
317
|
+
expect(bytesArr.value).toEqual(hexArr.value)
|
|
318
|
+
expect(bytesArr.bytesRead).toEqual(hexArr.bytesRead)
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
it('decode(Uint8Array) should enforce maxInputSize limit', () => {
|
|
322
|
+
const bytes = new Uint8Array([0x01])
|
|
323
|
+
// Should succeed with a large enough limit
|
|
324
|
+
expect(() => decode(bytes, { limits: { maxInputSize: 100 } })).not.toThrow()
|
|
325
|
+
|
|
326
|
+
// Should throw when bytes exceed the limit
|
|
327
|
+
const largeBytes = new Uint8Array(200)
|
|
328
|
+
largeBytes[0] = 0x01 // valid CBOR integer 1
|
|
329
|
+
expect(() => decode(largeBytes, { limits: { maxInputSize: 10 } })).toThrow(/exceeds limit/)
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
it('decode(Uint8Array) should handle tagged values', () => {
|
|
333
|
+
// d87980 = tag 121, empty array
|
|
334
|
+
const result = decode(new Uint8Array([0xd8, 0x79, 0x80]))
|
|
335
|
+
expect(result.value).toMatchObject({ tag: 121, value: [] })
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
it('decode(Uint8Array) should handle maps', () => {
|
|
339
|
+
// a16161 01 = {"a": 1}
|
|
340
|
+
const result = decode(new Uint8Array([0xa1, 0x61, 0x61, 0x01]))
|
|
341
|
+
expect(result.bytesRead).toBe(4)
|
|
342
|
+
// Map with string key "a" -> 1
|
|
343
|
+
expect(result.value).toBeInstanceOf(Map)
|
|
344
|
+
expect((result.value as Map<string, number>).get('a')).toBe(1)
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
it('decode(Uint8Array) should handle boolean and null', () => {
|
|
348
|
+
expect(decode(new Uint8Array([0xf5])).value).toBe(true)
|
|
349
|
+
expect(decode(new Uint8Array([0xf4])).value).toBe(false)
|
|
350
|
+
expect(decode(new Uint8Array([0xf6])).value).toBe(null)
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
it('decode(Uint8Array) should reject empty Uint8Array', () => {
|
|
354
|
+
expect(() => decode(new Uint8Array([]))).toThrow()
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
it('decode(Uint8Array) should accept options', () => {
|
|
358
|
+
const result = decode(new Uint8Array([0x18, 0x64]), { strict: true })
|
|
359
|
+
expect(result.value).toBe(100)
|
|
360
|
+
})
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
describe('Public API - Uint8Array DecodeWithSourceMap', () => {
|
|
364
|
+
it('decodeWithSourceMap() should accept Uint8Array input', () => {
|
|
365
|
+
// d87980 = tag 121, empty array
|
|
366
|
+
const result = decodeWithSourceMap(new Uint8Array([0xd8, 0x79, 0x80]))
|
|
367
|
+
expect(result.value).toMatchObject({ tag: 121, value: [] })
|
|
368
|
+
expect(result.sourceMap).toBeDefined()
|
|
369
|
+
expect(Array.isArray(result.sourceMap)).toBe(true)
|
|
370
|
+
expect(result.sourceMap.length).toBeGreaterThan(0)
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
it('decodeWithSourceMap(Uint8Array) should match hex string results', () => {
|
|
374
|
+
const hexResult = decodeWithSourceMap('d87980')
|
|
375
|
+
const bytesResult = decodeWithSourceMap(new Uint8Array([0xd8, 0x79, 0x80]))
|
|
376
|
+
expect(bytesResult.value).toEqual(hexResult.value)
|
|
377
|
+
expect(bytesResult.sourceMap.length).toBe(hexResult.sourceMap.length)
|
|
378
|
+
})
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
describe('Public API - Uint8Array CborDecoder Class', () => {
|
|
382
|
+
it('CborDecoder.decode() should accept Uint8Array', () => {
|
|
383
|
+
const decoder = new CborDecoder()
|
|
384
|
+
const result = decoder.decode(new Uint8Array([0x18, 0x64]))
|
|
385
|
+
expect(result.value).toBe(100)
|
|
386
|
+
expect(result.bytesRead).toBe(2)
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('CborDecoder.decodeWithSourceMap() should accept Uint8Array', () => {
|
|
390
|
+
const decoder = new CborDecoder()
|
|
391
|
+
const result = decoder.decodeWithSourceMap(new Uint8Array([0xd8, 0x79, 0x80]))
|
|
392
|
+
expect(result.value).toMatchObject({ tag: 121, value: [] })
|
|
393
|
+
expect(result.sourceMap).toBeDefined()
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
it('CborDecoder with options should work with Uint8Array', () => {
|
|
397
|
+
const decoder = new CborDecoder({ strict: true })
|
|
398
|
+
const result = decoder.decode(new Uint8Array([0x18, 0x64]))
|
|
399
|
+
expect(result.value).toBe(100)
|
|
400
|
+
})
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
describe('Public API - Uint8Array useCborParser', () => {
|
|
404
|
+
it('parse() should accept Uint8Array', () => {
|
|
405
|
+
const { parse } = useCborParser()
|
|
406
|
+
const result = parse(new Uint8Array([0x18, 0x64]))
|
|
407
|
+
expect(result.value).toBe(100)
|
|
408
|
+
expect(result.bytesRead).toBe(2)
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
it('parseWithSourceMap() should accept Uint8Array', () => {
|
|
412
|
+
const { parseWithSourceMap } = useCborParser()
|
|
413
|
+
const result = parseWithSourceMap(new Uint8Array([0xd8, 0x79, 0x80]))
|
|
414
|
+
expect(result.value).toMatchObject({ tag: 121, value: [] })
|
|
415
|
+
expect(result.sourceMap).toBeDefined()
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
it('parseSequence() should accept Uint8Array', () => {
|
|
419
|
+
const { parseSequence } = useCborParser()
|
|
420
|
+
// Three separate integers: 1, 2, 3
|
|
421
|
+
const result = parseSequence(new Uint8Array([0x01, 0x02, 0x03]))
|
|
422
|
+
expect(result).toEqual([1, 2, 3])
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
it('parseSequence(Uint8Array) should match hex string results', () => {
|
|
426
|
+
const { parseSequence } = useCborParser()
|
|
427
|
+
const hexResult = parseSequence('010203')
|
|
428
|
+
const bytesResult = parseSequence(new Uint8Array([0x01, 0x02, 0x03]))
|
|
429
|
+
expect(bytesResult).toEqual(hexResult)
|
|
430
|
+
})
|
|
431
|
+
})
|
|
432
|
+
|
|
280
433
|
describe('Public API - Constants and Enums', () => {
|
|
281
434
|
it('DEFAULT_OPTIONS should be exported', () => {
|
|
282
435
|
expect(DEFAULT_OPTIONS).toBeDefined()
|
|
@@ -155,13 +155,12 @@ describe('Round-trip: Floats', () => {
|
|
|
155
155
|
expect(roundTrip(0.0)).toBe(0)
|
|
156
156
|
})
|
|
157
157
|
|
|
158
|
-
it('should encode -0.0 as
|
|
159
|
-
// -0.0
|
|
160
|
-
//
|
|
161
|
-
// This is expected CBOR behavior: -0 only preserves via float encoding.
|
|
158
|
+
it('should encode -0.0 as float16 preserving sign', () => {
|
|
159
|
+
// -0.0 is detected by the encoder and encoded as float16 (f98000),
|
|
160
|
+
// which correctly preserves the negative zero sign bit.
|
|
162
161
|
const encoded = encode(-0.0)
|
|
163
|
-
expect(encoded.hex).toBe('
|
|
164
|
-
expect(roundTrip(-0.0)).toBe(
|
|
162
|
+
expect(encoded.hex).toBe('f98000')
|
|
163
|
+
expect(Object.is(roundTrip(-0.0), -0)).toBe(true)
|
|
165
164
|
})
|
|
166
165
|
|
|
167
166
|
it('should round-trip 1.5 (exact in float16)', () => {
|