Comparing version 0.0.20210930 to 0.0.20220501
@@ -1,4 +0,3 @@ | ||
import { Encoder } from "./mod_browser.js"; | ||
import { asDataView, fromUtf8 } from "@ndn/util"; | ||
import { NNI } from "./nni_browser.js"; | ||
import { fromUtf8 } from "./string_browser.js"; | ||
class DecodedTlv { | ||
@@ -51,3 +50,3 @@ constructor(type, buf, offsetT, offsetV, offsetE) { | ||
this.offset = 0; | ||
this.dv = Encoder.asDataView(input); | ||
this.dv = asDataView(input); | ||
} | ||
@@ -61,8 +60,10 @@ /** Determine whether end of input has been reached. */ | ||
const offsetT = this.offset; | ||
const type = this.readType(); | ||
const length = this.readLength(); | ||
const type = this.readVarNum(); | ||
const length = this.readVarNum(); | ||
const offsetV = this.offset; | ||
this.skipValue(length); | ||
const offsetE = this.offset; | ||
return new DecodedTlv(type, this.input, offsetT, offsetV, offsetE); | ||
if (length === undefined || (this.offset += length) > this.input.length) { | ||
throw new Error(`TLV at offset ${offsetT} is incomplete`); | ||
} | ||
// length!==undefined implies type!==undefined | ||
return new DecodedTlv(type, this.input, offsetT, offsetV, this.offset); | ||
} | ||
@@ -98,22 +99,2 @@ /** Read a Decodable object. */ | ||
} | ||
readType() { | ||
const n = this.readVarNum(); | ||
if (n === undefined) { | ||
throw new Error(`TLV-TYPE is missing near offset ${this.offset}`); | ||
} | ||
return n; | ||
} | ||
readLength() { | ||
const n = this.readVarNum(); | ||
if (n === undefined) { | ||
throw new Error(`TLV-LENGTH is missing near offset ${this.offset}`); | ||
} | ||
return n; | ||
} | ||
skipValue(length) { | ||
this.offset += length; | ||
if (this.offset > this.input.length) { | ||
throw new Error(`TLV-VALUE is incomplete near offset ${this.offset}`); | ||
} | ||
} | ||
} |
@@ -1,4 +0,3 @@ | ||
import { Encoder } from "./mod_node.js"; | ||
import { asDataView, fromUtf8 } from "@ndn/util"; | ||
import { NNI } from "./nni_node.js"; | ||
import { fromUtf8 } from "./string_node.js"; | ||
class DecodedTlv { | ||
@@ -51,3 +50,3 @@ constructor(type, buf, offsetT, offsetV, offsetE) { | ||
this.offset = 0; | ||
this.dv = Encoder.asDataView(input); | ||
this.dv = asDataView(input); | ||
} | ||
@@ -61,8 +60,10 @@ /** Determine whether end of input has been reached. */ | ||
const offsetT = this.offset; | ||
const type = this.readType(); | ||
const length = this.readLength(); | ||
const type = this.readVarNum(); | ||
const length = this.readVarNum(); | ||
const offsetV = this.offset; | ||
this.skipValue(length); | ||
const offsetE = this.offset; | ||
return new DecodedTlv(type, this.input, offsetT, offsetV, offsetE); | ||
if (length === undefined || (this.offset += length) > this.input.length) { | ||
throw new Error(`TLV at offset ${offsetT} is incomplete`); | ||
} | ||
// length!==undefined implies type!==undefined | ||
return new DecodedTlv(type, this.input, offsetT, offsetV, this.offset); | ||
} | ||
@@ -98,22 +99,2 @@ /** Read a Decodable object. */ | ||
} | ||
readType() { | ||
const n = this.readVarNum(); | ||
if (n === undefined) { | ||
throw new Error(`TLV-TYPE is missing near offset ${this.offset}`); | ||
} | ||
return n; | ||
} | ||
readLength() { | ||
const n = this.readVarNum(); | ||
if (n === undefined) { | ||
throw new Error(`TLV-LENGTH is missing near offset ${this.offset}`); | ||
} | ||
return n; | ||
} | ||
skipValue(length) { | ||
this.offset += length; | ||
if (this.offset > this.input.length) { | ||
throw new Error(`TLV-VALUE is incomplete near offset ${this.offset}`); | ||
} | ||
} | ||
} |
@@ -17,5 +17,2 @@ export interface Decodable<R> { | ||
private readVarNum; | ||
private readType; | ||
private readLength; | ||
private skipValue; | ||
} | ||
@@ -22,0 +19,0 @@ export declare namespace Decoder { |
@@ -0,1 +1,2 @@ | ||
import { asDataView } from "@ndn/util"; | ||
function sizeofVarNum(n) { | ||
@@ -11,3 +12,3 @@ if (n < 0xFD) { | ||
} | ||
// JavaScript cannot reliably represent 64-bit integers | ||
// 64-bit integers may lose precision in Number type, and it's rarely useful | ||
throw new Error("VAR-NUMBER is too large"); | ||
@@ -21,14 +22,12 @@ } | ||
room[off++] = 0xFD; | ||
Encoder.asDataView(room).setUint16(off, n); | ||
asDataView(room).setUint16(off, n); | ||
} | ||
else { | ||
room[off++] = 0xFE; | ||
Encoder.asDataView(room).setUint32(off, n); | ||
asDataView(room).setUint32(off, n); | ||
} | ||
} | ||
const BUF_INIT_SIZE = 2048; | ||
const BUF_GROW_SIZE = 2048; | ||
/** TLV encoder that accepts objects in reverse order. */ | ||
export class Encoder { | ||
constructor(initSize = BUF_INIT_SIZE) { | ||
constructor(initSize = 2048) { | ||
this.buf = new ArrayBuffer(initSize); | ||
@@ -47,6 +46,2 @@ this.off = initSize; | ||
slice(start = 0, length) { | ||
if (length === undefined) { | ||
// iOS would interpret length=undefined as length=0, so we need a conditional | ||
return new Uint8Array(this.buf, this.off + start); | ||
} | ||
return new Uint8Array(this.buf, this.off + start, length); | ||
@@ -80,17 +75,6 @@ } | ||
} | ||
/** | ||
* Prepend TLV structure. | ||
* @param tlvType TLV-TYPE number. | ||
* @param omitEmpty omit TLV altogether if set to Encoder.OmitEmpty | ||
* @param tlvValue TLV-VALUE objects. | ||
*/ | ||
prependTlv(tlvType, omitEmpty, ...tlvValue) { | ||
let hasOmitEmpty = false; | ||
if (omitEmpty) { | ||
if (omitEmpty === Encoder.OmitEmpty) { | ||
hasOmitEmpty = true; | ||
} | ||
else { | ||
tlvValue.unshift(omitEmpty); | ||
} | ||
const hasOmitEmpty = omitEmpty === Encoder.OmitEmpty; | ||
if (!hasOmitEmpty) { | ||
tlvValue.unshift(omitEmpty); | ||
} | ||
@@ -125,49 +109,13 @@ const sizeBefore = this.size; | ||
grow(sizeofRoom) { | ||
const sizeofGrow = BUF_GROW_SIZE + sizeofRoom; | ||
const buf = new ArrayBuffer(sizeofGrow + this.size); | ||
new Uint8Array(buf, sizeofGrow).set(this.output); | ||
const sizeofGrowth = 2048 + sizeofRoom; | ||
const buf = new ArrayBuffer(sizeofGrowth + this.size); | ||
new Uint8Array(buf, sizeofGrowth).set(this.output); | ||
this.buf = buf; | ||
this.off = sizeofGrow; | ||
this.off = sizeofGrowth; | ||
} | ||
} | ||
(function (Encoder) { | ||
/** Create a DataView over a Uint8Array. */ | ||
function asDataView(a) { | ||
return new DataView(a.buffer, a.byteOffset, a.byteLength); | ||
} | ||
Encoder.asDataView = asDataView; | ||
let DataViewPolyfill; | ||
(function (DataViewPolyfill) { | ||
function getBigUint64(dv, byteOffset, littleEndian) { | ||
if (littleEndian) { | ||
return (BigInt(dv.getUint32(byteOffset + 4, true)) << 32n) | BigInt(dv.getUint32(byteOffset, true)); | ||
} | ||
return BigInt(dv.getUint32(byteOffset + 4)) | (BigInt(dv.getUint32(byteOffset)) << 32n); | ||
} | ||
DataViewPolyfill.getBigUint64 = getBigUint64; | ||
function setBigUint64(dv, byteOffset, value, littleEndian) { | ||
if (littleEndian) { | ||
dv.setUint32(byteOffset + 4, Number(value >> 32n), true); | ||
dv.setUint32(byteOffset, Number(value & 0xffffffffn), true); | ||
} | ||
else { | ||
dv.setUint32(byteOffset + 4, Number(value & 0xffffffffn)); | ||
dv.setUint32(byteOffset, Number(value >> 32n)); | ||
} | ||
} | ||
DataViewPolyfill.setBigUint64 = setBigUint64; | ||
})(DataViewPolyfill = Encoder.DataViewPolyfill || (Encoder.DataViewPolyfill = {})); | ||
/** DataView.prototype.getBigUint64 with polyfill for iOS 14. */ | ||
Encoder.getBigUint64 = typeof DataView.prototype.getBigUint64 === "function" ? | ||
(dv, byteOffset, littleEndian) => dv.getBigUint64(byteOffset, littleEndian) : | ||
/* istanbul ignore next */ | ||
DataViewPolyfill.getBigUint64; | ||
/** DataView.prototype.setBigUint64 with polyfill for iOS 14. */ | ||
Encoder.setBigUint64 = typeof DataView.prototype.setBigUint64 === "function" ? | ||
(dv, byteOffset, value, littleEndian) => dv.setBigUint64(byteOffset, value, littleEndian) : | ||
/* istanbul ignore next */ | ||
DataViewPolyfill.setBigUint64; | ||
Encoder.OmitEmpty = Symbol("OmitEmpty"); | ||
/** Encode a single object into Uint8Array. */ | ||
function encode(obj, initBufSize = BUF_INIT_SIZE) { | ||
function encode(obj, initBufSize) { | ||
const encoder = new Encoder(initBufSize); | ||
@@ -174,0 +122,0 @@ encoder.encode(obj); |
@@ -0,1 +1,2 @@ | ||
import { asDataView } from "@ndn/util"; | ||
function sizeofVarNum(n) { | ||
@@ -11,3 +12,3 @@ if (n < 0xFD) { | ||
} | ||
// JavaScript cannot reliably represent 64-bit integers | ||
// 64-bit integers may lose precision in Number type, and it's rarely useful | ||
throw new Error("VAR-NUMBER is too large"); | ||
@@ -21,14 +22,12 @@ } | ||
room[off++] = 0xFD; | ||
Encoder.asDataView(room).setUint16(off, n); | ||
asDataView(room).setUint16(off, n); | ||
} | ||
else { | ||
room[off++] = 0xFE; | ||
Encoder.asDataView(room).setUint32(off, n); | ||
asDataView(room).setUint32(off, n); | ||
} | ||
} | ||
const BUF_INIT_SIZE = 2048; | ||
const BUF_GROW_SIZE = 2048; | ||
/** TLV encoder that accepts objects in reverse order. */ | ||
export class Encoder { | ||
constructor(initSize = BUF_INIT_SIZE) { | ||
constructor(initSize = 2048) { | ||
this.buf = new ArrayBuffer(initSize); | ||
@@ -47,6 +46,2 @@ this.off = initSize; | ||
slice(start = 0, length) { | ||
if (length === undefined) { | ||
// iOS would interpret length=undefined as length=0, so we need a conditional | ||
return new Uint8Array(this.buf, this.off + start); | ||
} | ||
return new Uint8Array(this.buf, this.off + start, length); | ||
@@ -80,17 +75,6 @@ } | ||
} | ||
/** | ||
* Prepend TLV structure. | ||
* @param tlvType TLV-TYPE number. | ||
* @param omitEmpty omit TLV altogether if set to Encoder.OmitEmpty | ||
* @param tlvValue TLV-VALUE objects. | ||
*/ | ||
prependTlv(tlvType, omitEmpty, ...tlvValue) { | ||
let hasOmitEmpty = false; | ||
if (omitEmpty) { | ||
if (omitEmpty === Encoder.OmitEmpty) { | ||
hasOmitEmpty = true; | ||
} | ||
else { | ||
tlvValue.unshift(omitEmpty); | ||
} | ||
const hasOmitEmpty = omitEmpty === Encoder.OmitEmpty; | ||
if (!hasOmitEmpty) { | ||
tlvValue.unshift(omitEmpty); | ||
} | ||
@@ -125,49 +109,13 @@ const sizeBefore = this.size; | ||
grow(sizeofRoom) { | ||
const sizeofGrow = BUF_GROW_SIZE + sizeofRoom; | ||
const buf = new ArrayBuffer(sizeofGrow + this.size); | ||
new Uint8Array(buf, sizeofGrow).set(this.output); | ||
const sizeofGrowth = 2048 + sizeofRoom; | ||
const buf = new ArrayBuffer(sizeofGrowth + this.size); | ||
new Uint8Array(buf, sizeofGrowth).set(this.output); | ||
this.buf = buf; | ||
this.off = sizeofGrow; | ||
this.off = sizeofGrowth; | ||
} | ||
} | ||
(function (Encoder) { | ||
/** Create a DataView over a Uint8Array. */ | ||
function asDataView(a) { | ||
return new DataView(a.buffer, a.byteOffset, a.byteLength); | ||
} | ||
Encoder.asDataView = asDataView; | ||
let DataViewPolyfill; | ||
(function (DataViewPolyfill) { | ||
function getBigUint64(dv, byteOffset, littleEndian) { | ||
if (littleEndian) { | ||
return (BigInt(dv.getUint32(byteOffset + 4, true)) << 32n) | BigInt(dv.getUint32(byteOffset, true)); | ||
} | ||
return BigInt(dv.getUint32(byteOffset + 4)) | (BigInt(dv.getUint32(byteOffset)) << 32n); | ||
} | ||
DataViewPolyfill.getBigUint64 = getBigUint64; | ||
function setBigUint64(dv, byteOffset, value, littleEndian) { | ||
if (littleEndian) { | ||
dv.setUint32(byteOffset + 4, Number(value >> 32n), true); | ||
dv.setUint32(byteOffset, Number(value & 0xffffffffn), true); | ||
} | ||
else { | ||
dv.setUint32(byteOffset + 4, Number(value & 0xffffffffn)); | ||
dv.setUint32(byteOffset, Number(value >> 32n)); | ||
} | ||
} | ||
DataViewPolyfill.setBigUint64 = setBigUint64; | ||
})(DataViewPolyfill = Encoder.DataViewPolyfill || (Encoder.DataViewPolyfill = {})); | ||
/** DataView.prototype.getBigUint64 with polyfill for iOS 14. */ | ||
Encoder.getBigUint64 = typeof DataView.prototype.getBigUint64 === "function" ? | ||
(dv, byteOffset, littleEndian) => dv.getBigUint64(byteOffset, littleEndian) : | ||
/* istanbul ignore next */ | ||
DataViewPolyfill.getBigUint64; | ||
/** DataView.prototype.setBigUint64 with polyfill for iOS 14. */ | ||
Encoder.setBigUint64 = typeof DataView.prototype.setBigUint64 === "function" ? | ||
(dv, byteOffset, value, littleEndian) => dv.setBigUint64(byteOffset, value, littleEndian) : | ||
/* istanbul ignore next */ | ||
DataViewPolyfill.setBigUint64; | ||
Encoder.OmitEmpty = Symbol("OmitEmpty"); | ||
/** Encode a single object into Uint8Array. */ | ||
function encode(obj, initBufSize = BUF_INIT_SIZE) { | ||
function encode(obj, initBufSize) { | ||
const encoder = new Encoder(initBufSize); | ||
@@ -174,0 +122,0 @@ encoder.encode(obj); |
@@ -12,3 +12,3 @@ /** An object that knows how to prepend itself to an Encoder. */ | ||
*/ | ||
export declare type EncodableTlv = [number, ...any[]]; | ||
export declare type EncodableTlv = [number, ...Encodable[]] | [number, typeof Encoder.OmitEmpty, ...Encodable[]]; | ||
/** An object acceptable to Encoder.encode(). */ | ||
@@ -37,9 +37,6 @@ export declare type Encodable = Uint8Array | undefined | EncodableObj | EncodableTlv; | ||
prependValue(...tlvValue: Encodable[]): void; | ||
/** | ||
* Prepend TLV structure. | ||
* @param tlvType TLV-TYPE number. | ||
* @param omitEmpty omit TLV altogether if set to Encoder.OmitEmpty | ||
* @param tlvValue TLV-VALUE objects. | ||
*/ | ||
prependTlv(tlvType: number, omitEmpty?: typeof Encoder.OmitEmpty | Encodable, ...tlvValue: Encodable[]): void; | ||
/** Prepend TLV structure. */ | ||
prependTlv(tlvType: number, ...tlvValue: Encodable[]): void; | ||
/** Prepend TLV structure, but skip if TLV-VALUE is empty. */ | ||
prependTlv(tlvType: number, omitEmpty: typeof Encoder.OmitEmpty, ...tlvValue: Encodable[]): void; | ||
/** Prepend an Encodable object. */ | ||
@@ -50,12 +47,2 @@ encode(obj: Encodable | readonly Encodable[]): void; | ||
export declare namespace Encoder { | ||
/** Create a DataView over a Uint8Array. */ | ||
function asDataView(a: Uint8Array): DataView; | ||
namespace DataViewPolyfill { | ||
function getBigUint64(dv: DataView, byteOffset: number, littleEndian?: boolean): bigint; | ||
function setBigUint64(dv: DataView, byteOffset: number, value: bigint, littleEndian?: boolean): void; | ||
} | ||
/** DataView.prototype.getBigUint64 with polyfill for iOS 14. */ | ||
const getBigUint64: (dv: DataView, byteOffset: number, littleEndian?: boolean) => bigint; | ||
/** DataView.prototype.setBigUint64 with polyfill for iOS 14. */ | ||
const setBigUint64: (dv: DataView, byteOffset: number, value: bigint, littleEndian?: boolean) => void; | ||
const OmitEmpty: unique symbol; | ||
@@ -62,0 +49,0 @@ /** Encode a single object into Uint8Array. */ |
@@ -0,1 +1,2 @@ | ||
import { assert } from "@ndn/util"; | ||
import { printTT } from "./string_browser.js"; | ||
@@ -19,15 +20,10 @@ const AUTO_ORDER_SKIP = 100; | ||
this.rules = new Map(); | ||
this.requiredTlvTypes = new Set(); | ||
this.requiredTT = new Set(); | ||
this.nextOrder = AUTO_ORDER_SKIP; | ||
this.isCriticalCb = isCritical; | ||
/** Callbacks to receive top-level TLV before decoding TLV-VALUE. */ | ||
this.beforeTopCallbacks = []; | ||
this.isCritical = isCritical; | ||
/** Callbacks before decoding TLV-VALUE. */ | ||
this.beforeValueCallbacks = []; | ||
this.beforeObservers = []; | ||
/** Callbacks after decoding TLV-VALUE. */ | ||
this.afterValueCallbacks = []; | ||
/** Callbacks to receive top-level TLV after decoding TLV-VALUE. */ | ||
this.afterTopCallbacks = []; | ||
this.afterObservers = []; | ||
this.topTT = Array.isArray(topTT) ? topTT : [topTT]; | ||
this.unknownCb = () => false; | ||
} | ||
@@ -37,20 +33,15 @@ /** | ||
* @param tt TLV-TYPE to match this rule. | ||
* @param cb callback to handle element TLV. | ||
* @param cb callback or nested EvDecoder to handle element TLV. | ||
* @param options additional rule options. | ||
*/ | ||
add(tt, cb, options) { | ||
if (this.rules.has(tt)) { | ||
throw new Error(`TLV-TYPE ${printTT(tt)} already has a rule`); | ||
} | ||
const rule = { | ||
add(tt, cb, { order = (this.nextOrder += AUTO_ORDER_SKIP), required = false, repeat = false, } = {}) { | ||
assert(!this.rules.has(tt), "duplicate rule for same TLV-TYPE"); | ||
this.rules.set(tt, { | ||
cb: cb instanceof EvDecoder ? nest(cb) : cb, | ||
order: this.nextOrder, | ||
required: false, | ||
repeat: false, | ||
...options, | ||
}; | ||
this.nextOrder += AUTO_ORDER_SKIP; | ||
this.rules.set(tt, rule); | ||
if (rule.required) { | ||
this.requiredTlvTypes.add(tt); | ||
order, | ||
required, | ||
repeat, | ||
}); | ||
if (required) { | ||
this.requiredTT.add(tt); | ||
} | ||
@@ -61,3 +52,3 @@ return this; | ||
setIsCritical(cb) { | ||
this.isCriticalCb = cb; | ||
this.isCritical = cb; | ||
return this; | ||
@@ -67,3 +58,3 @@ } | ||
setUnknown(cb) { | ||
this.unknownCb = cb; | ||
this.unknownHandler = cb; | ||
return this; | ||
@@ -78,26 +69,21 @@ } | ||
} | ||
for (const cb of this.beforeTopCallbacks) { | ||
cb(target, topTlv); | ||
} | ||
this.decodeValue(target, vd); | ||
for (const cb of this.afterTopCallbacks) { | ||
cb(target, topTlv); | ||
} | ||
return target; | ||
return this.decodeV(target, vd, topTlv); | ||
} | ||
/** Decode TLV-VALUE to target object. */ | ||
decodeValue(target, vd) { | ||
for (const cb of this.beforeValueCallbacks) { | ||
cb(target); | ||
return this.decodeV(target, vd); | ||
} | ||
decodeV(target, vd, topTlv) { | ||
for (const cb of this.beforeObservers) { | ||
cb(target, topTlv); | ||
} | ||
let currentOrder = 0; | ||
let currentCount = 0; | ||
const missingTlvTypes = new Set(this.requiredTlvTypes); | ||
const foundTT = new Set(); | ||
const missingTT = new Set(this.requiredTT); | ||
while (!vd.eof) { | ||
const tlv = vd.read(); | ||
const tt = tlv.type; | ||
missingTlvTypes.delete(tt); | ||
const rule = this.rules.get(tt); | ||
if (rule === undefined) { | ||
if (!this.unknownCb(target, tlv, currentOrder)) { | ||
if (!rule) { | ||
if (!this.unknownHandler?.(target, tlv, currentOrder)) { | ||
this.handleUnrecognized(tt, "unknown"); | ||
@@ -111,17 +97,15 @@ } | ||
} | ||
if (currentOrder < rule.order) { | ||
currentOrder = rule.order; | ||
currentCount = 0; | ||
} | ||
++currentCount; | ||
if (!rule.repeat && currentCount > 1) { | ||
currentOrder = rule.order; | ||
if (!rule.repeat && foundTT.has(tt)) { | ||
throw new Error(`TLV-TYPE ${printTT(tt)} cannot repeat in ${this.typeName}`); | ||
} | ||
foundTT.add(tt); | ||
missingTT.delete(tt); | ||
rule.cb(target, tlv); | ||
} | ||
if (missingTlvTypes.size > 0) { | ||
throw new Error(`TLV-TYPE ${Array.from(missingTlvTypes).map(printTT).join(",")} ${missingTlvTypes.size === 1 ? "is" : "are"} missing in ${this.typeName}`); | ||
if (missingTT.size > 0) { | ||
throw new Error(`TLV-TYPE ${Array.from(missingTT, printTT).join(",")} missing in ${this.typeName}`); | ||
} | ||
for (const cb of this.afterValueCallbacks) { | ||
cb(target); | ||
for (const cb of this.afterObservers) { | ||
cb(target, topTlv); | ||
} | ||
@@ -131,3 +115,3 @@ return target; | ||
handleUnrecognized(tt, reason) { | ||
if (this.isCriticalCb(tt)) { | ||
if (this.isCritical(tt)) { | ||
throw new Error(`TLV-TYPE ${printTT(tt)} is ${reason} in ${this.typeName}`); | ||
@@ -134,0 +118,0 @@ } |
@@ -0,1 +1,2 @@ | ||
import { assert } from "@ndn/util"; | ||
import { printTT } from "./string_node.js"; | ||
@@ -19,15 +20,10 @@ const AUTO_ORDER_SKIP = 100; | ||
this.rules = new Map(); | ||
this.requiredTlvTypes = new Set(); | ||
this.requiredTT = new Set(); | ||
this.nextOrder = AUTO_ORDER_SKIP; | ||
this.isCriticalCb = isCritical; | ||
/** Callbacks to receive top-level TLV before decoding TLV-VALUE. */ | ||
this.beforeTopCallbacks = []; | ||
this.isCritical = isCritical; | ||
/** Callbacks before decoding TLV-VALUE. */ | ||
this.beforeValueCallbacks = []; | ||
this.beforeObservers = []; | ||
/** Callbacks after decoding TLV-VALUE. */ | ||
this.afterValueCallbacks = []; | ||
/** Callbacks to receive top-level TLV after decoding TLV-VALUE. */ | ||
this.afterTopCallbacks = []; | ||
this.afterObservers = []; | ||
this.topTT = Array.isArray(topTT) ? topTT : [topTT]; | ||
this.unknownCb = () => false; | ||
} | ||
@@ -37,20 +33,15 @@ /** | ||
* @param tt TLV-TYPE to match this rule. | ||
* @param cb callback to handle element TLV. | ||
* @param cb callback or nested EvDecoder to handle element TLV. | ||
* @param options additional rule options. | ||
*/ | ||
add(tt, cb, options) { | ||
if (this.rules.has(tt)) { | ||
throw new Error(`TLV-TYPE ${printTT(tt)} already has a rule`); | ||
} | ||
const rule = { | ||
add(tt, cb, { order = (this.nextOrder += AUTO_ORDER_SKIP), required = false, repeat = false, } = {}) { | ||
assert(!this.rules.has(tt), "duplicate rule for same TLV-TYPE"); | ||
this.rules.set(tt, { | ||
cb: cb instanceof EvDecoder ? nest(cb) : cb, | ||
order: this.nextOrder, | ||
required: false, | ||
repeat: false, | ||
...options, | ||
}; | ||
this.nextOrder += AUTO_ORDER_SKIP; | ||
this.rules.set(tt, rule); | ||
if (rule.required) { | ||
this.requiredTlvTypes.add(tt); | ||
order, | ||
required, | ||
repeat, | ||
}); | ||
if (required) { | ||
this.requiredTT.add(tt); | ||
} | ||
@@ -61,3 +52,3 @@ return this; | ||
setIsCritical(cb) { | ||
this.isCriticalCb = cb; | ||
this.isCritical = cb; | ||
return this; | ||
@@ -67,3 +58,3 @@ } | ||
setUnknown(cb) { | ||
this.unknownCb = cb; | ||
this.unknownHandler = cb; | ||
return this; | ||
@@ -78,26 +69,21 @@ } | ||
} | ||
for (const cb of this.beforeTopCallbacks) { | ||
cb(target, topTlv); | ||
} | ||
this.decodeValue(target, vd); | ||
for (const cb of this.afterTopCallbacks) { | ||
cb(target, topTlv); | ||
} | ||
return target; | ||
return this.decodeV(target, vd, topTlv); | ||
} | ||
/** Decode TLV-VALUE to target object. */ | ||
decodeValue(target, vd) { | ||
for (const cb of this.beforeValueCallbacks) { | ||
cb(target); | ||
return this.decodeV(target, vd); | ||
} | ||
decodeV(target, vd, topTlv) { | ||
for (const cb of this.beforeObservers) { | ||
cb(target, topTlv); | ||
} | ||
let currentOrder = 0; | ||
let currentCount = 0; | ||
const missingTlvTypes = new Set(this.requiredTlvTypes); | ||
const foundTT = new Set(); | ||
const missingTT = new Set(this.requiredTT); | ||
while (!vd.eof) { | ||
const tlv = vd.read(); | ||
const tt = tlv.type; | ||
missingTlvTypes.delete(tt); | ||
const rule = this.rules.get(tt); | ||
if (rule === undefined) { | ||
if (!this.unknownCb(target, tlv, currentOrder)) { | ||
if (!rule) { | ||
if (!this.unknownHandler?.(target, tlv, currentOrder)) { | ||
this.handleUnrecognized(tt, "unknown"); | ||
@@ -111,17 +97,15 @@ } | ||
} | ||
if (currentOrder < rule.order) { | ||
currentOrder = rule.order; | ||
currentCount = 0; | ||
} | ||
++currentCount; | ||
if (!rule.repeat && currentCount > 1) { | ||
currentOrder = rule.order; | ||
if (!rule.repeat && foundTT.has(tt)) { | ||
throw new Error(`TLV-TYPE ${printTT(tt)} cannot repeat in ${this.typeName}`); | ||
} | ||
foundTT.add(tt); | ||
missingTT.delete(tt); | ||
rule.cb(target, tlv); | ||
} | ||
if (missingTlvTypes.size > 0) { | ||
throw new Error(`TLV-TYPE ${Array.from(missingTlvTypes).map(printTT).join(",")} ${missingTlvTypes.size === 1 ? "is" : "are"} missing in ${this.typeName}`); | ||
if (missingTT.size > 0) { | ||
throw new Error(`TLV-TYPE ${Array.from(missingTT, printTT).join(",")} missing in ${this.typeName}`); | ||
} | ||
for (const cb of this.afterValueCallbacks) { | ||
cb(target); | ||
for (const cb of this.afterObservers) { | ||
cb(target, topTlv); | ||
} | ||
@@ -131,3 +115,3 @@ return target; | ||
handleUnrecognized(tt, reason) { | ||
if (this.isCriticalCb(tt)) { | ||
if (this.isCritical(tt)) { | ||
throw new Error(`TLV-TYPE ${printTT(tt)} is ${reason} in ${this.typeName}`); | ||
@@ -134,0 +118,0 @@ } |
@@ -7,14 +7,10 @@ import type { Decoder } from "./decoder"; | ||
private readonly rules; | ||
private readonly requiredTlvTypes; | ||
private readonly requiredTT; | ||
private nextOrder; | ||
private isCriticalCb; | ||
private unknownCb; | ||
/** Callbacks to receive top-level TLV before decoding TLV-VALUE. */ | ||
readonly beforeTopCallbacks: Array<EvDecoder.TopElementCallback<T>>; | ||
private isCritical; | ||
private unknownHandler?; | ||
/** Callbacks before decoding TLV-VALUE. */ | ||
readonly beforeValueCallbacks: Array<EvDecoder.TargetCallback<T>>; | ||
readonly beforeObservers: Array<EvDecoder.TlvObserver<T>>; | ||
/** Callbacks after decoding TLV-VALUE. */ | ||
readonly afterValueCallbacks: Array<EvDecoder.TargetCallback<T>>; | ||
/** Callbacks to receive top-level TLV after decoding TLV-VALUE. */ | ||
readonly afterTopCallbacks: Array<EvDecoder.TopElementCallback<T>>; | ||
readonly afterObservers: Array<EvDecoder.TlvObserver<T>>; | ||
/** | ||
@@ -29,10 +25,10 @@ * Constructor. | ||
* @param tt TLV-TYPE to match this rule. | ||
* @param cb callback to handle element TLV. | ||
* @param cb callback or nested EvDecoder to handle element TLV. | ||
* @param options additional rule options. | ||
*/ | ||
add(tt: number, cb: EvDecoder.ElementCallback<T> | EvDecoder<T>, options?: Partial<EvDecoder.RuleOptions>): this; | ||
add(tt: number, cb: EvDecoder.ElementDecoder<T> | EvDecoder<T>, { order, required, repeat, }?: Partial<EvDecoder.RuleOptions>): this; | ||
/** Set callback to determine whether TLV-TYPE is critical. */ | ||
setIsCritical(cb: EvDecoder.IsCriticalCallback): this; | ||
setIsCritical(cb: EvDecoder.IsCritical): this; | ||
/** Set callback to handle unknown elements. */ | ||
setUnknown(cb: EvDecoder.UnknownElementCallback<T>): this; | ||
setUnknown(cb: EvDecoder.UnknownElementHandler<T>): this; | ||
/** Decode TLV to target object. */ | ||
@@ -42,2 +38,3 @@ decode<R extends T = T>(target: R, decoder: Decoder): R; | ||
decodeValue<R extends T = T>(target: R, vd: Decoder): R; | ||
private decodeV; | ||
private handleUnrecognized; | ||
@@ -47,12 +44,19 @@ } | ||
/** Invoked when a matching TLV element is found. */ | ||
type ElementCallback<T> = (target: T, tlv: Decoder.Tlv) => void; | ||
type ElementDecoder<T> = (target: T, tlv: Decoder.Tlv) => void; | ||
interface RuleOptions { | ||
/** | ||
* Expected order of appearance. | ||
* When using this option, it should be specified for all rules in a EvDecoder. | ||
* Default to the order in which rules were added to EvDecoder. | ||
*/ | ||
order: number; | ||
/** Whether TLV element must appear at least once. */ | ||
/** | ||
* Whether TLV element must appear at least once. | ||
* Default is false. | ||
*/ | ||
required: boolean; | ||
/** Whether TLV element may appear more than once. */ | ||
/** | ||
* Whether TLV element may appear more than once. | ||
* Default is false. | ||
*/ | ||
repeat: boolean; | ||
@@ -65,6 +69,14 @@ } | ||
*/ | ||
type UnknownElementCallback<T> = (target: T, tlv: Decoder.Tlv, order: number) => boolean; | ||
type IsCriticalCallback = (tt: number) => boolean; | ||
type TopElementCallback<T> = (target: T, tlv: Decoder.Tlv) => void; | ||
type TargetCallback<T> = (target: T) => void; | ||
type UnknownElementHandler<T> = (target: T, tlv: Decoder.Tlv, order: number) => boolean; | ||
/** | ||
* Function to determine whether a TLV-TYPE number is "critical". | ||
* Unrecognized or out-of-order TLV element with a critical TLV-TYPE number causes decoding error. | ||
*/ | ||
type IsCritical = (tt: number) => boolean; | ||
/** | ||
* Callback before or after decoding TLV-VALUE. | ||
* @param target target object. | ||
* @param topTlv top-level TLV element, available in EVD.decode but unavailable in EVD.decodeValue. | ||
*/ | ||
type TlvObserver<T> = (target: T, topTlv?: Decoder.Tlv) => void; | ||
} |
@@ -1,3 +0,2 @@ | ||
import { Encoder } from "./encoder_browser.js"; | ||
import { toHex } from "./string_browser.js"; | ||
import { asDataView, toHex } from "@ndn/util"; | ||
class Nni1 { | ||
@@ -16,3 +15,3 @@ constructor(n) { | ||
encodeTo(encoder) { | ||
Encoder.asDataView(encoder.prependRoom(2)).setUint16(0, this.n); | ||
asDataView(encoder.prependRoom(2)).setUint16(0, this.n); | ||
} | ||
@@ -25,3 +24,3 @@ } | ||
encodeTo(encoder) { | ||
Encoder.asDataView(encoder.prependRoom(4)).setUint32(0, this.n); | ||
asDataView(encoder.prependRoom(4)).setUint32(0, this.n); | ||
} | ||
@@ -34,5 +33,5 @@ } | ||
encodeTo(encoder) { | ||
const dv = Encoder.asDataView(encoder.prependRoom(8)); | ||
const dv = asDataView(encoder.prependRoom(8)); | ||
dv.setUint32(0, this.n / 0x100000000); | ||
dv.setUint32(4, this.n % 0x100000000); | ||
dv.setUint32(4, this.n); | ||
} | ||
@@ -45,4 +44,3 @@ } | ||
encodeTo(encoder) { | ||
const dv = Encoder.asDataView(encoder.prependRoom(8)); | ||
Encoder.setBigUint64(dv, 0, this.n); | ||
asDataView(encoder.prependRoom(8)).setBigUint64(0, this.n); | ||
} | ||
@@ -114,5 +112,5 @@ } | ||
} | ||
const dv = Encoder.asDataView(value); | ||
const dv = asDataView(value); | ||
if (big) { | ||
return dv.byteLength === 8 ? Encoder.getBigUint64(dv, 0) : BigInt(decode32(dv)); | ||
return dv.byteLength === 8 ? dv.getBigUint64(0) : BigInt(decode32(dv)); | ||
} | ||
@@ -131,8 +129,8 @@ if (dv.byteLength === 8) { | ||
const [min = 0, max = Number.MAX_SAFE_INTEGER] = typeof limit1 === "number" ? [limit0, limit1] : [0, limit0]; | ||
if (n < min || n > max) { | ||
if (!(n >= min && n <= max)) { | ||
throw new RangeError(`${n} is out of ${typeName} valid range`); | ||
} | ||
return Math.floor(n); | ||
return Math.trunc(n); | ||
} | ||
NNI.constrain = constrain; | ||
})(NNI || (NNI = {})); |
@@ -1,3 +0,2 @@ | ||
import { Encoder } from "./encoder_node.js"; | ||
import { toHex } from "./string_node.js"; | ||
import { asDataView, toHex } from "@ndn/util"; | ||
class Nni1 { | ||
@@ -16,3 +15,3 @@ constructor(n) { | ||
encodeTo(encoder) { | ||
Encoder.asDataView(encoder.prependRoom(2)).setUint16(0, this.n); | ||
asDataView(encoder.prependRoom(2)).setUint16(0, this.n); | ||
} | ||
@@ -25,3 +24,3 @@ } | ||
encodeTo(encoder) { | ||
Encoder.asDataView(encoder.prependRoom(4)).setUint32(0, this.n); | ||
asDataView(encoder.prependRoom(4)).setUint32(0, this.n); | ||
} | ||
@@ -34,5 +33,5 @@ } | ||
encodeTo(encoder) { | ||
const dv = Encoder.asDataView(encoder.prependRoom(8)); | ||
const dv = asDataView(encoder.prependRoom(8)); | ||
dv.setUint32(0, this.n / 0x100000000); | ||
dv.setUint32(4, this.n % 0x100000000); | ||
dv.setUint32(4, this.n); | ||
} | ||
@@ -45,4 +44,3 @@ } | ||
encodeTo(encoder) { | ||
const dv = Encoder.asDataView(encoder.prependRoom(8)); | ||
Encoder.setBigUint64(dv, 0, this.n); | ||
asDataView(encoder.prependRoom(8)).setBigUint64(0, this.n); | ||
} | ||
@@ -114,5 +112,5 @@ } | ||
} | ||
const dv = Encoder.asDataView(value); | ||
const dv = asDataView(value); | ||
if (big) { | ||
return dv.byteLength === 8 ? Encoder.getBigUint64(dv, 0) : BigInt(decode32(dv)); | ||
return dv.byteLength === 8 ? dv.getBigUint64(0) : BigInt(decode32(dv)); | ||
} | ||
@@ -131,8 +129,8 @@ if (dv.byteLength === 8) { | ||
const [min = 0, max = Number.MAX_SAFE_INTEGER] = typeof limit1 === "number" ? [limit0, limit1] : [0, limit0]; | ||
if (n < min || n > max) { | ||
if (!(n >= min && n <= max)) { | ||
throw new RangeError(`${n} is out of ${typeName} valid range`); | ||
} | ||
return Math.floor(n); | ||
return Math.trunc(n); | ||
} | ||
NNI.constrain = constrain; | ||
})(NNI || (NNI = {})); |
@@ -1,2 +0,2 @@ | ||
import { Encodable, Encoder } from "./encoder"; | ||
import { type Encodable, Encoder } from "./encoder"; | ||
declare class Nni1 { | ||
@@ -3,0 +3,0 @@ private readonly n; |
@@ -12,33 +12,1 @@ /** Pretty-print TLV-TYPE number. */ | ||
} | ||
const INT2HEX = []; | ||
const HEX2INT = {}; | ||
for (let b = 0; b <= 0xFF; ++b) { | ||
const s = b.toString(16).padStart(2, "0").toUpperCase(); | ||
INT2HEX.push(s); | ||
HEX2INT[s] = b; | ||
} | ||
/** Convert byte array to upper-case hexadecimal string. */ | ||
export function toHex(buf) { | ||
return Array.from(buf, (b) => INT2HEX[b]).join(""); | ||
} | ||
/** | ||
* Convert hexadecimal string to byte array. | ||
* | ||
* This function lacks error handling. Use on trusted input only. | ||
*/ | ||
export function fromHex(s) { | ||
s = s.toUpperCase(); | ||
const b = new Uint8Array(s.length / 2); | ||
for (let i = 0; i < b.length; ++i) { | ||
b[i] = HEX2INT[s.slice(i * 2, (i + 1) * 2)]; | ||
} | ||
return b; | ||
} | ||
const textEncoder = new TextEncoder(); | ||
const textDecoder = new TextDecoder(); | ||
export function toUtf8(s) { | ||
return textEncoder.encode(s); | ||
} | ||
export function fromUtf8(buf) { | ||
return textDecoder.decode(buf); | ||
} |
@@ -12,33 +12,1 @@ /** Pretty-print TLV-TYPE number. */ | ||
} | ||
const INT2HEX = []; | ||
const HEX2INT = {}; | ||
for (let b = 0; b <= 0xFF; ++b) { | ||
const s = b.toString(16).padStart(2, "0").toUpperCase(); | ||
INT2HEX.push(s); | ||
HEX2INT[s] = b; | ||
} | ||
/** Convert byte array to upper-case hexadecimal string. */ | ||
export function toHex(buf) { | ||
return Array.from(buf, (b) => INT2HEX[b]).join(""); | ||
} | ||
/** | ||
* Convert hexadecimal string to byte array. | ||
* | ||
* This function lacks error handling. Use on trusted input only. | ||
*/ | ||
export function fromHex(s) { | ||
s = s.toUpperCase(); | ||
const b = new Uint8Array(s.length / 2); | ||
for (let i = 0; i < b.length; ++i) { | ||
b[i] = HEX2INT[s.slice(i * 2, (i + 1) * 2)]; | ||
} | ||
return b; | ||
} | ||
const textEncoder = new TextEncoder(); | ||
const textDecoder = new TextDecoder(); | ||
export function toUtf8(s) { | ||
return textEncoder.encode(s); | ||
} | ||
export function fromUtf8(buf) { | ||
return textDecoder.decode(buf); | ||
} |
/** Pretty-print TLV-TYPE number. */ | ||
export declare function printTT(tlvType: number): string; | ||
/** Convert byte array to upper-case hexadecimal string. */ | ||
export declare function toHex(buf: Uint8Array): string; | ||
/** | ||
* Convert hexadecimal string to byte array. | ||
* | ||
* This function lacks error handling. Use on trusted input only. | ||
*/ | ||
export declare function fromHex(s: string): Uint8Array; | ||
export declare function toUtf8(s: string): Uint8Array; | ||
export declare function fromUtf8(buf: Uint8Array): string; |
{ | ||
"name": "@ndn/tlv", | ||
"version": "0.0.20210930", | ||
"version": "0.0.20220501", | ||
"description": "NDNts: TLV", | ||
@@ -25,9 +25,8 @@ "keywords": [ | ||
"dependencies": { | ||
"mnemonist": "^0.38.4", | ||
"tslib": "^2.3.1" | ||
"@ndn/util": "0.0.20220501", | ||
"mnemonist": "^0.39.1", | ||
"tslib": "^2.4.0" | ||
}, | ||
"engines": { | ||
"node": "^14.17.0 || ^16.8.0" | ||
}, | ||
"types": "lib/mod.d.ts" | ||
"types": "lib/mod.d.ts", | ||
"readme": "# @ndn/tlv\n\nThis package is part of [NDNts](https://yoursunny.com/p/NDNts/), Named Data Networking libraries for the modern web.\n\nThis package implements Type-Length-Value structure encoder and decoder as specified in [NDN Packet Format v0.3](https://named-data.net/doc/NDN-packet-spec/0.3/tlv.html).\nIt has full support for TLV evolvability guidelines.\n\n```ts\nimport { Encoder, Decoder, EvDecoder, NNI } from \"@ndn/tlv\";\n\n// other imports for examples\nimport { Name, TT as nameTT } from \"@ndn/packet\";\nimport { strict as assert } from \"node:assert\";\n```\n\n## Encoder\n\nThe **Encoder** has an internal buffer of `Uint8Array` type.\nIt prepends any encodable items to the internal buffer, and reallocates a larger buffer when necessary.\n\n```ts\n// Encode TLV object that implements EncodableObj interface:\nlet encoder = new Encoder();\nencoder.encode(new Name(\"/A\"));\n// Look at the output:\nassert.deepEqual(encoder.output, Uint8Array.of(0x07, 0x03, 0x08, 0x01, 0x41));\n\n// Prepend a TLV structure with specified TLV-TYPE and TLV-VALUE:\nencoder = new Encoder();\nencoder.encode([0xB0, Uint8Array.of(0xC0, 0xC1)]);\nassert.deepEqual(encoder.output, Uint8Array.of(0xB0, 0x02, 0xC0, 0xC1));\n\n// Prepend a non-negative integer\nencoder.encode(NNI(0x200110));\n// We are using the same Encoder instance, so it gets prepended:\nassert.deepEqual(encoder.output, Uint8Array.of(0x00, 0x20, 0x01, 0x10, 0xB0, 0x02, 0xC0, 0xC1));\n\n// Put multiple encodable items in TLV-VALUE:\nencoder = new Encoder();\nencoder.encode([0xB0, Uint8Array.of(0xC0, 0xC1), new Name(\"/A\")]);\nassert.deepEqual(encoder.output,\n Uint8Array.of(0xB0, 0x07, 0xC0, 0xC1, 0x07, 0x03, 0x08, 0x01, 0x41));\n```\n\n## Decoder\n\nThe **Decoder** is a basic sequential decoder.\n\n```ts\n// Read Type-Length-Value manually:\nlet decoder = new Decoder(Uint8Array.of(0x08, 0x01, 0x41, 0xFF));\nconst { type, length, value } = decoder.read();\nassert.equal(type, 0x08);\nassert.equal(length, 1);\nassert.deepEqual(value, Uint8Array.of(0x41));\n// The remaining [0xFF] is still in the buffer.\n// If you continue reading, you get an error due to incomplete TLV.\nassert.throws(() => decoder.read());\n\n// Decode into TLV object:\ndecoder = new Decoder(Uint8Array.of(0x07, 0x03, 0x08, 0x01, 0x41));\nconst name = decoder.decode(Name);\nassert(name instanceof Name);\nassert.equal(name.toString(), \"/8=A\");\n```\n\n## EvDecoder\n\nThe **EvDecoder** is a decoder that is aware of TLV evolvability guidelines.\nIt's used to implement decoding functions of TLV objects, such as `Interest.decodeFrom`.\n\nSuppose we want to decode [NLSR's LSDB Dataset](https://redmine.named-data.net/projects/nlsr/wiki/LSDB_DataSet/11):\n\n```abnf\nAdjacency = ADJACENCY-TYPE TLV-LENGTH\n Name\n Uri\n Cost\nUri = URI-TYPE TLV-LENGTH *VCHAR\nCost = COST-TYPE TLV-LENGTH nonNegativeInteger\n\nADJACENCY-TYPE = 0x84\nURI-TYPE = 0x8D\nCOST-TYPE = 0x8C\n```\n\n```ts\n// Declare a class to represent this type.\nclass Adjacency {\n public name = new Name();\n public uri = \"\";\n public cost = 0;\n}\n\n// Declare constants for TLV-TYPE numbers.\nconst TT = {\n ...nameTT,\n Adjacency: 0x84,\n Cost: 0x8C,\n Uri: 0x8D,\n};\n\n// Create the decoder.\nconst EVD = new EvDecoder<Adjacency>(\"Adjacency\", TT.Adjacency)\n .add(TT.Name, (t, { decoder }) => t.name = decoder.decode(Name), { required: true })\n .add(TT.Uri, (t, { text }) => t.uri = text, { required: true })\n .add(TT.Cost, (t, { nni }) => t.cost = nni, { required: true });\n// Each rule declares a possible sub TLV.\n// They are added in the order of expected appearance.\n// The callback receives two arguments:\n// (1) the target object we are decoding into, so that EVD instances are reusable;\n// (2) a Decoder.Tlv structure, where we can selectively access just the TLV-VALUE, the whole TLV,\n// the TLV-VALUE as a Decoder, the whole TLV as a Decoder, etc.\n\n// Suppose we receive this encoded TLV:\nconst adjacencyWire = Uint8Array.of(\n 0x84, 0x0D,\n 0x07, 0x03, 0x08, 0x01, 0x41, // Name\n 0x8D, 0x01, 0x42, // Uri\n 0xF0, 0x00, // unrecognized non-critical TLV-TYPE, ignored\n 0x8C, 0x01, 0x80, // Cost\n);\nconst adjacencyDecoder = new Decoder(adjacencyWire);\n\n// We can decode it with the EVD.\nconst adjacency = EVD.decode(new Adjacency(), adjacencyDecoder);\nassert.equal(adjacency.name.toString(), \"/8=A\");\nassert.equal(adjacency.uri, \"B\");\nassert.equal(adjacency.cost, 128);\n```\n" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
25
59700
3
1488
+ Added@ndn/util@0.0.20220501
+ Added@ndn/util@0.0.20220501(transitive)
+ Added@types/minimalistic-assert@1.0.3(transitive)
+ Addedminimalistic-assert@1.0.1(transitive)
+ Addedmnemonist@0.39.8(transitive)
- Removedmnemonist@0.38.5(transitive)
Updatedmnemonist@^0.39.1
Updatedtslib@^2.4.0