mirror of
https://github.com/pezkuwichain/pezkuwi-api.git
synced 2026-10-24 04:25:05 +00:00
- @pezkuwi/dev, dev-test, dev-ts ^0.86.3; disable directives renamed for the new rule names, or removed where the rule no longer reports - rethrown errors carry the original as `cause` (13 sites) - optional chains where a guard expression tested the same value - Init: the registry cache settles with the creator's own promise, dropping the entry in finally(), instead of re-wrapping it - Combinator: a typed call instead of `Function` - checkTypes.manual: synchronous checks run outside Promise.all - sortValues, createTypeUnsafe: initial values that were never read - typegen util: load docs.js for its side effect (it registers the `docs` Handlebars partial) now that its types are re-exported type-only - MockProvider keeps its Header assertion: the package's own build does not load api-augment, so createType returns a plain Codec there - Decorate keeps `delete queue[i]`: the hole marks a fired queue and later indexes must not shift
215 lines
6.6 KiB
TypeScript
215 lines
6.6 KiB
TypeScript
// Copyright 2017-2026 @pezkuwi/api authors & contributors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import type { ApiOptions, UnsubscribePromise } from '../types/index.js';
|
|
import type { CombinatorCallback, CombinatorFunction } from './Combinator.js';
|
|
|
|
import { noop, objectSpread } from '@pezkuwi/util';
|
|
|
|
import { ApiBase } from '../base/index.js';
|
|
import { Combinator } from './Combinator.js';
|
|
import { promiseTracker, toPromiseMethod } from './decorateMethod.js';
|
|
|
|
/**
|
|
* # @pezkuwi/api/promise
|
|
*
|
|
* ## Overview
|
|
*
|
|
* @name ApiPromise
|
|
* @description
|
|
* ApiPromise is a standard JavaScript wrapper around the RPC and interfaces on the Pezkuwi network. As a full Promise-based, all interface calls return Promises, including the static `.create(...)`. Subscription calls utilise `(value) => {}` callbacks to pass through the latest values.
|
|
*
|
|
* The API is well suited to real-time applications where either the single-shot state is needed or use is to be made of the subscription-based features of Pezkuwi (and Bizinikiwi) clients.
|
|
*
|
|
* @see [[ApiRx]]
|
|
*
|
|
* ## Usage
|
|
*
|
|
* Making rpc calls -
|
|
* <BR>
|
|
*
|
|
* ```javascript
|
|
* import ApiPromise from '@pezkuwi/api/promise';
|
|
*
|
|
* // initialise via static create
|
|
* const api = await ApiPromise.create();
|
|
*
|
|
* // make a subscription to the network head
|
|
* api.rpc.chain.subscribeNewHeads((header) => {
|
|
* console.log(`Chain is at #${header.number}`);
|
|
* });
|
|
* ```
|
|
* <BR>
|
|
*
|
|
* Subscribing to chain state -
|
|
* <BR>
|
|
*
|
|
* ```javascript
|
|
* import { ApiPromise, WsProvider } from '@pezkuwi/api';
|
|
*
|
|
* // initialise a provider with a specific endpoint
|
|
* const provider = new WsProvider('wss://example.com:9944')
|
|
*
|
|
* // initialise via isReady & new with specific provider
|
|
* const api = await new ApiPromise({ provider }).isReady;
|
|
*
|
|
* // retrieve the block target time
|
|
* const blockPeriod = await api.query.timestamp.blockPeriod().toNumber();
|
|
* let last = 0;
|
|
*
|
|
* // subscribe to the current block timestamp, updates automatically (callback provided)
|
|
* api.query.timestamp.now((timestamp) => {
|
|
* const elapsed = last
|
|
* ? `, ${timestamp.toNumber() - last}s since last`
|
|
* : '';
|
|
*
|
|
* last = timestamp.toNumber();
|
|
* console.log(`timestamp ${timestamp}${elapsed} (${blockPeriod}s target)`);
|
|
* });
|
|
* ```
|
|
* <BR>
|
|
*
|
|
* Submitting a transaction -
|
|
* <BR>
|
|
*
|
|
* ```javascript
|
|
* import ApiPromise from '@pezkuwi/api/promise';
|
|
*
|
|
* ApiPromise.create().then((api) => {
|
|
* const [nonce] = await api.query.system.account(keyring.alice.address);
|
|
*
|
|
* api.tx.balances
|
|
* // create transfer
|
|
* transfer(keyring.bob.address, 12345)
|
|
* // sign the transcation
|
|
* .sign(keyring.alice, { nonce })
|
|
* // send the transaction (optional status callback)
|
|
* .send((status) => {
|
|
* console.log(`current status ${status.type}`);
|
|
* })
|
|
* // retrieve the submitted extrinsic hash
|
|
* .then((hash) => {
|
|
* console.log(`submitted with hash ${hash}`);
|
|
* });
|
|
* });
|
|
* ```
|
|
*/
|
|
export class ApiPromise extends ApiBase<'promise'> {
|
|
#isReadyPromise: Promise<ApiPromise>;
|
|
#isReadyOrErrorPromise: Promise<ApiPromise>;
|
|
|
|
/**
|
|
* @description Creates an instance of the ApiPromise class
|
|
* @param options Options to create an instance. This can be either [[ApiOptions]] or
|
|
* an [[WsProvider]].
|
|
* @example
|
|
* <BR>
|
|
*
|
|
* ```javascript
|
|
* import Api from '@pezkuwi/api/promise';
|
|
*
|
|
* new Api().isReady.then((api) => {
|
|
* api.rpc.subscribeNewHeads((header) => {
|
|
* console.log(`new block #${header.number.toNumber()}`);
|
|
* });
|
|
* });
|
|
* ```
|
|
*/
|
|
constructor (options?: ApiOptions) {
|
|
super(options, 'promise', toPromiseMethod);
|
|
|
|
this.#isReadyPromise = new Promise((resolve): void => {
|
|
super.once('ready', () => resolve(this));
|
|
});
|
|
|
|
this.#isReadyOrErrorPromise = new Promise((resolve, reject): void => {
|
|
const tracker = promiseTracker(resolve, reject);
|
|
|
|
super.once('ready', () => tracker.resolve(this));
|
|
super.once('error', (error: Error) => tracker.reject(error));
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @description Creates an ApiPromise instance using the supplied provider. Returns an Promise containing the actual Api instance.
|
|
* @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or a
|
|
* provider (see the constructor arguments)
|
|
* @example
|
|
* <BR>
|
|
*
|
|
* ```javascript
|
|
* import Api from '@pezkuwi/api/promise';
|
|
*
|
|
* Api.create().then(async (api) => {
|
|
* const timestamp = await api.query.timestamp.now();
|
|
*
|
|
* console.log(`lastest block timestamp ${timestamp}`);
|
|
* });
|
|
* ```
|
|
*/
|
|
public static create (options?: ApiOptions): Promise<ApiPromise> {
|
|
const instance = new ApiPromise(options);
|
|
|
|
if (options?.throwOnConnect) {
|
|
return instance.isReadyOrError;
|
|
}
|
|
|
|
// Swallow any rejections on isReadyOrError
|
|
// (in Node 15.x this creates issues, when not being looked at)
|
|
instance.isReadyOrError.catch(noop);
|
|
|
|
return instance.isReady;
|
|
}
|
|
|
|
/**
|
|
* @description Promise that resolves the first time we are connected and loaded
|
|
*/
|
|
public get isReady (): Promise<ApiPromise> {
|
|
return this.#isReadyPromise;
|
|
}
|
|
|
|
/**
|
|
* @description Promise that resolves if we can connect, or reject if there is an error
|
|
*/
|
|
public get isReadyOrError (): Promise<ApiPromise> {
|
|
return this.#isReadyOrErrorPromise;
|
|
}
|
|
|
|
/**
|
|
* @description Returns a clone of this ApiPromise instance (new underlying provider connection)
|
|
*/
|
|
public clone (): ApiPromise {
|
|
return new ApiPromise(
|
|
objectSpread({}, this._options, { source: this })
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @description Creates a combinator that can be used to combine the latest results from multiple subscriptions
|
|
* @param fns An array of function to combine, each in the form of `(cb: (value: void)) => void`
|
|
* @param callback A callback that will return an Array of all the values this combinator has been applied to
|
|
* @example
|
|
* <BR>
|
|
*
|
|
* ```javascript
|
|
* const address = '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFacT7';
|
|
*
|
|
* // combines values from balance & nonce as it updates
|
|
* api.combineLatest([
|
|
* api.rpc.chain.subscribeNewHeads,
|
|
* (cb) => api.query.system.account(address, cb)
|
|
* ], ([head, [balance, nonce]]) => {
|
|
* console.log(`#${head.number}: You have ${balance.free} units, with ${nonce} transactions sent`);
|
|
* });
|
|
* ```
|
|
*/
|
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
public async combineLatest <T extends any[] = any[]> (fns: (CombinatorFunction | [CombinatorFunction, ...any[]])[], callback: CombinatorCallback<T>): UnsubscribePromise {
|
|
const combinator = new Combinator(fns, callback);
|
|
|
|
return (): void => {
|
|
combinator.unsubscribe();
|
|
};
|
|
}
|
|
}
|