Move to Ledger usage with hook (#4195)
* Move ledgerChains to apps-config * Swap Ledger capabilities to hook * Fix type usage * getLedger returns promise * Revert promise result * cleanup * use has ledger support (#4413) * use hasLedgerSupport * Bump common Co-authored-by: Jaco Greeff <jacogr@gmail.com> Co-authored-by: Thibaut Sardan <33178835+Tbaut@users.noreply.github.com>
This commit is contained in:
parent
3ba546d77d
commit
19d49f09be
@ -3,11 +3,12 @@
|
||||
|
||||
import type { SubmittableExtrinsic } from '@polkadot/api/types';
|
||||
import type { DeriveBalancesAll, DeriveDemocracyLock } from '@polkadot/api-derive/types';
|
||||
import type { Ledger } from '@polkadot/ledger';
|
||||
import type { ActionStatus } from '@polkadot/react-components/Status/types';
|
||||
import type { ThemeDef } from '@polkadot/react-components/types';
|
||||
import type { Option } from '@polkadot/types';
|
||||
import type { ProxyDefinition, RecoveryConfig } from '@polkadot/types/interfaces';
|
||||
import type { KeyringAddress } from '@polkadot/ui-keyring/types';
|
||||
import type { KeyringAddress, KeyringJson$Meta } from '@polkadot/ui-keyring/types';
|
||||
import type { Delegation } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
@ -15,9 +16,8 @@ import React, { useCallback, useContext, useEffect, useMemo, useState } from 're
|
||||
import styled, { ThemeContext } from 'styled-components';
|
||||
|
||||
import { ApiPromise } from '@polkadot/api';
|
||||
import { getLedger } from '@polkadot/react-api';
|
||||
import { AddressInfo, AddressMini, AddressSmall, Badge, Button, ChainLock, CryptoType, Forget, Icon, IdentityIcon, LinkExternal, Menu, Popup, StatusContext, Tags } from '@polkadot/react-components';
|
||||
import { useAccountInfo, useApi, useCall, useToggle } from '@polkadot/react-hooks';
|
||||
import { useAccountInfo, useApi, useCall, useLedger, useToggle } from '@polkadot/react-hooks';
|
||||
import { keyring } from '@polkadot/ui-keyring';
|
||||
import { BN_ZERO, formatBalance, formatNumber } from '@polkadot/util';
|
||||
|
||||
@ -74,6 +74,12 @@ function createClearDemocracyTx (api: ApiPromise, address: string, unlockableIds
|
||||
);
|
||||
}
|
||||
|
||||
async function showLedgerAddress (getLedger: () => Ledger, meta: KeyringJson$Meta): Promise<void> {
|
||||
const ledger = getLedger();
|
||||
|
||||
await ledger.getAddress(true, meta.accountOffset as number || 0, meta.addressOffset as number || 0);
|
||||
}
|
||||
|
||||
const transformRecovery = {
|
||||
transform: (opt: Option<RecoveryConfig>) => opt.unwrapOr(null)
|
||||
};
|
||||
@ -83,6 +89,7 @@ function Account ({ account: { address, meta }, className = '', delegation, filt
|
||||
const { theme } = useContext<ThemeDef>(ThemeContext);
|
||||
const { queueExtrinsic } = useContext(StatusContext);
|
||||
const api = useApi();
|
||||
const { getLedger } = useLedger();
|
||||
const bestNumber = useCall<BN>(api.api.derive.chain.bestNumber);
|
||||
const balancesAll = useCall<DeriveBalancesAll>(api.api.derive.balances.all, [address]);
|
||||
const democracyLocks = useCall<DeriveDemocracyLock[]>(api.api.derive.democracy?.locks, [address]);
|
||||
@ -191,13 +198,11 @@ function Account ({ account: { address, meta }, className = '', delegation, filt
|
||||
// TODO: we should check the hardwareType from metadata here as well,
|
||||
// for now we are always assuming hardwareType === 'ledger'
|
||||
(): void => {
|
||||
getLedger()
|
||||
.getAddress(true, meta.accountOffset as number || 0, meta.addressOffset as number || 0)
|
||||
.catch((error): void => {
|
||||
console.error(`ledger: ${(error as Error).message}`);
|
||||
});
|
||||
showLedgerAddress(getLedger, meta).catch((error): void => {
|
||||
console.error(`ledger: ${(error as Error).message}`);
|
||||
});
|
||||
},
|
||||
[meta]
|
||||
[getLedger, meta]
|
||||
);
|
||||
|
||||
if (!isVisible) {
|
||||
|
||||
@ -9,9 +9,8 @@ import BN from 'bn.js';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { isLedger } from '@polkadot/react-api';
|
||||
import { Button, Input, Table } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi, useCall, useFavorites, useIpfs, useLoadingDelay, useToggle } from '@polkadot/react-hooks';
|
||||
import { useAccounts, useApi, useCall, useFavorites, useIpfs, useLedger, useLoadingDelay, useToggle } from '@polkadot/react-hooks';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
import { BN_ZERO } from '@polkadot/util';
|
||||
|
||||
@ -49,6 +48,7 @@ function Overview ({ className = '', onStatusChange }: Props): React.ReactElemen
|
||||
const { api } = useApi();
|
||||
const { allAccounts, hasAccounts } = useAccounts();
|
||||
const { isIpfs } = useIpfs();
|
||||
const { isLedgerEnabled } = useLedger();
|
||||
const [isCreateOpen, toggleCreate] = useToggle();
|
||||
const [isImportOpen, toggleImport] = useToggle();
|
||||
const [isLedgerOpen, toggleLedger] = useToggle();
|
||||
@ -208,7 +208,7 @@ function Overview ({ className = '', onStatusChange }: Props): React.ReactElemen
|
||||
label={t<string>('Add via Qr')}
|
||||
onClick={toggleQr}
|
||||
/>
|
||||
{isLedger() && (
|
||||
{isLedgerEnabled && (
|
||||
<>
|
||||
<Button
|
||||
icon='question'
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
// Copyright 2017-2021 @polkadot/app-accounts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Ledger } from '@polkadot/ui-keyring';
|
||||
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
|
||||
import { getLedger } from '@polkadot/react-api';
|
||||
import { Button, Dropdown, MarkError, Modal } from '@polkadot/react-components';
|
||||
import { useLedger } from '@polkadot/react-hooks';
|
||||
import { keyring } from '@polkadot/ui-keyring';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
@ -22,21 +24,15 @@ interface Props {
|
||||
const AVAIL = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
|
||||
|
||||
// query the ledger for the address, adding it to the keyring
|
||||
async function queryLedger (accountOffset: number, addressOffset: number): Promise<void> {
|
||||
try {
|
||||
const ledger = getLedger();
|
||||
const { address } = await ledger.getAddress(false, accountOffset, addressOffset);
|
||||
async function queryLedger (getLedger: () => Ledger, accountOffset: number, addressOffset: number): Promise<void> {
|
||||
const { address } = await getLedger().getAddress(false, accountOffset, addressOffset);
|
||||
|
||||
keyring.addHardware(address, 'ledger', { accountOffset, addressOffset, name: `ledger ${accountOffset}/${addressOffset}` });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
keyring.addHardware(address, 'ledger', { accountOffset, addressOffset, name: `ledger ${accountOffset}/${addressOffset}` });
|
||||
}
|
||||
|
||||
function Ledger ({ className, onClose }: Props): React.ReactElement<Props> {
|
||||
function LedgerModal ({ className, onClose }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { getLedger } = useLedger();
|
||||
const [accIndex, setAccIndex] = useState(0);
|
||||
const [addIndex, setAddIndex] = useState(0);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
@ -57,14 +53,16 @@ function Ledger ({ className, onClose }: Props): React.ReactElement<Props> {
|
||||
setError(null);
|
||||
setIsBusy(true);
|
||||
|
||||
queryLedger(accIndex, addIndex)
|
||||
queryLedger(getLedger, accIndex, addIndex)
|
||||
.then(() => onClose())
|
||||
.catch((error): void => {
|
||||
console.error(error);
|
||||
|
||||
setIsBusy(false);
|
||||
setError(error);
|
||||
});
|
||||
},
|
||||
[accIndex, addIndex, onClose]
|
||||
[accIndex, addIndex, getLedger, onClose]
|
||||
);
|
||||
|
||||
return (
|
||||
@ -118,4 +116,4 @@ function Ledger ({ className, onClose }: Props): React.ReactElement<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Ledger);
|
||||
export default React.memo(LedgerModal);
|
||||
|
||||
@ -7,8 +7,8 @@ import type { SettingsStruct } from '@polkadot/ui-settings/types';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { createLanguages, createSs58 } from '@polkadot/apps-config';
|
||||
import { isLedgerCapable } from '@polkadot/react-api';
|
||||
import { Button, Dropdown } from '@polkadot/react-components';
|
||||
import { useLedger } from '@polkadot/react-hooks';
|
||||
import { settings } from '@polkadot/ui-settings';
|
||||
|
||||
import { useTranslation } from './translate';
|
||||
@ -22,6 +22,7 @@ const ledgerConnOptions = settings.availableLedgerConn;
|
||||
|
||||
function General ({ className = '' }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { isLedgerCapable } = useLedger();
|
||||
// tri-state: null = nothing changed, false = no reload, true = reload required
|
||||
const [changed, setChanged] = useState<boolean | null>(null);
|
||||
const [state, setSettings] = useState((): SettingsStruct => {
|
||||
@ -108,7 +109,7 @@ function General ({ className = '' }: Props): React.ReactElement<Props> {
|
||||
options={iconOptions}
|
||||
/>
|
||||
</div>
|
||||
{isLedgerCapable() && (
|
||||
{isLedgerCapable && (
|
||||
<div className='ui--row'>
|
||||
<Dropdown
|
||||
defaultValue={ledgerConn}
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
import Api, { api, DEFAULT_DECIMALS, DEFAULT_SS58 } from './Api';
|
||||
import ApiContext from './ApiContext';
|
||||
import { withApi, withCallDiv, withCalls, withMulti, withObservable } from './hoc';
|
||||
import { clearLedger, getLedger, isLedger, isLedgerCapable } from './ledger';
|
||||
|
||||
export {
|
||||
api,
|
||||
@ -16,9 +15,5 @@ export {
|
||||
withCalls,
|
||||
withCallDiv,
|
||||
withMulti,
|
||||
withObservable,
|
||||
clearLedger,
|
||||
getLedger,
|
||||
isLedger,
|
||||
isLedgerCapable
|
||||
withObservable
|
||||
};
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
// Copyright 2017-2021 @polkadot/react-api authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import networks from '@polkadot/networks';
|
||||
import { Ledger } from '@polkadot/ui-keyring';
|
||||
import uiSettings from '@polkadot/ui-settings';
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import { api } from './Api';
|
||||
|
||||
let ledger: Ledger | null = null;
|
||||
const ledgerChains = networks.filter((network) => network.hasLedgerSupport);
|
||||
|
||||
export function isLedgerCapable (): boolean {
|
||||
try {
|
||||
return (
|
||||
!!(window as unknown as { USB?: unknown }).USB &&
|
||||
!!api && ledgerChains.map(({ genesisHash }) => genesisHash[0]).includes(api.genesisHash.toHex())
|
||||
);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isLedger (): boolean {
|
||||
return isLedgerCapable() && uiSettings.ledgerConn !== 'none';
|
||||
}
|
||||
|
||||
export function clearLedger (): void {
|
||||
ledger = null;
|
||||
}
|
||||
|
||||
export function getLedger (): Ledger {
|
||||
if (!ledger) {
|
||||
const genesisHex = api.genesisHash.toHex();
|
||||
const def = api && ledgerChains.find(({ genesisHash }) => genesisHash[0] === genesisHex);
|
||||
|
||||
assert(def, `Unable to find supported chain for ${genesisHex}`);
|
||||
|
||||
ledger = new Ledger(uiSettings.ledgerConn as 'u2f', def.network);
|
||||
}
|
||||
|
||||
return ledger;
|
||||
}
|
||||
@ -20,6 +20,7 @@ export { useIncrement } from './useIncrement';
|
||||
export { calcInflation, useInflation } from './useInflation';
|
||||
export { useIpfs } from './useIpfs';
|
||||
export { useIsMountedRef } from './useIsMountedRef';
|
||||
export { useLedger } from './useLedger';
|
||||
export { useLoadingDelay } from './useLoadingDelay';
|
||||
export { useMembers } from './useMembers';
|
||||
export { useModal } from './useModal';
|
||||
|
||||
67
packages/react-hooks/src/useLedger.ts
Normal file
67
packages/react-hooks/src/useLedger.ts
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2017-2020 @polkadot/react-hooks authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ApiPromise } from '@polkadot/api';
|
||||
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import { Ledger } from '@polkadot/ledger';
|
||||
import networks from '@polkadot/networks';
|
||||
import uiSettings from '@polkadot/ui-settings';
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import { useApi } from './useApi';
|
||||
|
||||
interface StateBase {
|
||||
isLedgerCapable: boolean;
|
||||
isLedgerEnabled: boolean;
|
||||
}
|
||||
|
||||
interface State extends StateBase {
|
||||
getLedger: () => Ledger;
|
||||
}
|
||||
|
||||
const EMPTY_STATE: StateBase = {
|
||||
isLedgerCapable: false,
|
||||
isLedgerEnabled: false
|
||||
};
|
||||
|
||||
const hasWebUsb = !!(window as unknown as { USB?: unknown }).USB;
|
||||
let ledger: Ledger | null = null;
|
||||
const ledgerChains = networks.filter((network) => network.hasLedgerSupport);
|
||||
|
||||
function retrieveLedger (api: ApiPromise): Ledger {
|
||||
if (!ledger) {
|
||||
const genesisHex = api.genesisHash.toHex();
|
||||
const def = ledgerChains.find(({ genesisHash }) => genesisHash[0] === genesisHex);
|
||||
|
||||
assert(def, `Unable to find supported chain for ${genesisHex}`);
|
||||
|
||||
ledger = new Ledger(uiSettings.ledgerConn as 'u2f', def.network);
|
||||
}
|
||||
|
||||
return ledger;
|
||||
}
|
||||
|
||||
function getState (api: ApiPromise): StateBase {
|
||||
const isLedgerCapable = hasWebUsb && ledgerChains.map(({ genesisHash }) => genesisHash[0]).includes(api.genesisHash.toHex());
|
||||
|
||||
return {
|
||||
isLedgerCapable,
|
||||
isLedgerEnabled: isLedgerCapable && uiSettings.ledgerConn !== 'none'
|
||||
};
|
||||
}
|
||||
|
||||
export function useLedger (): State {
|
||||
const { api, isApiReady } = useApi();
|
||||
|
||||
const getLedger = useCallback(
|
||||
() => retrieveLedger(api),
|
||||
[api]
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({ ...(isApiReady ? getState(api) : EMPTY_STATE), getLedger }),
|
||||
[api, getLedger, isApiReady]
|
||||
);
|
||||
}
|
||||
@ -7,6 +7,7 @@ import type { KeyringPair } from '@polkadot/keyring/types';
|
||||
import type { QueueTx, QueueTxMessageSetStatus } from '@polkadot/react-components/Status/types';
|
||||
import type { Option } from '@polkadot/types';
|
||||
import type { Multisig, Timepoint } from '@polkadot/types/interfaces';
|
||||
import type { Ledger } from '@polkadot/ui-keyring';
|
||||
import type { AddressProxy, QrState } from './types';
|
||||
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
@ -15,7 +16,7 @@ import styled from 'styled-components';
|
||||
import { ApiPromise } from '@polkadot/api';
|
||||
import { web3FromSource } from '@polkadot/extension-dapp';
|
||||
import { Button, ErrorBoundary, Modal, Output, StatusContext, Toggle } from '@polkadot/react-components';
|
||||
import { useApi, useToggle } from '@polkadot/react-hooks';
|
||||
import { useApi, useLedger, useToggle } from '@polkadot/react-hooks';
|
||||
import { keyring } from '@polkadot/ui-keyring';
|
||||
import { assert, BN_ZERO } from '@polkadot/util';
|
||||
|
||||
@ -137,12 +138,12 @@ async function wrapTx (api: ApiPromise, currentItem: QueueTx, { isMultiCall, mul
|
||||
return tx;
|
||||
}
|
||||
|
||||
async function extractParams (api: ApiPromise, address: string, options: Partial<SignerOptions>, setQrState: (state: QrState) => void): Promise<['qr' | 'signing', string, Partial<SignerOptions>]> {
|
||||
async function extractParams (api: ApiPromise, address: string, options: Partial<SignerOptions>, getLedger: () => Ledger, setQrState: (state: QrState) => void): Promise<['qr' | 'signing', string, Partial<SignerOptions>]> {
|
||||
const pair = keyring.getPair(address);
|
||||
const { meta: { accountOffset, addressOffset, isExternal, isHardware, isInjected, source } } = pair;
|
||||
|
||||
if (isHardware) {
|
||||
return ['signing', address, { ...options, signer: new LedgerSigner(api.registry, accountOffset as number || 0, addressOffset as number || 0) }];
|
||||
return ['signing', address, { ...options, signer: new LedgerSigner(api.registry, getLedger, accountOffset as number || 0, addressOffset as number || 0) }];
|
||||
} else if (isExternal) {
|
||||
return ['qr', address, { ...options, signer: new QrSigner(api.registry, setQrState) }];
|
||||
} else if (isInjected) {
|
||||
@ -157,8 +158,9 @@ async function extractParams (api: ApiPromise, address: string, options: Partial
|
||||
}
|
||||
|
||||
function TxSigned ({ className, currentItem, requestAddress }: Props): React.ReactElement<Props> | null {
|
||||
const { api } = useApi();
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { getLedger } = useLedger();
|
||||
const { queueSetTxStatus } = useContext(StatusContext);
|
||||
const [flags, setFlags] = useState(extractExternal(requestAddress));
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
@ -240,7 +242,7 @@ function TxSigned ({ className, currentItem, requestAddress }: Props): React.Rea
|
||||
if (senderInfo.signAddress) {
|
||||
const [tx, [status, pairOrAddress, options]] = await Promise.all([
|
||||
wrapTx(api, currentItem, senderInfo),
|
||||
extractParams(api, senderInfo.signAddress, { nonce: -1, tip }, setQrState)
|
||||
extractParams(api, senderInfo.signAddress, { nonce: -1, tip }, getLedger, setQrState)
|
||||
]);
|
||||
|
||||
queueSetTxStatus(currentItem.id, status);
|
||||
@ -248,7 +250,7 @@ function TxSigned ({ className, currentItem, requestAddress }: Props): React.Rea
|
||||
await signAndSend(queueSetTxStatus, currentItem, tx, pairOrAddress, options);
|
||||
}
|
||||
},
|
||||
[api, tip]
|
||||
[api, getLedger, tip]
|
||||
);
|
||||
|
||||
const _onSign = useCallback(
|
||||
@ -256,13 +258,13 @@ function TxSigned ({ className, currentItem, requestAddress }: Props): React.Rea
|
||||
if (senderInfo.signAddress) {
|
||||
const [tx, [, pairOrAddress, options]] = await Promise.all([
|
||||
wrapTx(api, currentItem, senderInfo),
|
||||
extractParams(api, senderInfo.signAddress, { ...signedOptions, tip }, setQrState)
|
||||
extractParams(api, senderInfo.signAddress, { ...signedOptions, tip }, getLedger, setQrState)
|
||||
]);
|
||||
|
||||
setSignedTx(await signAsync(queueSetTxStatus, currentItem, tx, pairOrAddress, options));
|
||||
}
|
||||
},
|
||||
[api, signedOptions, tip]
|
||||
[api, getLedger, signedOptions, tip]
|
||||
);
|
||||
|
||||
const _doStart = useCallback(
|
||||
|
||||
@ -3,25 +3,26 @@
|
||||
|
||||
import type { Signer, SignerResult } from '@polkadot/api/types';
|
||||
import type { Registry, SignerPayloadJSON } from '@polkadot/types/types';
|
||||
|
||||
import { getLedger } from '@polkadot/react-api';
|
||||
import type { Ledger } from '@polkadot/ui-keyring';
|
||||
|
||||
let id = 0;
|
||||
|
||||
export default class LedgerSigner implements Signer {
|
||||
readonly #accountOffset: number;
|
||||
readonly #addressOffset: number;
|
||||
readonly #getLedger: () => Ledger;
|
||||
readonly #registry: Registry;
|
||||
|
||||
constructor (registry: Registry, accountOffset: number, addressOffset: number) {
|
||||
constructor (registry: Registry, getLedger: () => Ledger, accountOffset: number, addressOffset: number) {
|
||||
this.#accountOffset = accountOffset;
|
||||
this.#addressOffset = addressOffset;
|
||||
this.#getLedger = getLedger;
|
||||
this.#registry = registry;
|
||||
}
|
||||
|
||||
public async signPayload (payload: SignerPayloadJSON): Promise<SignerResult> {
|
||||
const raw = this.#registry.createType('ExtrinsicPayload', payload, { version: payload.version });
|
||||
const { signature } = await getLedger().sign(raw.toU8a(true), this.#accountOffset, this.#addressOffset);
|
||||
const { signature } = await this.#getLedger().sign(raw.toU8a(true), this.#accountOffset, this.#addressOffset);
|
||||
|
||||
return { id: ++id, signature };
|
||||
}
|
||||
|
||||
48
yarn.lock
48
yarn.lock
@ -3754,7 +3754,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:4.12.0, @typescript-eslint/typescript-estree@npm:^4.8.2":
|
||||
"@typescript-eslint/types@npm:4.13.0":
|
||||
version: 4.13.0
|
||||
resolution: "@typescript-eslint/types@npm:4.13.0"
|
||||
checksum: ed2e27ad1f7d0db28d13d3a5fe900b7638981689fea1b5556de4fcd87729f73cfe2e3792005cf4e05ba4c405f2abe9bc55c076354d4ac3f9576ded65c1b4ddf5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:4.12.0":
|
||||
version: 4.12.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:4.12.0"
|
||||
dependencies:
|
||||
@ -3773,6 +3780,25 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:^4.8.2":
|
||||
version: 4.13.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:4.13.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 4.13.0
|
||||
"@typescript-eslint/visitor-keys": 4.13.0
|
||||
debug: ^4.1.1
|
||||
globby: ^11.0.1
|
||||
is-glob: ^4.0.1
|
||||
lodash: ^4.17.15
|
||||
semver: ^7.3.2
|
||||
tsutils: ^3.17.1
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
checksum: 8b7485f192f247d659779f1bc95d927612e53b23801981acc462c2fd76219a61ac1094348bf7ac46034b58c1751793053128d11264268e378241eb6e686f5e8e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:4.12.0":
|
||||
version: 4.12.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:4.12.0"
|
||||
@ -3783,6 +3809,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:4.13.0":
|
||||
version: 4.13.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:4.13.0"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": 4.13.0
|
||||
eslint-visitor-keys: ^2.0.0
|
||||
checksum: 35f20062885da380ef9d6e07f058ea9629a36b3d264dcf7e776e81da7c8f544373a287d6f49c88fd4fc546bb4afea27efdcd4e5be972c4202746b46cf1682a2d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@vue/component-compiler-utils@npm:^3.2.0":
|
||||
version: 3.2.0
|
||||
resolution: "@vue/component-compiler-utils@npm:3.2.0"
|
||||
@ -17896,10 +17932,10 @@ fsevents@~2.1.2:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typedoc-default-themes@npm:0.12.0":
|
||||
version: 0.12.0
|
||||
resolution: "typedoc-default-themes@npm:0.12.0"
|
||||
checksum: 112a092fe01a9fde6255cd0992c952cb28056efb6c1fb13426131be21cbbda283a9f27d294fdc10f51fa8ab5b0b47a1da74d908e24e7ad0d814939a6eb629a86
|
||||
"typedoc-default-themes@npm:0.12.1":
|
||||
version: 0.12.1
|
||||
resolution: "typedoc-default-themes@npm:0.12.1"
|
||||
checksum: 491e8aeb5192760c6b9a563140a87a7f10322750e23412ffa526e5a417c366992321332db45096d5717dadf3f00a611b06e1d495ead0707c27d80f8d3a42bf58
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -17937,7 +17973,7 @@ fsevents@~2.1.2:
|
||||
progress: ^2.0.3
|
||||
shelljs: ^0.8.4
|
||||
shiki: ^0.2.7
|
||||
typedoc-default-themes: 0.12.0
|
||||
typedoc-default-themes: 0.12.1
|
||||
peerDependencies:
|
||||
typescript: 3.9.x || 4.0.x || 4.1.x
|
||||
bin:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user