@bcts/dcbor 1.0.0-alpha.17 → 1.0.0-alpha.18
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/README.md +1 -1
- package/dist/index.cjs +315 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +114 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +114 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +315 -0
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +308 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/bignum.ts +334 -0
- package/src/index.ts +10 -0
- package/src/prelude.ts +3 -0
- package/src/tags.ts +46 -0
package/README.md
CHANGED
|
@@ -10,4 +10,4 @@ The current specification of the norms and practices guiding the creation of thi
|
|
|
10
10
|
|
|
11
11
|
## Rust Reference Implementation
|
|
12
12
|
|
|
13
|
-
This TypeScript implementation is based on [bc-dcbor-rust](https://github.com/BlockchainCommons/bc-dcbor-rust) **v0.
|
|
13
|
+
This TypeScript implementation is based on [bc-dcbor-rust](https://github.com/BlockchainCommons/bc-dcbor-rust) **v0.25.1** ([commit](https://github.com/BlockchainCommons/bc-dcbor-rust/tree/8344a256cc858b3a626a89e0b805ef0bda4d3b50)).
|
package/dist/index.cjs
CHANGED
|
@@ -8045,6 +8045,268 @@ var CborDate = class CborDate {
|
|
|
8045
8045
|
}
|
|
8046
8046
|
};
|
|
8047
8047
|
|
|
8048
|
+
//#endregion
|
|
8049
|
+
//#region src/bignum.ts
|
|
8050
|
+
/**
|
|
8051
|
+
* CBOR bignum (tags 2 and 3) support.
|
|
8052
|
+
*
|
|
8053
|
+
* This module provides conversion between CBOR and JavaScript BigInt types,
|
|
8054
|
+
* implementing RFC 8949 §3.4.3 (Bignums) with dCBOR/CDE canonical encoding rules.
|
|
8055
|
+
*
|
|
8056
|
+
* Encoding:
|
|
8057
|
+
* - `biguintToCbor` always encodes as tag 2 (positive bignum) with a byte
|
|
8058
|
+
* string content.
|
|
8059
|
+
* - `bigintToCbor` encodes as tag 2 for non-negative values or tag 3
|
|
8060
|
+
* (negative bignum) for negative values.
|
|
8061
|
+
* - No numeric reduction is performed: values are always encoded as bignums,
|
|
8062
|
+
* even if they would fit in normal CBOR integers.
|
|
8063
|
+
*
|
|
8064
|
+
* Decoding:
|
|
8065
|
+
* - Accepts CBOR integers (major types 0 and 1) and converts them to bigints.
|
|
8066
|
+
* - Accepts tag 2 (positive bignum) and tag 3 (negative bignum) with byte
|
|
8067
|
+
* string content.
|
|
8068
|
+
* - Enforces shortest-form canonical representation for bignum magnitudes.
|
|
8069
|
+
* - Rejects floating-point values.
|
|
8070
|
+
*
|
|
8071
|
+
* @module bignum
|
|
8072
|
+
*/
|
|
8073
|
+
const TAG_2_POSITIVE_BIGNUM = 2;
|
|
8074
|
+
const TAG_3_NEGATIVE_BIGNUM = 3;
|
|
8075
|
+
/**
|
|
8076
|
+
* Validates that a bignum magnitude byte string is in shortest canonical form.
|
|
8077
|
+
*
|
|
8078
|
+
* Matches Rust's `validate_bignum_magnitude()`.
|
|
8079
|
+
*
|
|
8080
|
+
* Rules:
|
|
8081
|
+
* - For positive bignums (tag 2): empty byte string represents zero;
|
|
8082
|
+
* non-empty must not have leading zero bytes.
|
|
8083
|
+
* - For negative bignums (tag 3): byte string must not be empty
|
|
8084
|
+
* (magnitude zero is encoded as `0x00`); must not have leading zero bytes
|
|
8085
|
+
* except when the magnitude is zero (single `0x00`).
|
|
8086
|
+
*
|
|
8087
|
+
* @param bytes - The magnitude byte string to validate
|
|
8088
|
+
* @param isNegative - Whether this is for a negative bignum (tag 3)
|
|
8089
|
+
* @throws CborError with type NonCanonicalNumeric on validation failure
|
|
8090
|
+
*/
|
|
8091
|
+
function validateBignumMagnitude(bytes, isNegative) {
|
|
8092
|
+
if (isNegative) {
|
|
8093
|
+
if (bytes.length === 0) throw new CborError({ type: "NonCanonicalNumeric" });
|
|
8094
|
+
if (bytes.length > 1 && bytes[0] === 0) throw new CborError({ type: "NonCanonicalNumeric" });
|
|
8095
|
+
} else if (bytes.length > 0 && bytes[0] === 0) throw new CborError({ type: "NonCanonicalNumeric" });
|
|
8096
|
+
}
|
|
8097
|
+
/**
|
|
8098
|
+
* Strips leading zero bytes from a byte array, returning the minimal
|
|
8099
|
+
* representation.
|
|
8100
|
+
*
|
|
8101
|
+
* Matches Rust's `strip_leading_zeros()`.
|
|
8102
|
+
*
|
|
8103
|
+
* @param bytes - The byte array to strip
|
|
8104
|
+
* @returns A subarray with leading zeros removed
|
|
8105
|
+
*/
|
|
8106
|
+
function stripLeadingZeros(bytes) {
|
|
8107
|
+
let start = 0;
|
|
8108
|
+
while (start < bytes.length && bytes[start] === 0) start++;
|
|
8109
|
+
return bytes.subarray(start);
|
|
8110
|
+
}
|
|
8111
|
+
/**
|
|
8112
|
+
* Convert a non-negative bigint to a big-endian byte array.
|
|
8113
|
+
*
|
|
8114
|
+
* Zero returns an empty Uint8Array.
|
|
8115
|
+
*
|
|
8116
|
+
* @param value - A non-negative bigint value
|
|
8117
|
+
* @returns Big-endian byte representation
|
|
8118
|
+
*/
|
|
8119
|
+
function bigintToBytes(value) {
|
|
8120
|
+
if (value === 0n) return new Uint8Array(0);
|
|
8121
|
+
const hex = value.toString(16);
|
|
8122
|
+
const padded = hex.length % 2 !== 0 ? `0${hex}` : hex;
|
|
8123
|
+
const bytes = new Uint8Array(padded.length / 2);
|
|
8124
|
+
for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(padded.substring(i * 2, i * 2 + 2), 16);
|
|
8125
|
+
return bytes;
|
|
8126
|
+
}
|
|
8127
|
+
/**
|
|
8128
|
+
* Convert a big-endian byte array to a bigint.
|
|
8129
|
+
*
|
|
8130
|
+
* Empty array returns 0n.
|
|
8131
|
+
*
|
|
8132
|
+
* @param bytes - Big-endian byte representation
|
|
8133
|
+
* @returns The bigint value
|
|
8134
|
+
*/
|
|
8135
|
+
function bytesToBigint(bytes) {
|
|
8136
|
+
if (bytes.length === 0) return 0n;
|
|
8137
|
+
let result = 0n;
|
|
8138
|
+
for (const byte of bytes) result = result << 8n | BigInt(byte);
|
|
8139
|
+
return result;
|
|
8140
|
+
}
|
|
8141
|
+
/**
|
|
8142
|
+
* Encode a non-negative bigint as a CBOR tag 2 (positive bignum).
|
|
8143
|
+
*
|
|
8144
|
+
* Matches Rust's `From<BigUint> for CBOR`.
|
|
8145
|
+
*
|
|
8146
|
+
* The value is always encoded as a bignum regardless of size.
|
|
8147
|
+
* Zero is encoded as tag 2 with an empty byte string.
|
|
8148
|
+
*
|
|
8149
|
+
* @param value - A non-negative bigint (must be >= 0n)
|
|
8150
|
+
* @returns CBOR tagged value
|
|
8151
|
+
* @throws CborError with type OutOfRange if value is negative
|
|
8152
|
+
*/
|
|
8153
|
+
function biguintToCbor(value) {
|
|
8154
|
+
if (value < 0n) throw new CborError({ type: "OutOfRange" });
|
|
8155
|
+
return toTaggedValue(TAG_2_POSITIVE_BIGNUM, stripLeadingZeros(bigintToBytes(value)));
|
|
8156
|
+
}
|
|
8157
|
+
/**
|
|
8158
|
+
* Encode a bigint as a CBOR tag 2 or tag 3 bignum.
|
|
8159
|
+
*
|
|
8160
|
+
* Matches Rust's `From<BigInt> for CBOR`.
|
|
8161
|
+
*
|
|
8162
|
+
* - Non-negative values use tag 2 (positive bignum).
|
|
8163
|
+
* - Negative values use tag 3 (negative bignum), where the encoded
|
|
8164
|
+
* magnitude is `|value| - 1` per RFC 8949.
|
|
8165
|
+
*
|
|
8166
|
+
* @param value - Any bigint value
|
|
8167
|
+
* @returns CBOR tagged value
|
|
8168
|
+
*/
|
|
8169
|
+
function bigintToCbor(value) {
|
|
8170
|
+
if (value >= 0n) return biguintToCbor(value);
|
|
8171
|
+
const stripped = stripLeadingZeros(bigintToBytes(-value - 1n));
|
|
8172
|
+
return toTaggedValue(TAG_3_NEGATIVE_BIGNUM, stripped.length === 0 ? new Uint8Array([0]) : stripped);
|
|
8173
|
+
}
|
|
8174
|
+
/**
|
|
8175
|
+
* Decode a BigUint from an untagged CBOR byte string.
|
|
8176
|
+
*
|
|
8177
|
+
* Matches Rust's `biguint_from_untagged_cbor()`.
|
|
8178
|
+
*
|
|
8179
|
+
* This function is intended for use in tag summarizers where the tag has
|
|
8180
|
+
* already been stripped. It expects a CBOR byte string representing the
|
|
8181
|
+
* big-endian magnitude of a positive bignum (tag 2 content).
|
|
8182
|
+
*
|
|
8183
|
+
* Enforces canonical encoding: no leading zero bytes (except empty for zero).
|
|
8184
|
+
*
|
|
8185
|
+
* @param cbor - A CBOR value that should be a byte string
|
|
8186
|
+
* @returns Non-negative bigint
|
|
8187
|
+
* @throws CborError with type WrongType if not a byte string
|
|
8188
|
+
* @throws CborError with type NonCanonicalNumeric if encoding is non-canonical
|
|
8189
|
+
*/
|
|
8190
|
+
function biguintFromUntaggedCbor(cbor) {
|
|
8191
|
+
if (cbor.type !== MajorType.ByteString) throw new CborError({ type: "WrongType" });
|
|
8192
|
+
const bytes = cbor.value;
|
|
8193
|
+
validateBignumMagnitude(bytes, false);
|
|
8194
|
+
return bytesToBigint(bytes);
|
|
8195
|
+
}
|
|
8196
|
+
/**
|
|
8197
|
+
* Decode a BigInt from an untagged CBOR byte string for a negative bignum.
|
|
8198
|
+
*
|
|
8199
|
+
* Matches Rust's `bigint_from_negative_untagged_cbor()`.
|
|
8200
|
+
*
|
|
8201
|
+
* This function is intended for use in tag summarizers where the tag has
|
|
8202
|
+
* already been stripped. It expects a CBOR byte string representing `n` where
|
|
8203
|
+
* the actual value is `-1 - n` (tag 3 content per RFC 8949).
|
|
8204
|
+
*
|
|
8205
|
+
* Enforces canonical encoding: no leading zero bytes (except single `0x00`
|
|
8206
|
+
* for -1).
|
|
8207
|
+
*
|
|
8208
|
+
* @param cbor - A CBOR value that should be a byte string
|
|
8209
|
+
* @returns Negative bigint
|
|
8210
|
+
* @throws CborError with type WrongType if not a byte string
|
|
8211
|
+
* @throws CborError with type NonCanonicalNumeric if encoding is non-canonical
|
|
8212
|
+
*/
|
|
8213
|
+
function bigintFromNegativeUntaggedCbor(cbor) {
|
|
8214
|
+
if (cbor.type !== MajorType.ByteString) throw new CborError({ type: "WrongType" });
|
|
8215
|
+
const bytes = cbor.value;
|
|
8216
|
+
validateBignumMagnitude(bytes, true);
|
|
8217
|
+
return -(bytesToBigint(bytes) + 1n);
|
|
8218
|
+
}
|
|
8219
|
+
/**
|
|
8220
|
+
* Convert CBOR to a non-negative bigint.
|
|
8221
|
+
*
|
|
8222
|
+
* Matches Rust's `TryFrom<CBOR> for BigUint`.
|
|
8223
|
+
*
|
|
8224
|
+
* Accepts:
|
|
8225
|
+
* - Major type 0 (unsigned integer)
|
|
8226
|
+
* - Tag 2 (positive bignum) with canonical byte string
|
|
8227
|
+
*
|
|
8228
|
+
* Rejects:
|
|
8229
|
+
* - Major type 1 (negative integer) -> OutOfRange
|
|
8230
|
+
* - Tag 3 (negative bignum) -> OutOfRange
|
|
8231
|
+
* - Floating-point values -> WrongType
|
|
8232
|
+
* - Non-canonical bignum encodings -> NonCanonicalNumeric
|
|
8233
|
+
*
|
|
8234
|
+
* @param cbor - The CBOR value to convert
|
|
8235
|
+
* @returns Non-negative bigint
|
|
8236
|
+
* @throws CborError
|
|
8237
|
+
*/
|
|
8238
|
+
function cborToBiguint(cbor) {
|
|
8239
|
+
switch (cbor.type) {
|
|
8240
|
+
case MajorType.Unsigned: return BigInt(cbor.value);
|
|
8241
|
+
case MajorType.Negative: throw new CborError({ type: "OutOfRange" });
|
|
8242
|
+
case MajorType.Tagged: {
|
|
8243
|
+
const tagValue = Number(cbor.tag);
|
|
8244
|
+
if (tagValue === TAG_2_POSITIVE_BIGNUM) {
|
|
8245
|
+
const inner = cbor.value;
|
|
8246
|
+
if (inner.type !== MajorType.ByteString) throw new CborError({ type: "WrongType" });
|
|
8247
|
+
const bytes = inner.value;
|
|
8248
|
+
validateBignumMagnitude(bytes, false);
|
|
8249
|
+
return bytesToBigint(bytes);
|
|
8250
|
+
} else if (tagValue === TAG_3_NEGATIVE_BIGNUM) throw new CborError({ type: "OutOfRange" });
|
|
8251
|
+
throw new CborError({ type: "WrongType" });
|
|
8252
|
+
}
|
|
8253
|
+
case MajorType.ByteString:
|
|
8254
|
+
case MajorType.Text:
|
|
8255
|
+
case MajorType.Array:
|
|
8256
|
+
case MajorType.Map:
|
|
8257
|
+
case MajorType.Simple: throw new CborError({ type: "WrongType" });
|
|
8258
|
+
}
|
|
8259
|
+
}
|
|
8260
|
+
/**
|
|
8261
|
+
* Convert CBOR to a bigint (any sign).
|
|
8262
|
+
*
|
|
8263
|
+
* Matches Rust's `TryFrom<CBOR> for BigInt`.
|
|
8264
|
+
*
|
|
8265
|
+
* Accepts:
|
|
8266
|
+
* - Major type 0 (unsigned integer)
|
|
8267
|
+
* - Major type 1 (negative integer)
|
|
8268
|
+
* - Tag 2 (positive bignum) with canonical byte string
|
|
8269
|
+
* - Tag 3 (negative bignum) with canonical byte string
|
|
8270
|
+
*
|
|
8271
|
+
* Rejects:
|
|
8272
|
+
* - Floating-point values -> WrongType
|
|
8273
|
+
* - Non-canonical bignum encodings -> NonCanonicalNumeric
|
|
8274
|
+
*
|
|
8275
|
+
* @param cbor - The CBOR value to convert
|
|
8276
|
+
* @returns A bigint value
|
|
8277
|
+
* @throws CborError
|
|
8278
|
+
*/
|
|
8279
|
+
function cborToBigint(cbor) {
|
|
8280
|
+
switch (cbor.type) {
|
|
8281
|
+
case MajorType.Unsigned: return BigInt(cbor.value);
|
|
8282
|
+
case MajorType.Negative: return -(BigInt(cbor.value) + 1n);
|
|
8283
|
+
case MajorType.Tagged: {
|
|
8284
|
+
const tagValue = Number(cbor.tag);
|
|
8285
|
+
if (tagValue === TAG_2_POSITIVE_BIGNUM) {
|
|
8286
|
+
const inner = cbor.value;
|
|
8287
|
+
if (inner.type !== MajorType.ByteString) throw new CborError({ type: "WrongType" });
|
|
8288
|
+
const bytes = inner.value;
|
|
8289
|
+
validateBignumMagnitude(bytes, false);
|
|
8290
|
+
const mag = bytesToBigint(bytes);
|
|
8291
|
+
if (mag === 0n) return 0n;
|
|
8292
|
+
return mag;
|
|
8293
|
+
} else if (tagValue === TAG_3_NEGATIVE_BIGNUM) {
|
|
8294
|
+
const inner = cbor.value;
|
|
8295
|
+
if (inner.type !== MajorType.ByteString) throw new CborError({ type: "WrongType" });
|
|
8296
|
+
const bytes = inner.value;
|
|
8297
|
+
validateBignumMagnitude(bytes, true);
|
|
8298
|
+
return -(bytesToBigint(bytes) + 1n);
|
|
8299
|
+
}
|
|
8300
|
+
throw new CborError({ type: "WrongType" });
|
|
8301
|
+
}
|
|
8302
|
+
case MajorType.ByteString:
|
|
8303
|
+
case MajorType.Text:
|
|
8304
|
+
case MajorType.Array:
|
|
8305
|
+
case MajorType.Map:
|
|
8306
|
+
case MajorType.Simple: throw new CborError({ type: "WrongType" });
|
|
8307
|
+
}
|
|
8308
|
+
}
|
|
8309
|
+
|
|
8048
8310
|
//#endregion
|
|
8049
8311
|
//#region src/tags.ts
|
|
8050
8312
|
/**
|
|
@@ -8074,6 +8336,16 @@ const TAG_POSITIVE_BIGNUM = 2;
|
|
|
8074
8336
|
*/
|
|
8075
8337
|
const TAG_NEGATIVE_BIGNUM = 3;
|
|
8076
8338
|
/**
|
|
8339
|
+
* Name for tag 2 (positive bignum).
|
|
8340
|
+
* Matches Rust's `TAG_NAME_POSITIVE_BIGNUM`.
|
|
8341
|
+
*/
|
|
8342
|
+
const TAG_NAME_POSITIVE_BIGNUM = "positive-bignum";
|
|
8343
|
+
/**
|
|
8344
|
+
* Name for tag 3 (negative bignum).
|
|
8345
|
+
* Matches Rust's `TAG_NAME_NEGATIVE_BIGNUM`.
|
|
8346
|
+
*/
|
|
8347
|
+
const TAG_NAME_NEGATIVE_BIGNUM = "negative-bignum";
|
|
8348
|
+
/**
|
|
8077
8349
|
* Tag 4: Decimal fraction [exponent, mantissa]
|
|
8078
8350
|
*/
|
|
8079
8351
|
const TAG_DECIMAL_FRACTION = 4;
|
|
@@ -8164,6 +8436,41 @@ const registerTagsIn = (tagsStore) => {
|
|
|
8164
8436
|
};
|
|
8165
8437
|
}
|
|
8166
8438
|
});
|
|
8439
|
+
const biguintTag = createTag(TAG_POSITIVE_BIGNUM, TAG_NAME_POSITIVE_BIGNUM);
|
|
8440
|
+
const bigintTag = createTag(TAG_NEGATIVE_BIGNUM, TAG_NAME_NEGATIVE_BIGNUM);
|
|
8441
|
+
tagsStore.insertAll([biguintTag, bigintTag]);
|
|
8442
|
+
tagsStore.setSummarizer(TAG_POSITIVE_BIGNUM, (untaggedCbor, _flat) => {
|
|
8443
|
+
try {
|
|
8444
|
+
return {
|
|
8445
|
+
ok: true,
|
|
8446
|
+
value: `bignum(${biguintFromUntaggedCbor(untaggedCbor)})`
|
|
8447
|
+
};
|
|
8448
|
+
} catch (e) {
|
|
8449
|
+
return {
|
|
8450
|
+
ok: false,
|
|
8451
|
+
error: {
|
|
8452
|
+
type: "Custom",
|
|
8453
|
+
message: e instanceof Error ? e.message : String(e)
|
|
8454
|
+
}
|
|
8455
|
+
};
|
|
8456
|
+
}
|
|
8457
|
+
});
|
|
8458
|
+
tagsStore.setSummarizer(TAG_NEGATIVE_BIGNUM, (untaggedCbor, _flat) => {
|
|
8459
|
+
try {
|
|
8460
|
+
return {
|
|
8461
|
+
ok: true,
|
|
8462
|
+
value: `bignum(${bigintFromNegativeUntaggedCbor(untaggedCbor)})`
|
|
8463
|
+
};
|
|
8464
|
+
} catch (e) {
|
|
8465
|
+
return {
|
|
8466
|
+
ok: false,
|
|
8467
|
+
error: {
|
|
8468
|
+
type: "Custom",
|
|
8469
|
+
message: e instanceof Error ? e.message : String(e)
|
|
8470
|
+
}
|
|
8471
|
+
};
|
|
8472
|
+
}
|
|
8473
|
+
});
|
|
8167
8474
|
};
|
|
8168
8475
|
/**
|
|
8169
8476
|
* Register standard tags in the global tags store.
|
|
@@ -8914,6 +9221,8 @@ exports.TAG_EPOCH_DATE = TAG_EPOCH_DATE;
|
|
|
8914
9221
|
exports.TAG_EPOCH_DATE_TIME = TAG_EPOCH_DATE_TIME;
|
|
8915
9222
|
exports.TAG_MIME_MESSAGE = TAG_MIME_MESSAGE;
|
|
8916
9223
|
exports.TAG_NAME_DATE = TAG_NAME_DATE;
|
|
9224
|
+
exports.TAG_NAME_NEGATIVE_BIGNUM = TAG_NAME_NEGATIVE_BIGNUM;
|
|
9225
|
+
exports.TAG_NAME_POSITIVE_BIGNUM = TAG_NAME_POSITIVE_BIGNUM;
|
|
8917
9226
|
exports.TAG_NEGATIVE_BIGNUM = TAG_NEGATIVE_BIGNUM;
|
|
8918
9227
|
exports.TAG_POSITIVE_BIGNUM = TAG_POSITIVE_BIGNUM;
|
|
8919
9228
|
exports.TAG_REGEXP = TAG_REGEXP;
|
|
@@ -8942,9 +9251,15 @@ exports.asSingle = asSingle;
|
|
|
8942
9251
|
exports.asTaggedValue = asTaggedValue;
|
|
8943
9252
|
exports.asText = asText;
|
|
8944
9253
|
exports.asUnsigned = asUnsigned;
|
|
9254
|
+
exports.bigintFromNegativeUntaggedCbor = bigintFromNegativeUntaggedCbor;
|
|
9255
|
+
exports.bigintToCbor = bigintToCbor;
|
|
9256
|
+
exports.biguintFromUntaggedCbor = biguintFromUntaggedCbor;
|
|
9257
|
+
exports.biguintToCbor = biguintToCbor;
|
|
8945
9258
|
exports.bytesToHex = bytesToHex;
|
|
8946
9259
|
exports.cbor = cbor;
|
|
8947
9260
|
exports.cborData = cborData;
|
|
9261
|
+
exports.cborToBigint = cborToBigint;
|
|
9262
|
+
exports.cborToBiguint = cborToBiguint;
|
|
8948
9263
|
exports.createTag = createTag;
|
|
8949
9264
|
exports.createTaggedCbor = createTaggedCbor;
|
|
8950
9265
|
exports.decodeCbor = decodeCbor;
|