Add typings to messages (#130)
* Add typings to messages * Fix eslint (after merge) * Fix TypeScript error * Lint * Consistent naming * Better promise error handling * Constraint handle typings as much as tsc will allow * Revert to empty Props interfaces * SeedLengths type * Add type to transport subscription message * nit * Typecheck passing callback to sendMessage when none expected * Type subscription cb and replace KeyringJson w/ InjectedAccount Use InjectedAccount for `accounts.list`, `accounts.subscribe`
This commit is contained in:
parent
25026b8ec4
commit
251417b50e
@ -66,12 +66,12 @@ When there is more than one extension, each will populate an entry via the injec
|
||||
```js
|
||||
interface Injected {
|
||||
// the interface for Accounts, as detailed below
|
||||
read-only accounts: Accounts;
|
||||
readonly accounts: Accounts;
|
||||
// the standard Signer interface for the API, as detailed below
|
||||
read-only signer: Signer;
|
||||
readonly signer: Signer;
|
||||
// not injected as of yet, subscribable provider for polkadot-js API injection,
|
||||
// this can be passed to the API itself upon construction in the dapp
|
||||
// read-only provider: Provider
|
||||
// readonly provider?: Provider
|
||||
}
|
||||
|
||||
interface Account = {
|
||||
|
||||
@ -11,13 +11,13 @@ export type Unsubcall = () => void;
|
||||
|
||||
export interface InjectedAccount {
|
||||
address: string;
|
||||
name: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface InjectedAccountWithMeta {
|
||||
address: string;
|
||||
meta: {
|
||||
name: string;
|
||||
name?: string;
|
||||
source: string;
|
||||
};
|
||||
}
|
||||
@ -27,7 +27,6 @@ export interface InjectedAccounts {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
subscribe: (cb: (accounts: InjectedAccount[]) => any) => Unsubcall;
|
||||
}
|
||||
|
||||
export interface InjectedExtensionInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { MessageAuthorize } from '@polkadot/extension/background/types';
|
||||
import { RequestAuthorizeTab } from '@polkadot/extension/background/types';
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import styled from 'styled-components';
|
||||
@ -14,7 +14,7 @@ interface Props {
|
||||
authId: string;
|
||||
className?: string;
|
||||
isFirst: boolean;
|
||||
request: MessageAuthorize;
|
||||
request: RequestAuthorizeTab;
|
||||
url: string;
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import { ActionContext, Address, Button, Header, Loading, TextArea } from '../co
|
||||
import { createAccount, createSeed } from '../messaging';
|
||||
import { Back, Name, Password } from '../partials';
|
||||
|
||||
type Props = {};
|
||||
type Props = {}
|
||||
|
||||
export default function Create (): React.ReactElement<Props> {
|
||||
const onAction = useContext(ActionContext);
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { MessageExtrinsicSign } from '@polkadot/extension/background/types';
|
||||
import { RequestExtrinsicSign } from '@polkadot/extension/background/types';
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import { createType } from '@polkadot/types';
|
||||
@ -14,7 +14,7 @@ import Unlock from './Unlock';
|
||||
|
||||
interface Props {
|
||||
isFirst: boolean;
|
||||
request: MessageExtrinsicSign;
|
||||
request: RequestExtrinsicSign;
|
||||
signId: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AuthorizeRequest, SigningRequest } from '@polkadot/extension/background/types';
|
||||
import { KeyringJson } from '@polkadot/ui-keyring/types';
|
||||
import { InjectedAccount } from '@polkadot/extension-inject/types';
|
||||
import { Prefix } from '@polkadot/util-crypto/address/types';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
@ -31,7 +31,7 @@ const { prefix } = settings.get();
|
||||
setAddressPrefix((prefix === -1 ? 42 : prefix) as Prefix);
|
||||
|
||||
export default function Popup (): React.ReactElement<{}> {
|
||||
const [accounts, setAccounts] = useState<null | KeyringJson[]>(null);
|
||||
const [accounts, setAccounts] = useState<null | InjectedAccount[]>(null);
|
||||
const [authRequests, setAuthRequests] = useState<null | AuthorizeRequest[]>(null);
|
||||
const [signRequests, setSignRequests] = useState<null | SigningRequest[]>(null);
|
||||
const [isWelcomeDone, setWelcomeDone] = useState(false);
|
||||
|
||||
@ -2,17 +2,17 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { KeyringJson } from '@polkadot/ui-keyring/types';
|
||||
import { InjectedAccount } from '@polkadot/extension-inject/types';
|
||||
import { Prefix } from '@polkadot/util-crypto/address/types';
|
||||
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import Identicon from '@polkadot/react-identicon';
|
||||
import settings from '@polkadot/ui-settings';
|
||||
import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';
|
||||
|
||||
import IconBox from './IconBox';
|
||||
import { AccountContext } from './contexts';
|
||||
import { decodeAddress, encodeAddress } from '@polkadot/util-crypto';
|
||||
|
||||
interface Props {
|
||||
address?: string | null;
|
||||
@ -24,7 +24,7 @@ interface Props {
|
||||
|
||||
function Address ({ address, children, className, name, theme = 'polkadot' }: Props): React.ReactElement<Props> {
|
||||
const accounts = useContext(AccountContext);
|
||||
const [account, setAccount] = useState<KeyringJson | null>(null);
|
||||
const [account, setAccount] = useState<InjectedAccount | null>(null);
|
||||
const [formatted, setFormatted] = useState<string | null>(null);
|
||||
|
||||
useEffect((): void => {
|
||||
@ -58,7 +58,7 @@ function Address ({ address, children, className, name, theme = 'polkadot' }: Pr
|
||||
}
|
||||
intro={
|
||||
<>
|
||||
<div className='name'>{name || (account && account.meta.name) || '<unknown>'}</div>
|
||||
<div className='name'>{name || (account && account.name) || '<unknown>'}</div>
|
||||
<div className='address'>{formatted || '<unknown>'}</div>
|
||||
</>
|
||||
}
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AuthorizeRequest, SigningRequest } from '@polkadot/extension/background/types';
|
||||
import { KeyringJson } from '@polkadot/ui-keyring/types';
|
||||
import { InjectedAccount } from '@polkadot/extension-inject/types';
|
||||
|
||||
export type OmitProps<T, K> = Pick<T, Exclude<keyof T, K>>;
|
||||
export type SubtractProps<T, K> = OmitProps<T, keyof K>;
|
||||
|
||||
export type AccountsFromCtx = KeyringJson[];
|
||||
export type AccountsFromCtx = InjectedAccount[];
|
||||
export type OnActionFromCtx = (to?: string) => void;
|
||||
export type AuthRequestsFromCtx = AuthorizeRequest[];
|
||||
export type SignRequestsFromCtx = SigningRequest[];
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AuthorizeRequest, MessageTypes, SigningRequest } from '@polkadot/extension/background/types';
|
||||
import { KeyringJson } from '@polkadot/ui-keyring/types';
|
||||
import { AuthorizeRequest, SigningRequest, RequestTypes, MessageTypes, ResponseTypes, SeedLengths, SubscriptionMessageTypes, MessageTypesWithNullRequest, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions } from '@polkadot/extension/background/types';
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
import extension from 'extensionizer';
|
||||
import { PORT_POPUP } from '@polkadot/extension/defaults';
|
||||
import { InjectedAccount } from '@polkadot/extension-inject/types';
|
||||
|
||||
interface Handler {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@ -45,14 +45,17 @@ port.onMessage.addListener((data): void => {
|
||||
}
|
||||
});
|
||||
|
||||
function sendMessage<TMessageType extends MessageTypesWithNullRequest>(message: TMessageType): Promise<ResponseTypes[TMessageType]>;
|
||||
function sendMessage<TMessageType extends MessageTypesWithNoSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType]): Promise<ResponseTypes[TMessageType]>;
|
||||
function sendMessage<TMessageType extends MessageTypesWithSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise<ResponseTypes[TMessageType]>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function sendMessage (message: MessageTypes, request: any = {}, subscriber?: (data: any) => void): Promise<any> {
|
||||
function sendMessage<TMessageType extends MessageTypes> (message: TMessageType, request?: RequestTypes[TMessageType], subscriber?: (data: any) => void): Promise<ResponseTypes[TMessageType]> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
const id = `${Date.now()}.${++idCounter}`;
|
||||
|
||||
handlers[id] = { resolve, reject, subscriber };
|
||||
|
||||
port.postMessage({ id, message, request });
|
||||
port.postMessage({ id, message, request: request || {} });
|
||||
});
|
||||
}
|
||||
|
||||
@ -64,7 +67,7 @@ export async function forgetAccount (address: string): Promise<boolean> {
|
||||
return sendMessage('accounts.forget', { address });
|
||||
}
|
||||
|
||||
export async function getAccounts (): Promise<KeyringJson[]> {
|
||||
export async function getAccounts (): Promise<InjectedAccount[]> {
|
||||
return sendMessage('accounts.list');
|
||||
}
|
||||
|
||||
@ -96,20 +99,20 @@ export async function createAccount (name: string, password: string, suri: strin
|
||||
return sendMessage('accounts.create', { name, password, suri, type });
|
||||
}
|
||||
|
||||
export async function createSeed (length?: number, type?: KeypairType): Promise<{ address: string; seed: string }> {
|
||||
export async function createSeed (length?: SeedLengths, type?: KeypairType): Promise<{ address: string; seed: string }> {
|
||||
return sendMessage('seed.create', { length, type });
|
||||
}
|
||||
|
||||
export async function subscribeAccounts (cb: (accounts: KeyringJson[]) => void): Promise<boolean> {
|
||||
return sendMessage('accounts.subscribe', {}, cb);
|
||||
export async function subscribeAccounts (cb: (accounts: InjectedAccount[]) => void): Promise<boolean> {
|
||||
return sendMessage('accounts.subscribe', null, cb);
|
||||
}
|
||||
|
||||
export async function subscribeAuthorize (cb: (accounts: AuthorizeRequest[]) => void): Promise<boolean> {
|
||||
return sendMessage('authorize.subscribe', {}, cb);
|
||||
return sendMessage('authorize.subscribe', null, cb);
|
||||
}
|
||||
|
||||
export async function subscribeSigning (cb: (accounts: SigningRequest[]) => void): Promise<boolean> {
|
||||
return sendMessage('signing.subscribe', {}, cb);
|
||||
return sendMessage('signing.subscribe', null, cb);
|
||||
}
|
||||
|
||||
export async function validateSeed (suri: string, type?: KeypairType): Promise<{ address: string; suri: string }> {
|
||||
|
||||
@ -22,7 +22,7 @@ export default function Name ({ address, className, defaultValue, isFocussed, la
|
||||
const accounts = useContext(AccountContext);
|
||||
const [name, setName] = useState('');
|
||||
const account = accounts.find((account): boolean => account.address === address);
|
||||
const startValue = (account && account.meta.name) || defaultValue;
|
||||
const startValue = (account && account.name) || defaultValue;
|
||||
const isError = !name && startValue
|
||||
? false
|
||||
: (name.length < MIN_LENGTH);
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||
import { KeyringJson } from '@polkadot/ui-keyring/types';
|
||||
import { AuthorizeRequest, MessageTypes, MessageAccountCreate, MessageAccountEdit, MessageAuthorizeApprove, MessageAuthorizeReject, MessageExtrinsicSignApprove, MessageExtrinsicSignCancel, MessageSeedCreate, MessageSeedCreateResponse, MessageSeedValidate, MessageSeedValidateResponse, MessageAccountForget, SigningRequest } from '../types';
|
||||
import { AuthorizeRequest, RequestAccountCreate, RequestAccountEdit, RequestAuthorizeApprove, RequestAuthorizeReject, RequestSigningApprove, RequestSigningCancel, RequestSeedCreate, ResponseSeedCreate, RequestSeedValidate, ResponseSeedValidate, RequestAccountForget, SigningRequest, RequestTypes, ResponseTypes, MessageTypes } from '../types';
|
||||
import { InjectedAccount } from '@polkadot/extension-inject/types';
|
||||
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import accountsObservable from '@polkadot/ui-keyring/observable/accounts';
|
||||
@ -18,8 +18,8 @@ import { createSubscription, unsubscribe } from './subscriptions';
|
||||
const SEED_DEFAULT_LENGTH = 12;
|
||||
const SEED_LENGTHS = [12, 24];
|
||||
|
||||
function transformAccounts (accounts: SubjectInfo): KeyringJson[] {
|
||||
return Object.values(accounts).map(({ json }): KeyringJson => json);
|
||||
function transformAccounts (accounts: SubjectInfo): InjectedAccount[] {
|
||||
return Object.values(accounts).map(({ json }): InjectedAccount => ({ ...json, name: json.meta && json.meta.name }));
|
||||
}
|
||||
|
||||
export default class Extension {
|
||||
@ -29,13 +29,13 @@ export default class Extension {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
private accountsCreate ({ name, password, suri, type }: MessageAccountCreate): boolean {
|
||||
private accountsCreate ({ name, password, suri, type }: RequestAccountCreate): boolean {
|
||||
keyring.addUri(suri, password, { name }, type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private accountsEdit ({ address, name }: MessageAccountEdit): boolean {
|
||||
private accountsEdit ({ address, name }: RequestAccountEdit): boolean {
|
||||
const pair = keyring.getPair(address);
|
||||
|
||||
assert(pair, 'Unable to find pair');
|
||||
@ -45,19 +45,19 @@ export default class Extension {
|
||||
return true;
|
||||
}
|
||||
|
||||
private accountsForget ({ address }: MessageAccountForget): boolean {
|
||||
private accountsForget ({ address }: RequestAccountForget): boolean {
|
||||
keyring.forgetAccount(address);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private accountsList (): KeyringJson[] {
|
||||
private accountsList (): InjectedAccount[] {
|
||||
return transformAccounts(accountsObservable.subject.getValue());
|
||||
}
|
||||
|
||||
// FIXME This looks very much like what we have in Tabs
|
||||
private accountsSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
||||
const cb = createSubscription(id, port);
|
||||
const cb = createSubscription<'accounts.subscribe'>(id, port);
|
||||
const subscription = accountsObservable.subject.subscribe((accounts: SubjectInfo): void =>
|
||||
cb(transformAccounts(accounts))
|
||||
);
|
||||
@ -70,7 +70,7 @@ export default class Extension {
|
||||
return true;
|
||||
}
|
||||
|
||||
private authorizeApprove ({ id }: MessageAuthorizeApprove): boolean {
|
||||
private authorizeApprove ({ id }: RequestAuthorizeApprove): boolean {
|
||||
const queued = this.state.getAuthRequest(id);
|
||||
|
||||
assert(queued, 'Unable to find request');
|
||||
@ -82,7 +82,7 @@ export default class Extension {
|
||||
return true;
|
||||
}
|
||||
|
||||
private authorizeReject ({ id }: MessageAuthorizeReject): boolean {
|
||||
private authorizeReject ({ id }: RequestAuthorizeReject): boolean {
|
||||
const queued = this.state.getAuthRequest(id);
|
||||
|
||||
assert(queued, 'Unable to find request');
|
||||
@ -100,7 +100,7 @@ export default class Extension {
|
||||
|
||||
// FIXME This looks very much like what we have in accounts
|
||||
private authorizeSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
||||
const cb = createSubscription(id, port);
|
||||
const cb = createSubscription<'authorize.subscribe'>(id, port);
|
||||
const subscription = this.state.authSubject.subscribe((requests: AuthorizeRequest[]): void =>
|
||||
cb(requests)
|
||||
);
|
||||
@ -113,7 +113,7 @@ export default class Extension {
|
||||
return true;
|
||||
}
|
||||
|
||||
private seedCreate ({ length = SEED_DEFAULT_LENGTH, type }: MessageSeedCreate): MessageSeedCreateResponse {
|
||||
private seedCreate ({ length = SEED_DEFAULT_LENGTH, type }: RequestSeedCreate): ResponseSeedCreate {
|
||||
const seed = mnemonicGenerate(length);
|
||||
|
||||
return {
|
||||
@ -122,7 +122,7 @@ export default class Extension {
|
||||
};
|
||||
}
|
||||
|
||||
private seedValidate ({ suri, type }: MessageSeedValidate): MessageSeedValidateResponse {
|
||||
private seedValidate ({ suri, type }: RequestSeedValidate): ResponseSeedValidate {
|
||||
const { phrase } = keyExtractSuri(suri);
|
||||
|
||||
assert(SEED_LENGTHS.includes(phrase.split(' ').length), `Mnemonic needs to contain ${SEED_LENGTHS.join(', ')} words`);
|
||||
@ -134,7 +134,7 @@ export default class Extension {
|
||||
};
|
||||
}
|
||||
|
||||
private signingApprove ({ id, password }: MessageExtrinsicSignApprove): boolean {
|
||||
private signingApprove ({ id, password }: RequestSigningApprove): boolean {
|
||||
const queued = this.state.getSignRequest(id);
|
||||
|
||||
assert(queued, 'Unable to find request');
|
||||
@ -163,7 +163,7 @@ export default class Extension {
|
||||
return true;
|
||||
}
|
||||
|
||||
private signingCancel ({ id }: MessageExtrinsicSignCancel): boolean {
|
||||
private signingCancel ({ id }: RequestSigningCancel): boolean {
|
||||
const queued = this.state.getSignRequest(id);
|
||||
|
||||
assert(queued, 'Unable to find request');
|
||||
@ -181,7 +181,7 @@ export default class Extension {
|
||||
|
||||
// FIXME This looks very much like what we have in authorization
|
||||
private signingSubscribe (id: string, port: chrome.runtime.Port): boolean {
|
||||
const cb = createSubscription(id, port);
|
||||
const cb = createSubscription<'signing.subscribe'>(id, port);
|
||||
const subscription = this.state.signSubject.subscribe((requests: SigningRequest[]): void =>
|
||||
cb(requests)
|
||||
);
|
||||
@ -194,56 +194,57 @@ export default class Extension {
|
||||
return true;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/require-await
|
||||
public async handle (id: string, type: MessageTypes, request: any, port: chrome.runtime.Port): Promise<any> {
|
||||
switch (type) {
|
||||
case 'authorize.approve':
|
||||
return this.authorizeApprove(request);
|
||||
public handle<TMessageType extends MessageTypes> (id: string, type: TMessageType, request: RequestTypes[TMessageType], port: chrome.runtime.Port): Promise<ResponseTypes[keyof ResponseTypes]> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
switch (type) {
|
||||
case 'authorize.approve':
|
||||
return resolve(this.authorizeApprove(request as RequestAuthorizeApprove));
|
||||
|
||||
case 'authorize.reject':
|
||||
return this.authorizeReject(request);
|
||||
case 'authorize.reject':
|
||||
return resolve(this.authorizeReject(request as RequestAuthorizeApprove));
|
||||
|
||||
case 'authorize.requests':
|
||||
return this.authorizeRequests();
|
||||
case 'authorize.requests':
|
||||
return resolve(this.authorizeRequests());
|
||||
|
||||
case 'authorize.subscribe':
|
||||
return this.authorizeSubscribe(id, port);
|
||||
case 'authorize.subscribe':
|
||||
return resolve(this.authorizeSubscribe(id, port));
|
||||
|
||||
case 'accounts.create':
|
||||
return this.accountsCreate(request);
|
||||
case 'accounts.create':
|
||||
return resolve(this.accountsCreate(request as RequestAccountCreate));
|
||||
|
||||
case 'accounts.forget':
|
||||
return this.accountsForget(request);
|
||||
case 'accounts.forget':
|
||||
return resolve(this.accountsForget(request as RequestAccountForget));
|
||||
|
||||
case 'accounts.edit':
|
||||
return this.accountsEdit(request);
|
||||
case 'accounts.edit':
|
||||
return resolve(this.accountsEdit(request as RequestAccountEdit));
|
||||
|
||||
case 'accounts.list':
|
||||
return this.accountsList();
|
||||
case 'accounts.list':
|
||||
return resolve(this.accountsList());
|
||||
|
||||
case 'accounts.subscribe':
|
||||
return this.accountsSubscribe(id, port);
|
||||
case 'accounts.subscribe':
|
||||
return resolve(this.accountsSubscribe(id, port));
|
||||
|
||||
case 'seed.create':
|
||||
return this.seedCreate(request);
|
||||
case 'seed.create':
|
||||
return resolve(this.seedCreate(request as RequestSeedCreate));
|
||||
|
||||
case 'seed.validate':
|
||||
return this.seedValidate(request);
|
||||
case 'seed.validate':
|
||||
return resolve(this.seedValidate(request as RequestSeedValidate));
|
||||
|
||||
case 'signing.approve':
|
||||
return this.signingApprove(request);
|
||||
case 'signing.approve':
|
||||
return resolve(this.signingApprove(request as RequestSigningApprove));
|
||||
|
||||
case 'signing.cancel':
|
||||
return this.signingCancel(request);
|
||||
case 'signing.cancel':
|
||||
return resolve(this.signingCancel(request as RequestSigningCancel));
|
||||
|
||||
case 'signing.requests':
|
||||
return this.signingRequests();
|
||||
case 'signing.requests':
|
||||
return resolve(this.signingRequests());
|
||||
|
||||
case 'signing.subscribe':
|
||||
return this.signingSubscribe(id, port);
|
||||
case 'signing.subscribe':
|
||||
return resolve(this.signingSubscribe(id, port));
|
||||
|
||||
default:
|
||||
throw new Error(`Unable to handle message of type ${type}`);
|
||||
}
|
||||
default:
|
||||
return reject(new Error(`Unable to handle message of type ${type}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AuthorizeRequest, MessageAuthorize, MessageExtrinsicSign, MessageExtrinsicSignResponse, SigningRequest } from '../types';
|
||||
import { AuthorizeRequest, RequestAuthorizeTab, RequestExtrinsicSign, ResponseExtrinsicSign, SigningRequest } from '../types';
|
||||
|
||||
import extension from 'extensionizer';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
@ -11,7 +11,7 @@ import { assert } from '@polkadot/util';
|
||||
interface AuthRequest {
|
||||
id: string;
|
||||
idStr: string;
|
||||
request: MessageAuthorize;
|
||||
request: RequestAuthorizeTab;
|
||||
resolve: (result: boolean) => void;
|
||||
reject: (error: Error) => void;
|
||||
url: string;
|
||||
@ -27,8 +27,8 @@ type AuthUrls = Record<string, {
|
||||
|
||||
interface SignRequest {
|
||||
id: string;
|
||||
request: MessageExtrinsicSign;
|
||||
resolve: (result: MessageExtrinsicSignResponse) => void;
|
||||
request: RequestExtrinsicSign;
|
||||
resolve: (result: ResponseExtrinsicSign) => void;
|
||||
reject: (error: Error) => void;
|
||||
url: string;
|
||||
}
|
||||
@ -124,8 +124,8 @@ export default class State {
|
||||
};
|
||||
}
|
||||
|
||||
private signComplete = (id: string, fn: Function): (result: MessageExtrinsicSignResponse | Error) => void => {
|
||||
return (result: MessageExtrinsicSignResponse | Error): void => {
|
||||
private signComplete = (id: string, fn: Function): (result: ResponseExtrinsicSign | Error) => void => {
|
||||
return (result: ResponseExtrinsicSign | Error): void => {
|
||||
delete this._signRequests[id];
|
||||
this.updateIconSign(true);
|
||||
|
||||
@ -167,7 +167,7 @@ export default class State {
|
||||
this.updateIcon(shouldClose);
|
||||
}
|
||||
|
||||
public async authorizeUrl (url: string, request: MessageAuthorize): Promise<boolean> {
|
||||
public async authorizeUrl (url: string, request: RequestAuthorizeTab): Promise<boolean> {
|
||||
const idStr = this.stripUrl(url);
|
||||
|
||||
if (this._authUrls[idStr]) {
|
||||
@ -210,7 +210,7 @@ export default class State {
|
||||
return this._signRequests[id];
|
||||
}
|
||||
|
||||
public signQueue (url: string, request: MessageExtrinsicSign): Promise<MessageExtrinsicSignResponse> {
|
||||
public signQueue (url: string, request: RequestExtrinsicSign): Promise<ResponseExtrinsicSign> {
|
||||
const id = getId();
|
||||
|
||||
return new Promise((resolve, reject): void => {
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { InjectedAccount } from '@polkadot/extension-inject/types';
|
||||
import { RequestAuthorizeTab, RequestExtrinsicSign, ResponseExtrinsicSign, RequestTypes, ResponseTypes, MessageTypes } from '../types';
|
||||
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||
import { MessageTypes, MessageAuthorize, MessageExtrinsicSign, MessageExtrinsicSignResponse } from '../types';
|
||||
|
||||
import keyring from '@polkadot/ui-keyring';
|
||||
import accountsObservable from '@polkadot/ui-keyring/observable/accounts';
|
||||
@ -12,14 +13,8 @@ import { assert } from '@polkadot/util';
|
||||
import State from './State';
|
||||
import { createSubscription, unsubscribe } from './subscriptions';
|
||||
|
||||
interface Account {
|
||||
address: string;
|
||||
name?: string;
|
||||
}
|
||||
type Accounts = Account[];
|
||||
|
||||
function transformAccounts (accounts: SubjectInfo): Accounts {
|
||||
return Object.values(accounts).map(({ json: { address, meta: { name } } }): Account => ({
|
||||
function transformAccounts (accounts: SubjectInfo): InjectedAccount[] {
|
||||
return Object.values(accounts).map(({ json: { address, meta: { name } } }): InjectedAccount => ({
|
||||
address, name
|
||||
}));
|
||||
}
|
||||
@ -31,18 +26,18 @@ export default class Tabs {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
private authorize (url: string, request: MessageAuthorize): Promise<boolean> {
|
||||
private authorize (url: string, request: RequestAuthorizeTab): Promise<boolean> {
|
||||
return this.state.authorizeUrl(url, request);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
private accountsList (url: string): Accounts {
|
||||
private accountsList (url: string): InjectedAccount[] {
|
||||
return transformAccounts(accountsObservable.subject.getValue());
|
||||
}
|
||||
|
||||
// FIXME This looks very much like what we have in Extension
|
||||
private accountsSubscribe (url: string, id: string, port: chrome.runtime.Port): boolean {
|
||||
const cb = createSubscription(id, port);
|
||||
const cb = createSubscription<'accounts.subscribe'>(id, port);
|
||||
const subscription = accountsObservable.subject.subscribe((accounts: SubjectInfo): void =>
|
||||
cb(transformAccounts(accounts))
|
||||
);
|
||||
@ -55,7 +50,7 @@ export default class Tabs {
|
||||
return true;
|
||||
}
|
||||
|
||||
private extrinsicSign (url: string, request: MessageExtrinsicSign): Promise<MessageExtrinsicSignResponse> {
|
||||
private extrinsicSign (url: string, request: RequestExtrinsicSign): Promise<ResponseExtrinsicSign> {
|
||||
const { address } = request;
|
||||
const pair = keyring.getPair(address);
|
||||
|
||||
@ -64,15 +59,14 @@ export default class Tabs {
|
||||
return this.state.signQueue(url, request);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public async handle (id: string, type: MessageTypes, request: any, url: string, port: chrome.runtime.Port): Promise<any> {
|
||||
public async handle<TMessageType extends MessageTypes> (id: string, type: TMessageType, request: RequestTypes[TMessageType], url: string, port: chrome.runtime.Port): Promise<ResponseTypes[keyof ResponseTypes]> {
|
||||
if (type !== 'authorize.tab') {
|
||||
this.state.ensureUrlAuthorized(url);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'authorize.tab':
|
||||
return this.authorize(url, request);
|
||||
return this.authorize(url, request as RequestAuthorizeTab);
|
||||
|
||||
case 'accounts.list':
|
||||
return this.accountsList(url);
|
||||
@ -81,7 +75,7 @@ export default class Tabs {
|
||||
return this.accountsSubscribe(url, id, port);
|
||||
|
||||
case 'extrinsic.sign':
|
||||
return this.extrinsicSign(url, request);
|
||||
return this.extrinsicSign(url, request as RequestExtrinsicSign);
|
||||
|
||||
default:
|
||||
throw new Error(`Unable to handle message of type ${type}`);
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { MessageRequest } from '../types';
|
||||
import { TransportRequestMessage, MessageTypes } from '../types';
|
||||
|
||||
import { PORT_POPUP } from '../../defaults';
|
||||
import Extension from './Extension';
|
||||
@ -13,7 +13,7 @@ const state = new State();
|
||||
const extension = new Extension(state);
|
||||
const tabs = new Tabs(state);
|
||||
|
||||
export default function handler ({ id, message, request }: MessageRequest, port: chrome.runtime.Port): void {
|
||||
export default function handler<TMessageType extends MessageTypes> ({ id, message, request }: TransportRequestMessage<TMessageType>, port: chrome.runtime.Port): void {
|
||||
const isPopup = port.name === PORT_POPUP;
|
||||
const sender = port.sender as chrome.runtime.MessageSender;
|
||||
const from = isPopup
|
||||
|
||||
@ -2,13 +2,14 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { SubscriptionMessageTypes, MessageTypesWithSubscriptions } from '../types';
|
||||
|
||||
type Subscriptions = Record<string, chrome.runtime.Port>;
|
||||
|
||||
const subscriptions: Subscriptions = {};
|
||||
|
||||
// return a subscription callback, that will send the data to the caller via the port
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function createSubscription (id: string, port: chrome.runtime.Port): (data: any) => void {
|
||||
export function createSubscription<TMessageType extends MessageTypesWithSubscriptions> (id: string, port: chrome.runtime.Port): (data: SubscriptionMessageTypes[TMessageType]) => void {
|
||||
subscriptions[id] = port;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
@ -2,91 +2,171 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
import { InjectedAccount } from '@polkadot/extension-inject/types';
|
||||
import { SignerPayloadJSON } from '@polkadot/types/types';
|
||||
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
export type MessageTypes = 'authorize.approve' | 'authorize.reject' | 'authorize.requests' | 'authorize.subscribe' | 'authorize.tab' | 'accounts.create' | 'accounts.edit' | 'accounts.forget' | 'accounts.list' | 'accounts.subscribe' | 'extrinsic.sign' | 'seed.create' | 'seed.validate' | 'signing.approve' | 'signing.cancel' | 'signing.requests' | 'signing.subscribe';
|
||||
type KeysWithDefinedValues<T> = {
|
||||
[K in keyof T]: T[K] extends undefined ? never : K
|
||||
}[keyof T];
|
||||
|
||||
export type AuthorizeRequest = [string, MessageAuthorize, string];
|
||||
type NoUndefinedValues<T> = {
|
||||
[K in KeysWithDefinedValues<T>]: T[K]
|
||||
};
|
||||
|
||||
export type SigningRequest = [string, MessageExtrinsicSign, string];
|
||||
type IsNull<T, K extends keyof T> = { [K1 in Exclude<keyof T, K>]: T[K1] } & T[K] extends null ? K : never
|
||||
type NullKeys<T> = { [K in keyof T]: IsNull<T, K> }[keyof T]
|
||||
|
||||
export interface MessageAuthorize {
|
||||
export type SeedLengths = 12 | 24;
|
||||
|
||||
export type AuthorizeRequest = [string, RequestAuthorizeTab, string];
|
||||
export type SigningRequest = [string, RequestExtrinsicSign, string];
|
||||
|
||||
// [MessageType]: [RequestType, ResponseType, SubscriptionMessageType?]
|
||||
export interface RequestSignatures {
|
||||
'accounts.create': [RequestAccountCreate, boolean];
|
||||
'accounts.edit': [RequestAccountEdit, boolean];
|
||||
'accounts.forget': [RequestAccountForget, boolean];
|
||||
'accounts.list': [RequestAccountList, InjectedAccount[]];
|
||||
'accounts.subscribe': [RequestAccountSubscribe, boolean, InjectedAccount[]];
|
||||
'authorize.tab': [RequestAuthorizeTab, null];
|
||||
'authorize.approve': [RequestAuthorizeApprove, boolean];
|
||||
'authorize.reject': [RequestAuthorizeReject, boolean];
|
||||
'authorize.requests': [RequestAuthorizeRequests, AuthorizeRequest[]];
|
||||
'authorize.subscribe': [RequestAuthorizeSubscribe, boolean, AuthorizeRequest[]];
|
||||
'extrinsic.sign': [RequestExtrinsicSign, ResponseExtrinsicSign];
|
||||
'seed.create': [RequestSeedCreate, ResponseSeedCreate];
|
||||
'seed.validate': [RequestSeedValidate, ResponseSeedValidate];
|
||||
'signing.approve': [RequestSigningApprove, boolean];
|
||||
'signing.cancel': [RequestSigningCancel, boolean];
|
||||
'signing.requests': [RequestSigningRequests, SigningRequest[]];
|
||||
'signing.subscribe': [RequestSigningSubscribe, boolean, SigningRequest[]];
|
||||
}
|
||||
|
||||
export type MessageTypes = keyof RequestSignatures;
|
||||
|
||||
// Requests
|
||||
|
||||
export type RequestTypes = {
|
||||
[MessageType in keyof RequestSignatures]: RequestSignatures[MessageType][0]
|
||||
};
|
||||
|
||||
export type MessageTypesWithNullRequest = NullKeys<RequestTypes>
|
||||
|
||||
export interface TransportRequestMessage<TMessageType extends MessageTypes> {
|
||||
id: string;
|
||||
message: TMessageType;
|
||||
origin: 'page' | 'popup';
|
||||
request: RequestTypes[TMessageType];
|
||||
}
|
||||
|
||||
export interface RequestAuthorizeTab {
|
||||
origin: string;
|
||||
}
|
||||
|
||||
export interface MessageAuthorizeApprove {
|
||||
export interface RequestAuthorizeApprove {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface MessageAuthorizeReject {
|
||||
export interface RequestAuthorizeReject {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface MessageRequest {
|
||||
id: string;
|
||||
message: MessageTypes;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
request: any;
|
||||
}
|
||||
export type RequestAuthorizeRequests = null;
|
||||
|
||||
export interface MessageResponse {
|
||||
error?: string;
|
||||
id: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
response?: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
subscription?: any;
|
||||
}
|
||||
export type RequestAuthorizeSubscribe = null;
|
||||
|
||||
export interface MessageAccountCreate {
|
||||
export interface RequestAccountCreate {
|
||||
name: string;
|
||||
password: string;
|
||||
suri: string;
|
||||
type?: KeypairType;
|
||||
}
|
||||
|
||||
export interface MessageAccountEdit {
|
||||
export interface RequestAccountEdit {
|
||||
address: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface MessageAccountForget {
|
||||
export interface RequestAccountForget {
|
||||
address: string;
|
||||
}
|
||||
|
||||
export interface MessageExtrinsicSignApprove {
|
||||
export type RequestAccountList = null;
|
||||
|
||||
export type RequestAccountSubscribe = null;
|
||||
|
||||
export type RequestExtrinsicSign = SignerPayloadJSON;
|
||||
|
||||
export interface RequestSigningApprove {
|
||||
id: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface MessageExtrinsicSignCancel {
|
||||
export interface RequestSigningCancel {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export type MessageExtrinsicSign = SignerPayloadJSON;
|
||||
export type RequestSigningRequests = null;
|
||||
|
||||
export interface MessageExtrinsicSignResponse {
|
||||
export type RequestSigningSubscribe = null;
|
||||
|
||||
export interface RequestSeedCreate {
|
||||
length?: SeedLengths;
|
||||
type?: KeypairType;
|
||||
}
|
||||
|
||||
export interface RequestSeedValidate {
|
||||
suri: string;
|
||||
type?: KeypairType;
|
||||
}
|
||||
|
||||
// Responses
|
||||
|
||||
export type ResponseTypes = {
|
||||
[MessageType in keyof RequestSignatures]: RequestSignatures[MessageType][1]
|
||||
};
|
||||
|
||||
interface TransportResponseMessageSub<TMessageType extends MessageTypesWithSubscriptions> {
|
||||
error?: string;
|
||||
id: string;
|
||||
response?: ResponseTypes[TMessageType];
|
||||
subscription?: SubscriptionMessageTypes[TMessageType];
|
||||
}
|
||||
|
||||
interface TransportResponseMessageNoSub<TMessageType extends MessageTypesWithNoSubscriptions> {
|
||||
error?: string;
|
||||
id: string;
|
||||
response?: ResponseTypes[TMessageType];
|
||||
}
|
||||
|
||||
export type TransportResponseMessage<TMessageType extends MessageTypes> =
|
||||
TMessageType extends MessageTypesWithNoSubscriptions
|
||||
? TransportResponseMessageNoSub<TMessageType>
|
||||
: TMessageType extends MessageTypesWithSubscriptions
|
||||
? TransportResponseMessageSub<TMessageType>
|
||||
: never;
|
||||
|
||||
export interface ResponseExtrinsicSign {
|
||||
id: string;
|
||||
signature: string;
|
||||
}
|
||||
|
||||
export interface MessageSeedCreateResponse {
|
||||
export interface ResponseSeedCreate {
|
||||
address: string;
|
||||
seed: string;
|
||||
}
|
||||
|
||||
export interface MessageSeedCreate {
|
||||
length?: 12 | 24;
|
||||
type?: KeypairType;
|
||||
}
|
||||
|
||||
export interface MessageSeedValidate {
|
||||
suri: string;
|
||||
type?: KeypairType;
|
||||
}
|
||||
|
||||
export interface MessageSeedValidateResponse {
|
||||
export interface ResponseSeedValidate {
|
||||
address: string;
|
||||
suri: string;
|
||||
}
|
||||
|
||||
// Subscriptions
|
||||
|
||||
export type SubscriptionMessageTypes = NoUndefinedValues<{
|
||||
[MessageType in keyof RequestSignatures]: RequestSignatures[MessageType][2]
|
||||
}>;
|
||||
|
||||
export type MessageTypesWithSubscriptions = keyof SubscriptionMessageTypes;
|
||||
export type MessageTypesWithNoSubscriptions = Exclude<MessageTypes, keyof SubscriptionMessageTypes>
|
||||
|
||||
@ -2,13 +2,13 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Injected as InjectedInjected } from '@polkadot/extension-inject/types';
|
||||
import { Injected } from '@polkadot/extension-inject/types';
|
||||
import { SendRequest } from './types';
|
||||
|
||||
import Accounts from './Accounts';
|
||||
import Signer from './Signer';
|
||||
|
||||
export default class Injected implements InjectedInjected {
|
||||
export default class implements Injected {
|
||||
public readonly accounts: Accounts;
|
||||
|
||||
public readonly signer: Signer;
|
||||
|
||||
@ -2,11 +2,10 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { MessageTypes } from '../background/types';
|
||||
|
||||
import { injectExtension } from '@polkadot/extension-inject';
|
||||
|
||||
import Injected from './Injected';
|
||||
import { ResponseTypes, TransportRequestMessage, TransportResponseMessage, MessageTypes, RequestTypes, MessageTypesWithNullRequest, SubscriptionMessageTypes, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions } from '../background/types';
|
||||
|
||||
// when sending a message from the injector to the extension, we
|
||||
// - create an event - this we send to the loader
|
||||
@ -30,14 +29,24 @@ let idCounter = 0;
|
||||
|
||||
// a generic message sender that creates an event, returning a promise that will
|
||||
// resolve once the event is resolved (by the response listener just below this)
|
||||
function sendMessage<TMessageType extends MessageTypesWithNullRequest>(message: TMessageType): Promise<ResponseTypes[TMessageType]>;
|
||||
function sendMessage<TMessageType extends MessageTypesWithNoSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType]): Promise<ResponseTypes[TMessageType]>;
|
||||
function sendMessage<TMessageType extends MessageTypesWithSubscriptions> (message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise<ResponseTypes[TMessageType]>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function sendMessage (message: MessageTypes, request: any = null, subscriber?: (data: any) => void): Promise<any> {
|
||||
function sendMessage<TMessageType extends MessageTypes> (message: TMessageType, request?: RequestTypes[TMessageType], subscriber?: (data: any) => void): Promise<ResponseTypes[TMessageType]> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
const id = `${Date.now()}.${++idCounter}`;
|
||||
|
||||
handlers[id] = { resolve, reject, subscriber };
|
||||
|
||||
window.postMessage({ id, message, origin: 'page', request }, '*');
|
||||
const transportRequestMessage: TransportRequestMessage<TMessageType> = {
|
||||
id,
|
||||
message,
|
||||
origin: 'page',
|
||||
request: request || null as RequestTypes[TMessageType]
|
||||
};
|
||||
|
||||
window.postMessage(transportRequestMessage, '*');
|
||||
});
|
||||
}
|
||||
|
||||
@ -48,17 +57,12 @@ async function enable (origin: string): Promise<Injected> {
|
||||
return new Injected(sendMessage);
|
||||
}
|
||||
|
||||
// setup a response listener (events created by the loader for extension responses)
|
||||
window.addEventListener('message', ({ data, source }): void => {
|
||||
// only allow messages from our window, by the loader
|
||||
if (source !== window || data.origin !== 'content') {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function handleResponse<TMessageType extends MessageTypes> (data: TransportResponseMessage<TMessageType> & {subscription?: any}): void {
|
||||
const handler = handlers[data.id];
|
||||
|
||||
if (!handler) {
|
||||
console.error(`Uknown response: ${JSON.stringify(data)}`);
|
||||
console.error(`Unknown response: ${JSON.stringify(data)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -73,6 +77,20 @@ window.addEventListener('message', ({ data, source }): void => {
|
||||
} else {
|
||||
handler.resolve(data.response);
|
||||
}
|
||||
}
|
||||
|
||||
// setup a response listener (events created by the loader for extension responses)
|
||||
window.addEventListener('message', ({ data, source }): void => {
|
||||
// only allow messages from our window, by the loader
|
||||
if (source !== window || data.origin !== 'content') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.id) {
|
||||
handleResponse(data);
|
||||
} else {
|
||||
console.error('Missing id for response.');
|
||||
}
|
||||
});
|
||||
|
||||
injectExtension(enable, {
|
||||
|
||||
@ -2,9 +2,10 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { MessageTypes } from '../background/types';
|
||||
import { RequestTypes, ResponseTypes, SubscriptionMessageTypes, MessageTypesWithNullRequest, MessageTypesWithNoSubscriptions, MessageTypesWithSubscriptions } from '../background/types';
|
||||
|
||||
export interface SendRequest {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(message: MessageTypes, request?: any, subscriber?: (data: any) => any): Promise<any>;
|
||||
<TMessageType extends MessageTypesWithNullRequest>(message: TMessageType): Promise<ResponseTypes[TMessageType]>;
|
||||
<TMessageType extends MessageTypesWithNoSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType]): Promise<ResponseTypes[TMessageType]>;
|
||||
<TMessageType extends MessageTypesWithSubscriptions>(message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise<ResponseTypes[TMessageType]>;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user