Adjust assert usage (#4966)

* Adjust assert usage

* Adjust assert

* Apply suggestions from code review
This commit is contained in:
Jaco
2022-06-20 20:37:17 +03:00
committed by GitHub
parent 94c5c025a9
commit 3a8b1c5683
16 changed files with 146 additions and 80 deletions
+7 -3
View File
@@ -12,7 +12,7 @@ import type { RpcInterfaceMethod } from './types';
import { Observable, publishReplay, refCount } from 'rxjs';
import { rpcDefinitions } from '@polkadot/types';
import { assert, hexToU8a, isFunction, isNull, isUndefined, lazyMethod, logger, memoize, objectSpread, u8aConcat, u8aToU8a } from '@polkadot/util';
import { hexToU8a, isFunction, isNull, isUndefined, lazyMethod, logger, memoize, objectSpread, u8aConcat, u8aToU8a } from '@polkadot/util';
import { drr, refCountDelay } from './util';
@@ -106,7 +106,9 @@ export class RpcCore {
*/
constructor (instanceId: string, registry: Registry, provider: ProviderInterface, userRpc: Record<string, Record<string, DefinitionRpc | DefinitionRpcSub>> = {}) {
// eslint-disable-next-line @typescript-eslint/unbound-method
assert(provider && isFunction(provider.send), 'Expected Provider to API create');
if (!provider || !isFunction(provider.send)) {
throw new Error('Expected Provider to API create');
}
this.#instanceId = instanceId;
this.#registryDefault = registry;
@@ -360,7 +362,9 @@ export class RpcCore {
? ''
: ` (${def.params.length - reqArgCount} optional)`;
assert(inputs.length >= reqArgCount && inputs.length <= def.params.length, () => `Expected ${def.params.length} parameters${optText}, ${inputs.length} found instead`);
if (inputs.length < reqArgCount || inputs.length > def.params.length) {
throw new Error(`Expected ${def.params.length} parameters${optText}, ${inputs.length} found instead`);
}
return inputs.map((input, index): Codec =>
registry.createTypeUnsafe(def.params[index].type, [input], { blockHash })
+18 -4
View File
@@ -3,7 +3,7 @@
import type { JsonRpcRequest, JsonRpcResponse, JsonRpcResponseBaseError } from '../types';
import { assert, isNumber, isString, isUndefined, stringify } from '@polkadot/util';
import { isNumber, isString, isUndefined, stringify } from '@polkadot/util';
import RpcError from './error';
@@ -36,15 +36,29 @@ export class RpcCoder {
#id = 0;
public decodeResponse (response?: JsonRpcResponse): unknown {
assert(response && response.jsonrpc === '2.0', 'Invalid jsonrpc field in decoded object');
if (!response || response.jsonrpc !== '2.0') {
throw new Error('Invalid jsonrpc field in decoded object');
}
const isSubscription = !isUndefined(response.params) && !isUndefined(response.method);
assert(isNumber(response.id) || (isSubscription && (isNumber(response.params.subscription) || isString(response.params.subscription))), 'Invalid id field in decoded object');
if (
!isNumber(response.id) &&
(
!isSubscription || (
!isNumber(response.params.subscription) &&
!isString(response.params.subscription)
)
)
) {
throw new Error('Invalid id field in decoded object');
}
checkError(response.error);
assert(!isUndefined(response.result) || isSubscription, 'No result found in jsonrpc response');
if (response.result === undefined && !isSubscription) {
throw new Error('No result found in jsonrpc response');
}
if (isSubscription) {
checkError(response.params.error);
+7 -3
View File
@@ -3,7 +3,7 @@
import type { JsonRpcResponse, ProviderInterface, ProviderInterfaceCallback, ProviderInterfaceEmitCb, ProviderInterfaceEmitted, ProviderStats } from '../types';
import { assert, logger } from '@polkadot/util';
import { logger } from '@polkadot/util';
import { fetch } from '@polkadot/x-fetch';
import { RpcCoder } from '../coder';
@@ -49,7 +49,9 @@ export class HttpProvider implements ProviderInterface {
* @param {string} endpoint The endpoint url starting with http://
*/
constructor (endpoint: string = defaults.HTTP_URL, headers: Record<string, string> = {}) {
assert(/^(https|http):\/\//.test(endpoint), () => `Endpoint should start with 'http://', received '${endpoint}'`);
if (!/^(https|http):\/\//.test(endpoint)) {
throw new Error(`Endpoint should start with 'http://' or 'https://', received '${endpoint}'`);
}
this.#coder = new RpcCoder();
this.#endpoint = endpoint;
@@ -156,7 +158,9 @@ export class HttpProvider implements ProviderInterface {
method: 'POST'
});
assert(response.ok, () => `[${response.status}]: ${response.statusText}`);
if (!response.ok) {
throw new Error(`[${response.status}]: ${response.statusText}`);
}
const result = await response.text();
+10 -4
View File
@@ -16,7 +16,7 @@ import jsonrpc from '@polkadot/types/interfaces/jsonrpc';
import rpcHeader from '@polkadot/types-support/json/Header.004.json';
import rpcSignedBlock from '@polkadot/types-support/json/SignedBlock.004.immortal.json';
import rpcMetadata from '@polkadot/types-support/metadata/static-substrate';
import { assert, BN, bnToU8a, logger, u8aToHex } from '@polkadot/util';
import { BN, bnToU8a, logger, u8aToHex } from '@polkadot/util';
import { randomAsU8a } from '@polkadot/util-crypto';
const INTERVAL = 1000;
@@ -130,7 +130,9 @@ export class MockProvider implements ProviderInterface {
public async send <T = any> (method: string, params: unknown[]): Promise<T> {
l.debug(() => ['send', method, params]);
assert(this.requests[method], () => `provider.send: Invalid method '${method}'`);
if (!this.requests[method]) {
throw new Error(`provider.send: Invalid method '${method}'`);
}
return this.requests[method](this.db, params) as T;
}
@@ -139,7 +141,9 @@ export class MockProvider implements ProviderInterface {
public async subscribe (type: string, method: string, ...params: unknown[]): Promise<number> {
l.debug(() => ['subscribe', method, params]);
assert(this.subscriptions[method], () => `provider.subscribe: Invalid method '${method}'`);
if (!this.subscriptions[method]) {
throw new Error(`provider.subscribe: Invalid method '${method}'`);
}
const callback = params.pop() as MockStateSubscriptionCallback;
const id = ++this.subscriptionId;
@@ -160,7 +164,9 @@ export class MockProvider implements ProviderInterface {
l.debug(() => ['unsubscribe', id, sub]);
assert(sub, () => `Unable to find subscription for ${id}`);
if (!sub) {
throw new Error(`Unable to find subscription for ${id}`);
}
delete this.subscriptionMap[id];
delete this.subscriptions[sub].callbacks[id];
@@ -1,8 +1,6 @@
// Copyright 2017-2022 @polkadot/rpc-provider authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { assert } from '@polkadot/util';
export interface SmoldotHealth {
isSyncing: boolean
peers: number
@@ -63,10 +61,9 @@ export function healthChecker (): HealthChecker {
return checker.responsePassThrough(jsonRpcResponse);
},
sendJsonRpc: (request) => {
assert(
sendJsonRpc,
'setSendJsonRpc must be called before sending requests'
);
if (!sendJsonRpc) {
throw new Error('setSendJsonRpc must be called before sending requests');
}
if (checker === null) {
sendJsonRpc(request);
@@ -78,14 +75,12 @@ export function healthChecker (): HealthChecker {
sendJsonRpc = cb;
},
start: (healthCallback) => {
assert(
checker === null,
"Can't start the health checker multiple times in parallel"
);
assert(
sendJsonRpc,
'setSendJsonRpc must be called before starting the health checks'
);
if (checker !== null) {
throw new Error("Can't start the health checker multiple times in parallel");
} else if (!sendJsonRpc) {
throw new Error('setSendJsonRpc must be called before starting the health checks');
}
checker = new InnerChecker(healthCallback, sendJsonRpc);
checker.update(true);
},
@@ -216,7 +211,9 @@ class InnerChecker {
const id: string = parsedResponse.id;
// Need to remove the `extern:` prefix.
assert(id.startsWith('extern:'), 'State inconsistency in health checker');
if (!id.startsWith('extern:')) {
throw new Error('State inconsistency in health checker');
}
const newId = JSON.parse(id.slice('extern:'.length)) as string;
@@ -283,10 +280,9 @@ class InnerChecker {
};
startSubscription = (): void => {
assert(
!this.#currentSubunsubRequestId && !this.#currentSubscriptionId,
'Internal error in health checker'
);
if (this.#currentSubunsubRequestId || this.#currentSubscriptionId) {
throw new Error('Internal error in health checker');
}
this.#currentSubunsubRequestId = 'health-checker:'.concat(
this.#nextRequestId.toString()
@@ -303,10 +299,9 @@ class InnerChecker {
};
endSubscription = (): void => {
assert(
!this.#currentSubunsubRequestId && this.#currentSubscriptionId,
'Internal error in health checker'
);
if (this.#currentSubunsubRequestId || !this.#currentSubscriptionId) {
throw new Error('Internal error in health checker');
}
this.#currentSubunsubRequestId = 'health-checker:'.concat(
this.#nextRequestId.toString()
@@ -7,7 +7,7 @@ import type { JsonRpcResponse, ProviderInterface, ProviderInterfaceCallback, Pro
import { Chain, createScClient, ScClient, WellKnownChain } from '@substrate/connect';
import EventEmitter from 'eventemitter3';
import { assert, isError } from '@polkadot/util';
import { isError } from '@polkadot/util';
import { RpcCoder } from '../coder';
import { healthChecker } from './Health';
@@ -71,7 +71,9 @@ export class ScProvider implements ProviderInterface {
// Config details can be found in @substrate/connect repo following the link:
// https://github.com/paritytech/substrate-connect/blob/main/packages/connect/src/connector/index.ts
async connect (config?: ScConfig): Promise<void> {
assert(!this.isConnected, 'Already connected!');
if (this.isConnected) {
throw new Error('Already connected!');
}
// it could happen that after emitting `disconnected` due to the fact taht
// smoldot is syncing, the consumer tries to reconnect after a certain amount
@@ -91,7 +93,10 @@ export class ScProvider implements ProviderInterface {
? scClients.get(this.#sharedSandbox)
: createScClient(config);
assert(client, 'Unkown ScProvider!');
if (!client) {
throw new Error('Unkown ScProvider!');
}
scClients.set(this, client);
const hc = healthChecker();
@@ -156,7 +161,9 @@ export class ScProvider implements ProviderInterface {
const stale = staleSubscriptions.pop();
assert(stale, 'Unable to get stale subscription');
if (!stale) {
throw new Error('Unable to get stale subscription');
}
const { id, unsubscribeMethod } = stale;
@@ -264,7 +271,9 @@ export class ScProvider implements ProviderInterface {
}
public async send<T = any> (method: string, params: unknown[]): Promise<T> {
assert(this.isConnected && this.#chain, 'Provider is not connected');
if (!this.isConnected || !this.#chain) {
throw new Error('Provider is not connected');
}
const chain = await this.#chain;
const [id, json] = this.#coder.encodeJson(method, params);
@@ -302,10 +311,9 @@ export class ScProvider implements ProviderInterface {
params: any[],
callback: ProviderInterfaceCallback
): Promise<number | string> {
assert(
subscriptionUnsubscriptionMethods.has(method),
`Unsupported subscribe method: ${method}`
);
if (!subscriptionUnsubscriptionMethods.has(method)) {
throw new Error(`Unsupported subscribe method: ${method}`);
}
const id = await this.send<number | string>(method, params);
const subscriptionId = `${type}::${id}`;
@@ -320,7 +328,9 @@ export class ScProvider implements ProviderInterface {
const unsubscribeMethod = subscriptionUnsubscriptionMethods.get(method);
assert(unsubscribeMethod, 'Invalid unsubscribe method found');
if (!unsubscribeMethod) {
throw new Error('Invalid unsubscribe method found');
}
this.#subscriptions.set(subscriptionId, [cb, { id, unsubscribeMethod }]);
@@ -332,10 +342,9 @@ export class ScProvider implements ProviderInterface {
method: string,
id: number | string
): Promise<boolean> {
assert(
this.isConnected,
'Provider is not connected'
);
if (!this.isConnected) {
throw new Error('Provider is not connected');
}
const subscriptionId = `${type}::${id}`;
+13 -5
View File
@@ -7,7 +7,7 @@ import type { JsonRpcResponse, ProviderInterface, ProviderInterfaceCallback, Pro
import EventEmitter from 'eventemitter3';
import { assert, isChildClass, isNull, isUndefined, logger, objectSpread } from '@polkadot/util';
import { isChildClass, isNull, isUndefined, logger, objectSpread } from '@polkadot/util';
import { xglobal } from '@polkadot/x-global';
import { WebSocket } from '@polkadot/x-ws';
@@ -122,10 +122,14 @@ export class WsProvider implements ProviderInterface {
? endpoint
: [endpoint];
assert(endpoints.length !== 0, 'WsProvider requires at least one Endpoint');
if (endpoints.length === 0) {
throw new Error('WsProvider requires at least one Endpoint');
}
endpoints.forEach((endpoint) => {
assert(/^(wss|ws):\/\//.test(endpoint), () => `Endpoint should start with 'ws://', received '${endpoint}'`);
if (!/^(wss|ws):\/\//.test(endpoint)) {
throw new Error(`Endpoint should start with 'ws://', received '${endpoint}'`);
}
});
this.#eventemitter = new EventEmitter();
@@ -319,7 +323,9 @@ export class WsProvider implements ProviderInterface {
async #send <T> (id: number, body: string, method: string, params: unknown[], subscription?: SubscriptionHandler): Promise<T> {
return new Promise<T>((resolve, reject): void => {
try {
assert(this.isConnected && !isNull(this.#websocket), 'WebSocket is not connected');
if (!this.isConnected || this.#websocket === null) {
throw new Error('WebSocket is not connected');
}
const callback = (error?: Error | null, result?: T): void => {
error
@@ -531,7 +537,9 @@ export class WsProvider implements ProviderInterface {
};
#onSocketOpen = (): boolean => {
assert(!isNull(this.#websocket), 'WebSocket cannot be null in onOpen');
if (this.#websocket === null) {
throw new Error('WebSocket cannot be null in onOpen');
}
l.debug(() => ['connected to', this.#endpoints[this.#endpointIndex]]);
+3 -3
View File
@@ -4,8 +4,6 @@
import fs from 'fs';
import path from 'path';
import { assert } from '@polkadot/util';
import { packageInfo } from '../packageInfo';
export function writeFile (dest: string, generator: () => string, noLog?: boolean): void {
@@ -35,7 +33,9 @@ export function readTemplate (template: string): string {
.map((p) => path.join(rootDir, p, `${template}.hbs`))
.find((p) => fs.existsSync(p));
assert(file, `Unable to locate ${template}.hbs from ${rootDir}`);
if (!file) {
throw new Error(`Unable to locate ${template}.hbs from ${rootDir}`);
}
return fs.readFileSync(file).toString();
}
@@ -1,7 +1,7 @@
// Copyright 2017-2022 @polkadot/types-known authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { assert, u8aEq } from '@polkadot/util';
import { u8aEq } from '@polkadot/util';
import all from '.';
@@ -43,7 +43,9 @@ describe('upgrades', (): void => {
describe(network, (): void => {
const chain = all.find((n) => n.network === network);
assert(chain, `Unable to find the entry for ${network}`);
if (!chain) {
throw new Error(`Unable to find the entry for ${network}`);
}
it('has a valid genesisHash', (): void => {
expect(u8aEq(chain.genesisHash, genesisHash)).toBe(true);
+7 -3
View File
@@ -5,7 +5,7 @@ import type { ChainUpgrades } from '@polkadot/types/types';
import type { ChainUpgradesRaw } from './types';
import { selectableNetworks } from '@polkadot/networks';
import { assert, BN, hexToU8a, stringify } from '@polkadot/util';
import { BN, hexToU8a, stringify } from '@polkadot/util';
import kusama from './kusama';
import polkadot from './polkadot';
@@ -30,7 +30,9 @@ function checkOrder (network: string, versions: ChainUpgradesRaw): [number, numb
: curr[0] <= prev[0] || curr[1] <= prev[1];
});
assert(!ooo.length, () => `${network}: Mismatched upgrade ordering: ${stringify(ooo)}`);
if (ooo.length) {
throw new Error(`${network}: Mismatched upgrade ordering: ${stringify(ooo)}`);
}
return versions;
}
@@ -39,7 +41,9 @@ function checkOrder (network: string, versions: ChainUpgradesRaw): [number, numb
function mapRaw ([network, versions]: [string, ChainUpgradesRaw]): ChainUpgrades {
const chain = selectableNetworks.find((n) => n.network === network) || NET_EXTRA[network];
assert(chain, () => `Unable to find info for chain ${network}`);
if (!chain) {
throw new Error(`Unable to find info for chain ${network}`);
}
return {
genesisHash: hexToU8a(chain.genesisHash[0]),
+4 -2
View File
@@ -11,7 +11,7 @@ import type { GenericExtrinsicEra } from './ExtrinsicEra';
import type { ExtrinsicValueV4 } from './v4/Extrinsic';
import { AbstractBase } from '@polkadot/types-codec';
import { assert, compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util';
import { compactAddLength, compactFromU8a, compactToU8a, isHex, isU8a, objectProperty, objectSpread, u8aConcat, u8aToHex, u8aToU8a } from '@polkadot/util';
import { BIT_SIGNED, BIT_UNSIGNED, DEFAULT_VERSION, UNMASK_VERSION } from './constants';
@@ -69,7 +69,9 @@ function decodeU8a (registry: Registry, value: Uint8Array, version: number): Ext
const [offset, length] = compactFromU8a(value);
const total = offset + length.toNumber();
assert(total <= value.length, () => `Extrinsic: length less than remainder, expected at least ${total}, found ${value.length}`);
if (total > value.length) {
throw new Error(`Extrinsic: length less than remainder, expected at least ${total}, found ${value.length}`);
}
const data = value.subarray(offset, total);
+10 -4
View File
@@ -6,7 +6,7 @@ import type { BN } from '@polkadot/util';
import type { IExtrinsicEra, INumber } from '../types';
import { Enum, Raw, Tuple, U64 } from '@polkadot/types-codec';
import { assert, bnToBn, formatNumber, hexToU8a, isHex, isObject, isU8a, u8aToBn, u8aToU8a } from '@polkadot/util';
import { bnToBn, formatNumber, hexToU8a, isHex, isObject, isU8a, u8aToBn, u8aToU8a } from '@polkadot/util';
import { IMMORTAL_ERA } from './constants';
@@ -76,7 +76,9 @@ function decodeMortalU8a (registry: Registry, value: Uint8Array): MortalEraValue
const quantizeFactor = Math.max(period >> 12, 1);
const phase = (encoded >> 4) * quantizeFactor;
assert(period >= 4 && phase < period, 'Invalid data passed to Mortal era');
if (period < 4 || phase >= period) {
throw new Error('Invalid data passed to Mortal era');
}
return [new U64(registry, period), new U64(registry, phase)];
}
@@ -255,7 +257,9 @@ export class GenericExtrinsicEra extends Enum implements IExtrinsicEra {
* @description Returns the item as a [[ImmortalEra]]
*/
public get asImmortalEra (): ImmortalEra {
assert(this.isImmortalEra, () => `Cannot convert '${this.type}' via asImmortalEra`);
if (!this.isImmortalEra) {
throw new Error(`Cannot convert '${this.type}' via asImmortalEra`);
}
return this.inner as ImmortalEra;
}
@@ -264,7 +268,9 @@ export class GenericExtrinsicEra extends Enum implements IExtrinsicEra {
* @description Returns the item as a [[MortalEra]]
*/
public get asMortalEra (): MortalEra {
assert(this.isMortalEra, () => `Cannot convert '${this.type}' via asMortalEra`);
if (!this.isMortalEra) {
throw new Error(`Cannot convert '${this.type}' via asMortalEra`);
}
return this.inner as MortalEra;
}
@@ -8,7 +8,7 @@ import type { ExtrinsicPayloadValue, ICompact, IExtrinsicSignature, IKeyringPair
import type { ExtrinsicSignatureOptions } from '../types';
import { Struct } from '@polkadot/types-codec';
import { assert, isU8a, isUndefined, objectProperties, objectSpread, stringify, u8aToHex } from '@polkadot/util';
import { isU8a, isUndefined, objectProperties, objectSpread, stringify, u8aToHex } from '@polkadot/util';
import { EMPTY_U8A, IMMORTAL_ERA } from '../constants';
import { GenericExtrinsicPayloadV4 } from './ExtrinsicPayload';
@@ -165,7 +165,9 @@ export class GenericExtrinsicSignatureV4 extends Struct implements IExtrinsicSig
* @description Generate a payload and applies the signature from a keypair
*/
public sign (method: Call, account: IKeyringPair, options: SignatureOptions): IExtrinsicSignature {
assert(account && account.addressRaw, () => `Expected a valid keypair for signing, found ${stringify(account)}`);
if (!account || !account.addressRaw) {
throw new Error(`Expected a valid keypair for signing, found ${stringify(account)}`);
}
const payload = this.createPayload(method, options);
@@ -180,7 +182,9 @@ export class GenericExtrinsicSignatureV4 extends Struct implements IExtrinsicSig
* @description Generate a payload and applies a fake signature
*/
public signFake (method: Call, address: Address | Uint8Array | string, options: SignatureOptions): IExtrinsicSignature {
assert(address, () => `Expected a valid address for signing, found ${stringify(address)}`);
if (!address) {
throw new Error(`Expected a valid address for signing, found ${stringify(address)}`);
}
const payload = this.createPayload(method, options);
+4 -2
View File
@@ -4,7 +4,7 @@
import type { AnyString, AnyU8a, Registry } from '@polkadot/types-codec/types';
import { U8aFixed } from '@polkadot/types-codec';
import { assert, hexToU8a, isHex, isString, isU8a, u8aToU8a } from '@polkadot/util';
import { hexToU8a, isHex, isString, isU8a, u8aToU8a } from '@polkadot/util';
import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';
/** @internal */
@@ -34,7 +34,9 @@ export class GenericAccountId extends U8aFixed {
const decoded = decodeAccountId(value);
// Part of stream containing >= 32 bytes or a all empty (defaults)
assert(decoded.length >= 32 || !decoded.some((b) => b), () => `Invalid AccountId provided, expected 32 bytes, found ${decoded.length}`);
if (decoded.length < 32 && decoded.some((b) => b)) {
throw new Error(`Invalid AccountId provided, expected 32 bytes, found ${decoded.length}`);
}
super(registry, decoded, 256);
}
+4 -2
View File
@@ -5,7 +5,7 @@ import type { IU8a, Registry } from '@polkadot/types-codec/types';
import type { H256 } from '../interfaces/runtime';
import { Bytes, Enum } from '@polkadot/types-codec';
import { assert, isString, isU8a, u8aToU8a } from '@polkadot/util';
import { isString, isU8a, u8aToU8a } from '@polkadot/util';
/** @internal */
function decodeDataU8a (registry: Registry, value: Uint8Array): [undefined | Uint8Array, number | undefined] {
@@ -56,7 +56,9 @@ export class Data extends Enum {
ShaThree256: 'H256' // 5
}, ...decodeData(registry, value));
assert(!this.isRaw || this.asRaw.length <= 32, 'Data.Raw values are limited to a maximum length of 32 bytes');
if (this.isRaw && this.asRaw.length > 32) {
throw new Error('Data.Raw values are limited to a maximum length of 32 bytes');
}
}
public get asBlakeTwo256 (): H256 {
+7 -3
View File
@@ -9,7 +9,7 @@ import type { InterfaceTypes, IStorageKey, Registry } from '../types';
import type { StorageEntry } from './types';
import { Bytes } from '@polkadot/types-codec';
import { assert, isFunction, isString, isU8a } from '@polkadot/util';
import { isFunction, isString, isU8a } from '@polkadot/util';
import { getSiName } from '../metadata/util';
@@ -71,12 +71,16 @@ function decodeStorageKey (value?: string | Uint8Array | StorageKey | StorageEnt
} else if (Array.isArray(value)) {
const [fn, args = []] = value;
assert(isFunction(fn), 'Expected function input for key construction');
if (!isFunction(fn)) {
throw new Error('Expected function input for key construction');
}
if (fn.meta && fn.meta.type.isMap) {
const map = fn.meta.type.asMap;
assert(Array.isArray(args) && args.length === map.hashers.length, () => `Expected an array of ${map.hashers.length} values as params to a Map query`);
if (!Array.isArray(args) || args.length !== map.hashers.length) {
throw new Error(`Expected an array of ${map.hashers.length} values as params to a Map query`);
}
}
return {