diff --git a/README.md b/README.md index 4e1f47e0d..33e1252f4 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ For detailed information about the different API calls in `client`, visit https: Running examples requires access to a running node. Follow the instructions in Algorand's [developer resources](https://developer.algorand.org/docs/run-a-node/setup/install/) to install a node on your computer. -**As portions of the codebase are written in TypeScript, examples cannot be run directly using `node`**. Please refer to the instructions described in the [examples/README.md](examples/README.md) file for more information regarding running the examples. +**As portions of the codebase are written in TypeScript, example files cannot be run directly using `node`**. Please refer to the instructions described in the [examples/README.md](examples/README.md) file for more information regarding running the examples. ## SDK Development diff --git a/examples/README.md b/examples/README.md index 556919928..38e0d177d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -8,6 +8,8 @@ Portions of the codebase are written in TypeScript, so running examples requires $ npm run example examples/generate_sender_receiver.js ``` +> **Disclaimer:** `ts-node` is **not** required when importing this module from NPM. Code imported from the `algosdk` NPM module will be pre-compiled to plain JavaScript. + ## Configuration Many of the examples in this folder require configuration information such as: diff --git a/src/client/v2/algod/accountInformation.ts b/src/client/v2/algod/accountInformation.ts index b9aee7b8a..4a175bc1e 100644 --- a/src/client/v2/algod/accountInformation.ts +++ b/src/client/v2/algod/accountInformation.ts @@ -1,6 +1,6 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; export default class AccountInformation extends JSONRequest { constructor( diff --git a/src/client/v2/algod/getApplicationByID.ts b/src/client/v2/algod/getApplicationByID.ts index a69af6d26..91e1c12e1 100644 --- a/src/client/v2/algod/getApplicationByID.ts +++ b/src/client/v2/algod/getApplicationByID.ts @@ -1,6 +1,6 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; export default class GetApplicationByID extends JSONRequest { constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { diff --git a/src/client/v2/algod/getAssetByID.ts b/src/client/v2/algod/getAssetByID.ts index 7ec3a28a7..3abb5696d 100644 --- a/src/client/v2/algod/getAssetByID.ts +++ b/src/client/v2/algod/getAssetByID.ts @@ -1,6 +1,6 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; export default class GetAssetByID extends JSONRequest { constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { diff --git a/src/client/v2/algod/proof.ts b/src/client/v2/algod/proof.ts index 32e730ca2..a8b78e21d 100644 --- a/src/client/v2/algod/proof.ts +++ b/src/client/v2/algod/proof.ts @@ -1,6 +1,6 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; export default class Proof extends JSONRequest { constructor( diff --git a/src/client/v2/algod/sendRawTransaction.ts b/src/client/v2/algod/sendRawTransaction.ts index f57106dd8..80163ea6e 100644 --- a/src/client/v2/algod/sendRawTransaction.ts +++ b/src/client/v2/algod/sendRawTransaction.ts @@ -1,11 +1,11 @@ import JSONRequest from '../jsonrequest'; -import HTTPClient from '../../client'; +import { HTTPClient } from '../../client'; +import { concatArrays } from '../../../utils/utils'; /** * Sets the default header (if not previously set) for sending a raw * transaction. * @param headers - * @returns {*} */ export function setSendTransactionHeaders(headers = {}) { let hdrs = headers; @@ -16,7 +16,7 @@ export function setSendTransactionHeaders(headers = {}) { return hdrs; } -function isByteArray(array: Uint8Array) { +function isByteArray(array: any): array is Uint8Array { return array && array.byteLength !== undefined; } @@ -34,13 +34,12 @@ export default class SendRawTransaction extends JSONRequest { if (!stxOrStxs.every(isByteArray)) { throw new TypeError('Array elements must be byte arrays'); } - forPosting = Array.prototype.concat( - ...stxOrStxs.map((arr) => Array.from(arr)) - ); - } else if (!isByteArray(forPosting as Uint8Array)) { + // Flatten into a single Uint8Array + forPosting = concatArrays(...stxOrStxs); + } else if (!isByteArray(forPosting)) { throw new TypeError('Argument must be byte array'); } - this.txnBytesToPost = forPosting as Uint8Array; + this.txnBytesToPost = forPosting; } // eslint-disable-next-line class-methods-use-this diff --git a/src/client/v2/algod/statusAfterBlock.ts b/src/client/v2/algod/statusAfterBlock.ts index ddddbb84c..5d340c6fd 100644 --- a/src/client/v2/algod/statusAfterBlock.ts +++ b/src/client/v2/algod/statusAfterBlock.ts @@ -1,6 +1,6 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; export default class StatusAfterBlock extends JSONRequest { constructor(c: HTTPClient, intDecoding: IntDecoding, private round: number) { diff --git a/src/client/v2/algod/supply.ts b/src/client/v2/algod/supply.ts index d5b123802..0e30f56e2 100644 --- a/src/client/v2/algod/supply.ts +++ b/src/client/v2/algod/supply.ts @@ -1,7 +1,7 @@ import JSONRequest from '../jsonrequest'; export default class Supply extends JSONRequest { - // eslint-disable-next-line no-underscore-dangle,class-methods-use-this + // eslint-disable-next-line class-methods-use-this path() { return '/v2/ledger/supply'; } diff --git a/src/client/v2/indexer/lookupAccountByID.ts b/src/client/v2/indexer/lookupAccountByID.ts index 3bd611dc8..22e3df694 100644 --- a/src/client/v2/indexer/lookupAccountByID.ts +++ b/src/client/v2/indexer/lookupAccountByID.ts @@ -1,6 +1,6 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; export default class LookupAccountByID extends JSONRequest { constructor( diff --git a/src/client/v2/indexer/lookupAccountTransactions.ts b/src/client/v2/indexer/lookupAccountTransactions.ts index d62f3582c..b3e75ce33 100644 --- a/src/client/v2/indexer/lookupAccountTransactions.ts +++ b/src/client/v2/indexer/lookupAccountTransactions.ts @@ -1,6 +1,18 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; + +/** + * Accept base64 string or Uint8Array and output base64 string + * @param data Base64 string or Uint8Array + * @returns The inputted base64 string, or a base64 string representation of the Uint8Array + */ +export function base64StringFunnel(data: Uint8Array | string) { + if (typeof data === 'string') { + return data; + } + return Buffer.from(data).toString('base64'); +} export default class LookupAccountTransactions extends JSONRequest { constructor( @@ -16,9 +28,12 @@ export default class LookupAccountTransactions extends JSONRequest { return `/v2/accounts/${this.account}/transactions`; } - // notePrefix to filter with, as uint8array - notePrefix(prefix: string) { - this.query['note-prefix'] = prefix; + /** + * notePrefix to filter with + * @param prefix base64 string or uint8array + */ + notePrefix(prefix: Uint8Array | string) { + this.query['note-prefix'] = base64StringFunnel(prefix); return this; } diff --git a/src/client/v2/indexer/lookupApplications.ts b/src/client/v2/indexer/lookupApplications.ts index 7d5de2f87..b0267083e 100644 --- a/src/client/v2/indexer/lookupApplications.ts +++ b/src/client/v2/indexer/lookupApplications.ts @@ -1,6 +1,6 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; export default class LookupApplications extends JSONRequest { constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { diff --git a/src/client/v2/indexer/lookupAssetBalances.ts b/src/client/v2/indexer/lookupAssetBalances.ts index d7afe5545..398632756 100644 --- a/src/client/v2/indexer/lookupAssetBalances.ts +++ b/src/client/v2/indexer/lookupAssetBalances.ts @@ -1,6 +1,6 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; export default class LookupAssetBalances extends JSONRequest { constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { diff --git a/src/client/v2/indexer/lookupAssetByID.ts b/src/client/v2/indexer/lookupAssetByID.ts index b24f506e3..57eb40a53 100644 --- a/src/client/v2/indexer/lookupAssetByID.ts +++ b/src/client/v2/indexer/lookupAssetByID.ts @@ -1,6 +1,6 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; export default class LookupAssetByID extends JSONRequest { constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { diff --git a/src/client/v2/indexer/lookupAssetTransactions.ts b/src/client/v2/indexer/lookupAssetTransactions.ts index 0a4a2d23c..535ab211f 100644 --- a/src/client/v2/indexer/lookupAssetTransactions.ts +++ b/src/client/v2/indexer/lookupAssetTransactions.ts @@ -1,6 +1,7 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; +import { base64StringFunnel } from './lookupAccountTransactions'; export default class LookupAssetTransactions extends JSONRequest { constructor(c: HTTPClient, intDecoding: IntDecoding, private index: number) { @@ -12,9 +13,12 @@ export default class LookupAssetTransactions extends JSONRequest { return `/v2/assets/${this.index}/transactions`; } - // notePrefix to filter with, as uint8array - notePrefix(prefix: string) { - this.query['note-prefix'] = prefix; + /** + * notePrefix to filter with + * @param prefix base64 string or uint8array + */ + notePrefix(prefix: Uint8Array | string) { + this.query['note-prefix'] = base64StringFunnel(prefix); return this; } @@ -37,7 +41,7 @@ export default class LookupAssetTransactions extends JSONRequest { } // round to filter with, as int - round(round) { + round(round: number) { this.query.round = round; return this; } diff --git a/src/client/v2/indexer/lookupBlock.ts b/src/client/v2/indexer/lookupBlock.ts index 23fef8eb6..9261b52ca 100644 --- a/src/client/v2/indexer/lookupBlock.ts +++ b/src/client/v2/indexer/lookupBlock.ts @@ -1,6 +1,6 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; export default class LookupBlock extends JSONRequest { constructor(c: HTTPClient, intDecoding: IntDecoding, private round: number) { diff --git a/src/client/v2/indexer/lookupTransactionByID.ts b/src/client/v2/indexer/lookupTransactionByID.ts index fbd4df40f..4cd98700a 100644 --- a/src/client/v2/indexer/lookupTransactionByID.ts +++ b/src/client/v2/indexer/lookupTransactionByID.ts @@ -1,9 +1,9 @@ import JSONRequest from '../jsonrequest'; import HTTPClient from '../../client'; -import { IntDecoding } from '../../../types/intDecoding'; +import IntDecoding from '../../../types/intDecoding'; export default class LookupTransactionByID extends JSONRequest { - constructor(c: HTTPClient, intDecoding: IntDecoding, private txID) { + constructor(c: HTTPClient, intDecoding: IntDecoding, private txID: string) { super(c, intDecoding); this.txID = txID; } diff --git a/src/client/v2/indexer/searchForTransactions.ts b/src/client/v2/indexer/searchForTransactions.ts index c2f74a7c8..af44db124 100644 --- a/src/client/v2/indexer/searchForTransactions.ts +++ b/src/client/v2/indexer/searchForTransactions.ts @@ -1,4 +1,5 @@ import JSONRequest from '../jsonrequest'; +import { base64StringFunnel } from './lookupAccountTransactions'; export default class SearchForTransactions extends JSONRequest { // eslint-disable-next-line class-methods-use-this @@ -6,9 +7,12 @@ export default class SearchForTransactions extends JSONRequest { return '/v2/transactions'; } - // notePrefix to filter with, as uint8array - notePrefix(prefix: Uint8Array) { - this.query['note-prefix'] = prefix; + /** + * notePrefix to filter with + * @param prefix base64 string or uint8array + */ + notePrefix(prefix: Uint8Array | string) { + this.query['note-prefix'] = base64StringFunnel(prefix); return this; } diff --git a/src/client/v2/jsonrequest.ts b/src/client/v2/jsonrequest.ts index 71931e87d..41b90e726 100644 --- a/src/client/v2/jsonrequest.ts +++ b/src/client/v2/jsonrequest.ts @@ -1,5 +1,5 @@ import HTTPClient from '../client'; -import { IntDecoding } from '../../types/intDecoding'; +import IntDecoding from '../../types/intDecoding'; /** * Base abstract class for JSON requests. @@ -13,7 +13,8 @@ export default abstract class JSONRequest< Body = Data > { c: HTTPClient; - query: Record = {}; + query: Record; + intDecoding: IntDecoding; /** * @param {HttpClient} client HTTPClient object. @@ -21,9 +22,10 @@ export default abstract class JSONRequest< * for decoding integers from this request's response. See the setIntDecoding method for more * details. */ - constructor(client: HTTPClient, public intDecoding?: IntDecoding) { + constructor(client: HTTPClient, intDecoding?: IntDecoding) { this.c = client; - this.intDecoding = intDecoding || 'default'; + this.query = {}; + this.intDecoding = intDecoding || IntDecoding.DEFAULT; } /** @@ -53,13 +55,7 @@ export default abstract class JSONRequest< if (this.intDecoding !== 'default') { jsonOptions.intDecoding = this.intDecoding; } - const res = await this.c.get( - // eslint-disable-next-line no-underscore-dangle - this.path(), - this.query, - headers, - jsonOptions - ); + const res = await this.c.get(this.path(), this.query, headers, jsonOptions); return this.prepare(res.body); } diff --git a/src/logic/logic.js b/src/logic/logic.ts similarity index 75% rename from src/logic/logic.js rename to src/logic/logic.ts index 0b441cadc..87b246cad 100644 --- a/src/logic/logic.js +++ b/src/logic/logic.ts @@ -3,14 +3,36 @@ * Utilities for working with program bytes. */ -const langspec = require('./langspec.json'); +import langspec from './langspec.json'; -let opcodes; +/** + * Langspec Op Structure + */ +interface OpStructure { + Opcode: number; + Name: string; + Args?: string; + Returns?: string; + Cost: number; + Size: number; + ArgEnum?: string[]; + ArgEnumTypes?: string; + Doc: string; + DocExtra?: string; + ImmediateNote?: string; + Groups: string[]; +} + +let opcodes: { + [key: number]: OpStructure; +}; const maxCost = 20000; const maxLength = 1000; -function parseUvarint(array) { +export function parseUvarint( + array: Uint8Array +): [numberFound: number, size: number] { let x = 0; let s = 0; for (let i = 0; i < array.length; i++) { @@ -27,7 +49,10 @@ function parseUvarint(array) { return [0, 0]; } -function readIntConstBlock(program, pc) { +function readIntConstBlock( + program: Uint8Array, + pc: number +): [size: number, ints: number[]] { let size = 1; const parsed = parseUvarint(program.slice(pc + size)); const numInts = parsed[0]; @@ -35,13 +60,13 @@ function readIntConstBlock(program, pc) { if (bytesUsed <= 0) { throw new Error(`could not decode int const block size at pc=${pc + size}`); } - const ints = []; + const ints: number[] = []; size += bytesUsed; for (let i = 0; i < numInts; i++) { if (pc + size >= program.length) { throw new Error('intcblock ran past end of program'); } - let numberFound; + let numberFound: number; [numberFound, bytesUsed] = parseUvarint(program.slice(pc + size)); if (bytesUsed <= 0) { throw new Error( @@ -54,7 +79,10 @@ function readIntConstBlock(program, pc) { return [size, ints]; } -function readByteConstBlock(program, pc) { +function readByteConstBlock( + program: Uint8Array, + pc: number +): [size: number, byteArrays: Uint8Array[]] { let size = 1; const parsed = parseUvarint(program.slice(pc + size)); const numInts = parsed[0]; @@ -64,13 +92,13 @@ function readByteConstBlock(program, pc) { `could not decode []byte const block size at pc=${pc + size}` ); } - const byteArrays = []; + const byteArrays: Uint8Array[] = []; size += bytesUsed; for (let i = 0; i < numInts; i++) { if (pc + size >= program.length) { throw new Error('bytecblock ran past end of program'); } - let itemLen; + let itemLen: number; [itemLen, bytesUsed] = parseUvarint(program.slice(pc + size)); if (bytesUsed <= 0) { throw new Error( @@ -88,7 +116,10 @@ function readByteConstBlock(program, pc) { return [size, byteArrays]; } -function readPushIntOp(program, pc) { +function readPushIntOp( + program: Uint8Array, + pc: number +): [size: number, numberFound: number] { let size = 1; const [numberFound, bytesUsed] = parseUvarint(program.slice(pc + size)); if (bytesUsed <= 0) { @@ -98,7 +129,10 @@ function readPushIntOp(program, pc) { return [size, numberFound]; } -function readPushByteOp(program, pc) { +function readPushByteOp( + program: Uint8Array, + pc: number +): [size: number, byteArray: Uint8Array] { let size = 1; const [itemLen, bytesUsed] = parseUvarint(program.slice(pc + size)); if (bytesUsed <= 0) { @@ -122,7 +156,10 @@ function readPushByteOp(program, pc) { * @throws {Error} * @returns {[Uint8Array, [Uint8Array], boolean]} */ -function readProgram(program, args) { +export function readProgram( + program: Uint8Array, + args: Uint8Array[] +): [ints: number[], byteArrays: Uint8Array[], valid: boolean] { const intcblockOpcode = 32; const bytecblockOpcode = 38; const pushbytesOpcode = 128; @@ -165,8 +202,8 @@ function readProgram(program, args) { } let pc = vlen; - let ints = []; - let byteArrays = []; + let ints: number[] = []; + let byteArrays: Uint8Array[] = []; while (pc < program.length) { const op = opcodes[program[pc]]; if (op === undefined) { @@ -178,19 +215,19 @@ function readProgram(program, args) { if (size === 0) { switch (op.Opcode) { case intcblockOpcode: { - let foundInts; + let foundInts: number[]; [size, foundInts] = readIntConstBlock(program, pc); ints = ints.concat(foundInts); break; } case bytecblockOpcode: { - let foundByteArrays; + let foundByteArrays: Uint8Array[]; [size, foundByteArrays] = readByteConstBlock(program, pc); byteArrays = byteArrays.concat(foundByteArrays); break; } case pushintOpcode: { - let foundInt; + let foundInt: number; [size, foundInt] = readPushIntOp(program, pc); ints.push(foundInt); break; @@ -221,41 +258,32 @@ function readProgram(program, args) { * @param {Uint8Array} program Program to check * @param {[Uint8Array]} args Program arguments as array of Uint8Array arrays * @throws {Error} - * @returns {boolean} true if success + * @returns true if success */ -function checkProgram(program, args) { +export function checkProgram(program: Uint8Array, args: Uint8Array[]) { const [, , success] = readProgram(program, args); return success; } -function checkIntConstBlock(program, pc) { +export function checkIntConstBlock(program: Uint8Array, pc: number) { const [size] = readIntConstBlock(program, pc); return size; } -function checkByteConstBlock(program, pc) { +export function checkByteConstBlock(program: Uint8Array, pc: number) { const [size] = readByteConstBlock(program, pc); return size; } -function checkPushIntOp(program, pc) { +export function checkPushIntOp(program: Uint8Array, pc: number) { const [size] = readPushIntOp(program, pc); return size; } -function checkPushByteOp(program, pc) { +export function checkPushByteOp(program: Uint8Array, pc: number) { const [size] = readPushByteOp(program, pc); return size; } -module.exports = { - checkProgram, - readProgram, - parseUvarint, - checkIntConstBlock, - checkByteConstBlock, - checkPushIntOp, - checkPushByteOp, - langspecEvalMaxVersion: langspec.EvalMaxVersion, - langspecLogicSigVersion: langspec.LogicSigVersion, -}; +export const langspecEvalMaxVersion = langspec.EvalMaxVersion; +export const langspecLogicSigVersion = langspec.LogicSigVersion; diff --git a/src/main.ts b/src/main.ts index 92a817479..6a09575bb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -242,6 +242,7 @@ export const ERROR_INVALID_MICROALGOS = new Error( export * from './types/transactions'; export { default as Algodv2 } from './client/v2/algod/algod'; export { default as Kmd } from './client/kmd'; +export { default as IntDecoding } from './types/intDecoding'; export { default as Indexer } from './client/v2/indexer/indexer'; export { isValidAddress, diff --git a/src/types/intDecoding.d.ts b/src/types/intDecoding.d.ts deleted file mode 100644 index b92936a92..000000000 --- a/src/types/intDecoding.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Configure how integers in JSON response will be decoded. - * - * The options are: - * * "default": All integers will be decoded as Numbers, meaning any values greater than - * Number.MAX_SAFE_INTEGER will lose precision. - * * "safe": All integers will be decoded as Numbers, but if any values are greater than - * Number.MAX_SAFE_INTEGER an error will be thrown. - * * "mixed": Integers will be decoded as Numbers if they are less than or equal to - * Number.MAX_SAFE_INTEGER, otherwise they will be decoded as BigInts. - * * "bigint": All integers will be decoded as BigInts. - * - * Defaults to "default" if not included. - */ -export type IntDecoding = 'default' | 'safe' | 'mixed' | 'bigint'; diff --git a/src/types/intDecoding.ts b/src/types/intDecoding.ts new file mode 100644 index 000000000..999d3baa1 --- /dev/null +++ b/src/types/intDecoding.ts @@ -0,0 +1,31 @@ +/** + * Configure how integers in JSON response will be decoded. + */ +/* eslint-disable no-unused-vars,no-shadow */ +enum IntDecoding { + /** + * All integers will be decoded as Numbers, meaning any values greater than + * Number.MAX_SAFE_INTEGER will lose precision. + */ + DEFAULT = 'default', + + /** + * All integers will be decoded as Numbers, but if any values are greater than + * Number.MAX_SAFE_INTEGER an error will be thrown. + */ + SAFE = 'safe', + + /** + * Integers will be decoded as Numbers if they are less than or equal to + * Number.MAX_SAFE_INTEGER, otherwise they will be decoded as BigInts. + */ + MIXED = 'mixed', + + /** + * All integers will be decoded as BigInts. + */ + BIGINT = 'bigint', +} +/* eslint-enable no-unused-vars,no-shadow */ + +export default IntDecoding; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 935fe2cc1..ac3968500 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,5 +1,5 @@ import JSONbigWithoutConfig from 'json-bigint'; -import { IntDecoding } from '../types/intDecoding'; +import IntDecoding from '../types/intDecoding'; const JSONbig = JSONbigWithoutConfig({ useNativeBigInt: true, strict: true }); @@ -27,7 +27,7 @@ export interface JSONOptions { */ export function parseJSON(str: string, options?: JSONOptions) { const intDecoding = - options && options.intDecoding ? options.intDecoding : 'default'; + options && options.intDecoding ? options.intDecoding : IntDecoding.DEFAULT; const parsed = JSONbig.parse(str, (_, value) => { if ( value != null && @@ -80,15 +80,20 @@ export function arrayEqual(a: ArrayLike, b: ArrayLike) { } /** - * ConcatArrays takes two array and returns a joint array of both - * @param a - * @param b + * ConcatArrays takes n number arrays and returns a joint Uint8Array + * @param arrs An arbitrary number of n array-like number list arguments * @returns [a,b] */ -export function concatArrays(a: ArrayLike, b: ArrayLike) { - const c = new Uint8Array(a.length + b.length); - c.set(a); - c.set(b, a.length); +export function concatArrays(...arrs: ArrayLike[]) { + const size = arrs.reduce((sum, arr) => sum + arr.length, 0); + const c = new Uint8Array(size); + + let offset = 0; + for (let i = 0; i < arrs.length; i++) { + c.set(arrs[i], offset); + offset += arrs[i].length; + } + return c; } diff --git a/tests/4.Utils.ts b/tests/4.Utils.ts new file mode 100644 index 000000000..a06abbb61 --- /dev/null +++ b/tests/4.Utils.ts @@ -0,0 +1,35 @@ +import assert from 'assert'; +import * as utils from '../src/utils/utils'; + +describe('utils', () => { + describe('concatArrays', () => { + it('should concat two Uint8Arrays', () => { + const a = new Uint8Array([1, 2, 3]); + const b = new Uint8Array([4, 5, 6]); + + const expected = new Uint8Array([1, 2, 3, 4, 5, 6]); + const actual = utils.concatArrays(a, b); + assert.deepStrictEqual(actual, expected); + }); + + it('should concat two number arrays as a Uint8Array', () => { + const a = [1, 2, 3]; + const b = [4, 5, 6]; + + const expected = new Uint8Array([1, 2, 3, 4, 5, 6]); + const actual = utils.concatArrays(a, b); + assert.deepStrictEqual(actual, expected); + assert(actual instanceof Uint8Array); + }); + + it('should concat three Uint8Arrays', () => { + const a = new Uint8Array([1, 2]); + const b = new Uint8Array([3, 4]); + const c = new Uint8Array([5, 6]); + + const expected = new Uint8Array([1, 2, 3, 4, 5, 6]); + const actual = utils.concatArrays(a, b, c); + assert.deepStrictEqual(expected, actual); + }); + }); +}); diff --git a/tests/mocha.js b/tests/mocha.js index 129218487..fcb903806 100644 --- a/tests/mocha.js +++ b/tests/mocha.js @@ -13,7 +13,10 @@ async function testRunner() { const testFiles = fs .readdirSync(__dirname) - .filter((file) => file !== 'mocha.js' && file.endsWith('.js')) + .filter( + (file) => + file !== 'mocha.js' && (file.endsWith('.js') || file.endsWith('.ts')) + ) .map((file) => path.join(__dirname, file)); if (browser) { diff --git a/tsconfig.json b/tsconfig.json index ce7958cbf..b4d7f9e34 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "allowJs": true, "target": "es2020", "moduleResolution": "node", + "resolveJsonModule": true, "esModuleInterop": true, "sourceMap": true, "declaration": true