docs: added jsdocs to utils.
This commit is contained in:
parent
29ead2696c
commit
f30cdf6262
@ -3,10 +3,16 @@ import { getBytes, getBytesCopy } from "./data.js";
|
||||
import type { BytesLike } from "./data.js";
|
||||
|
||||
|
||||
export function decodeBase64(textData: string): Uint8Array {
|
||||
return getBytesCopy(Buffer.from(textData, "base64"));
|
||||
/**
|
||||
* Decodes the base-64 encoded %%base64Data%%.
|
||||
*/
|
||||
export function decodeBase64(base64Data: string): Uint8Array {
|
||||
return getBytesCopy(Buffer.from(base64Data, "base64"));
|
||||
};
|
||||
|
||||
/**
|
||||
* Encodes %%data%% as base-64 encoded data.
|
||||
*/
|
||||
export function encodeBase64(data: BytesLike): string {
|
||||
return Buffer.from(getBytes(data)).toString("base64");
|
||||
}
|
||||
|
@ -22,15 +22,39 @@ function _getBytes(value: BytesLike, name?: string, copy?: boolean): Uint8Array
|
||||
return throwArgumentError("invalid BytesLike value", name || "value", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a typed Uint8Array for %%value%%. If already a Uint8Array
|
||||
* the original %%value%% is returned; if a copy is required use
|
||||
* [[getBytesCopy]].
|
||||
*
|
||||
* @see: getBytesCopy
|
||||
*/
|
||||
export function getBytes(value: BytesLike, name?: string): Uint8Array {
|
||||
return _getBytes(value, name, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a typed Uint8Array for %%value%%, creating a copy if necessary
|
||||
* to prevent any modifications of the returned value from being
|
||||
* reflected elsewhere.
|
||||
*
|
||||
* @see: getBytes
|
||||
*/
|
||||
export function getBytesCopy(value: BytesLike, name?: string): Uint8Array {
|
||||
return _getBytes(value, name, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if %%value%% is a valid [[HexString]], with additional
|
||||
* optional constraints depending on %%length%%.
|
||||
*
|
||||
* If %%length%% is //true//, then %%value%% must additionally be a valid
|
||||
* [[HexDataString]] (i.e. even length).
|
||||
*
|
||||
* If %%length%% is //a number//, then %%value%% must represent that many
|
||||
* bytes of data (e.g. ``0x1234`` is 2 bytes).
|
||||
*/
|
||||
export function isHexString(value: any, length?: number | boolean): value is `0x${ string }` {
|
||||
if (typeof(value) !== "string" || !value.match(/^0x[0-9A-Fa-f]*$/)) {
|
||||
return false
|
||||
@ -42,11 +66,19 @@ export function isHexString(value: any, length?: number | boolean): value is `0x
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if %%value%% is a valid representation of arbitrary
|
||||
* data (i.e. a valid [[HexDataString]] or a Uint8Array).
|
||||
*/
|
||||
export function isBytesLike(value: any): value is BytesLike {
|
||||
return (isHexString(value, true) || (value instanceof Uint8Array));
|
||||
}
|
||||
|
||||
const HexCharacters: string = "0123456789abcdef";
|
||||
|
||||
/**
|
||||
* Returns a [[HexDataString]] representation of %%data%%.
|
||||
*/
|
||||
export function hexlify(data: BytesLike): string {
|
||||
const bytes = getBytes(data);
|
||||
|
||||
@ -58,15 +90,28 @@ export function hexlify(data: BytesLike): string {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [[HexDataString]] by concatenating all values
|
||||
* within %%data%%.
|
||||
*/
|
||||
export function concat(datas: ReadonlyArray<BytesLike>): string {
|
||||
return "0x" + datas.map((d) => hexlify(d).substring(2)).join("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of %%data%%, in bytes.
|
||||
*/
|
||||
export function dataLength(data: BytesLike): number {
|
||||
if (isHexString(data, true)) { return (data.length - 2) / 2; }
|
||||
return getBytes(data).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [[HexDataString]] by slicing %%data%% from the %%start%%
|
||||
* offset to the %%end%% offset.
|
||||
*
|
||||
* By default %%start%% is 0 and %%end%% is the length of %%data%%.
|
||||
*/
|
||||
export function dataSlice(data: BytesLike, start?: number, end?: number): string {
|
||||
const bytes = getBytes(data);
|
||||
if (end != null && end > bytes.length) { throwError("cannot slice beyond data bounds", "BUFFER_OVERRUN", {
|
||||
@ -75,13 +120,16 @@ export function dataSlice(data: BytesLike, start?: number, end?: number): string
|
||||
return hexlify(bytes.slice((start == null) ? 0: start, (end == null) ? bytes.length: end));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the [[HexDataString]] result by stripping all **leading**
|
||||
** zero bytes from %%data%%.
|
||||
*/
|
||||
export function stripZerosLeft(data: BytesLike): string {
|
||||
let bytes = hexlify(data).substring(2);
|
||||
while (bytes.substring(0, 2) == "00") { bytes = bytes.substring(2); }
|
||||
return "0x" + bytes;
|
||||
}
|
||||
|
||||
|
||||
function zeroPad(data: BytesLike, length: number, left: boolean): string {
|
||||
const bytes = getBytes(data);
|
||||
if (length < bytes.length) {
|
||||
@ -103,10 +151,18 @@ function zeroPad(data: BytesLike, length: number, left: boolean): string {
|
||||
return hexlify(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the [[HexDataString]] of %%data%% padded on the **left**
|
||||
* to %%length%% bytes.
|
||||
*/
|
||||
export function zeroPadValue(data: BytesLike, length: number): string {
|
||||
return zeroPad(data, length, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the [[HexDataString]] of %%data%% padded on the **right**
|
||||
* to %%length%% bytes.
|
||||
*/
|
||||
export function zeroPadBytes(data: BytesLike, length: number): string {
|
||||
return zeroPad(data, length, false);
|
||||
}
|
||||
|
@ -72,12 +72,16 @@ async function gatewayData(url: string, signal?: FetchCancelSignal): Promise<Fet
|
||||
}
|
||||
}
|
||||
|
||||
export function getIpfsGatewayFunc(base: string): FetchGatewayFunc {
|
||||
/**
|
||||
* Returns a [[FetchGatewayFunc]] for fetching content from a standard
|
||||
* IPFS gateway hosted at %%baseUrl%%.
|
||||
*/
|
||||
export function getIpfsGatewayFunc(baseUrl: string): FetchGatewayFunc {
|
||||
async function gatewayIpfs(url: string, signal?: FetchCancelSignal): Promise<FetchRequest | FetchResponse> {
|
||||
try {
|
||||
const match = url.match(reIpfs);
|
||||
if (!match) { throw new Error("invalid link"); }
|
||||
return new FetchRequest(`${ base }${ match[2] }`);
|
||||
return new FetchRequest(`${ baseUrl }${ match[2] }`);
|
||||
} catch (error) {
|
||||
return new FetchResponse(599, "BAD REQUEST (invalid IPFS URI)", { }, null, new FetchRequest(url));
|
||||
}
|
||||
@ -136,6 +140,12 @@ function checkSignal(signal?: FetchCancelSignal): FetchCancelSignal {
|
||||
return signal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a request for a resource using a URI.
|
||||
*
|
||||
* Requests can occur over http/https, data: URI or any
|
||||
* URI scheme registered via the static [[register]] method.
|
||||
*/
|
||||
export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
#allowInsecure: boolean;
|
||||
#gzip: boolean;
|
||||
@ -155,13 +165,33 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
|
||||
#signal?: FetchCancelSignal;
|
||||
|
||||
// URL
|
||||
/**
|
||||
* The fetch URI to requrest.
|
||||
*/
|
||||
get url(): string { return this.#url; }
|
||||
set url(url: string) {
|
||||
this.#url = String(url);
|
||||
}
|
||||
|
||||
// Body
|
||||
/**
|
||||
* The fetch body, if any, to send as the request body.
|
||||
*
|
||||
* When setting a body, the intrinsic ``Content-Type`` is automatically
|
||||
* set and will be used if **not overridden** by setting a custom
|
||||
* header.
|
||||
*
|
||||
* If %%body%% is null, the body is cleared (along with the
|
||||
* intrinsic ``Content-Type``) and the .
|
||||
*
|
||||
* If %%body%% is a string, the intrincis ``Content-Type`` is set to
|
||||
* ``text/plain``.
|
||||
*
|
||||
* If %%body%% is a Uint8Array, the intrincis ``Content-Type`` is set to
|
||||
* ``application/octet-stream``.
|
||||
*
|
||||
* If %%body%% is any other object, the intrincis ``Content-Type`` is
|
||||
* set to ``application/json``.
|
||||
*/
|
||||
get body(): null | Uint8Array {
|
||||
if (this.#body == null) { return null; }
|
||||
return new Uint8Array(this.#body);
|
||||
@ -184,11 +214,18 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request has a body.
|
||||
*/
|
||||
hasBody(): this is FetchRequestWithBody {
|
||||
return (this.#body != null);
|
||||
}
|
||||
|
||||
// Method (default: GET with no body, POST with a body)
|
||||
/**
|
||||
* The HTTP method to use when requesting the URI. If no method
|
||||
* has been explicitly set, then ``GET`` is used if the body is
|
||||
* null and ``POST`` otherwise.
|
||||
*/
|
||||
get method(): string {
|
||||
if (this.#method) { return this.#method; }
|
||||
if (this.hasBody()) { return "POST"; }
|
||||
@ -199,7 +236,9 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
this.#method = String(method).toUpperCase();
|
||||
}
|
||||
|
||||
// Headers (automatically fills content-type if not explicitly set)
|
||||
/**
|
||||
* The headers that will be used when requesting the URI.
|
||||
*/
|
||||
get headers(): Readonly<Record<string, string>> {
|
||||
const headers = Object.assign({ }, this.#headers);
|
||||
|
||||
@ -218,12 +257,25 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
|
||||
return Object.freeze(headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header for %%key%%.
|
||||
*/
|
||||
getHeader(key: string): string {
|
||||
return this.headers[key.toLowerCase()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the header for %%key%% to %%value%%. All values are coerced
|
||||
* to a string.
|
||||
*/
|
||||
setHeader(key: string, value: string | number): void {
|
||||
this.#headers[String(key).toLowerCase()] = String(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all headers.
|
||||
*/
|
||||
clearHeaders(): void {
|
||||
this.#headers = { };
|
||||
}
|
||||
@ -245,10 +297,16 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
};
|
||||
}
|
||||
|
||||
// Configure an Authorization header
|
||||
/**
|
||||
* The value that will be sent for the ``Authorization`` header.
|
||||
*/
|
||||
get credentials(): null | string {
|
||||
return this.#creds || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an ``Authorization`` for %%username%% with %%password%%.
|
||||
*/
|
||||
setCredentials(username: string, password: string): void {
|
||||
if (username.match(/:/)) {
|
||||
throwArgumentError("invalid basic authentication username", "username", "[REDACTED]");
|
||||
@ -256,7 +314,9 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
this.#creds = `${ username }:${ password }`;
|
||||
}
|
||||
|
||||
// Configure the request to allow gzipped responses
|
||||
/**
|
||||
* Allow gzip-encoded responses.
|
||||
*/
|
||||
get allowGzip(): boolean {
|
||||
return this.#gzip;
|
||||
}
|
||||
@ -264,7 +324,10 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
this.#gzip = !!value;
|
||||
}
|
||||
|
||||
// Allow credentials to be sent over an insecure (non-HTTPS) channel
|
||||
/**
|
||||
* Allow ``Authentication`` credentials to be sent over insecure
|
||||
* channels.
|
||||
*/
|
||||
get allowInsecureAuthentication(): boolean {
|
||||
return !!this.#allowInsecure;
|
||||
}
|
||||
@ -272,14 +335,22 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
this.#allowInsecure = !!value;
|
||||
}
|
||||
|
||||
// Timeout (milliseconds)
|
||||
/**
|
||||
* The timeout (in milliseconds) to wait for a complere response.
|
||||
*/
|
||||
get timeout(): number { return this.#timeout; }
|
||||
set timeout(timeout: number) {
|
||||
assertArgument(timeout >= 0, "timeout must be non-zero", "timeout", timeout);
|
||||
this.#timeout = timeout;
|
||||
}
|
||||
|
||||
// Preflight called before each request is sent
|
||||
/**
|
||||
* This function is called prior to each request, for example
|
||||
* during a redirection or retry in case of server throttling.
|
||||
*
|
||||
* This offers an opportunity to populate headers or update
|
||||
* content before sending a request.
|
||||
*/
|
||||
get preflightFunc(): null | FetchPreflightFunc {
|
||||
return this.#preflight || null;
|
||||
}
|
||||
@ -287,7 +358,16 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
this.#preflight = preflight;
|
||||
}
|
||||
|
||||
// Preflight called before each request is sent
|
||||
/**
|
||||
* This function is called after each response, offering an
|
||||
* opportunity to provide client-level throttling or updating
|
||||
* response data.
|
||||
*
|
||||
* Any error thrown in this causes the ``send()`` to throw.
|
||||
*
|
||||
* To schedule a retry attempt (assuming the maximum retry limit
|
||||
* has not been reached), use [[response.throwThrottleError]].
|
||||
*/
|
||||
get processFunc(): null | FetchProcessFunc {
|
||||
return this.#process || null;
|
||||
}
|
||||
@ -295,7 +375,9 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
this.#process = process;
|
||||
}
|
||||
|
||||
// Preflight called before each request is sent
|
||||
/**
|
||||
* This function is called on each retry attempt.
|
||||
*/
|
||||
get retryFunc(): null | FetchRetryFunc {
|
||||
return this.#retry || null;
|
||||
}
|
||||
@ -407,6 +489,9 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the response by sending the request.
|
||||
*/
|
||||
send(): Promise<FetchResponse> {
|
||||
if (this.#signal != null) {
|
||||
return throwError("request already sent", "UNSUPPORTED_OPERATION", { operation: "fetchRequest.send" });
|
||||
@ -415,6 +500,10 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
return this.#send(0, getTime() + this.timeout, 0, this, new FetchResponse(0, "", { }, null, this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the inflight response, causing a ``CANCELLED``
|
||||
* error to be rejected from the [[send]].
|
||||
*/
|
||||
cancel(): void {
|
||||
if (this.#signal == null) {
|
||||
return throwError("request has not been sent", "UNSUPPORTED_OPERATION", { operation: "fetchRequest.cancel" });
|
||||
@ -460,6 +549,9 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
return req;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new copy of this request.
|
||||
*/
|
||||
clone(): FetchRequest {
|
||||
const clone = new FetchRequest(this.url);
|
||||
|
||||
@ -488,14 +580,24 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks all static configuration for gateways and FetchGetUrlFunc
|
||||
* registration.
|
||||
*/
|
||||
static lockConfig(): void {
|
||||
locked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Gateway function for %%scheme%%.
|
||||
*/
|
||||
static getGateway(scheme: string): null | FetchGatewayFunc {
|
||||
return Gateways[scheme.toLowerCase()] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the FetchGatewayFunc for %%scheme%% to %%func%%.
|
||||
*/
|
||||
static registerGateway(scheme: string, func: FetchGatewayFunc): void {
|
||||
scheme = scheme.toLowerCase();
|
||||
if (scheme === "http" || scheme === "https") {
|
||||
@ -505,6 +607,9 @@ export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
Gateways[scheme] = func;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom function for fetching HTTP and HTTPS requests.
|
||||
*/
|
||||
static registerGetUrl(getUrl: FetchGetUrlFunc): void {
|
||||
if (locked) { throw new Error("gateways locked"); }
|
||||
getUrlFunc = getUrl;
|
||||
@ -521,6 +626,9 @@ interface ThrottleError extends Error {
|
||||
throttle: true;
|
||||
};
|
||||
|
||||
/**
|
||||
* The response for a FetchREquest.
|
||||
*/
|
||||
export class FetchResponse implements Iterable<[ key: string, value: string ]> {
|
||||
#statusCode: number;
|
||||
#statusMessage: string;
|
||||
@ -534,12 +642,33 @@ export class FetchResponse implements Iterable<[ key: string, value: string ]> {
|
||||
return `<Response status=${ this.statusCode } body=${ this.#body ? hexlify(this.#body): "null" }>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The response status code.
|
||||
*/
|
||||
get statusCode(): number { return this.#statusCode; }
|
||||
|
||||
/**
|
||||
* The response status message.
|
||||
*/
|
||||
get statusMessage(): string { return this.#statusMessage; }
|
||||
|
||||
/**
|
||||
* The response headers.
|
||||
*/
|
||||
get headers(): Record<string, string> { return this.#headers; }
|
||||
|
||||
/**
|
||||
* The response body.
|
||||
*/
|
||||
get body(): null | Readonly<Uint8Array> {
|
||||
return (this.#body == null) ? null: new Uint8Array(this.#body);
|
||||
}
|
||||
|
||||
/**
|
||||
* The response body as a UTF-8 encoded string.
|
||||
*
|
||||
* An error is thrown if the body is invalid UTF-8 data.
|
||||
*/
|
||||
get bodyText(): string {
|
||||
try {
|
||||
return (this.#body == null) ? "": toUtf8String(this.#body);
|
||||
@ -549,6 +678,12 @@ export class FetchResponse implements Iterable<[ key: string, value: string ]> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The response body, decoded as JSON.
|
||||
*
|
||||
* An error is thrown if the body is invalid JSON-encoded data.
|
||||
*/
|
||||
get bodyJson(): any {
|
||||
try {
|
||||
return JSON.parse(this.bodyText);
|
||||
@ -589,6 +724,11 @@ export class FetchResponse implements Iterable<[ key: string, value: string ]> {
|
||||
this.#error = { message: "" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Response with matching headers and body, but with
|
||||
* an error status code (i.e. 599) and %%message%% with an
|
||||
* optional %%error%%.
|
||||
*/
|
||||
makeServerError(message?: string, error?: Error): FetchResponse {
|
||||
let statusMessage: string;
|
||||
if (!message) {
|
||||
@ -603,6 +743,10 @@ export class FetchResponse implements Iterable<[ key: string, value: string ]> {
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* If called within the [[processFunc]], causes the request to
|
||||
* retry as if throttled for %%stall%% milliseconds.
|
||||
*/
|
||||
throwThrottleError(message?: string, stall?: number): never {
|
||||
if (stall == null) {
|
||||
stall = -1;
|
||||
@ -617,20 +761,35 @@ export class FetchResponse implements Iterable<[ key: string, value: string ]> {
|
||||
throw error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header value for %%key%%.
|
||||
*/
|
||||
getHeader(key: string): string {
|
||||
return this.headers[key.toLowerCase()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true of the response has a body.
|
||||
*/
|
||||
hasBody(): this is FetchResponseWithBody {
|
||||
return (this.#body != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The request made for this response.
|
||||
*/
|
||||
get request(): null | FetchRequest { return this.#request; }
|
||||
|
||||
/**
|
||||
* Returns true if this response was a success statuscode.
|
||||
*/
|
||||
ok(): boolean {
|
||||
return (this.#error.message === "" && this.statusCode >= 200 && this.statusCode < 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a ``SERVER_ERROR`` if this response is not ok.
|
||||
*/
|
||||
assertOk(): void {
|
||||
if (this.ok()) { return; }
|
||||
let { message, error } = this.#error;
|
||||
|
@ -3,8 +3,14 @@ import { throwArgumentError } from "./errors.js";
|
||||
|
||||
import type { BytesLike } from "./data.js";
|
||||
|
||||
|
||||
/**
|
||||
* Any type that can be used where a numeric value is needed.
|
||||
*/
|
||||
export type Numeric = number | bigint;
|
||||
|
||||
/**
|
||||
* Any type that can be used where a big number is needed.
|
||||
*/
|
||||
export type BigNumberish = string | Numeric;
|
||||
|
||||
|
||||
@ -54,6 +60,10 @@ export function mask(_value: BigNumberish, _bits: Numeric): bigint {
|
||||
return value & ((BN_1 << bits) - BN_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a [[BigInt]] from %%value%%. If it is an invalid value for
|
||||
* a BigInt, then an ArgumentError will be thrown for %%name%%.
|
||||
*/
|
||||
export function getBigInt(value: BigNumberish, name?: string): bigint {
|
||||
switch (typeof(value)) {
|
||||
case "bigint": return value;
|
||||
@ -75,11 +85,12 @@ export function getBigInt(value: BigNumberish, name?: string): bigint {
|
||||
}
|
||||
|
||||
|
||||
const Nibbles = "0123456789abcdef";
|
||||
|
||||
/*
|
||||
* Converts %%value%% to a BigInt. If %%value%% is a Uint8Array, it
|
||||
* is treated as Big Endian data.
|
||||
*/
|
||||
const Nibbles = "0123456789abcdef";
|
||||
export function toBigInt(value: BigNumberish | Uint8Array): bigint {
|
||||
if (value instanceof Uint8Array) {
|
||||
let result = "0x0";
|
||||
@ -93,6 +104,10 @@ export function toBigInt(value: BigNumberish | Uint8Array): bigint {
|
||||
return getBigInt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a //number// from %%value%%. If it is an invalid value for
|
||||
* a //number//, then an ArgumentError will be thrown for %%name%%.
|
||||
*/
|
||||
export function getNumber(value: BigNumberish, name?: string): number {
|
||||
switch (typeof(value)) {
|
||||
case "bigint":
|
||||
@ -130,7 +145,6 @@ export function toNumber(value: BigNumberish | Uint8Array): number {
|
||||
* Converts %%value%% to a Big Endian hexstring, optionally padded to
|
||||
* %%width%% bytes.
|
||||
*/
|
||||
// Converts value to hex, optionally padding on the left to width bytes
|
||||
export function toHex(_value: BigNumberish, _width?: Numeric): string {
|
||||
const value = getBigInt(_value, "value");
|
||||
if (value < 0) { throw new Error("cannot convert negative value to hex"); }
|
||||
@ -173,6 +187,13 @@ export function toArray(_value: BigNumberish): Uint8Array {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [[HexString]] for %%value%% safe to use as a //Quantity//.
|
||||
*
|
||||
* A //Quantity// does not have and leading 0 values unless the value is
|
||||
* the literal value `0x0`. This is most commonly used for JSSON-RPC
|
||||
* numeric values.
|
||||
*/
|
||||
export function toQuantity(value: BytesLike | BigNumberish): string {
|
||||
let result = hexlify(isBytesLike(value) ? value: toArray(value)).substring(2);
|
||||
while (result.substring(0, 1) === "0") { result = result.substring(1); }
|
||||
|
@ -98,6 +98,9 @@ function _decode(data: Uint8Array, offset: number): { consumed: number, result:
|
||||
return { consumed: 1, result: hexlifyByte(data[offset]) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes %%data%% into the structured data it represents.
|
||||
*/
|
||||
export function decodeRlp(_data: BytesLike): RlpStructuredData {
|
||||
const data = getBytes(_data, "data");
|
||||
const decoded = _decode(data, 0);
|
||||
|
@ -51,6 +51,9 @@ function _encode(object: Array<any> | string): Array<number> {
|
||||
|
||||
const nibbles = "0123456789abcdef";
|
||||
|
||||
/**
|
||||
* Encodes %%object%% as an RLP-encoded [[HexDataString]].
|
||||
*/
|
||||
export function encodeRlp(object: RlpStructuredData): string {
|
||||
let result = "0x";
|
||||
for (const v of _encode(object)) {
|
||||
|
@ -1,4 +1,7 @@
|
||||
|
||||
/**
|
||||
* An RLP-encoded structure.
|
||||
*/
|
||||
export type RlpStructuredData = string | Array<RlpStructuredData>;
|
||||
|
||||
export { decodeRlp } from "./rlp-decode.js";
|
||||
|
Loading…
Reference in New Issue
Block a user