diff --git a/docs/generated/changelog.html b/docs/generated/changelog.html
index 9e4cc4be0..49d701632 100644
--- a/docs/generated/changelog.html
+++ b/docs/generated/changelog.html
@@ -16,6 +16,7 @@
Version 0.14.0
Adds retry logic to HttpAgent. By default, retries three times before throwing an error,
to offer a more cohesive workflow
+ Improves and truncates error messages in Candid
fixes flaky tests for syncTime
Adds a top-level fetchCandid()
function which retrieves the Candid interface
diff --git a/packages/candid/src/idl.ts b/packages/candid/src/idl.ts
index c2de9b8b9..4b246da05 100644
--- a/packages/candid/src/idl.ts
+++ b/packages/candid/src/idl.ts
@@ -45,6 +45,7 @@ const enum 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: TX[], ys: TY[], f: (a: TX, b: TY) => TR): TR[] {
return xs.map((x, i) => f(x, ys[i]));
@@ -260,7 +261,7 @@ export class EmptyClass extends PrimitiveType {
}
public covariant(x: any): x is never {
- return false;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(): never {
@@ -301,7 +302,7 @@ export class UnknownClass extends Type {
}
public covariant(x: any): x is any {
- return false;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(): never {
@@ -363,7 +364,8 @@ export class BoolClass extends PrimitiveType {
}
public covariant(x: any): x is boolean {
- return typeof x === 'boolean';
+ if (typeof x === 'boolean') return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: boolean): ArrayBuffer {
@@ -400,7 +402,8 @@ export class NullClass extends PrimitiveType {
}
public covariant(x: any): x is null {
- return x === null;
+ if (x === null) return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue() {
@@ -462,7 +465,8 @@ export class TextClass extends PrimitiveType {
}
public covariant(x: any): x is string {
- return typeof x === 'string';
+ if (typeof x === 'string') return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: string) {
@@ -503,7 +507,8 @@ export class IntClass extends PrimitiveType {
public covariant(x: any): x is bigint {
// We allow encoding of JavaScript plain numbers.
// But we will always decode to bigint.
- return typeof x === 'bigint' || Number.isInteger(x);
+ if (typeof x === 'bigint' || Number.isInteger(x)) return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: bigint | number) {
@@ -539,7 +544,8 @@ export class NatClass extends PrimitiveType {
public covariant(x: any): x is bigint {
// We allow encoding of JavaScript plain numbers.
// But we will always decode to bigint.
- return (typeof x === 'bigint' && x >= BigInt(0)) || (Number.isInteger(x) && x >= 0);
+ if ((typeof x === 'bigint' && x >= BigInt(0)) || (Number.isInteger(x) && x >= 0)) return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: bigint | number) {
@@ -579,7 +585,8 @@ export class FloatClass extends PrimitiveType {
}
public covariant(x: any): x is number {
- return typeof x === 'number' || x instanceof Number;
+ if (typeof x === 'number' || x instanceof Number) return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: number) {
@@ -633,14 +640,18 @@ export class FixedIntClass extends PrimitiveType {
public covariant(x: any): x is bigint {
const min = iexp2(this._bits - 1) * BigInt(-1);
const max = iexp2(this._bits - 1) - BigInt(1);
+ let ok = false;
if (typeof x === 'bigint') {
- return x >= min && x <= max;
+ ok = x >= min && x <= max;
} else if (Number.isInteger(x)) {
const v = BigInt(x);
- return v >= min && v <= max;
+ ok = v >= min && v <= max;
} else {
- return false;
+ ok = false;
}
+
+ if (ok) return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: bigint | number) {
@@ -685,14 +696,17 @@ export class FixedNatClass extends PrimitiveType {
public covariant(x: any): x is bigint {
const max = iexp2(this._bits);
+ let ok = false;
if (typeof x === 'bigint' && x >= BigInt(0)) {
- return x < max;
+ ok = x < max;
} else if (Number.isInteger(x) && x >= 0) {
const v = BigInt(x);
- return v < max;
+ ok = v < max;
} else {
- return false;
+ ok = false;
}
+ if (ok) return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: bigint | number) {
@@ -759,10 +773,21 @@ export class VecClass extends ConstructType {
: this._type instanceof FixedIntClass
? this._type._bits
: 0;
- return (
+
+ if (
(ArrayBuffer.isView(x) && bits == (x as any).BYTES_PER_ELEMENT * 8) ||
- (Array.isArray(x) && x.every(v => this._type.covariant(v)))
- );
+ (Array.isArray(x) &&
+ x.every((v, idx) => {
+ try {
+ return this._type.covariant(v);
+ } catch (e: any) {
+ throw new Error(`Invalid ${this.display()} argument: \n\nindex ${idx} -> ${e.message}`);
+ }
+ }))
+ )
+ return true;
+
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: T[]) {
@@ -862,7 +887,15 @@ export class OptClass extends ConstructType<[T] | []> {
}
public covariant(x: any): x is [T] | [] {
- return Array.isArray(x) && (x.length === 0 || (x.length === 1 && this._type.covariant(x[0])));
+ try {
+ if (Array.isArray(x) && (x.length === 0 || (x.length === 1 && this._type.covariant(x[0]))))
+ return true;
+ } catch (e: any) {
+ throw new Error(
+ `Invalid ${this.display()} argument: ${toReadableString(x)} \n\n-> ${e.message}`,
+ );
+ }
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: [T] | []) {
@@ -942,16 +975,23 @@ export class RecordClass extends ConstructType> {
}
public covariant(x: any): x is Record {
- return (
+ 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}".`);
}
- return t.covariant(x[k]);
+ try {
+ return t.covariant(x[k]);
+ } catch (e: any) {
+ throw new Error(`Invalid ${this.display()} argument: \n\nfield ${k} -> ${e.message}`);
+ }
})
- );
+ )
+ return true;
+
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: Record) {
@@ -1062,11 +1102,21 @@ export class TupleClass extends RecordClass {
public covariant(x: any): x is T {
// `>=` because tuples can be covariant when encoded.
- return (
+
+ if (
Array.isArray(x) &&
x.length >= this._fields.length &&
- this._components.every((t, i) => t.covariant(x[i]))
- );
+ this._components.every((t, i) => {
+ try {
+ return t.covariant(x[i]);
+ } catch (e: any) {
+ throw new Error(`Invalid ${this.display()} argument: \n\nindex ${i} -> ${e.message}`);
+ }
+ })
+ )
+ return true;
+
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: any[]) {
@@ -1122,14 +1172,21 @@ export class VariantClass extends ConstructType> {
}
public covariant(x: any): x is Record {
- return (
+ if (
typeof x === 'object' &&
Object.entries(x).length === 1 &&
this._fields.every(([k, v]) => {
- // eslint-disable-next-line
- return !x.hasOwnProperty(k) || v.covariant(x[k]);
+ try {
+ // eslint-disable-next-line
+ return !x.hasOwnProperty(k) || v.covariant(x[k]);
+ } catch (e: any) {
+ throw new Error(`Invalid ${this.display()} argument: \n\nvariant ${k} -> ${e.message}`);
+ }
})
- );
+ )
+ return true;
+
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: Record) {
@@ -1230,7 +1287,8 @@ export class RecClass extends ConstructType {
}
public covariant(x: any): x is T {
- return this._type ? this._type.covariant(x) : false;
+ if (this._type ? this._type.covariant(x) : false) return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: T) {
@@ -1294,7 +1352,8 @@ export class PrincipalClass extends PrimitiveType {
}
public covariant(x: any): x is PrincipalId {
- return x && x._isPrincipal;
+ if (x && x._isPrincipal) return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: PrincipalId): ArrayBuffer {
@@ -1342,9 +1401,9 @@ export class FuncClass extends ConstructType<[PrincipalId, string]> {
return v.visitFunc(this, d);
}
public covariant(x: any): x is [PrincipalId, string] {
- return (
- Array.isArray(x) && x.length === 2 && x[0] && x[0]._isPrincipal && typeof x[1] === 'string'
- );
+ if (Array.isArray(x) && x.length === 2 && x[0] && x[0]._isPrincipal && typeof x[1] === 'string')
+ return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue([principal, methodName]: [PrincipalId, string]) {
@@ -1426,7 +1485,8 @@ export class ServiceClass extends ConstructType {
return v.visitService(this, d);
}
public covariant(x: any): x is PrincipalId {
- return x && x._isPrincipal;
+ if (x && x._isPrincipal) return true;
+ throw new Error(`Invalid ${this.display()} argument: ${toReadableString(x)}`);
}
public encodeValue(x: PrincipalId) {
@@ -1467,9 +1527,13 @@ export class ServiceClass extends ConstructType {
* @returns {string}
*/
function toReadableString(x: unknown): string {
- return JSON.stringify(x, (_key, value) =>
+ const str = JSON.stringify(x, (_key, value) =>
typeof value === 'bigint' ? `BigInt(${value})` : value,
);
+
+ return str && str.length > toReadableString_max
+ ? str.substring(0, toReadableString_max - 3) + '...'
+ : str;
}
/**
@@ -1492,8 +1556,11 @@ export function encode(argTypes: Array>, args: any[]): ArrayBuffer {
const typs = concat(...argTypes.map(t => t.encodeType(typeTable)));
const vals = concat(
...zipWith(argTypes, args, (t, x) => {
- if (!t.covariant(x)) {
- throw new Error(`Invalid ${t.display()} argument: ${toReadableString(x)}`);
+ try {
+ t.covariant(x);
+ } catch (e: any) {
+ const err = new Error(e.message + '\n\n');
+ throw err;
}
return t.encodeValue(x);