@dfinity/candid
Version:
JavaScript and TypeScript library to work with candid interfaces
1,458 lines • 76.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Principal = exports.Nat64 = exports.Nat32 = exports.Nat16 = exports.Nat8 = exports.Int64 = exports.Int32 = exports.Int16 = exports.Int8 = exports.Float64 = exports.Float32 = exports.Nat = exports.Int = exports.Text = exports.Null = exports.Bool = exports.Unknown = exports.Reserved = exports.Empty = exports.ServiceClass = exports.FuncClass = exports.PrincipalClass = exports.RecClass = exports.VariantClass = exports.TupleClass = exports.RecordClass = exports.OptClass = exports.VecClass = exports.FixedNatClass = exports.FixedIntClass = exports.FloatClass = exports.NatClass = exports.IntClass = exports.TextClass = exports.ReservedClass = exports.NullClass = exports.BoolClass = exports.UnknownClass = exports.EmptyClass = exports.ConstructType = exports.PrimitiveType = exports.Type = exports.Visitor = void 0;
exports.encode = encode;
exports.decode = decode;
exports.Tuple = Tuple;
exports.Vec = Vec;
exports.Opt = Opt;
exports.Record = Record;
exports.Variant = Variant;
exports.Rec = Rec;
exports.Func = Func;
exports.Service = Service;
exports.resetSubtypeCache = resetSubtypeCache;
exports.subtype = subtype;
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */
const principal_1 = require("@dfinity/principal");
const buffer_ts_1 = require("./utils/buffer.js");
const hash_ts_1 = require("./utils/hash.js");
const leb128_ts_1 = require("./utils/leb128.js");
const bigint_math_ts_1 = require("./utils/bigint-math.js");
/**
* This module provides a combinator library to create serializers/deserializers
* between JavaScript values and IDL used by canisters on the Internet Computer,
* as documented at https://github.com/dfinity/candid/blob/119703ba342d2fef6ab4972d2541b9fe36ae8e36/spec/Candid.md
*/
var IDLTypeIds;
(function (IDLTypeIds) {
IDLTypeIds[IDLTypeIds["Null"] = -1] = "Null";
IDLTypeIds[IDLTypeIds["Bool"] = -2] = "Bool";
IDLTypeIds[IDLTypeIds["Nat"] = -3] = "Nat";
IDLTypeIds[IDLTypeIds["Int"] = -4] = "Int";
IDLTypeIds[IDLTypeIds["Float32"] = -13] = "Float32";
IDLTypeIds[IDLTypeIds["Float64"] = -14] = "Float64";
IDLTypeIds[IDLTypeIds["Text"] = -15] = "Text";
IDLTypeIds[IDLTypeIds["Reserved"] = -16] = "Reserved";
IDLTypeIds[IDLTypeIds["Empty"] = -17] = "Empty";
IDLTypeIds[IDLTypeIds["Opt"] = -18] = "Opt";
IDLTypeIds[IDLTypeIds["Vector"] = -19] = "Vector";
IDLTypeIds[IDLTypeIds["Record"] = -20] = "Record";
IDLTypeIds[IDLTypeIds["Variant"] = -21] = "Variant";
IDLTypeIds[IDLTypeIds["Func"] = -22] = "Func";
IDLTypeIds[IDLTypeIds["Service"] = -23] = "Service";
IDLTypeIds[IDLTypeIds["Principal"] = -24] = "Principal";
})(IDLTypeIds || (IDLTypeIds = {}));
const magicNumber = 'DIDL';
const toReadableString_max = 400; // will not display arguments after 400chars. Makes sure 2mb blobs don't get inside the error
function zipWith(xs, ys, f) {
return xs.map((x, i) => f(x, ys[i]));
}
/**
* An IDL Type Table, which precedes the data in the stream.
*/
class TypeTable {
constructor() {
// List of types. Needs to be an array as the index needs to be stable.
this._typs = [];
this._idx = new Map();
this._idxRefCount = new Map();
}
has(obj) {
return this._idx.has(obj.name);
}
add(type, buf) {
const idx = this._typs.length;
this._idx.set(type.name, idx);
this._idxRefCount.set(idx, 1);
this._typs.push(buf);
}
merge(obj, knot) {
const idx = this._idx.get(obj.name);
const knotIdx = this._idx.get(knot);
if (idx === undefined) {
throw new Error('Missing type index for ' + obj);
}
if (knotIdx === undefined) {
throw new Error('Missing type index for ' + knot);
}
// Point the recursive placeholder (obj) to the concrete type bytes
this._typs[idx] = this._typs[knotIdx];
// Merge mappings so BOTH names point to the same index
// This avoids losing lookups for the concrete type name (e.g. "opt nat8")
// which may still be referenced elsewhere in the type table build.
const idxRefCount = this._getIdxRefCount(idx);
const knotRefCount = this._getIdxRefCount(knotIdx);
this._idxRefCount.set(idx, idxRefCount + knotRefCount);
// Re-point the knot name to the resolved index and mark the old index as unused
this._idx.set(knot, idx);
this._idxRefCount.set(knotIdx, 0);
// Remove unused trailing entries if possible
this._compactFromEnd();
}
_getIdxRefCount(idx) {
return this._idxRefCount.get(idx) || 0;
}
_compactFromEnd() {
// Remove unused entries from the end of the array
while (this._typs.length > 0) {
const lastIndex = this._typs.length - 1;
if (this._getIdxRefCount(lastIndex) > 0) {
break;
}
this._typs.pop();
this._idxRefCount.delete(lastIndex);
}
}
encode() {
const len = (0, leb128_ts_1.lebEncode)(this._typs.length);
const buf = (0, buffer_ts_1.concat)(...this._typs);
return (0, buffer_ts_1.concat)(len, buf);
}
indexOf(typeName) {
if (!this._idx.has(typeName)) {
throw new Error('Missing type index for ' + typeName);
}
return (0, leb128_ts_1.slebEncode)(this._idx.get(typeName) || 0);
}
}
class Visitor {
visitType(_t, _data) {
throw new Error('Not implemented');
}
visitPrimitive(t, data) {
return this.visitType(t, data);
}
visitEmpty(t, data) {
return this.visitPrimitive(t, data);
}
visitBool(t, data) {
return this.visitPrimitive(t, data);
}
visitNull(t, data) {
return this.visitPrimitive(t, data);
}
visitReserved(t, data) {
return this.visitPrimitive(t, data);
}
visitText(t, data) {
return this.visitPrimitive(t, data);
}
visitNumber(t, data) {
return this.visitPrimitive(t, data);
}
visitInt(t, data) {
return this.visitNumber(t, data);
}
visitNat(t, data) {
return this.visitNumber(t, data);
}
visitFloat(t, data) {
return this.visitPrimitive(t, data);
}
visitFixedInt(t, data) {
return this.visitNumber(t, data);
}
visitFixedNat(t, data) {
return this.visitNumber(t, data);
}
visitPrincipal(t, data) {
return this.visitPrimitive(t, data);
}
visitConstruct(t, data) {
return this.visitType(t, data);
}
visitVec(t, _ty, data) {
return this.visitConstruct(t, data);
}
visitOpt(t, _ty, data) {
return this.visitConstruct(t, data);
}
visitRecord(t, _fields, data) {
return this.visitConstruct(t, data);
}
visitTuple(t, components, data) {
const fields = components.map((ty, i) => [`_${i}_`, ty]);
return this.visitRecord(t, fields, data);
}
visitVariant(t, _fields, data) {
return this.visitConstruct(t, data);
}
visitRec(_t, ty, data) {
return this.visitConstruct(ty, data);
}
visitFunc(t, data) {
return this.visitConstruct(t, data);
}
visitService(t, data) {
return this.visitConstruct(t, data);
}
}
exports.Visitor = Visitor;
// We try to use hard-to-accidentally-pick names to avoid potential collisions with other types.
var IdlTypeName;
(function (IdlTypeName) {
IdlTypeName["EmptyClass"] = "__IDL_EmptyClass__";
IdlTypeName["UnknownClass"] = "__IDL_UnknownClass__";
IdlTypeName["BoolClass"] = "__IDL_BoolClass__";
IdlTypeName["NullClass"] = "__IDL_NullClass__";
IdlTypeName["ReservedClass"] = "__IDL_ReservedClass__";
IdlTypeName["TextClass"] = "__IDL_TextClass__";
IdlTypeName["IntClass"] = "__IDL_IntClass__";
IdlTypeName["NatClass"] = "__IDL_NatClass__";
IdlTypeName["FloatClass"] = "__IDL_FloatClass__";
IdlTypeName["FixedIntClass"] = "__IDL_FixedIntClass__";
IdlTypeName["FixedNatClass"] = "__IDL_FixedNatClass__";
IdlTypeName["VecClass"] = "__IDL_VecClass__";
IdlTypeName["OptClass"] = "__IDL_OptClass__";
IdlTypeName["RecordClass"] = "__IDL_RecordClass__";
IdlTypeName["TupleClass"] = "__IDL_TupleClass__";
IdlTypeName["VariantClass"] = "__IDL_VariantClass__";
IdlTypeName["RecClass"] = "__IDL_RecClass__";
IdlTypeName["PrincipalClass"] = "__IDL_PrincipalClass__";
IdlTypeName["FuncClass"] = "__IDL_FuncClass__";
IdlTypeName["ServiceClass"] = "__IDL_ServiceClass__";
})(IdlTypeName || (IdlTypeName = {}));
/**
* Represents an IDL type.
*/
class Type {
/* Display type name */
display() {
return this.name;
}
valueToString(x) {
return toReadableString(x);
}
/* Implement `T` in the IDL spec, only needed for non-primitive types */
buildTypeTable(typeTable) {
if (!typeTable.has(this)) {
this._buildTypeTableImpl(typeTable);
}
}
}
exports.Type = Type;
class PrimitiveType extends Type {
checkType(t) {
if (this.name !== t.name) {
throw new Error(`type mismatch: type on the wire ${t.name}, expect type ${this.name}`);
}
return t;
}
_buildTypeTableImpl(_typeTable) {
// No type table encoding for Primitive types.
return;
}
}
exports.PrimitiveType = PrimitiveType;
class ConstructType extends Type {
checkType(t) {
if (t instanceof RecClass) {
const ty = t.getType();
if (typeof ty === 'undefined') {
throw new Error('type mismatch with uninitialized type');
}
return ty;
}
throw new Error(`type mismatch: type on the wire ${t.name}, expect type ${this.name}`);
}
encodeType(typeTable) {
return typeTable.indexOf(this.name);
}
}
exports.ConstructType = ConstructType;
/**
* Represents an IDL Empty, a type which has no inhabitants.
* Since no values exist for this type, it cannot be serialised or deserialised.
* Result types like `Result<Text, Empty>` should always succeed.
*/
class EmptyClass extends PrimitiveType {
get typeName() {
return IdlTypeName.EmptyClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.EmptyClass;
}
accept(v, d) {
return v.visitEmpty(this, d);
}
covariant(x) {
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue() {
throw new Error('Empty cannot appear as a function argument');
}
valueToString() {
throw new Error('Empty cannot appear as a value');
}
encodeType() {
return (0, leb128_ts_1.slebEncode)(IDLTypeIds.Empty);
}
decodeValue() {
throw new Error('Empty cannot appear as an output');
}
get name() {
return 'empty';
}
}
exports.EmptyClass = EmptyClass;
/**
* Represents an IDL Unknown, a placeholder type for deserialization only.
* When decoding a value as Unknown, all fields will be retained but the names are only available in
* hashed form.
* A deserialized unknown will offer it's actual type by calling the `type()` function.
* Unknown cannot be serialized and attempting to do so will throw an error.
*/
class UnknownClass extends Type {
get typeName() {
return IdlTypeName.UnknownClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.UnknownClass;
}
checkType(_t) {
throw new Error('Method not implemented for unknown.');
}
accept(v, d) {
throw v.visitType(this, d);
}
covariant(x) {
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue() {
throw new Error('Unknown cannot appear as a function argument');
}
valueToString() {
throw new Error('Unknown cannot appear as a value');
}
encodeType() {
throw new Error('Unknown cannot be serialized');
}
decodeValue(b, t) {
let decodedValue = t.decodeValue(b, t);
if (Object(decodedValue) !== decodedValue) {
// decodedValue is primitive. Box it, otherwise we cannot add the type() function.
// The type() function is important for primitives because otherwise we cannot tell apart the
// different number types.
decodedValue = Object(decodedValue);
}
let typeFunc;
if (t instanceof RecClass) {
typeFunc = () => t.getType();
}
else {
typeFunc = () => t;
}
// Do not use 'decodedValue.type = typeFunc' because this would lead to an enumerable property
// 'type' which means it would be serialized if the value would be candid encoded again.
// This in turn leads to problems if the decoded value is a variant because these values are
// only allowed to have a single property.
Object.defineProperty(decodedValue, 'type', {
value: typeFunc,
writable: true,
enumerable: false,
configurable: true,
});
return decodedValue;
}
_buildTypeTableImpl() {
throw new Error('Unknown cannot be serialized');
}
get name() {
return 'Unknown';
}
}
exports.UnknownClass = UnknownClass;
/**
* Represents an IDL Bool
*/
class BoolClass extends PrimitiveType {
get typeName() {
return IdlTypeName.BoolClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.BoolClass;
}
accept(v, d) {
return v.visitBool(this, d);
}
covariant(x) {
if (typeof x === 'boolean')
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
return new Uint8Array([x ? 1 : 0]);
}
encodeType() {
return (0, leb128_ts_1.slebEncode)(IDLTypeIds.Bool);
}
decodeValue(b, t) {
this.checkType(t);
switch ((0, leb128_ts_1.safeReadUint8)(b)) {
case 0:
return false;
case 1:
return true;
default:
throw new Error('Boolean value out of range');
}
}
get name() {
return 'bool';
}
}
exports.BoolClass = BoolClass;
/**
* Represents an IDL Null
*/
class NullClass extends PrimitiveType {
get typeName() {
return IdlTypeName.NullClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.NullClass;
}
accept(v, d) {
return v.visitNull(this, d);
}
covariant(x) {
if (x === null)
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue() {
return new Uint8Array(0);
}
encodeType() {
return (0, leb128_ts_1.slebEncode)(IDLTypeIds.Null);
}
decodeValue(_b, t) {
this.checkType(t);
return null;
}
get name() {
return 'null';
}
}
exports.NullClass = NullClass;
/**
* Represents an IDL Reserved
*/
class ReservedClass extends PrimitiveType {
get typeName() {
return IdlTypeName.ReservedClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.ReservedClass;
}
accept(v, d) {
return v.visitReserved(this, d);
}
covariant(_x) {
return true;
}
encodeValue() {
return new Uint8Array(0);
}
encodeType() {
return (0, leb128_ts_1.slebEncode)(IDLTypeIds.Reserved);
}
decodeValue(b, t) {
if (t.name !== this.name) {
t.decodeValue(b, t);
}
return null;
}
get name() {
return 'reserved';
}
}
exports.ReservedClass = ReservedClass;
/**
* Represents an IDL Text
*/
class TextClass extends PrimitiveType {
get typeName() {
return IdlTypeName.TextClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.TextClass;
}
accept(v, d) {
return v.visitText(this, d);
}
covariant(x) {
if (typeof x === 'string')
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
const buf = new TextEncoder().encode(x);
const len = (0, leb128_ts_1.lebEncode)(buf.byteLength);
return (0, buffer_ts_1.concat)(len, buf);
}
encodeType() {
return (0, leb128_ts_1.slebEncode)(IDLTypeIds.Text);
}
decodeValue(b, t) {
this.checkType(t);
const len = (0, leb128_ts_1.lebDecode)(b);
const buf = (0, leb128_ts_1.safeRead)(b, Number(len));
const decoder = new TextDecoder('utf8', { fatal: true });
return decoder.decode(buf);
}
get name() {
return 'text';
}
valueToString(x) {
return '"' + x + '"';
}
}
exports.TextClass = TextClass;
/**
* Represents an IDL Int
*/
class IntClass extends PrimitiveType {
get typeName() {
return IdlTypeName.IntClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.IntClass;
}
accept(v, d) {
return v.visitInt(this, d);
}
covariant(x) {
// We allow encoding of JavaScript plain numbers.
// But we will always decode to bigint.
if (typeof x === 'bigint' || Number.isInteger(x))
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
return (0, leb128_ts_1.slebEncode)(x);
}
encodeType() {
return (0, leb128_ts_1.slebEncode)(IDLTypeIds.Int);
}
decodeValue(b, t) {
this.checkType(t);
return (0, leb128_ts_1.slebDecode)(b);
}
get name() {
return 'int';
}
valueToString(x) {
return x.toString();
}
}
exports.IntClass = IntClass;
/**
* Represents an IDL Nat
*/
class NatClass extends PrimitiveType {
get typeName() {
return IdlTypeName.NatClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.NatClass;
}
accept(v, d) {
return v.visitNat(this, d);
}
covariant(x) {
// We allow encoding of JavaScript plain numbers.
// But we will always decode to bigint.
if ((typeof x === 'bigint' && x >= BigInt(0)) || (Number.isInteger(x) && x >= 0))
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
return (0, leb128_ts_1.lebEncode)(x);
}
encodeType() {
return (0, leb128_ts_1.slebEncode)(IDLTypeIds.Nat);
}
decodeValue(b, t) {
this.checkType(t);
return (0, leb128_ts_1.lebDecode)(b);
}
get name() {
return 'nat';
}
valueToString(x) {
return x.toString();
}
}
exports.NatClass = NatClass;
/**
* Represents an IDL Float
*/
class FloatClass extends PrimitiveType {
get typeName() {
return IdlTypeName.FloatClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.FloatClass;
}
constructor(_bits) {
super();
this._bits = _bits;
if (_bits !== 32 && _bits !== 64) {
throw new Error('not a valid float type');
}
}
accept(v, d) {
return v.visitFloat(this, d);
}
covariant(x) {
if (typeof x === 'number' || x instanceof Number)
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
const buf = new ArrayBuffer(this._bits / 8);
const view = new DataView(buf);
if (this._bits === 32) {
view.setFloat32(0, x, true);
}
else {
view.setFloat64(0, x, true);
}
return new Uint8Array(buf);
}
encodeType() {
const opcode = this._bits === 32 ? IDLTypeIds.Float32 : IDLTypeIds.Float64;
return (0, leb128_ts_1.slebEncode)(opcode);
}
decodeValue(b, t) {
this.checkType(t);
const bytes = (0, leb128_ts_1.safeRead)(b, this._bits / 8);
const view = (0, buffer_ts_1.uint8ToDataView)(bytes);
if (this._bits === 32) {
return view.getFloat32(0, true);
}
else {
return view.getFloat64(0, true);
}
}
get name() {
return 'float' + this._bits;
}
valueToString(x) {
return x.toString();
}
}
exports.FloatClass = FloatClass;
/**
* Represents an IDL fixed-width Int(n)
*/
class FixedIntClass extends PrimitiveType {
get typeName() {
return IdlTypeName.FixedIntClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.FixedIntClass;
}
constructor(_bits) {
super();
this._bits = _bits;
}
accept(v, d) {
return v.visitFixedInt(this, d);
}
covariant(x) {
const min = (0, bigint_math_ts_1.iexp2)(this._bits - 1) * BigInt(-1);
const max = (0, bigint_math_ts_1.iexp2)(this._bits - 1) - BigInt(1);
let ok = false;
if (typeof x === 'bigint') {
ok = x >= min && x <= max;
}
else if (Number.isInteger(x)) {
const v = BigInt(x);
ok = v >= min && v <= max;
}
else {
ok = false;
}
if (ok)
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
return (0, leb128_ts_1.writeIntLE)(x, this._bits / 8);
}
encodeType() {
const offset = Math.log2(this._bits) - 3;
return (0, leb128_ts_1.slebEncode)(-9 - offset);
}
decodeValue(b, t) {
this.checkType(t);
const num = (0, leb128_ts_1.readIntLE)(b, this._bits / 8);
if (this._bits <= 32) {
return Number(num);
}
else {
return num;
}
}
get name() {
return `int${this._bits}`;
}
valueToString(x) {
return x.toString();
}
}
exports.FixedIntClass = FixedIntClass;
/**
* Represents an IDL fixed-width Nat(n)
*/
class FixedNatClass extends PrimitiveType {
get typeName() {
return IdlTypeName.FixedNatClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.FixedNatClass;
}
constructor(_bits) {
super();
this._bits = _bits;
}
accept(v, d) {
return v.visitFixedNat(this, d);
}
covariant(x) {
const max = (0, bigint_math_ts_1.iexp2)(this._bits);
let ok = false;
if (typeof x === 'bigint' && x >= BigInt(0)) {
ok = x < max;
}
else if (Number.isInteger(x) && x >= 0) {
const v = BigInt(x);
ok = v < max;
}
else {
ok = false;
}
if (ok)
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
return (0, leb128_ts_1.writeUIntLE)(x, this._bits / 8);
}
encodeType() {
const offset = Math.log2(this._bits) - 3;
return (0, leb128_ts_1.slebEncode)(-5 - offset);
}
decodeValue(b, t) {
this.checkType(t);
const num = (0, leb128_ts_1.readUIntLE)(b, this._bits / 8);
if (this._bits <= 32) {
return Number(num);
}
else {
return num;
}
}
get name() {
return `nat${this._bits}`;
}
valueToString(x) {
return x.toString();
}
}
exports.FixedNatClass = FixedNatClass;
/**
* Represents an IDL Array
*
* Arrays of fixed-sized nat/int type (e.g. nat8), are encoded from and decoded to TypedArrays (e.g. Uint8Array).
* Arrays of float or other non-primitive types are encoded/decoded as untyped array in Javascript.
* @param {Type} t
*/
class VecClass extends ConstructType {
get typeName() {
return IdlTypeName.VecClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.VecClass;
}
constructor(_type) {
super();
this._type = _type;
// If true, this vector is really a blob and we can just use memcpy.
//
// NOTE:
// With support of encoding/dencoding of TypedArrays, this optimization is
// only used when plain array of bytes are passed as encoding input in order
// to be backward compatible.
this._blobOptimization = false;
if (_type instanceof FixedNatClass && _type._bits === 8) {
this._blobOptimization = true;
}
}
accept(v, d) {
return v.visitVec(this, this._type, d);
}
covariant(x) {
// Special case for ArrayBuffer
const bits = this._type instanceof FixedNatClass
? this._type._bits
: this._type instanceof FixedIntClass
? this._type._bits
: 0;
if ((ArrayBuffer.isView(x) && bits == x.BYTES_PER_ELEMENT * 8) ||
(Array.isArray(x) &&
x.every((v, idx) => {
try {
return this._type.covariant(v);
}
catch (e) {
throw new Error(`Invalid ${this.display()} argument: \n\nindex ${idx} -> ${e.message}`);
}
})))
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
const len = (0, leb128_ts_1.lebEncode)(x.length);
if (this._blobOptimization) {
return (0, buffer_ts_1.concat)(len, new Uint8Array(x));
}
if (ArrayBuffer.isView(x)) {
// Handle TypedArrays with endianness concerns
if (x instanceof Int16Array || x instanceof Uint16Array) {
const buffer = new DataView(new ArrayBuffer(x.length * 2));
for (let i = 0; i < x.length; i++) {
if (x instanceof Int16Array) {
buffer.setInt16(i * 2, x[i], true); // true = little-endian
}
else {
buffer.setUint16(i * 2, x[i], true);
}
}
return (0, buffer_ts_1.concat)(len, new Uint8Array(buffer.buffer));
}
else if (x instanceof Int32Array || x instanceof Uint32Array) {
const buffer = new DataView(new ArrayBuffer(x.length * 4));
for (let i = 0; i < x.length; i++) {
if (x instanceof Int32Array) {
buffer.setInt32(i * 4, x[i], true);
}
else {
buffer.setUint32(i * 4, x[i], true);
}
}
return (0, buffer_ts_1.concat)(len, new Uint8Array(buffer.buffer));
}
else if (x instanceof BigInt64Array || x instanceof BigUint64Array) {
const buffer = new DataView(new ArrayBuffer(x.length * 8));
for (let i = 0; i < x.length; i++) {
if (x instanceof BigInt64Array) {
buffer.setBigInt64(i * 8, x[i], true);
}
else {
buffer.setBigUint64(i * 8, x[i], true);
}
}
return (0, buffer_ts_1.concat)(len, new Uint8Array(buffer.buffer));
}
else {
// For Uint8Array, Int8Array, etc. that don't have endianness concerns
return (0, buffer_ts_1.concat)(len, new Uint8Array(x.buffer, x.byteOffset, x.byteLength));
}
}
const buf = new buffer_ts_1.PipeArrayBuffer(new Uint8Array(len.byteLength + x.length), 0);
buf.write(len);
for (const d of x) {
const encoded = this._type.encodeValue(d);
buf.write(new Uint8Array(encoded));
}
return buf.buffer;
}
_buildTypeTableImpl(typeTable) {
this._type.buildTypeTable(typeTable);
const opCode = (0, leb128_ts_1.slebEncode)(IDLTypeIds.Vector);
const buffer = this._type.encodeType(typeTable);
typeTable.add(this, (0, buffer_ts_1.concat)(opCode, buffer));
}
decodeValue(b, t) {
const vec = this.checkType(t);
if (!(vec instanceof VecClass)) {
throw new Error('Not a vector type');
}
const len = Number((0, leb128_ts_1.lebDecode)(b));
if (this._type instanceof FixedNatClass) {
if (this._type._bits == 8) {
return new Uint8Array(b.read(len));
}
if (this._type._bits == 16) {
const bytes = b.read(len * 2);
// Check if we need to swap bytes for endianness
const u16 = new Uint16Array(bytes.buffer, bytes.byteOffset, len);
return u16;
}
if (this._type._bits == 32) {
const bytes = b.read(len * 4);
const u32 = new Uint32Array(bytes.buffer, bytes.byteOffset, len);
return u32;
}
if (this._type._bits == 64) {
return new BigUint64Array(b.read(len * 8).buffer);
}
}
if (this._type instanceof FixedIntClass) {
if (this._type._bits == 8) {
return new Int8Array(b.read(len));
}
if (this._type._bits == 16) {
const bytes = b.read(len * 2);
// Create a DataView to properly handle endianness
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
// Create result array with correct endianness
const result = new Int16Array(len);
for (let i = 0; i < len; i++) {
// Read each value as little-endian (Candid wire format is little-endian)
result[i] = view.getInt16(i * 2, true);
}
return result;
}
if (this._type._bits == 32) {
const bytes = b.read(len * 4);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
const result = new Int32Array(len);
for (let i = 0; i < len; i++) {
result[i] = view.getInt32(i * 4, true);
}
return result;
}
if (this._type._bits == 64) {
const bytes = b.read(len * 8);
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
const result = new BigInt64Array(len);
for (let i = 0; i < len; i++) {
result[i] = view.getBigInt64(i * 8, true);
}
return result;
}
}
const rets = [];
for (let i = 0; i < len; i++) {
rets.push(this._type.decodeValue(b, vec._type));
}
return rets;
}
get name() {
return `vec ${this._type.name}`;
}
display() {
return `vec ${this._type.display()}`;
}
valueToString(x) {
const elements = x.map(e => this._type.valueToString(e));
return 'vec {' + elements.join('; ') + '}';
}
}
exports.VecClass = VecClass;
/**
* Represents an IDL Option
* @param {Type} t
*/
class OptClass extends ConstructType {
get typeName() {
return IdlTypeName.OptClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.OptClass;
}
constructor(_type) {
super();
this._type = _type;
}
accept(v, d) {
return v.visitOpt(this, this._type, d);
}
covariant(x) {
try {
if (Array.isArray(x) && (x.length === 0 || (x.length === 1 && this._type.covariant(x[0]))))
return true;
}
catch (e) {
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)} \n\n-> ${e.message}`);
}
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
if (x.length === 0) {
return new Uint8Array([0]);
}
else {
return (0, buffer_ts_1.concat)(new Uint8Array([1]), this._type.encodeValue(x[0]));
}
}
_buildTypeTableImpl(typeTable) {
this._type.buildTypeTable(typeTable);
const opCode = (0, leb128_ts_1.slebEncode)(IDLTypeIds.Opt);
const buffer = this._type.encodeType(typeTable);
typeTable.add(this, (0, buffer_ts_1.concat)(opCode, buffer));
}
decodeValue(b, t) {
if (t instanceof NullClass) {
return [];
}
if (t instanceof ReservedClass) {
return [];
}
let wireType = t;
// unfold wireType, if needed
if (t instanceof RecClass) {
const ty = t.getType();
if (typeof ty === 'undefined') {
throw new Error('type mismatch with uninitialized type');
}
else
wireType = ty;
}
if (wireType instanceof OptClass) {
switch ((0, leb128_ts_1.safeReadUint8)(b)) {
case 0:
return [];
case 1: {
// Save the current state of the Pipe `b` to allow rollback in case of an error
const checkpoint = b.save();
try {
// Attempt to decode a value using the `_type` of the current instance
const v = this._type.decodeValue(b, wireType._type);
return [v];
}
catch (e) {
// If an error occurs during decoding, restore the Pipe `b` to its previous state
b.restore(checkpoint);
// Skip the value at the current wire type to advance the Pipe `b` position
wireType._type.decodeValue(b, wireType._type);
// Return an empty array to indicate a `none` value
return [];
}
}
default:
throw new Error('Not an option value');
}
}
else if (
// this check corresponds to `not (null <: <t>)` in the spec
this._type instanceof NullClass ||
this._type instanceof OptClass ||
this._type instanceof ReservedClass) {
// null <: <t> :
// skip value at wire type (to advance b) and return "null", i.e. []
wireType.decodeValue(b, wireType);
return [];
}
else {
// not (null <: t) :
// try constituent type
const checkpoint = b.save();
try {
const v = this._type.decodeValue(b, t);
return [v];
}
catch (e) {
// decoding failed, but this is opt, so return "null", i.e. []
b.restore(checkpoint);
// skip value at wire type (to advance b)
wireType.decodeValue(b, t);
// return "null"
return [];
}
}
}
get name() {
return `opt ${this._type.name}`;
}
display() {
return `opt ${this._type.display()}`;
}
valueToString(x) {
if (x.length === 0) {
return 'null';
}
else {
return `opt ${this._type.valueToString(x[0])}`;
}
}
}
exports.OptClass = OptClass;
/**
* Represents an IDL Record
* @param {object} [fields] - mapping of function name to Type
*/
class RecordClass extends ConstructType {
get typeName() {
return IdlTypeName.RecordClass;
}
static [Symbol.hasInstance](instance) {
// TupleClass extends RecordClass, so we need to check both here
return (instance.typeName === IdlTypeName.RecordClass || instance.typeName === IdlTypeName.TupleClass);
}
constructor(fields = {}) {
super();
this._fields = Object.entries(fields).sort((a, b) => (0, hash_ts_1.idlLabelToId)(a[0]) - (0, hash_ts_1.idlLabelToId)(b[0]));
}
accept(v, d) {
return v.visitRecord(this, this._fields, d);
}
tryAsTuple() {
const res = [];
for (let i = 0; i < this._fields.length; i++) {
const [key, type] = this._fields[i];
if (key !== `_${i}_`) {
return null;
}
res.push(type);
}
return res;
}
covariant(x) {
if (typeof x === 'object' &&
this._fields.every(([k, t]) => {
// eslint-disable-next-line
if (!x.hasOwnProperty(k)) {
throw new Error(`Record is missing key "${k}".`);
}
try {
return t.covariant(x[k]);
}
catch (e) {
throw new Error(`Invalid ${this.display()} argument: \n\nfield ${k} -> ${e.message}`);
}
}))
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
const values = this._fields.map(([key]) => x[key]);
const bufs = zipWith(this._fields, values, ([, c], d) => c.encodeValue(d));
return (0, buffer_ts_1.concat)(...bufs);
}
_buildTypeTableImpl(T) {
this._fields.forEach(([_, value]) => value.buildTypeTable(T));
const opCode = (0, leb128_ts_1.slebEncode)(IDLTypeIds.Record);
const len = (0, leb128_ts_1.lebEncode)(this._fields.length);
const fields = this._fields.map(([key, value]) => (0, buffer_ts_1.concat)((0, leb128_ts_1.lebEncode)((0, hash_ts_1.idlLabelToId)(key)), value.encodeType(T)));
T.add(this, (0, buffer_ts_1.concat)(opCode, len, (0, buffer_ts_1.concat)(...fields)));
}
decodeValue(b, t) {
const record = this.checkType(t);
if (!(record instanceof RecordClass)) {
throw new Error('Not a record type');
}
const x = {};
let expectedRecordIdx = 0;
let actualRecordIdx = 0;
while (actualRecordIdx < record._fields.length) {
const [hash, type] = record._fields[actualRecordIdx];
if (expectedRecordIdx >= this._fields.length) {
// skip unexpected left over fields present on the wire
type.decodeValue(b, type);
actualRecordIdx++;
continue;
}
const [expectKey, expectType] = this._fields[expectedRecordIdx];
const expectedId = (0, hash_ts_1.idlLabelToId)(this._fields[expectedRecordIdx][0]);
const actualId = (0, hash_ts_1.idlLabelToId)(hash);
if (expectedId === actualId) {
// the current field on the wire matches the expected field
x[expectKey] = expectType.decodeValue(b, type);
expectedRecordIdx++;
actualRecordIdx++;
}
else if (actualId > expectedId) {
// The expected field does not exist on the wire
if (expectType instanceof OptClass || expectType instanceof ReservedClass) {
x[expectKey] = [];
expectedRecordIdx++;
}
else {
throw new Error('Cannot find required field ' + expectKey);
}
}
else {
// The field on the wire does not exist in the output type, so we can skip it
type.decodeValue(b, type);
actualRecordIdx++;
}
}
// initialize left over expected optional fields
for (const [expectKey, expectType] of this._fields.slice(expectedRecordIdx)) {
if (expectType instanceof OptClass || expectType instanceof ReservedClass) {
// TODO this assumes null value in opt is represented as []
x[expectKey] = [];
}
else {
throw new Error('Cannot find required field ' + expectKey);
}
}
return x;
}
get fieldsAsObject() {
const fields = {};
for (const [name, ty] of this._fields) {
fields[(0, hash_ts_1.idlLabelToId)(name)] = ty;
}
return fields;
}
get name() {
const fields = this._fields.map(([key, value]) => key + ':' + value.name);
return `record {${fields.join('; ')}}`;
}
display() {
const fields = this._fields.map(([key, value]) => key + ':' + value.display());
return `record {${fields.join('; ')}}`;
}
valueToString(x) {
const values = this._fields.map(([key]) => x[key]);
const fields = zipWith(this._fields, values, ([k, c], d) => k + '=' + c.valueToString(d));
return `record {${fields.join('; ')}}`;
}
}
exports.RecordClass = RecordClass;
/**
* Represents Tuple, a syntactic sugar for Record.
* @param {Type} components
*/
class TupleClass extends RecordClass {
get typeName() {
return IdlTypeName.TupleClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.TupleClass;
}
constructor(_components) {
const x = {};
_components.forEach((e, i) => (x['_' + i + '_'] = e));
super(x);
this._components = _components;
}
accept(v, d) {
return v.visitTuple(this, this._components, d);
}
covariant(x) {
// `>=` because tuples can be covariant when encoded.
if (Array.isArray(x) &&
x.length >= this._fields.length &&
this._components.every((t, i) => {
try {
return t.covariant(x[i]);
}
catch (e) {
throw new Error(`Invalid ${this.display()} argument: \n\nindex ${i} -> ${e.message}`);
}
}))
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
const bufs = zipWith(this._components, x, (c, d) => c.encodeValue(d));
return (0, buffer_ts_1.concat)(...bufs);
}
decodeValue(b, t) {
const tuple = this.checkType(t);
if (!(tuple instanceof TupleClass)) {
throw new Error('not a tuple type');
}
if (tuple._components.length < this._components.length) {
throw new Error('tuple mismatch');
}
const res = [];
for (const [i, wireType] of tuple._components.entries()) {
if (i >= this._components.length) {
// skip value
wireType.decodeValue(b, wireType);
}
else {
res.push(this._components[i].decodeValue(b, wireType));
}
}
return res;
}
display() {
const fields = this._components.map(value => value.display());
return `record {${fields.join('; ')}}`;
}
valueToString(values) {
const fields = zipWith(this._components, values, (c, d) => c.valueToString(d));
return `record {${fields.join('; ')}}`;
}
}
exports.TupleClass = TupleClass;
/**
* Represents an IDL Variant
* @param {object} [fields] - mapping of function name to Type
*/
class VariantClass extends ConstructType {
get typeName() {
return IdlTypeName.VariantClass;
}
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.VariantClass;
}
constructor(fields = {}) {
super();
this._fields = Object.entries(fields).sort((a, b) => (0, hash_ts_1.idlLabelToId)(a[0]) - (0, hash_ts_1.idlLabelToId)(b[0]));
}
accept(v, d) {
return v.visitVariant(this, this._fields, d);
}
covariant(x) {
if (typeof x === 'object' &&
Object.entries(x).length === 1 &&
this._fields.every(([k, v]) => {
try {
// eslint-disable-next-line
return !x.hasOwnProperty(k) || v.covariant(x[k]);
}
catch (e) {
throw new Error(`Invalid ${this.display()} argument: \n\nvariant ${k} -> ${e.message}`);
}
}))
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
for (let i = 0; i < this._fields.length; i++) {
const [name, type] = this._fields[i];
// eslint-disable-next-line
if (x.hasOwnProperty(name)) {
const idx = (0, leb128_ts_1.lebEncode)(i);
const buf = type.encodeValue(x[name]);
return (0, buffer_ts_1.concat)(idx, buf);
}
}
throw Error('Variant has no data: ' + x);
}
_buildTypeTableImpl(typeTable) {
this._fields.forEach(([, type]) => {
type.buildTypeTable(typeTable);
});
const opCode = (0, leb128_ts_1.slebEncode)(IDLTypeIds.Variant);
const len = (0, leb128_ts_1.lebEncode)(this._fields.length);
const fields = this._fields.map(([key, value]) => (0, buffer_ts_1.concat)((0, leb128_ts_1.lebEncode)((0, hash_ts_1.idlLabelToId)(key)), value.encodeType(typeTable)));
typeTable.add(this, (0, buffer_ts_1.concat)(opCode, len, ...fields));
}
decodeValue(b, t) {
const variant = this.checkType(t);
if (!(variant instanceof VariantClass)) {
throw new Error('Not a variant type');
}
const idx = Number((0, leb128_ts_1.lebDecode)(b));
if (idx >= variant._fields.length) {
throw Error('Invalid variant index: ' + idx);
}
const [wireHash, wireType] = variant._fields[idx];
for (const [key, expectType] of this._fields) {
if ((0, hash_ts_1.idlLabelToId)(wireHash) === (0, hash_ts_1.idlLabelToId)(key)) {
const value = expectType.decodeValue(b, wireType);
return { [key]: value };
}
}
throw new Error('Cannot find field hash ' + wireHash);
}
get name() {
const fields = this._fields.map(([key, type]) => key + ':' + type.name);
return `variant {${fields.join('; ')}}`;
}
display() {
const fields = this._fields.map(([key, type]) => key + (type.name === 'null' ? '' : `:${type.display()}`));
return `variant {${fields.join('; ')}}`;
}
valueToString(x) {
for (const [name, type] of this._fields) {
// eslint-disable-next-line
if (x.hasOwnProperty(name)) {
const value = type.valueToString(x[name]);
if (value === 'null') {
return `variant {${name}}`;
}
else {
return `variant {${name}=${value}}`;
}
}
}
throw new Error('Variant has no data: ' + x);
}
get alternativesAsObject() {
const alternatives = {};
for (const [name, ty] of this._fields) {
alternatives[(0, hash_ts_1.idlLabelToId)(name)] = ty;
}
return alternatives;
}
}
exports.VariantClass = VariantClass;
/**
* Represents a reference to an IDL type, used for defining recursive data
* types.
*/
class RecClass extends ConstructType {
constructor() {
super(...arguments);
this._id = RecClass._counter++;
}
get typeName() {
return IdlTypeName.RecClass;
}
static { this._counter = 0; }
static [Symbol.hasInstance](instance) {
return instance.typeName === IdlTypeName.RecClass;
}
accept(v, d) {
if (!this._type) {
throw Error('Recursive type uninitialized.');
}
return v.visitRec(this, this._type, d);
}
fill(t) {
this._type = t;
}
getType() {
return this._type;
}
covariant(x) {
if (this._type ? this._type.covariant(x) : false)
return true;
throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
encodeValue(x) {
if (!this._type) {
throw Error('Recursive type uninitialized.');
}
return this._type.encodeValue(x);
}
_buildTypeTableImpl(typeTable) {
if (!this._type) {
throw Error('Recursive type uninitialized.');
}
typeTable.add(this, new Uint8Array([]));
this._type.buildTypeTable(typeTable);
typeTable.merge(this, this._type.name);
}
decodeValue(b, t) {
if (!this._type) {
throw Error('Recursive type uninitialized.');
}
return this._type.decodeValue(b, t);
}
get name() {
return `rec_${this._id}`;
}
display() {
if (!this._type) {
throw Error('Recursive type uninitialized.');
}
return `μ${this.name}.${this._type.name}`;
}
valueToString(x) {
if (!this._type) {
throw Error('Recursive type uninitial