feat: lots

This commit is contained in:
Filipe Medeiros 2021-11-28 04:31:04 +00:00
parent 69d83ef95c
commit b41e8c88f7
16 changed files with 341 additions and 296 deletions

View file

@ -125,7 +125,11 @@ const BottomMenu: FC<Props> = ({ className }) => {
)}
onClick={() => push('/receive/qr')}
>
<LoginIcon className="h-full text-purple-50 dark:text-gray-900 w-full -rotate-90" />
<LoginIcon
className={
'h-full text-purple-50 dark:text-gray-900 w-full -rotate-child-90'
}
/>
</button>
</div>

View file

@ -2,12 +2,14 @@ import { ClockIcon } from '@heroicons/react/outline'
import { DownloadIcon, UploadIcon } from '@heroicons/react/solid'
import clsx from 'clsx'
import { tools } from 'nanocurrency-web'
import type { FC } from 'react'
import { FC, useCallback, useState } from 'react'
import { useCurrentAccount } from '../lib/context/accountContext'
import useAccountHistory from '../lib/hooks/useAccountHistory'
import useAccountReceivable from '../lib/hooks/useAccountReceivable'
import useListenToConfirmations from '../lib/hooks/useListenToConfirmations'
import useReceiveNano from '../lib/hooks/useReceiveNano'
import { ConfirmationMessage } from '../lib/types'
const rawToNanoDisplay = (raw: string) =>
Number(tools.convert(raw, 'RAW', 'NANO').slice(0, 20))
@ -25,15 +27,15 @@ const RecentTransactions: FC<Props> = ({ className }) => {
const {
accountReceivable,
blocksInfo: receivableBlocksInfo,
loading: loadingReceivable,
mutate,
} = useAccountReceivable()
const { accountHistory, loading: loadingHistory } = useAccountHistory()
const { accountHistory } = useAccountHistory()
const account = useCurrentAccount()
const hasReceivable =
!loadingReceivable && Object.keys(accountReceivable.blocks).length > 0
const hasHistory = !loadingHistory && accountHistory.history.length > 0
accountReceivable !== undefined &&
Object.values(accountReceivable.blocks).some(account => account !== '')
const receivable = Object.entries(
accountReceivable?.blocks[account?.address ?? ''] ?? {}
@ -43,12 +45,69 @@ const RecentTransactions: FC<Props> = ({ className }) => {
from: source,
}))
const [listenedReceivables, setListenedReceivables] = useState<
ConfirmationMessage[]
>([])
const onConfirmation = useCallback(
(confirmation: ConfirmationMessage) => {
const alreadyHaveConfirmation =
accountHistory?.history !== '' &&
accountHistory?.history.some(
txn => txn.hash === confirmation.message.hash
) &&
receivable.some(txn => txn.hash === confirmation.message.hash)
if (!alreadyHaveConfirmation)
setListenedReceivables(prev => [...prev, confirmation])
},
[accountHistory, receivable]
)
useListenToConfirmations(onConfirmation)
console.log(listenedReceivables)
return (
<div className={clsx('flex flex-col gap-6 w-full', className)}>
{hasReceivable && (
<section className="flex flex-col gap-3 w-full items-center">
<h2 className="text-2xl font-semibold text-purple-50">receivable</h2>
<ol className="flex flex-col gap-3 w-full">
{listenedReceivables.map(({ message, time }) => (
<li
key={message.hash}
className="bg-purple-50 shadow rounded px-3 py-3 flex items-center justify-between gap-2 text-black border-r-4 border-blue-500"
>
<button
className="contents"
onClick={() => receive(message.hash, message.amount)}
>
<ClockIcon className="w-6 flex-shrink-0 text-blue-500" />
<div className="overflow-hidden overflow-ellipsis text-left flex-1 whitespace-nowrap">
{Intl.DateTimeFormat([], {
day: '2-digit',
month: '2-digit',
year: '2-digit',
}).format(Number(time) * 1000)}{' '}
- {<span className="text-xs">{message.block.account}</span>}
</div>
<span className="flex-shrink-0 font-medium">
Ӿ{' '}
{rawToNanoDisplay(message.amount) === 'small' ? (
'<.01'
) : rawToNanoDisplay(message.amount).startsWith('0.') ? (
<>
<span className="text-sm font-semibold">0</span>
{rawToNanoDisplay(message.amount).substring(1)}
</>
) : (
rawToNanoDisplay(message.amount)
)}
</span>
</button>
</li>
))}
{receivable.map(receivable => (
<li
key={receivable.hash}
@ -92,43 +151,29 @@ const RecentTransactions: FC<Props> = ({ className }) => {
</ol>
</section>
)}
{hasHistory && hasReceivable && <hr />}
{hasHistory && (
{accountHistory !== undefined &&
accountHistory.history !== '' &&
hasReceivable && <hr />}
{accountHistory !== undefined && accountHistory.history !== '' && (
<section className="flex flex-col gap-3 w-full items-center">
<h2 className="text-2xl font-semibold text-purple-50">
recent transactions
</h2>
<ol className="flex flex-col gap-3 w-full">
{[
{
hash: 'string',
send: 'string',
receivable: true,
amount: '0',
account: '',
timestamp: '',
},
].map(txn => (
{accountHistory.history.map(txn => (
<li
key={txn.hash}
className={clsx(
'bg-purple-50 shadow rounded px-3 py-3 flex items-center justify-between gap-2 text-black border-r-4',
txn.send
? 'border-yellow-500'
: txn.receivable
? 'border-blue-500'
: 'border-green-500'
txn.type === 'send' ? 'border-yellow-500' : 'border-green-500'
)}
>
<button className="contents" onClick={() => {}}>
{txn.send ? (
{txn.type === 'send' ? (
<UploadIcon className="w-6 text-yellow-500 flex-shrink-0" />
) : (
<DownloadIcon
className={clsx(
'w-6 flex-shrink-0',
txn.receivable ? 'text-blue-500' : 'text-green-500'
)}
className={clsx('w-6 flex-shrink-0 text-green-500')}
/>
)}
<div className="overflow-hidden overflow-ellipsis text-left flex-1 whitespace-nowrap">
@ -136,7 +181,7 @@ const RecentTransactions: FC<Props> = ({ className }) => {
day: '2-digit',
month: '2-digit',
year: '2-digit',
}).format(Number(txn.timestamp) * 1000)}{' '}
}).format(Number(txn.local_timestamp) * 1000)}{' '}
- {<span className="text-xs">{txn.account}</span>}
</div>
<span className="flex-shrink-0 font-medium">
@ -158,16 +203,17 @@ const RecentTransactions: FC<Props> = ({ className }) => {
</ol>
</section>
)}
{!hasReceivable && !hasHistory && (
<div className="text-center pt-8 text-purple-50">
<p className="pb-4">no transactions yet...</p>
<p>
get your first nano
<br />
to see something here!
</p>
</div>
)}
{!hasReceivable &&
(accountHistory === undefined || accountHistory.history === '') && (
<div className="text-center pt-8 text-purple-50">
<p className="pb-4">no transactions yet...</p>
<p>
get your first nano
<br />
to see something here!
</p>
</div>
)}
{false && (
<button
className="bg-purple-200 py-2 px-4 rounded dark:text-gray-900 font-bold shadow"

View file

@ -4,11 +4,17 @@ import {
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react'
import computeWorkAsync from '../computeWorkAsync'
import { addPrecomputedWork, getAllAccounts, putAccount } from '../db/accounts'
import {
addPrecomputedWork,
consumePrecomputedWork,
getAllAccounts,
putAccount,
} from '../db/accounts'
import { AccountInfoCache } from '../types'
import fetchAccountInfo from '../xno/fetchAccountInfo'
@ -25,15 +31,26 @@ const accountContext = createContext<AccountContextValue | undefined>(undefined)
const refreshAccountFromNetwork = async (account: AccountInfoCache) => {
const infoResponse = await fetchAccountInfo(account.address)
const frontier =
'error' in infoResponse ? null : infoResponse.confirmed_frontier
const representative =
'error' in infoResponse ? null : infoResponse.confirmed_representative
const balance =
'error' in infoResponse ? null : infoResponse.confirmed_balance
const freshAccountInfo = {
...account,
frontier: 'error' in infoResponse ? null : infoResponse.confirmed_frontier,
representative:
'error' in infoResponse ? null : infoResponse.confirmed_representative,
balance: 'error' in infoResponse ? null : infoResponse.confirmed_balance,
frontier,
representative,
balance,
}
putAccount(freshAccountInfo)
return freshAccountInfo
const diff =
account.frontier !== frontier ||
account.representative !== representative ||
account.balance !== balance
if (diff) putAccount(freshAccountInfo)
return { freshAccountInfo, diff }
}
export const useAccounts = () => {
@ -70,31 +87,37 @@ export const AccountProvider: FC = ({ children }) => {
useEffect(() => {
const refreshAccountsFromNetwork = async (accounts: AccountInfoCache[]) =>
accounts.forEach(async account => {
const freshAccount = await refreshAccountFromNetwork(account)
setAccount(freshAccount)
const { freshAccountInfo, diff } = await refreshAccountFromNetwork(
account
)
if (diff) setAccount(freshAccountInfo)
})
const getAccountsFromIdb = async () => {
const accountList = await getAllAccounts()
const accounts: AccountContextValue['accounts'] = {}
for (const account of accountList) {
accountList.forEach(async account => {
accounts[account.index] = account
if (account.precomputedWork === null) {
computeWorkAsync(account.frontier ?? account.address, {
send: account.frontier !== null,
}).then(work => {
if (work !== null) {
setAccounts(prev => ({
...prev,
[account.index]: {
...prev[account.index],
precomputedWork: work,
},
}))
addPrecomputedWork(account.address, work)
const work = await computeWorkAsync(
account.frontier ?? account.publicKey,
{
send: account.frontier !== null,
}
})
)
if (work !== null) {
setAccounts(prev => ({
...prev,
[account.index]: {
...prev[account.index],
precomputedWork: work,
},
}))
addPrecomputedWork(account.address, work)
}
}
}
})
setAccounts(accounts)
refreshAccountsFromNetwork(accountList)
}
@ -102,7 +125,10 @@ export const AccountProvider: FC = ({ children }) => {
}, [setAccount])
const [currAccountIndex, setCurrAccountIndex] = useState<number>(0)
const currAccount = accounts?.[currAccountIndex]
const currAccount = useMemo(
() => accounts?.[currAccountIndex],
[currAccountIndex, accounts]
)
const removeAccount = useCallback((index: number) => {
setAccounts(prev => {

View file

@ -1,11 +1,11 @@
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { useCurrentAccount } from '../context/accountContext'
import type { AccountReceivableResponse, BlocksInfoResponse } from '../types'
import fetchAccountReceivable from '../xno/fetchAccountReceivable'
import fetchBlocksInfo from '../xno/fetchBlocksInfo'
type ReturnValue =
type ReturnValue = (
| {
accountReceivable: undefined
blocksInfo: undefined
@ -16,6 +16,15 @@ type ReturnValue =
blocksInfo: BlocksInfoResponse
loading: false
}
) & {
mutate: (prev: {
accountReceivable: AccountReceivableResponse | undefined
blocksInfo: BlocksInfoResponse | undefined
}) => {
accountReceivable: AccountReceivableResponse | undefined
blocksInfo: BlocksInfoResponse | undefined
}
}
const useAccountReceivable = (): ReturnValue => {
const [accountReceivableWithInfo, setAccountReceivableWithInfo] = useState<{
@ -47,7 +56,11 @@ const useAccountReceivable = (): ReturnValue => {
const loading =
accountReceivableWithInfo.accountReceivable === undefined &&
accountReceivableWithInfo.blocksInfo === undefined
return { ...accountReceivableWithInfo, loading } as ReturnValue
return {
...accountReceivableWithInfo,
loading,
mutate: setAccountReceivableWithInfo,
} as ReturnValue
}
export default useAccountReceivable

View file

@ -0,0 +1,54 @@
import { useEffect, useRef } from 'react'
import { useCurrentAccount } from '../context/accountContext'
import type { ConfirmationMessage } from '../types'
/**
* _please don't forget to memo `onConfirmation`_ :)
* @param onConfirmation the callback to call with the new confirmation
*/
const useListenToConfirmations = (
onConfirmation: (confirmation: ConfirmationMessage) => void
) => {
const account = useCurrentAccount()
const wsRef = useRef<WebSocket>()
useEffect(() => {
wsRef.current = new WebSocket('wss://node.somenano.com/websocket')
return () => wsRef.current?.close()
}, [])
useEffect(() => {
if (
account !== undefined &&
wsRef.current !== undefined &&
wsRef.current.readyState === WebSocket.OPEN
) {
wsRef.current!.send(
JSON.stringify({
action: 'subscribe',
topic: 'confirmation',
options: {
accounts: [account.address],
},
})
)
wsRef.current!.addEventListener('message', ({ data }) => {
const parsed = JSON.parse(data) as ConfirmationMessage
if (parsed.topic !== 'confirmation') return
onConfirmation(parsed)
})
return () =>
wsRef.current!.send(
JSON.stringify({
action: 'unsubscribe',
topic: 'confirmation',
})
)
}
}, [account, onConfirmation])
}
export default useListenToConfirmations

View file

@ -1,35 +0,0 @@
import { useEffect } from 'react'
import { useAccounts } from '../context/accountContext'
import { ConfirmationMessage } from '../types'
const useListenToTxn = (
onConfirmation: (confirmation: ConfirmationMessage) => void
) => {
const { accounts } = useAccounts()
useEffect(() => {
if (accounts !== undefined) {
const ws = new WebSocket('wss://ws.mynano.ninja/')
ws.onopen = () => {
ws.send(
JSON.stringify({
action: 'subscribe',
topic: 'confirmation',
options: {
accounts: [accounts],
},
})
)
ws.addEventListener('message', ({ data }) => {
const parsed = JSON.parse(data) as ConfirmationMessage
onConfirmation(parsed)
})
}
return () => ws.close()
}
}, [accounts, onConfirmation])
}
export default useListenToTxn

View file

@ -1,15 +1,15 @@
import { computeWork, hashBlock } from 'nanocurrency'
import Big from 'bignumber.js'
import { useCallback, useState } from 'react'
import computeWorkAsync from '../computeWorkAsync'
import { useAccount } from '../context/accountContext'
import { getPrecomputedWork } from '../db/accounts'
import fetcher from '../fetcher'
import { useAccount, useAccounts } from '../context/accountContext'
import { consumePrecomputedWork, getPrecomputedWork } from '../db/accounts'
import { zeroString } from '../xno/constants'
import receiveNano from '../xno/receiveNano'
const useReceiveNano = () => {
const account = useAccount()
const { setAccount } = useAccounts()
const [generatingWork, setGeneratingWork] = useState(false)
const receive = useCallback(
@ -19,13 +19,13 @@ const useReceiveNano = () => {
if (precomputedWork === null) {
setGeneratingWork(true)
precomputedWork = await computeWorkAsync(
account.frontier ?? account.address,
account.frontier ?? account.publicKey,
{ send: false }
)
setGeneratingWork(false)
}
if (precomputedWork === null) throw new Error('couldnt_compute_work')
await receiveNano(
if (precomputedWork === null) throw new Error('cant_compute_work')
const processResponse = await receiveNano(
{
transactionHash: hash,
walletBalanceRaw: account.balance ?? '0',
@ -37,8 +37,17 @@ const useReceiveNano = () => {
},
account.index
)
await consumePrecomputedWork(account.address)
const work = await computeWorkAsync(processResponse.hash, { send: false })
setAccount({
...account,
frontier: processResponse.hash,
balance: new Big(account.balance ?? 0).plus(new Big(amount)).toString(),
...(work !== null ? { work } : {}),
})
},
[account]
[account, setAccount]
)
return { receive, generatingWork }

View file

@ -1,17 +1,14 @@
import { hashBlock } from 'nanocurrency'
import Big from 'bignumber.js'
import { useCallback, useState } from 'react'
import computeWorkAsync from '../computeWorkAsync'
import { useAccount } from '../context/accountContext'
import {
addPrecomputedWork,
consumePrecomputedWork,
getPrecomputedWork,
} from '../db/accounts'
import { useAccount, useAccounts } from '../context/accountContext'
import { consumePrecomputedWork, getPrecomputedWork } from '../db/accounts'
import sendNano from '../xno/sendNano'
const useSendNano = () => {
const account = useAccount()
const { setAccount } = useAccounts()
const [generatingWork, setGeneratingWork] = useState(false)
const send = useCallback(
@ -27,13 +24,14 @@ const useSendNano = () => {
if (precomputedWork === null) {
setGeneratingWork(true)
precomputedWork = await computeWorkAsync(
account.frontier ?? account.address,
account.frontier ?? account.publicKey,
{ send: true }
)
setGeneratingWork(false)
}
if (precomputedWork === null) throw new Error('couldnt_compute_work')
await sendNano(
if (precomputedWork === null) throw new Error('cant_compute_work')
const processResponse = await sendNano(
{
walletBalanceRaw: account.balance,
fromAddress: account.address,
@ -45,8 +43,17 @@ const useSendNano = () => {
},
account.index
)
consumePrecomputedWork(account.address)
const work = await computeWorkAsync(processResponse.hash, { send: false })
setAccount({
...account,
frontier: processResponse.hash,
balance: new Big(account.balance).minus(new Big(amount)).toString(),
...(work !== null ? { work } : {}),
})
},
[account]
[account, setAccount]
)
return { send, generatingWork }

View file

@ -18,12 +18,14 @@ export interface AccountHistoryResponse {
export interface AccountReceivableResponse {
blocks: {
[destinationAddress: string]: {
[blockHash: string]: {
amount: string
source: string
}
}
[destinationAddress: string]:
| {
[blockHash: string]: {
amount: string
source: string
}
}
| ''
}
}

View file

@ -18,21 +18,22 @@ const sendNano = async (
)
const signedBlock = block.receive(blockData, privateKey)
fetcher<ProcessResponse>('https://mynano.ninja/api/node', {
method: 'POST',
headers: [['Content-Type', 'application/json']],
body: JSON.stringify({
action: 'process',
json_block: 'true',
subtype: 'receive',
block: signedBlock,
}),
}).then(async data => {
if ('error' in data) throw new Error()
await consumePrecomputedWork(blockData.toAddress)
const work = await computeWorkAsync(hashBlock(signedBlock), { send: false })
if (work !== null) addPrecomputedWork(blockData.toAddress, work)
})
const processResponse = await fetcher<ProcessResponse>(
'https://mynano.ninja/api/node',
{
method: 'POST',
body: {
action: 'process',
json_block: 'true',
subtype: 'receive',
block: signedBlock,
},
}
)
if ('error' in processResponse) throw new Error()
return processResponse
}
export default sendNano

View file

@ -1,4 +1,3 @@
import { hashBlock } from 'nanocurrency'
import { block } from 'nanocurrency-web'
import computeWorkAsync from '../computeWorkAsync'
@ -18,21 +17,22 @@ const sendNano = async (
)
const signedBlock = block.send(blockData, privateKey)
return fetcher<ProcessResponse>('https://mynano.ninja/api/node', {
method: 'POST',
headers: [['Content-Type', 'application/json']],
body: JSON.stringify({
action: 'process',
json_block: 'true',
subtype: 'send',
block: signedBlock,
}),
}).then(async data => {
if ('error' in data) throw new Error()
await consumePrecomputedWork(blockData.fromAddress)
const work = await computeWorkAsync(hashBlock(signedBlock), { send: true })
if (work !== null) addPrecomputedWork(blockData.fromAddress, work)
})
const processResponse = await fetcher<ProcessResponse>(
'https://mynano.ninja/api/node',
{
method: 'POST',
body: {
action: 'process',
json_block: 'true',
subtype: 'send',
block: signedBlock,
},
}
)
if ('error' in processResponse) throw new Error()
return processResponse
}
export default sendNano

View file

@ -19,6 +19,7 @@
},
"dependencies": {
"@heroicons/react": "^1.0.5",
"bignumber.js": "^9.0.1",
"cbor": "^8.1.0",
"clsx": "^1.1.1",
"crypto-js": "^4.1.1",
@ -29,7 +30,7 @@
"nanocurrency-web": "^1.3.5",
"next": "latest",
"prettier": "^2.4.1",
"qrcode": "^1.4.4",
"qrcode": "^1.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},

View file

@ -1,6 +1,5 @@
import type { AppProps } from 'next/app'
import { FC } from 'react'
import 'tailwindcss/tailwind.css'
import Layout from '../components/Layout'
import MemCacheProvider from '../lib/context/memCacheContextProvider'

View file

@ -1,4 +1,6 @@
[
"expand target gospel goose oppose acquire genius hurdle trade size huge pact square silk canal bar curve shallow pistol push crowd glory slice news",
"81631b9773ab0f4ff0d61a0ae092ec564acc01912aedbd849c420e10b2c38c7edc2c5494f778d3122a5933261c3a09b17f1b1be33d3b068d3228529a24bf94b3"
"81631b9773ab0f4ff0d61a0ae092ec564acc01912aedbd849c420e10b2c38c7edc2c5494f778d3122a5933261c3a09b17f1b1be33d3b068d3228529a24bf94b3",
"738b5cb3315dd267fc882f1388911162ee9a241c800bf583c07c1bbbc34c64a9c2099e01b5d5e3ab217012717e3ecfbe3f735af431c7e105e1719eeb03476af5",
"75edd150e09bada0f5c68fab14c0b385239cc396ef6600029dea0f7b66be63fa6a7d8ebe30aaff66634f583c4e68067c0de136e27dddc96be76d2ac5bb14ef71"
]

View file

@ -4,3 +4,14 @@ body,
html {
font-family: Manrope;
}
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
.-rotate-child-90 > * {
transform: rotate(-90deg);
transform-origin: center;
}
}

175
yarn.lock
View file

@ -784,11 +784,6 @@ ansi-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
ansi-regex@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
@ -967,7 +962,7 @@ balanced-match@^2.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9"
integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
base64-js@^1.0.2, base64-js@^1.3.1:
base64-js@^1.0.2:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
@ -977,7 +972,7 @@ big.js@^5.2.2:
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
bignumber.js@9.0.1, bignumber.js@^9.0.0:
bignumber.js@9.0.1, bignumber.js@^9.0.0, bignumber.js@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5"
integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==
@ -1110,29 +1105,6 @@ browserslist@^4.17.5:
node-releases "^2.0.1"
picocolors "^1.0.0"
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
buffer-alloc@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
dependencies:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
buffer-from@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
buffer-xor@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
@ -1146,14 +1118,6 @@ buffer@5.6.0:
base64-js "^1.0.2"
ieee754 "^1.1.4"
buffer@^5.4.3:
version "5.7.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.1.13"
builtin-status-codes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
@ -1347,14 +1311,14 @@ cli-truncate@^2.1.0:
slice-ansi "^3.0.0"
string-width "^4.2.0"
cliui@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
cliui@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
dependencies:
string-width "^3.1.0"
strip-ansi "^5.2.0"
wrap-ansi "^5.1.0"
string-width "^4.2.0"
strip-ansi "^6.0.0"
wrap-ansi "^6.2.0"
clone-regexp@^2.1.0:
version "2.2.0"
@ -1767,11 +1731,6 @@ elliptic@^6.5.3:
minimalistic-assert "^1.0.1"
minimalistic-crypto-utils "^1.0.1"
emoji-regex@^7.0.1:
version "7.0.3"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
@ -1787,6 +1746,11 @@ emojis-list@^2.0.0:
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
encode-utf8@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda"
integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==
encoding@0.1.13:
version "0.1.13"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
@ -2216,13 +2180,6 @@ find-up@^2.1.0:
dependencies:
locate-path "^2.0.0"
find-up@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
dependencies:
locate-path "^3.0.0"
find-up@^4.0.0, find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
@ -2621,7 +2578,7 @@ idb@^7.0.0:
resolved "https://registry.yarnpkg.com/idb/-/idb-7.0.0.tgz#f349b418c128f625961147a7d6b0e4b526fd34ed"
integrity sha512-jSx0WOY9Nj+QzP6wX5e7g64jqh8ExtDs/IAuOrOEZCD/h6+0HqyrKsDMfdJc0hqhSvh0LsrwqrkDn+EtjjzSRA==
ieee754@^1.1.13, ieee754@^1.1.4:
ieee754@^1.1.4:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
@ -2795,11 +2752,6 @@ is-extglob@^2.1.1:
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
@ -2933,11 +2885,6 @@ is-weakref@^1.0.1:
dependencies:
call-bind "^1.0.0"
isarray@^2.0.1:
version "2.0.5"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@ -3130,14 +3077,6 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
locate-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
dependencies:
p-locate "^3.0.0"
path-exists "^3.0.0"
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
@ -3650,7 +3589,7 @@ p-limit@^1.1.0:
dependencies:
p-try "^1.0.0"
p-limit@^2.0.0, p-limit@^2.2.0:
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
@ -3664,13 +3603,6 @@ p-locate@^2.0.0:
dependencies:
p-limit "^1.1.0"
p-locate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
dependencies:
p-limit "^2.0.0"
p-locate@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
@ -3825,10 +3757,10 @@ platform@1.3.6:
resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7"
integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==
pngjs@^3.3.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==
pngjs@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==
postcss-js@^3.0.3:
version "3.0.3"
@ -3992,18 +3924,15 @@ purgecss@^4.0.3:
postcss "^8.2.1"
postcss-selector-parser "^6.0.2"
qrcode@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.4.4.tgz#f0c43568a7e7510a55efc3b88d9602f71963ea83"
integrity sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==
qrcode@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.0.tgz#95abb8a91fdafd86f8190f2836abbfc500c72d1b"
integrity sha512-9MgRpgVc+/+47dFvQeD6U2s0Z92EsKzcHogtum4QB+UNd025WOJSHvn/hjk9xmzj7Stj95CyUAs31mrjxliEsQ==
dependencies:
buffer "^5.4.3"
buffer-alloc "^1.2.0"
buffer-from "^1.1.1"
dijkstrajs "^1.0.1"
isarray "^2.0.1"
pngjs "^3.3.0"
yargs "^13.2.4"
encode-utf8 "^1.0.3"
pngjs "^5.0.0"
yargs "^15.3.1"
querystring-es3@0.2.1:
version "0.2.1"
@ -4525,15 +4454,6 @@ string-hash@1.1.3:
resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b"
integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=
string-width@^3.0.0, string-width@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
dependencies:
emoji-regex "^7.0.1"
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
@ -4603,13 +4523,6 @@ strip-ansi@^3.0.0:
dependencies:
ansi-regex "^2.0.0"
strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
dependencies:
ansi-regex "^4.1.0"
strip-ansi@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
@ -5116,15 +5029,6 @@ word-wrap@^1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
wrap-ansi@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
dependencies:
ansi-styles "^3.2.0"
string-width "^3.0.0"
strip-ansi "^5.0.0"
wrap-ansi@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
@ -5178,10 +5082,10 @@ yaml@^1.10.0, yaml@^1.10.2:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yargs-parser@^13.1.2:
version "13.1.2"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
yargs-parser@^18.1.2:
version "18.1.3"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
@ -5191,21 +5095,22 @@ yargs-parser@^20.2.3, yargs-parser@^20.2.7:
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
yargs@^13.2.4:
version "13.3.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
yargs@^15.3.1:
version "15.4.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
dependencies:
cliui "^5.0.0"
find-up "^3.0.0"
cliui "^6.0.0"
decamelize "^1.2.0"
find-up "^4.1.0"
get-caller-file "^2.0.1"
require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^3.0.0"
string-width "^4.2.0"
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^13.1.2"
yargs-parser "^18.1.2"
yocto-queue@^0.1.0:
version "0.1.0"