diff --git a/README.md b/README.md index 8732315577..1ee45aa4bc 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,10 @@ $ yarn test If you don't want to bother building from source, you can download a binary from [releases](https://github.com/nervosnetwork/neuron/releases). We offer pre-built binaries for Windows, Linux and macOS. +## Use Perun Channels + +To use the Perun Channels you need to clone the perun-wallet-wrapper and link it to the src file of this project. Additionally uncomments all necessary functions and imports in the neuon-ui/src/components/Perun/index.tsx file. + ## License Neuron is released under the terms of the MIT license. See [COPYING](COPYING) for more information or see [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT). diff --git a/package.json b/package.json index 21b80bd5ec..de208d177c 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "update:client-versions": "node ./scripts/update-ckb-client-versions.js" }, "devDependencies": { - "@babel/core": "7.23.3", + "@babel/core": "7.23.5", "@types/jest": "27.5.2", "@types/node": "18.16.18", "@types/npmlog": "4.1.6", @@ -53,12 +53,12 @@ "eslint-config-prettier": "8.10.0", "eslint-plugin-prettier": "4.2.1", "husky": "8.0.3", - "lerna": "7.1.0", + "lerna": "7.4.2", "lint-staged": "13.3.0", "ncp": "2.0.0", "prettier": "2.8.8", "ts-jest": "29.1.1", - "typescript": "5.3.2", + "typescript": "5.3.3", "wait-on": "7.0.1" }, "dependencies": {}, diff --git a/packages/neuron-ui/package.json b/packages/neuron-ui/package.json index 6a8520f77e..5c8ac7bfb2 100644 --- a/packages/neuron-ui/package.json +++ b/packages/neuron-ui/package.json @@ -61,8 +61,10 @@ "office-ui-fabric-react": "7.204.0", "qr.js": "0.0.0", "react": "17.0.2", + "react-bootstrap": "^2.9.1", "react-dom": "17.0.2", "react-i18next": "12.1.5", + "react-icons": "^4.11.0", "react-router-dom": "6.14.0", "react-transition-group": "4.4.5", "sass": "1.63.6" @@ -80,7 +82,7 @@ "@storybook/react": "7.0.24", "@storybook/react-webpack5": "7.0.24", "@storybook/testing-library": "0.2.2", - "@types/enzyme": "3.10.16", + "@types/enzyme": "3.10.18", "@types/enzyme-adapter-react-16": "1.0.9", "@types/node": "18.16.18", "@types/react": "17.0.71", @@ -93,10 +95,10 @@ "enzyme": "3.11.0", "enzyme-adapter-react-16": "1.15.7", "eslint-config-airbnb": "19.0.4", - "eslint-plugin-import": "2.27.5", + "eslint-plugin-import": "2.29.0", "eslint-plugin-jsx-a11y": "6.7.1", "eslint-plugin-react": "7.32.2", - "jest-styled-components": "7.1.1", + "jest-styled-components": "7.2.0", "react-app-rewired": "2.2.1", "react-scripts": "5.0.1", "react-test-renderer": "16.14.0", diff --git a/packages/neuron-ui/src/components/Perun/hooks.ts b/packages/neuron-ui/src/components/Perun/hooks.ts new file mode 100644 index 0000000000..c986207320 --- /dev/null +++ b/packages/neuron-ui/src/components/Perun/hooks.ts @@ -0,0 +1,7 @@ +import { useState } from '../../states' + +export const usePerun = () => { + const { perunState } = useState() + // TODO: Register some callbacks here if needed. + return perunState +} diff --git a/packages/neuron-ui/src/components/Perun/index.tsx b/packages/neuron-ui/src/components/Perun/index.tsx new file mode 100644 index 0000000000..ea2efcde3d --- /dev/null +++ b/packages/neuron-ui/src/components/Perun/index.tsx @@ -0,0 +1,760 @@ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ +/* eslint-disable consistent-return */ +/* eslint-disable no-console */ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useState, useEffect, useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import { useState as useGlobalState } from 'states' +import { PerunState as PerunStateSubject } from 'services/subjects' +import { bytes } from '@ckb-lumos/codec' +import { blockchain } from '@ckb-lumos/base' + +import PageContainer from 'components/PageContainer' +import { Form, Container, Button, Modal } from 'react-bootstrap' +import { BiX } from 'react-icons/bi' +import { + SerializeOffChainParticipant, + SerializeSEC1EncodedPubKey, +} from '@polycrypt/perun-wallet-wrapper/ckb/serialization' +import { channelIdToString, channelIdFromString } from '@polycrypt/perun-wallet-wrapper/translator' +import * as wire from '@polycrypt/perun-wallet-wrapper/wire' + +import { ControllerResponse } from 'services/remote/remoteApiWrapper' +import { + OfflineSignStatus, + OfflineSignType, + getCurrentWalletAccountExtendedPubKey, + perunServiceAction, + respondPerunRequest, + signRawMessage, + signTransactionOnly, + showErrorMessage, +} from 'services/remote' +import { addressToScript, bytesToHex, scriptToAddress } from '@nervosnetwork/ckb-sdk-utils' +import { ErrorCode, errorFormatter, isSuccessResponse } from 'utils' +import { PasswordDialog } from 'components/SignAndVerify' +import { State } from '@polycrypt/perun-wallet-wrapper/wire' +import styles from './perun.module.scss' + +// eslint-disable-next-line @typescript-eslint/no-shadow +function bigintFromBEBytes(bytes: Uint8Array): bigint { + let result = BigInt(0) + + for (let i = 0; i < bytes.length; i++) { + // eslint-disable-next-line no-bitwise + result = (result << BigInt(8)) + BigInt(bytes[i]) + } + + return result +} + +const Perun = () => { + const { wallet } = useGlobalState() + const [t, _] = useTranslation() + const [amount, setAmount] = useState() + const [updateAmount, setUpdateAmount] = useState() + const [peerAddress, setPeerAddress] = useState('') + const [peerAmount, setPeerAmount] = useState() + const [challengeDuration, setChallengeDuration] = useState() + const [validInputs, setValidInputs] = useState(false) + const [channels] = useState(new Map()) + const [showRejectionModal, setShowRejectionModal] = useState(false) + const [rejectionReason, setRejectionReason] = useState('') + const [updateChannelDialog, setUpdateChannelDialog] = useState(false) + const [channelID, setChannelID] = useState() + const [perunState, setPerunState] = useState({ type: 'SignMessage' }) + + const [showPrompt, setShowPrompt] = useState(false) + + const [showPasswordDialog, setShowPasswordDialog] = useState(false) + + // TODO: Use global state instead of local, otherwise the state will be lost + // when the user does not actively have the page open. + PerunStateSubject.subscribe(setPerunState) + + const handleAmountChange = (am: string) => { + const amountNum = parseFloat(am) + if (Number.isNaN(amountNum)) { + return + } + setAmount(amountNum) + } + + const handlePeerAmountChange = (am: string) => { + const amountNum = parseFloat(am) + if (Number.isNaN(amountNum)) { + return + } + setPeerAmount(amountNum) + } + + const handleUpdateAmountChange = (am: string) => { + const amountNum = parseFloat(am) + if (Number.isNaN(amountNum)) { + return + } + setUpdateAmount(amountNum) + } + + const handlePeerAddressChange = (ad: string) => { + setPeerAddress(ad) + } + + const handleChallengeDurationChange = (cd: string) => { + const cdNum = parseFloat(cd) + if (Number.isNaN(cdNum)) { + return + } + setChallengeDuration(cdNum) + } + + const handleRejected = (reason: React.SetStateAction) => { + console.log('HANDLE REJECTED REASON: ', reason) + setRejectionReason(reason) + setShowRejectionModal(true) + } + + const handleCloseRejectionModal = () => { + setShowRejectionModal(false) + } + + useEffect(() => { + if (amount && peerAddress && challengeDuration) { + setValidInputs(true) + } + }, [amount, peerAddress, challengeDuration]) + + useEffect(() => { + if (!perunState.request) { + return + } + + // Prompt the user upon receiving a perun request. + setShowPrompt(true) + }, [perunState]) + + const handleSigningRequest = async (password: string) => { + const handleSignMessage = async (request: any) => { + const addressBytes = request.pubkey.data + // Uint8Array -> String + const address = new TextDecoder().decode(new Uint8Array(addressBytes)) + console.log('signing request for address:', address) + const msgToSign = bytesToHex(new Uint8Array(request.data.data)) + // TODO: It would be nice to have a decoder for the Perun encoded messages. + // We could fetch the channel state here, display it to the user AND update + // the state cache upon successful signing. + const res: ControllerResponse = await signRawMessage({ + walletID: wallet?.id ?? '', + address, + message: msgToSign, + password, + }) + + console.log(`message to sign: ${msgToSign}`) + + if (isSuccessResponse(res)) { + await respondPerunRequest({ + type: 'SignMessage', + response: { + data: res.result, + }, + }) + } else if (res.status === ErrorCode.PasswordIncorrect) { + showErrorMessage('Error', 'Password incorrect') + } else if (res.status === ErrorCode.AddressNotFound) { + showErrorMessage('Error', 'Address not found') + } + setShowPrompt(false) + setShowPasswordDialog(false) + return res + } + + const handleSignTransaction = async (request: any) => { + console.log('handleSignTransaction', request) + console.log('inputs', request.transaction.inputs) + // TODO: Inject NetworkType. + const offlineTx = { + transaction: { ...request.transaction, fee: '1' }, + status: OfflineSignStatus.Unsigned, + type: OfflineSignType.Regular, + description: 'Perun channel transaction', + walletID: wallet?.id ?? '', + password, + } + console.log(`trying to sign with wallet ${wallet?.id}`) + const res: ControllerResponse = await signTransactionOnly(offlineTx as any) + + if (!isSuccessResponse(res)) { + showErrorMessage('Error', errorFormatter(res.message, t)) + return res + } + + console.log('sign transaction success') + + // Bring into backend compatible JSON format. + const sdkTx = res.result.transaction + const camelToSnakeReplacer = (s: string) => { + return s.replace(/([A-Z])/g, '_$1').toLowerCase() + } + const camelToSnakeCloner = (obj: any, valueModifier: (key: any, value: any) => [any, any]) => { + return Object.keys(obj).reduce((acc: any, key) => { + const newKey = camelToSnakeReplacer(key) + const val = obj[key as keyof CKBComponents.Transaction] + if (Array.isArray(val)) { + acc[newKey] = val.map((v: any) => { + if (typeof v === 'object' && v !== null) { + return camelToSnakeCloner(v, valueModifier) + } + // eslint-disable-next-line @typescript-eslint/no-shadow + const [_, modVal] = valueModifier('', v) + return modVal + }) + } else if (typeof val === 'object' && val !== null) { + acc[newKey] = camelToSnakeCloner(val, valueModifier) + } else { + const [modKey, modVal] = valueModifier(newKey, val) + acc[modKey] = modVal + } + return acc + }, {}) + } + const bigIntStringToHex = (s: string) => { + return `0x${BigInt(s).toString(16)}` + } + const compatibleTx = camelToSnakeCloner(sdkTx, (key: any, value: any) => { + let newValue = value + switch (key) { + case 'dep_type': + newValue = camelToSnakeReplacer(value) + break + case 'since': + newValue = `0x${value}` + break + case 'input_index': + newValue = `0x${value}` + break + case 'index': + newValue = `0x${value}` + break + case 'capacity': + newValue = bigIntStringToHex(value) + break + case 'version': + newValue = `0x${value}` + break + default: + } + return [key, newValue] + }) + await respondPerunRequest({ + type: 'SignTransaction', + response: { + data: JSON.stringify(compatibleTx), + }, + }) + setShowPrompt(false) + setShowPasswordDialog(false) + return res + } + + switch (perunState.type) { + case 'SignMessage': + return handleSignMessage(perunState.request) + case 'SignTransaction': + return handleSignTransaction(perunState.request) as any + default: + } + } + + const handleUpdateNotification = async (request: any) => { + console.log('handleUpdateNotification', request) + const decodedState = wire.State.decode(request.encodedState) + const channelId = channelIdToString(decodedState.id) + const channel = channels.get(channelId) + if (!channel) { + showErrorMessage('Error', `channel ${channelId} not found`) + return + } + + // Update accepted channel update. + channel.version = decodedState.version + channel.allocation = decodedState.allocation + channel.isFinal = decodedState.isFinal + channels.set(channelId, channel) + + await respondPerunRequest({ + type: 'UpdateNotification', + response: { + data: true, + }, + }) + } + + const meAsParticipant = (myAddress: string, myPubKey: string) => { + const sec1bytes = bytes.bytify(`0x${myPubKey}`) + const lockScript = addressToScript(myAddress) + + const serializedPubKey = SerializeSEC1EncodedPubKey(sec1bytes.buffer) + const serializableScript = { + code_hash: bytes.bytify(lockScript.codeHash).buffer, + hash_type: blockchain.HashType.pack(lockScript.hashType), + args: bytes.bytify(lockScript.args).buffer, + } + + const buf = SerializeOffChainParticipant({ + payment_script: serializableScript, + unlock_script: serializableScript, + pub_key: serializedPubKey, + }) + return new Uint8Array(buf) + } + + const encodeCkbAddress = (address: string) => { + return Uint8Array.from(Buffer.from(address)) + } + + const equalNumPaddedHex = (num: bigint) => { + const hex = num.toString(16) + const res = hex.length % 2 === 0 ? hex : `0${hex}` + return `0x${res}` + } + + const handleOpenChannel = async () => { + const myAddress = wallet.addresses[0].address + const res = await getCurrentWalletAccountExtendedPubKey({ type: 0, index: 0 }) + if (!isSuccessResponse(res)) { + handleRejected((res.message as any).content) + return + } + const myPubKey = res.result + const myBalanceCKB = amount! + const myBalanceShannon = equalNumPaddedHex(BigInt(myBalanceCKB * 1e8)) + const peerBalanceCKB = peerAmount! + const peerBalanceShannon = equalNumPaddedHex(BigInt(peerBalanceCKB * 1e8)) + console.log('myaddress', myAddress) + console.log('myPubKey', myPubKey) + const actionRes = await perunServiceAction({ + type: 'open', + payload: { + me: meAsParticipant(myAddress, myPubKey), + peer: encodeCkbAddress(peerAddress), + balances: [bytes.bytify(myBalanceShannon), bytes.bytify(peerBalanceShannon)], + challengeDuration: Number(challengeDuration) as number, + }, + }) + if (!isSuccessResponse(actionRes)) { + showErrorMessage('Error', errorFormatter(actionRes.message, t)) + } + } + + const handleUpdateChannel = async (channelId: Uint8Array, swapAmount: bigint) => { + console.log('HANDLE UPDATE CHANNEL') + const res = await perunServiceAction({ + type: 'update', + payload: { + channelId: channelIdToString(channelId), + index: 0, + amount: swapAmount, + }, + }) + console.log('HANDLE UPDATE CHANNEL RES: ', res) + if (!isSuccessResponse(res)) { + handleRejected(res.message as string) + return + } + // If we could update the chanenl, we cache the updated channel state. + const updatedChannelState = res.result.state + const channel = channels.get(channelIdToString(channelId)) + if (!channel) { + showErrorMessage('Error', `channel ${channelId} not found`) + return + } + channel.version = updatedChannelState.version + channel.allocation = updatedChannelState.allocation + channel.isFinal = updatedChannelState.isFinal + channels.set(channelIdToString(channelId), channel) + } + + const handleCloseChannel = async (channelId: Uint8Array) => { + const res = await perunServiceAction({ + type: 'close', + payload: { + channelId, + }, + }) + + if (!isSuccessResponse(res)) { + handleRejected(res.message as string) + return + } + channels.delete(channelIdToString(res.result.channelId)) + } + + const getChannels = async () => { + console.log('Getting channels') + const myAddress = wallet.addresses[0].address + const res = await getCurrentWalletAccountExtendedPubKey({ type: 0, index: 0 }) + if (!isSuccessResponse(res)) { + handleRejected((res.message as any).content) + return + } + const myPubKey = res.result + const actionRes = await perunServiceAction({ + type: 'get', + payload: { + requester: meAsParticipant(myAddress, myPubKey), + }, + }) + if (!isSuccessResponse(actionRes)) { + return + } + if (!actionRes.result) { + return + } + // console.log('GET CHANNELS result: ', JSON.stringify(actionRes)) + const id = actionRes.result.channels.id.data + const { version } = actionRes.result.channels + const { app } = actionRes.result.channels + const alloc = wire.Allocation.create({ + assets: [new Uint8Array(32)], + balances: wire.Balances.create({ + balances: [ + { + balance: [ + actionRes.result.channels.allocation.balances.balances[0].balance[0].data, + actionRes.result.channels.allocation.balances.balances[0].balance[1].data, + ], + }, + ], + }), + locked: [], + }) + const { data } = actionRes.result.channels + const { isFinal } = actionRes.result.channels + + const state = wire.State.create({ + id, + version, + app, + allocation: alloc, + data, + isFinal, + }) + + channels.set(channelIdToString(id), state) + } + setInterval(getChannels, 50000) + + const rejectPerunRequest = async (type: 'SignMessage' | 'SignTransaction' | 'UpdateNotification', reason: string) => { + await respondPerunRequest({ + type, + response: { + rejected: { + reason, + }, + data: undefined, + }, + }) + } + + const renderPerunRequest = useCallback( + (state: Subject.PerunState) => { + if (!state.request) { + return <>No request + } + + switch (state.type) { + case 'SignMessage': { + const addressBytes = state.request.pubkey.data + const address = new TextDecoder().decode(new Uint8Array(addressBytes)) + return ( + <> +
Address:
+
{address}
+
Message:
+
{bytesToHex(new Uint8Array(state.request.data.data))}
+ + ) + } + case 'SignTransaction': { + const { identifier, transaction } = state.request as { + identifier: CKBComponents.Script + transaction: CKBComponents.Transaction + } + const addr = scriptToAddress(identifier, false) + console.log('sign transaction identifier', addr) + return ( + <> +
With address:
+
{JSON.stringify(addr)}
+
Transaction:
+
{JSON.stringify(transaction)}
+ + ) + } + case 'UpdateNotification': { + console.log('UpdateNotification request: ', state.request) + const ps = state.request.state + const id = ps.id.data + const { version } = ps + const alloc = wire.Allocation.create({ + assets: [new Uint8Array(32)], + balances: wire.Balances.create({ + balances: [ + { + balance: [ + ps.allocation.balances.balances[0].balance[0].data, + ps.allocation.balances.balances[0].balance[1].data, + ], + }, + ], + }), + locked: [], + }) + const { isFinal } = ps + return ( + <> +

Update Notification

+

{`Channel ID: ${channelIdToString(id)}`}

+

{`State: `}

+

{`Version: ${version}`}

+

{`Balances: A: ${bigintFromBEBytes(alloc.balances?.balances[0].balance[0]!)}, B: ${bigintFromBEBytes( + alloc.balances?.balances[0].balance[1]! + )}`}

+

{`IsFinal: ${isFinal}`}

+ + ) + } + default: + } + }, + [perunState] + ) + + return ( + Perun Payment Channels}> +
+
+ {showPrompt && ( +
+
+
+
{t(`perun.signing-request`)}
+
setShowPrompt(false)} + onKeyDown={e => { + if (e.key === 'Enter' || e.key === ' ') { + setShowPrompt(false) + } + }} + role="button" + tabIndex={0} + > + +
+
+
+
{t(`perun.signing-request-text`)}
+
{renderPerunRequest(perunState)}
+
+
+ + +
+
+ {showPasswordDialog && ( + + handleSigningRequest(pass).catch(err => rejectPerunRequest(perunState.type, err.message)) + } + onCancel={() => setShowPrompt(false)} + /> + )} +
+ )} +
+
+
+ + {t(`perun.insert-amount`)}: + { + handleAmountChange(event.currentTarget.value) + }} + /> + CKB + + + {t(`perun.peer-address`)} : + { + handlePeerAddressChange(event.currentTarget.value) + }} + /> + + + {t(`perun.peer-amount`)} : + { + handlePeerAmountChange(event.currentTarget.value) + }} + /> + + + {t(`perun.challenge-duration`)} : + { + handleChallengeDurationChange(event.currentTarget.value) + }} + /> + +
+
+ +
+
+ +
+
+ {t(`perun.channels`)} :{' '} +
+ {channels.size === 0 &&
{t(`perun.no-open`)}
} + {Array.from(channels).map(channel => ( + +
+

{`Channel ID: ${channelIdToString(channel[1].id)}`}

+

{`State: `}

+

{`Version: ${channel[1].version}`}

+

{`Balances: A: ${ + bigintFromBEBytes(channel[1].allocation?.balances?.balances[0].balance[0]!) / BigInt(1e8) + }, B: ${ + bigintFromBEBytes(channel[1].allocation?.balances?.balances[0].balance[1]!) / BigInt(1e8) + }`}

+

{`IsFinal: ${channel[1].isFinal}`}

+
+
+ + +
+
+ ))} +
+ handleCloseRejectionModal()}> + + {t(`perun.rejection-reason`)} : + + +

${rejectionReason}

+
+ + + +
+ {updateChannelDialog && channelID !== undefined && ( +
+ +

{t(`perun.enter-amount`)}

+ + Amount: + { + handleUpdateAmountChange(event.currentTarget.value) + }} + /> + CKB + + +
+ )} +
+
+ ) +} + +Perun.displayName = 'Perun' + +export default Perun diff --git a/packages/neuron-ui/src/components/Perun/perun.module.scss b/packages/neuron-ui/src/components/Perun/perun.module.scss new file mode 100644 index 0000000000..0759202371 --- /dev/null +++ b/packages/neuron-ui/src/components/Perun/perun.module.scss @@ -0,0 +1,412 @@ +@import '../../styles/mixin.scss'; + +.pageHead { + display: flex; + align-items: center; + gap: 8px; + + & > svg { + cursor: pointer; + + path { + fill: currentColor; + } + } +} + +.header { + display: flex; + flex: 1; + gap: 16px; + height:95%; +} + +.daoContainer { + display: flex; + width: 45%; + height: 100%; + flex-direction: column; + gap: 16px; +} + +.daoOverview { + position: relative; + z-index: 0; + display: grid; + flex: 1; + grid-template: + 'free free' auto + 'locked apc' auto/ + 1fr 1fr; + gap: 32px; + border-radius: 16px; + padding: 20px 16px; + background: var(--primary-color); + color: var(--primary-text-color); + + &::after { + $left: 47px; + $top: 12px; + + content: ' '; + display: block; + position: absolute; + z-index: -1; + left: $left; + top: $top; + width: calc(100% - $left * 2); + height: calc(100% - $top); + background: var(--primary-color); + filter: blur(20px); + opacity: 0.5; + } + + .field { + .name { + display: flex; + align-items: center; + font-weight: 500; + font-size: 14px; + line-height: 20px; + + .tooltip { + position: relative; + display: flex; + align-items: center; + margin-left: 5px; + + & > svg { + path { + fill: currentColor; + } + } + + &::after { + display: none; + position: absolute; + left: -20px; + bottom: 120%; + content: attr(data-tooltip); + padding: 4px 12px; + border-radius: 6px; + background: #cccccc99; + font-weight: 300; + } + + &:hover::after { + display: block; + } + } + } + + .value { + margin-top: 16px; + font-weight: 500; + font-size: 16px; + line-height: 22px; + + .number { + font-family: 'D-DIN-PRO'; + font-size: 20px; + line-height: 20px; + letter-spacing: 0.5px; + } + } + } + + .free { + grid-area: free; + + .value { + font-size: 16px; + line-height: 22px; + + .number { + font-size: 32px; + line-height: 32px; + } + } + } + + .locked { + grid-area: locked; + } + + .apc { + grid-area: apc; + } +} + +.perunPrompt { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent black background */ + z-index: 1000; /* Ensure it's above other content */ + display: flex; + align-items: center; + justify-content: center; +} + +.perunModal { + position: relative; + width: 500px; + background-color: #fff; + border-radius: 10px; + padding: 20px; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); + z-index: 999; + text-align: center; +} +.perunModalHeader { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} +.perunModalTitle { + font-size: 20px; + font-weight: 500; + color: #333; +} +.perunModalClose { + cursor: pointer; + color: #333; + font-size: 24px; + border: none; + background-color: transparent; +} +.perunModalBody { + margin-bottom: 20px; +} +.perunModalBodyMessage { + background-color: #f5f5f5; + border-radius: 10px; + padding: 20px; + margin-bottom: 20px; + text-align: left; + font-size: 14px; + font-weight: 300; + color: #333; + overflow: auto; + max-height: 200px; +} +.perunModalBodyText { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + color: #333; +} +.perunModalFooter { + display: flex; + justify-content: flex-end; + align-items: center; +} +.perunModalAcceptButton { + background: var(--primary-color); + color: white; + padding: 10px 20px; + border: none; + border-radius: 6px; + padding: 10px 20px; + cursor: pointer; + font-size: 16px; +} +.perunModalRejectButton { + background: transparent; + color: #aa0000; + padding: 10px 20px; + border: none; + border-radius: 6px; + padding: 10px 20px; + cursor: pointer; + font-size: 16px; +} + +.openChannel { + position: relative; + flex: 1; + border-radius: 16px; + padding: 20px 16px; + background: var(--secondary-background-color); + color: var(--primary-color); + display: flex; + flex-direction: column; + align-items: stretch; + + .formContainer { + display: flex; + gap: 10px; + flex-direction: column; + } + + .formGroup { + margin-bottom: 15px; + display: flex; + flex-direction: row; + margin: 10px; + align-items: center; + } + + .formLabel { + width: 150px; + text-align: left; + margin-right: 8px; + font-weight: 500; + } + + .formText { + width: 50px; + text-align: left; + margin-right: 8px; + font-weight: 500; + } + + .openButton { + margin-top: 70px; + position: bottom; + background: var(--primary-color); + color: white; + padding: 10px 20px; + border: none; + border-radius: 6px; + cursor: pointer; + } +} + +.channels { + position: relative; + flex: 1; + width: 50%; + flex-direction: column; + align-items: center; + justify-content: center; + border-radius: 16px; + background: var(--secondary-background-color); + color: var(--primary-color); + gap:20px; + overflow-y: auto; + overflow-x: hidden; + + .header { + height: 30px; + background-color: var(--primary-color); + border-radius: 16px; + align-items: center; + flex-direction: column; + padding-top: 7px; + color: #fff; + font-weight: bold; + font-size: larger; + } + .channel-container { + width: 100%; + height: 100%; + } + + .channel-entry { + display: flex; + align-items: center; + justify-content: space-between; + border-radius: 10px; + flex-direction: column; + padding: 10px; + background-color: #D3D3D3; + gap: 10px; + } + + .channel-info { + display: flex; + flex-direction: column; + align-items: flex-start; + overflow-x: auto; + } + + .info-button { + width: auto!important; + background: transparent; + border: none; + cursor: pointer; + } + + .channel-buttons { + display: flex; + flex-direction: row; + justify-content: space-between; + gap: 5px; + } + + .channel-button { + background-color: var(--primary-color); + color: #fff; + border: none; + border-radius: 5px; + padding: 5px 10px; + cursor: pointer; + margin: 0 5px; + width: 135px; + height: 40px; + } + + .channel-update-button { + align-self: center; + } + + .channel-close-button { + align-self: center; + } + + .channel-entry + .channel-entry { + margin-top: 10px; /* Adjust the margin value as needed */ + } +} + +.dialogContainer { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 300px; + background: white; + border-radius: 10px; + padding: 20px; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); + z-index: 999; + text-align: center; + overflow: auto; + max-height: 200px; + + .closeButton { + position: absolute; + top: 10px; + right: 10px; + cursor: pointer; + color: #333; + font-size: 24px; + border: none; + background-color: transparent; + } + + .formGroup { + margin-bottom: 20px; + } + + .updateButton { + background: var(--primary-color); + color: white; + padding: 10px 20px; + border: none; + border-radius: 6px; + padding: 10px 20px; + cursor: pointer; + font-size: 16px; + } + + @media (max-width: 768px) { + .dialog-container { + width: 90%; /* Adjust for smaller screens */ + } + } +} diff --git a/packages/neuron-ui/src/components/SignAndVerify/index.tsx b/packages/neuron-ui/src/components/SignAndVerify/index.tsx index d8cd73ea1d..da6db593fe 100644 --- a/packages/neuron-ui/src/components/SignAndVerify/index.tsx +++ b/packages/neuron-ui/src/components/SignAndVerify/index.tsx @@ -32,7 +32,7 @@ interface PasswordDialogProps { onSubmit: (pwd: string) => Promise } -const PasswordDialog = ({ show, walletName, onCancel, onSubmit }: PasswordDialogProps) => { +export const PasswordDialog = ({ show, walletName, onCancel, onSubmit }: PasswordDialogProps) => { const [t, i18n] = useTranslation() const [password, setPassword] = useState('') const [error, setError] = useState('') diff --git a/packages/neuron-ui/src/containers/Navbar/index.tsx b/packages/neuron-ui/src/containers/Navbar/index.tsx index 6c38bdbfe7..b4f3927f6c 100644 --- a/packages/neuron-ui/src/containers/Navbar/index.tsx +++ b/packages/neuron-ui/src/containers/Navbar/index.tsx @@ -7,7 +7,7 @@ import { VerifyExternalCkbNodeRes, checkForUpdates, getVersion, verifyExternalCk import { AppUpdater as AppUpdaterSubject } from 'services/subjects' import Badge from 'widgets/Badge' import Logo from 'widgets/Icons/Logo.png' -import { Overview, History, NervosDAO, Settings, Experimental, MenuExpand, ArrowNext } from 'widgets/Icons/icon' +import { Overview, History, NervosDAO, Perun, Settings, Experimental, MenuExpand, ArrowNext } from 'widgets/Icons/icon' import { RoutePath, clsx, isSuccessResponse, useOnLocaleChange } from 'utils' import Tooltip from 'widgets/Tooltip' @@ -19,6 +19,7 @@ const menuItems = [ { name: 'navbar.overview', key: RoutePath.Overview, url: RoutePath.Overview, icon: }, { name: 'navbar.history', key: RoutePath.History, url: RoutePath.History, icon: }, { name: 'navbar.nervos-dao', key: RoutePath.NervosDAO, url: RoutePath.NervosDAO, icon: }, + { name: 'navbar.perun', key: RoutePath.Perun, url: RoutePath.Perun, icon: }, { name: 'navbar.settings', key: RoutePath.Settings, url: RoutePath.Settings, icon: }, { name: 'navbar.experimental-functions', diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index 0720469ccc..1073f94e2a 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -17,6 +17,7 @@ "history": "History", "addresses": "Addresses", "nervos-dao": "Nervos DAO", + "perun": "Perun Channels", "settings": "Settings", "special-assets": "Customized Assets", "sync-not-start": "Sync not started yet", @@ -742,6 +743,29 @@ "cancel": "Cancel", "next": "Next" }, + "perun": { + "open-channel": "Open Perun Channel", + "insert-Addresses": "Insert the sender- and receiver-address", + "update-channel": "update Channel", + "close-channel": "close Channel", + "event-manager": "Channel events", + "insert-amount": "Insert the amount you want to deposit", + "accept": "Accept", + "reject": "Reject", + "amount": "amount", + "peer-address": "Peer Address", + "peer-amount": "Peer Amount", + "signing-request": "Signing Request", + "signing-request-text": "Received a request to sign the following message", + "challenge-duration": "Challenge duration", + "channels": "Perun Channels", + "no-open": "No open Channels yet", + "rejection-reason": "Rejection reason", + "ok": "Okay", + "enter-amount": "Please enter the amount you want to swap", + "state": "State" + + }, "lock-info-dialog": { "address-info": "Address Information", "deprecated-address": "Deprecated address" diff --git a/packages/neuron-ui/src/router.tsx b/packages/neuron-ui/src/router.tsx index 76114bd4f2..345f24679d 100644 --- a/packages/neuron-ui/src/router.tsx +++ b/packages/neuron-ui/src/router.tsx @@ -12,6 +12,7 @@ import HistoryDetailPage from 'components/HistoryDetailPage' import LaunchScreen from 'components/LaunchScreen' import PasswordRequest from 'components/PasswordRequest' import NervosDAO from 'components/NervosDAO' +import Perun from 'components/Perun' import NervosDAODetail from 'components/NervosDAODetail' import SpecialAssetList from 'components/SpecialAssetList' import SUDTAccountList from 'components/SUDTAccountList' @@ -183,6 +184,21 @@ const mainRouterConfig: RouteObject[] = [ }, ], }, + { + path: RoutePath.Perun, + children: [ + { + path: '', + element: ( + <> + + + + ), + children: [...toolsRouters], + }, + ], + }, { path: RoutePath.SpecialAssets, element: ( diff --git a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts index 3ca2ffdff6..385c860a91 100644 --- a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts +++ b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts @@ -56,6 +56,7 @@ type Action = | 'set-current-wallet' | 'import-mnemonic' | 'import-keystore' + | 'get-wallet-xpubkey' | 'create-wallet' | 'update-wallet' | 'delete-wallet' @@ -69,6 +70,7 @@ type Action = | 'generate-mnemonic' | 'validate-mnemonic' | 'sign-message' + | 'sign-raw-message' | 'verify-signature' // Transactions | 'get-transaction-list' @@ -158,6 +160,9 @@ type Action = | 'update-live-cell-local-info' | 'get-locked-balance' | 'update-live-cells-lock-status' + // Perun + | 'respond-perun-request' + | 'perun-service-action' export const remoteApi =

(action: Action) => diff --git a/packages/neuron-ui/src/services/remote/wallets.ts b/packages/neuron-ui/src/services/remote/wallets.ts index 89da4ef168..b1ba59542b 100644 --- a/packages/neuron-ui/src/services/remote/wallets.ts +++ b/packages/neuron-ui/src/services/remote/wallets.ts @@ -5,6 +5,7 @@ export const getCurrentWallet = remoteApi('get-current-wallet') export const setCurrentWallet = remoteApi('set-current-wallet') export const importMnemonic = remoteApi('import-mnemonic') export const importKeystore = remoteApi('import-keystore') +export const getCurrentWalletAccountExtendedPubKey = remoteApi('get-wallet-xpubkey') export const createWallet = remoteApi('create-wallet') export const updateWallet = remoteApi('update-wallet') export const deleteWallet = remoteApi('delete-wallet') @@ -30,4 +31,9 @@ export const generateDaoClaimTx = remoteApi('withdraw-fr // Sign and Verify export const signMessage = remoteApi('sign-message') +export const signRawMessage = remoteApi('sign-raw-message') export const verifyMessage = remoteApi('verify-signature') + +// Perun +export const respondPerunRequest = remoteApi('respond-perun-request') +export const perunServiceAction = remoteApi('perun-service-action') diff --git a/packages/neuron-ui/src/services/subjects.ts b/packages/neuron-ui/src/services/subjects.ts index b78aebf9d2..50f77cfe2c 100644 --- a/packages/neuron-ui/src/services/subjects.ts +++ b/packages/neuron-ui/src/services/subjects.ts @@ -31,7 +31,8 @@ const SubjectConstructor = ( | 'device-sign-index' | 'multisig-output-update' | 'migrate' - | 'show-global-dialog', + | 'show-global-dialog' + | 'perun-request', isMulti?: boolean ) => { return ipcRenderer @@ -69,6 +70,7 @@ export const SetLocale = SubjectConstructor<(typeof LOCALES)[number]>('set-local export const DeviceSignIndex = SubjectConstructor('device-sign-index') export const MultisigOutputUpdate = SubjectConstructor('multisig-output-update') export const Migrate = SubjectConstructor<'need-migrate' | 'migrating' | 'failed' | 'finish'>('migrate', true) +export const PerunState = SubjectConstructor('perun-request') export default { DataUpdate, @@ -78,6 +80,7 @@ export default { CurrentNetworkID, ConnectionStatus, SyncState, + PerunState, AppUpdater, Command, Navigation, diff --git a/packages/neuron-ui/src/states/init/index.ts b/packages/neuron-ui/src/states/init/index.ts index fda355db12..c299acadb3 100644 --- a/packages/neuron-ui/src/states/init/index.ts +++ b/packages/neuron-ui/src/states/init/index.ts @@ -21,6 +21,8 @@ export const initStates = { updater, experimental: null, sUDTAccounts: [], + // TODO: Remove this dummy. + perunState: { request: { hello: 'world' } }, } export default initStates diff --git a/packages/neuron-ui/src/stories/Icon.stories.tsx b/packages/neuron-ui/src/stories/Icon.stories.tsx index 3a4f77fb4a..685fb6ddd0 100644 --- a/packages/neuron-ui/src/stories/Icon.stories.tsx +++ b/packages/neuron-ui/src/stories/Icon.stories.tsx @@ -7,6 +7,7 @@ const backgroundForIcons: Record = { Experimental: '#aaa', History: '#aaa', NervosDAO: '#aaa', + Perun: '#aaa', Overview: '#aaa', Send: '#aaa', Receive: '#aaa', diff --git a/packages/neuron-ui/src/types/App/index.d.ts b/packages/neuron-ui/src/types/App/index.d.ts index 3916fb842d..4159079ade 100644 --- a/packages/neuron-ui/src/types/App/index.d.ts +++ b/packages/neuron-ui/src/types/App/index.d.ts @@ -322,6 +322,10 @@ declare namespace State { } } + interface PerunState { + request: any + } + interface AppWithNeuronWallet { app: App chain: Chain @@ -331,6 +335,7 @@ declare namespace State { updater: AppUpdater sUDTAccounts: SUDTAccount[] experimental: Experimental | null + perunState: PerunState consumeCells?: { outPoint: OutPoint; capacity: string }[] } diff --git a/packages/neuron-ui/src/types/Controller/index.d.ts b/packages/neuron-ui/src/types/Controller/index.d.ts index 012dd89cac..3ea788db3b 100644 --- a/packages/neuron-ui/src/types/Controller/index.d.ts +++ b/packages/neuron-ui/src/types/Controller/index.d.ts @@ -24,6 +24,11 @@ declare namespace Controller { password: string } + interface GetAddressPubKey { + type: number + index: number + } + interface ImportKeystoreParams { name: string keystorePath: string @@ -153,6 +158,39 @@ declare namespace Controller { feeRate: string } + interface OpenChannelParams { + me: Uint8Array + peer: Uint8Array + balances: [Uint8Array, Uint8Array] + challengeDuration: number + } + interface UpdateChannelParams { + channelId: string + index: number + amount: bigint + } + interface CloseChannelParams { + channelId: Uint8Array + } + interface GetChannelParams { + requester: Uint8Array + } + interface PerunServiceActionParams { + type: 'open' | 'update' | 'close' | 'get' + payload: OpenChannelParams | UpdateChannelParams | CloseChannelParams | GetChannelParams + } + interface RespondPerunRequestParams { + type: 'SignMessage' | 'SignTransaction' | 'UpdateNotification' + response: { rejected?: { reason: string }; data: any } + } + + interface SignRawMessageParams { + walletID: string + address: string + password: string + message: string + } + interface SignMessageParams { walletID: string address: string diff --git a/packages/neuron-ui/src/types/Subject/index.d.ts b/packages/neuron-ui/src/types/Subject/index.d.ts index fcba2123dd..6fd91dcefe 100644 --- a/packages/neuron-ui/src/types/Subject/index.d.ts +++ b/packages/neuron-ui/src/types/Subject/index.d.ts @@ -69,4 +69,9 @@ declare namespace Subject { type: 'success' | 'failed' | 'warning' action?: 'ok' | 'cancel' } + + interface PerunState { + type: 'SignMessage' | 'SignTransaction' | 'UpdateNotification' + request?: any + } } diff --git a/packages/neuron-ui/src/utils/enums.ts b/packages/neuron-ui/src/utils/enums.ts index ce71adb71e..872d46d57c 100644 --- a/packages/neuron-ui/src/utils/enums.ts +++ b/packages/neuron-ui/src/utils/enums.ts @@ -28,6 +28,7 @@ export enum RoutePath { OfflineSign = 'offline-sign', SignVerify = 'sign-verify', MultisigAddress = 'multisig-address', + Perun = '/perun', } export enum CapacityUnit { diff --git a/packages/neuron-ui/src/widgets/Icons/Perun.svg b/packages/neuron-ui/src/widgets/Icons/Perun.svg new file mode 100644 index 0000000000..e5301dce19 --- /dev/null +++ b/packages/neuron-ui/src/widgets/Icons/Perun.svg @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/packages/neuron-ui/src/widgets/Icons/icon.tsx b/packages/neuron-ui/src/widgets/Icons/icon.tsx index 464dc6e7d1..5ed6ddc5e1 100644 --- a/packages/neuron-ui/src/widgets/Icons/icon.tsx +++ b/packages/neuron-ui/src/widgets/Icons/icon.tsx @@ -51,6 +51,7 @@ import { ReactComponent as LockSvg } from './Lock.svg' import { ReactComponent as LockCellSvg } from './LockCell.svg' import { ReactComponent as UnLockSvg } from './Unlock.svg' import { ReactComponent as ConsumeSvg } from './Consume.svg' +import { ReactComponent as PerunSvg } from './Perun.svg' import styles from './icon.module.scss' @@ -118,3 +119,4 @@ export const Lock = WrapSvg(LockSvg, styles.withTheme) export const LockCell = WrapSvg(LockCellSvg) export const UnLock = WrapSvg(UnLockSvg) export const Consume = WrapSvg(ConsumeSvg) +export const Perun = WrapSvg(PerunSvg) diff --git a/packages/neuron-ui/webpack.config.js b/packages/neuron-ui/webpack.config.js new file mode 100644 index 0000000000..ef4ff83138 --- /dev/null +++ b/packages/neuron-ui/webpack.config.js @@ -0,0 +1,37 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path') + +module.exports = { + entry: './src/index.tsx', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'bundle.js', + }, + module: { + rules: [ + { + test: /\.(ts|tsx|js)$/, + use: { + loader: 'babel-loader', + options: { + plugins: ['@babel/plugin-proposal-class-properties'], + }, + }, + exclude: /node_modules/, + }, + { + enforce: 'pre', + test: /\.ts$/, + loader: 'eslint-loader', + exclude: /node_modules/, + }, + ], + }, + mode: 'production', + resolve: { + fallback: { + crypto: require.resolve('crypto-browserify'), + stream: require.resolve('stream-browserify'), + }, + }, +} diff --git a/packages/neuron-wallet/package.json b/packages/neuron-wallet/package.json index e7d7aca14f..177234a391 100644 --- a/packages/neuron-wallet/package.json +++ b/packages/neuron-wallet/package.json @@ -84,7 +84,7 @@ "@types/electron-devtools-installer": "2.2.5", "@types/elliptic": "6.4.18", "@types/iarna__toml": "2.0.5", - "@types/jest-when": "3.5.2", + "@types/jest-when": "3.5.5", "@types/ledgerhq__hw-transport": "4.21.8", "@types/ledgerhq__hw-transport-node-hid": "4.22.5", "@types/leveldown": "4.0.6", @@ -96,8 +96,8 @@ "electron-build-env": "0.2.0", "electron-builder": "23.6.0", "electron-devtools-installer": "3.2.0", - "jest-when": "3.5.2", + "jest-when": "3.6.0", "neuron-ui": "0.112.0", - "typescript": "5.3.2" + "typescript": "5.3.3" } } diff --git a/packages/neuron-wallet/src/controllers/anyone-can-pay.ts b/packages/neuron-wallet/src/controllers/anyone-can-pay.ts index b74267ea19..6ce97115b5 100644 --- a/packages/neuron-wallet/src/controllers/anyone-can-pay.ts +++ b/packages/neuron-wallet/src/controllers/anyone-can-pay.ts @@ -21,7 +21,7 @@ export interface SendAnyoneCanPayTxParams { walletID: string tx: Transaction password: string - skipLastInputs?: boolean + skipLastInput?: boolean } export default class AnyoneCanPayController { @@ -64,7 +64,7 @@ export default class AnyoneCanPayController { params.walletID, txModel, params.password, - params?.skipLastInputs ?? true, + params?.skipLastInput ?? true, skipSign ) diff --git a/packages/neuron-wallet/src/controllers/api.ts b/packages/neuron-wallet/src/controllers/api.ts index 45f5f4c8f2..a41b0188ab 100644 --- a/packages/neuron-wallet/src/controllers/api.ts +++ b/packages/neuron-wallet/src/controllers/api.ts @@ -64,6 +64,8 @@ import { resetSyncTaskQueue } from '../block-sync-renderer' import DataUpdateSubject from '../models/subjects/data-update' import CellManagement from './cell-management' import { UpdateCellLocalInfo } from '../database/chain/entities/cell-local-info' +import PerunController from './perun' +import { AddressType } from '@ckb-lumos/hd' export type Command = 'export-xpubkey' | 'import-xpubkey' | 'delete-wallet' | 'backup-wallet' | 'migrate-acp' // Handle channel messages from renderer process and user actions. @@ -80,11 +82,13 @@ export default class ApiController { #offlineSignController = new OfflineSignController() #sudtController = new SUDTController() #multisigController = new MultisigController() + #perunController = new PerunController() public async mount() { this.#registerHandlers() await this.#networksController.start() + await this.#perunController.start() nativeTheme.themeSource = SettingsService.getInstance().themeSource } @@ -327,6 +331,10 @@ export default class ApiController { return this.#walletsController.importKeystore(params) }) + handle('get-wallet-xpubkey', async (_, params: { type: AddressType; index: number }) => { + return this.#walletsController.getCurrentAccountExtendedPubKey(params.type, params.index) + }) + handle('create-wallet', async (_, params: { name: string; password: string; mnemonic: string }) => { return this.#walletsController.create(params) }) @@ -645,8 +653,20 @@ export default class ApiController { return this.#signAndVerifyController.verify(params) }) - // sUDT + // Perun + handle('sign-raw-message', async (_, params: Controller.Params.SignRawParams) => { + return this.#signAndVerifyController.signRaw(params) + }) + handle('respond-perun-request', async (_, params: Controller.Params.RespondPerunRequestParams) => { + return this.#perunController.respondPerunRequest(params) + }) + + handle('perun-service-action', async (_, params: Controller.Params.PerunServiceActionParams) => { + return this.#perunController.perunServiceAction(params) + }) + + // sUDT handle('get-anyone-can-pay-script', () => { return this.#anyoneCanPayController.getScript() }) diff --git a/packages/neuron-wallet/src/controllers/app/index.ts b/packages/neuron-wallet/src/controllers/app/index.ts index 888e087d19..80140eabab 100644 --- a/packages/neuron-wallet/src/controllers/app/index.ts +++ b/packages/neuron-wallet/src/controllers/app/index.ts @@ -15,6 +15,8 @@ import SyncApiController from '../../controllers/sync-api' import { SETTINGS_WINDOW_TITLE } from '../../utils/const' import { stopCkbNode } from '../../services/ckb-runner' import { CKBLightRunner } from '../../services/light-runner' +import { PerunServiceRunner } from '../../services/perun/service-runner' +import PerunController from '../perun' const app = electronApp @@ -48,6 +50,7 @@ export default class AppController { */ await this.apiController.mount() SyncApiController.getInstance().mount() + PerunController.getInstance().mount() await this.openWindow() } @@ -59,7 +62,7 @@ export default class AppController { if (env.isTestMode) { return } - await Promise.all([stopCkbNode(), CKBLightRunner.getInstance().stop()]) + await Promise.all([stopCkbNode(), CKBLightRunner.getInstance().stop(), PerunServiceRunner.getInstance().stop()]) } public registerChannels(win: BrowserWindow, channels: string[]) { diff --git a/packages/neuron-wallet/src/controllers/app/subscribe.ts b/packages/neuron-wallet/src/controllers/app/subscribe.ts index 8de629a2cd..a9e01b6ad8 100644 --- a/packages/neuron-wallet/src/controllers/app/subscribe.ts +++ b/packages/neuron-wallet/src/controllers/app/subscribe.ts @@ -16,6 +16,8 @@ import MigrateSubject from '../../models/subjects/migrate-subject' import startMonitor, { stopMonitor } from '../../services/monitor' import { clearCkbNodeCache } from '../../services/ckb-runner' import ShowGlobalDialogSubject from '../../models/subjects/show-global-dialog' +import PerunRequestSubject from '../../models/subjects/perun' +import logger from '../../utils/logger' interface AppResponder { sendMessage: (channel: string, arg: any) => void @@ -51,6 +53,12 @@ export const subscribe = (dispatcher: AppResponder) => { dispatcher.runCommand('migrate-acp', '') }) + PerunRequestSubject.pipe(debounceTime(50)).subscribe(request => { + // Forward backend signing requests to the renderer process. + logger.info('forwarding backend signing request to the renderer process') + dispatcher.sendMessage('perun-request', request) + }) + CommandSubject.subscribe(params => { if (params.dispatchToUI) { BrowserWindow.getFocusedWindow()?.webContents.send('command', params) diff --git a/packages/neuron-wallet/src/controllers/offline-sign.ts b/packages/neuron-wallet/src/controllers/offline-sign.ts index 1ca40ba019..84e4697b42 100644 --- a/packages/neuron-wallet/src/controllers/offline-sign.ts +++ b/packages/neuron-wallet/src/controllers/offline-sign.ts @@ -15,6 +15,7 @@ import { getMultisigStatus } from '../utils/multisig' import { generateRPC } from '../utils/ckb-rpc' import ShowGlobalDialogSubject from '../models/subjects/show-global-dialog' import NetworksService from '../services/networks' +import logger from '../utils/logger' export default class OfflineSignController { public async exportTransactionAsJSON({ @@ -94,13 +95,25 @@ export default class OfflineSignController { context ) } else { - tx = await new TransactionSender().sign( - walletID, - Transaction.fromObject(transaction), - password, - type === SignType.SendSUDT, - context - ) + tx = await new TransactionSender() + .sign(walletID, Transaction.fromObject(transaction), password, type === SignType.SendSUDT, context) + .then(({ tx: t, metadata }) => { + // TODO: maybe unidentified inputs can be skipped in offline sign + if (metadata.locks.skipped.size) { + try { + throw new Error( + `Fail to sign transaction, following lock scripts cannot be identified: ${[ + ...metadata.locks.skipped.values(), + ]}` + ) + } catch (err) { + // FIXME: remove this before being merged into develop branch + logger.debug('Ignore the following error for debugging polycrypt') + logger.error(err) + } + } + return t + }) } const signer = OfflineSign.fromJSON({ diff --git a/packages/neuron-wallet/src/controllers/perun/index.ts b/packages/neuron-wallet/src/controllers/perun/index.ts new file mode 100644 index 0000000000..84b31fcb4c --- /dev/null +++ b/packages/neuron-wallet/src/controllers/perun/index.ts @@ -0,0 +1,180 @@ +import EventEmitter from 'events' +import PerunRequestSubject from '../../models/subjects/perun' +import PerunService from '../../services/perun/service' +import logger from '../../utils/logger' +import { ResponseCode } from '../../utils/const' +import { SimpleChannelServiceClient } from '@polycrypt/perun-wallet-wrapper/services' +import { AddressEncoder, channelIdFromString, channelIdToString } from '@polycrypt/perun-wallet-wrapper/translator' +import { mkSimpleChannelServiceClient } from '@polycrypt/perun-wallet-wrapper/client' +import { bytes } from '@ckb-lumos/codec' +import { Allocation, Balances } from '@polycrypt/perun-wallet-wrapper/wire' + +const defaultAddressEncoder: AddressEncoder = (add: Uint8Array | string) => { + if (typeof add === 'string') { + return bytes.bytify(add) + } + return add +} + +export default class PerunController { + static emiter = new EventEmitter() + private static instance: PerunController + private static serviceClient: SimpleChannelServiceClient + + public static getInstance() { + if (!PerunController.instance) { + PerunController.instance = new PerunController() + PerunController.serviceClient = PerunController.mkClient() + } + return PerunController.instance + } + + public async start() { + return PerunService.getInstance().start() + } + + public mount() { + this.registerHandlers() + } + + private registerHandlers = () => { + PerunController.emiter.on('perun-request', req => { + logger.info('PerunController: received perun request', req) + PerunRequestSubject.next(req) + }) + } + + public respondPerunRequest(params: Controller.Params.RespondPerunRequestParams): Promise { + if (!PerunController.emiter.emit('perun-response', params)) { + return Promise.reject(new Error('Failed to send perun response, no listener registered')) + } + return Promise.resolve({ + status: ResponseCode.Success, + }) + } + + public perunServiceAction(params: Controller.Params.PerunServiceActionParams): Promise { + switch (params.type) { + case 'open': + return this.openChannel(params.payload as Controller.Params.OpenChannelParams) + case 'update': + return this.updateChannel(params.payload as Controller.Params.UpdateChannelParams) + case 'close': + return this.closeChannel(params.payload as Controller.Params.CloseChannelParams) + case 'get': + return this.getChannels(params.payload as Controller.Params.GetChannelsParams) + default: + return Promise.reject(new Error('Invalid perun service action type')) + } + } + + // Create a new client for each call, in case the connection break for some reason. + private static mkClient(): SimpleChannelServiceClient { + const rpcEndpoint = 'http://localhost:42025' + return mkSimpleChannelServiceClient(defaultAddressEncoder, rpcEndpoint) + } + + async openChannel(params: Controller.Params.OpenChannelParams): Promise { + const alloc = Allocation.create({ + assets: [new Uint8Array(32)], + balances: Balances.create({ + balances: [ + { + balance: params.balances, + }, + ], + }), + }) + const res = await PerunController.serviceClient + .openChannel(params.me, params.peer, alloc, params.challengeDuration) + .catch(e => { + return { + rejected: { + reason: e.message, + }, + channelId: undefined, + } + }) + if (res.rejected) { + return { + status: ResponseCode.Fail, + message: res.rejected.reason, + } + } + const channelId = channelIdToString(new Uint8Array(res.channelId!)) + console.log('Controler Buffer channelId', res.channelId!) + console.log('Controler channelID', channelId) + return { + status: ResponseCode.Success, + result: { + channelId: channelId, + alloc: alloc, + }, + } + } + + async updateChannel(params: Controller.Params.UpdateChannelParams): Promise { + const res = await PerunController.serviceClient + .updateChannel(channelIdFromString(params.channelId), params.index, params.amount) + .catch(e => { + return { + rejected: { + reason: e.message, + }, + update: undefined, + } + }) + + if (res.rejected) { + return { + status: ResponseCode.Fail, + message: res.rejected.reason, + } + } + + const state = res.update!.state! + + return { + status: ResponseCode.Success, + result: { + state: state, + }, + } + } + + async closeChannel(params: Controller.Params.CloseChannelParams): Promise { + const res = await PerunController.serviceClient.closeChannel(params.channelId) + + if (res.rejected) { + return { + status: ResponseCode.Fail, + message: res.rejected.reason, + } + } + + return { + status: ResponseCode.Success, + result: { + channelId: res.close!.channelId!, + }, + } + } + + async getChannels(params: Controller.Params.GetChannelsParams): Promise { + const res = await PerunController.serviceClient.getChannels(params.requester) + + if (res.rejected) { + return { + status: ResponseCode.Fail, + message: res.rejected.reason, + } + } + + return { + status: ResponseCode.Success, + result: { + channels: res.state, + }, + } + } +} diff --git a/packages/neuron-wallet/src/controllers/sign-message.ts b/packages/neuron-wallet/src/controllers/sign-message.ts index a81916b7f6..22ad120f0d 100644 --- a/packages/neuron-wallet/src/controllers/sign-message.ts +++ b/packages/neuron-wallet/src/controllers/sign-message.ts @@ -3,6 +3,23 @@ import { ServiceHasNoResponse } from '../exceptions' import { ResponseCode } from '../utils/const' export default class SignMessageController { + public async signRaw(params: Controller.Params.SignRawParams): Promise> { + const signature: string = await SignMessage.signRaw( + params.walletID, + params.address.trim(), + params.password, + params.message + ) + if (!signature) { + throw new ServiceHasNoResponse('SignRaw') + } + + return { + status: ResponseCode.Success, + result: signature, + } + } + public async sign(params: Controller.Params.SignParams): Promise> { const signature: string = await SignMessage.sign( params.walletID, diff --git a/packages/neuron-wallet/src/controllers/wallets.ts b/packages/neuron-wallet/src/controllers/wallets.ts index 1870a1e25c..e40b34dea5 100644 --- a/packages/neuron-wallet/src/controllers/wallets.ts +++ b/packages/neuron-wallet/src/controllers/wallets.ts @@ -33,6 +33,7 @@ import { DeviceInfo, ExtendedPublicKey } from '../services/hardware/common' import AddressParser from '../models/address-parser' import MultisigConfigModel from '../models/multisig-config' import { generateRPC } from '../utils/ckb-rpc' +import { AddressType } from '@ckb-lumos/hd' export default class WalletsController { public async getAll(): Promise[]>> { @@ -147,6 +148,21 @@ export default class WalletsController { } } + public async getCurrentAccountExtendedPubKey(type: AddressType, index: number): Promise> { + const walletsService = WalletsService.getInstance() + const wallet = walletsService.getCurrent() + if (!wallet) { + return { + status: ResponseCode.Fail, + } + } + const accountExtendedPublicKey = wallet.accountExtendedPublicKey() + return { + status: ResponseCode.Success, + result: accountExtendedPublicKey.addressPublicKey(type, index), + } + } + public async importKeystore({ name, password, @@ -176,6 +192,7 @@ export default class WalletsController { accountKeychain.publicKey.toString('hex'), accountKeychain.chainCode.toString('hex') ) + logger.info(`extended-pubkey: ${accountExtendedPublicKey.serialize()}`) const walletsService = WalletsService.getInstance() const wallet = walletsService.create({ diff --git a/packages/neuron-wallet/src/models/keys/key.ts b/packages/neuron-wallet/src/models/keys/key.ts index 5e5ae9c939..e8b9d5bbb2 100644 --- a/packages/neuron-wallet/src/models/keys/key.ts +++ b/packages/neuron-wallet/src/models/keys/key.ts @@ -60,7 +60,7 @@ export class AccountExtendedPublicKey extends ExtendedPublicKey { return Address.fromPublicKey(this.addressPublicKey(type, index), Address.pathFor(type, index), isMainnet) } - private addressPublicKey = (type = AddressType.Receiving, index: number) => { + public addressPublicKey = (type = AddressType.Receiving, index: number) => { const keychain = Keychain.fromPublicKey( Buffer.from(this.publicKey, 'hex'), Buffer.from(this.chainCode, 'hex'), diff --git a/packages/neuron-wallet/src/models/subjects/perun.ts b/packages/neuron-wallet/src/models/subjects/perun.ts new file mode 100644 index 0000000000..dfd533a94c --- /dev/null +++ b/packages/neuron-wallet/src/models/subjects/perun.ts @@ -0,0 +1,6 @@ +import { BehaviorSubject } from 'rxjs' + +// TODO: Add type. +const PerunRequestSubject = new BehaviorSubject({}) + +export default PerunRequestSubject diff --git a/packages/neuron-wallet/src/services/hardware/hardware.ts b/packages/neuron-wallet/src/services/hardware/hardware.ts index 20af74e74c..a0db5a5435 100644 --- a/packages/neuron-wallet/src/services/hardware/hardware.ts +++ b/packages/neuron-wallet/src/services/hardware/hardware.ts @@ -25,12 +25,12 @@ export abstract class Hardware { walletID: string, tx: Transaction, txHash: string, - skipLastInputs: boolean = true, + skipLastInput: boolean = true, context?: RPC.RawTransaction[] ) { const wallet = WalletService.getInstance().get(walletID) const addressInfos = await AddressService.getAddressesByWalletId(walletID) - const witnessSigningEntries = tx.inputs.slice(0, skipLastInputs ? -1 : tx.inputs.length).map((input, index) => { + const witnessSigningEntries = tx.inputs.slice(0, skipLastInput ? -1 : tx.inputs.length).map((input, index) => { const lockArgs: string = input.lock!.args! const wit: WitnessArgs | string = tx.witnesses[index] const witnessArgs: WitnessArgs = wit instanceof WitnessArgs ? wit : WitnessArgs.generateEmpty() @@ -60,6 +60,7 @@ export abstract class Hardware { } else if (args.length === 42) { return addressInfos.find(i => i.blake160 === args)!.path } else { + // FIXME: should not be a fallback const addressInfo = AssetAccountInfo.findSignPathForCheque(addressInfos, args) return addressInfo!.path } @@ -67,6 +68,13 @@ export abstract class Hardware { const lockHashes = new Set(witnessSigningEntries.map(w => w.lockHash)) + const metadata = { + locks: { + skipped: new Set(), + }, + skipLastInput, + } + for (const [index, lockHash] of [...lockHashes].entries()) { DeviceSignIndexSubject.next(index) const witnessesArgs = witnessSigningEntries.filter(w => w.lockHash === lockHash) @@ -75,6 +83,17 @@ export abstract class Hardware { const path = findPath(witnessesArgs[0].lockArgs) + if (!path) { + metadata.locks.skipped.add(lockHash) + witnessSigningEntries.forEach((entry, idx) => { + if (entry.lockHash === lockHash) { + const rawWitness = tx.witnesses[idx] + entry.witness = typeof rawWitness === 'string' ? rawWitness : serializeWitnessArgs(rawWitness) + } + }) + continue + } + if (isMultisig) { const serializedWitnesses = witnessesArgs.map(value => { const args = value.witnessArgs @@ -131,7 +150,7 @@ export abstract class Hardware { tx.witnesses = witnessSigningEntries.map(w => w.witness || '0x') tx.hash = txHash - return tx + return { tx, metadata } } public abstract getPublicKey(path: string): Promise diff --git a/packages/neuron-wallet/src/services/perun/server/index.ts b/packages/neuron-wallet/src/services/perun/server/index.ts new file mode 100644 index 0000000000..467d9535ce --- /dev/null +++ b/packages/neuron-wallet/src/services/perun/server/index.ts @@ -0,0 +1,20 @@ +import { mkWalletServiceServer } from '@polycrypt/perun-wallet-wrapper/services' +import { IPCWalletBackend } from './wallet-backend' + +// Entry point for the GRPC wallet service. +function main() { + try { + const localUrl = '127.0.0.1:50051' + const backend = new IPCWalletBackend() + const server = mkWalletServiceServer(backend, () => { + return {} + }) + server.listen(localUrl).then(port => { + console.log(`Wallet service listening on localhost on port ${port}`) + }) + } catch (e) { + console.error('GRPC:', e) + } +} + +main() diff --git a/packages/neuron-wallet/src/services/perun/server/wallet-backend.ts b/packages/neuron-wallet/src/services/perun/server/wallet-backend.ts new file mode 100644 index 0000000000..286f2db9d6 --- /dev/null +++ b/packages/neuron-wallet/src/services/perun/server/wallet-backend.ts @@ -0,0 +1,123 @@ +import { bytes } from '@ckb-lumos/codec' +import { UpdateNotificationRequest, SignTransactionRequest } from '@polycrypt/perun-wallet-wrapper/perun-wallet' +import { WalletBackend } from '@polycrypt/perun-wallet-wrapper/services' +import { ValidOpenChannelRequest, ValidSignMessageRequest } from '@polycrypt/perun-wallet-wrapper/verifier' + +export type IPCMessageRequest = + | 'openChannelRequest' + | 'updateNotificationRequest' + | 'signMessageRequest' + | 'signTransactionRequest' +export type IPCMessageResponse = + | 'openChannelResponse' + | 'updateNotificationResponse' + | 'signMessageResponse' + | 'signTransactionResponse' + +// The IPCWalletBackend is a WalletBackend that uses IPC to communicate with +// the wallet. It expects to be run in a separate process from the wallet but +// communicates with it via IPC. +export class IPCWalletBackend implements WalletBackend<{}> { + openChannelRequest( + req: ValidOpenChannelRequest + ): Promise<{ rejected?: { reason?: string }; nonceShare?: Uint8Array }> { + // Create a new Promise which resovles if the response from the IPC parent process is received. + return new Promise((resolve, reject) => { + // Send the request to the IPC parent process. + console.log('IPCWalletBackend: sending openChannelRequest to HOST process') + const res = process.send!({ type: 'openChannelRequest', req }) + if (!res) { + return reject(new Error('Failed to send IPC message')) + } + + console.log('IPCWalletBackend: waiting for HOST process response') + // Listen for the response from the IPC parent process. + // TODO: Unsubscribe from the message listener when the promise is resolved. + process.once('message', (message: { type: IPCMessageResponse; req: unknown }) => { + console.log('IPCWalletBackend: received HOST process response', message) + if (message.type === 'openChannelResponse') { + // Resolve the promise with the response. + return resolve(message.req as any) + } + }) + }) + } + + updateNotificationRequest(req: UpdateNotificationRequest): Promise<{ accepted?: boolean | undefined }> { + return new Promise((resolve, _) => { + // Make sure we send the serialized state, otherwise we have to account for weird IPC serialization issues. + console.log("wallet: updateNotificationRequest: state = ", req.state!) + //const encodedState = wire.State.encode(req.state!).finish() + //console.log("wallet: updateNotificationRequest: encodedState = ", encodedState) + + /*const res = process.send!({ + // type: 'updateNotificationRequest', + req + }) + if (!res) { + return reject(new Error('Failed to send IPC message')) + }*/ + resolve({ accepted: true }) + + /*process.once('message', (message: { type: IPCMessageResponse; req: unknown }) => { + if (message.type === 'updateNotificationResponse') { + return resolve(message.req as any) + } + })*/ + }) + } + + signMessageRequest( + req: ValidSignMessageRequest<{}> + ): Promise<{ rejected?: { reason?: string | undefined } | undefined; signature?: Uint8Array | undefined }> { + return new Promise((resolve, reject) => { + const res = process.send!({ type: 'signMessageRequest', req }) + if (!res) { + return reject(new Error('Failed to send IPC message')) + } + + process.once('message', (message: { type: IPCMessageResponse; req: any }) => { + if (message.type === 'signMessageResponse') { + console.info('received signMessageResponse', message) + // The resulting signature is string, so we convert it back into a + // Uint8Array. + return resolve({ + signature: bytes.bytify(message.req.signature as string), + }) + } + console.error("signMessageRequest: message.type != 'signMessageResponse'", message) + }) + }) + } + + signTransactionRequest( + req: SignTransactionRequest + ): Promise<{ rejected?: { reason?: string | undefined } | undefined; transaction?: Uint8Array | undefined }> { + return new Promise((resolve, reject) => { + console.log('signTransactionRequest', req) + console.log(new Uint8Array((req as any).identifier)) + console.log(new Uint8Array((req as any).transaction)) + const stringifiedReq: any = { + identifier: new TextDecoder('utf-8').decode(new Uint8Array((req as any).identifier)), + transaction: new TextDecoder('utf-8').decode(new Uint8Array((req as any).transaction)), + } + console.log('Sending signTransactionRequest', stringifiedReq) + const res = process.send!({ + type: 'signTransactionRequest', + req: stringifiedReq, + }) + if (!res) { + return reject(new Error('Failed to send IPC message')) + } + + process.once('message', (message: { type: IPCMessageResponse; req: unknown }) => { + if (message.type === 'signTransactionResponse') { + console.log('Received signTransactionResponse', message) + return resolve({ + transaction: new TextEncoder().encode((message.req as any).transaction), + }) + } + }) + }) + } +} diff --git a/packages/neuron-wallet/src/services/perun/service-runner.ts b/packages/neuron-wallet/src/services/perun/service-runner.ts new file mode 100644 index 0000000000..d336ffd52a --- /dev/null +++ b/packages/neuron-wallet/src/services/perun/service-runner.ts @@ -0,0 +1,362 @@ +import crypto from 'crypto' + +import { ChildProcess, fork } from 'child_process' +import fs from 'fs' +import logger from '../../utils/logger' +import { IPCMessageRequest, IPCMessageResponse } from './server/wallet-backend' +import path from 'path' +import { WalletBackend } from '@polycrypt/perun-wallet-wrapper/services' +import PerunController from '../../controllers/perun' +import { bytes } from '@ckb-lumos/codec' +import Script from '../../models/chain/script' +import Transaction from '../../models/chain/transaction' +import Input from '../../models/chain/input' +import CellsService from '../../services/cells' +import OutPoint from '../../models/chain/out-point' +import RpcService from '../../services/rpc-service' +import { TransactionsService } from '../tx' +import NetworksService from '../networks' + +// Architecture overview: +// +// [ChannelService] <-via RPC-> [PerunServiceServer] <-via IPC-> [Neuron] +export class PerunServiceRunner { + private static instance: PerunServiceRunner + + protected runnerProcess?: ChildProcess + + private logStream?: fs.WriteStream + + static getInstance(): PerunServiceRunner { + if (!PerunServiceRunner.instance) { + logger.info('Creating new PerunServiceRunner instance') + PerunServiceRunner.instance = new PerunServiceRunner() + } + return PerunServiceRunner.instance + } + + async start() { + if (this.runnerProcess) { + logger.info('PerunServiceRunner already started, shutting down first...') + await this.stop() + } + + this.runnerProcess = this.spawnProcess() + + if (!this.logStream) { + this.logStream = fs.createWriteStream('perun-service.log') + } + + this.runnerProcess.stderr && + this.runnerProcess.stderr.on('data', data => { + logger.error(`PerunServiceRunner stderr: ${data}`) + this.logStream?.write(data) + }) + + this.runnerProcess.stdout && + this.runnerProcess.stdout.on('data', data => { + logger.info(`PerunServiceRunner stdout: ${data}`) + this.logStream?.write(data) + }) + + this.runnerProcess.on('error', error => { + logger.error('PerunServiceRunner error:', error) + this.runnerProcess?.kill() + this.runnerProcess = undefined + }) + + this.runnerProcess.on('close', code => { + logger.info(`PerunServiceRunner exited with code ${code}`) + this.runnerProcess = undefined + }) + + this.runnerProcess.on('message', this.ipcMessageHandler) + } + + private ipcMessageHandler = (message: { type: IPCMessageRequest; req: unknown }) => { + // TODO: Properly type the req paramter. E.g. use an indexed type derived from the WalletBackend interface. + switch (message.type) { + case 'openChannelRequest': + return this.handleOpenChannelRequest(message.req as any) + case 'updateNotificationRequest': + return this.handleUpdateNotificationRequest(message.req as any) + case 'signMessageRequest': + return this.handleSignMessageRequest(message.req as any) + case 'signTransactionRequest': + return this.handleSignTransactionRequest(message.req as any) + default: { + logger.info('Unknown IPC message type', message.type) + } + } + logger.info('PerunServiceRunner received unexpected IPC message', message) + } + + private ipcReturn(type: IPCMessageResponse, req: unknown) { + return new Promise((resolve, reject) => { + this.runnerProcess?.send({ type, req }, error => { + if (error) { + logger.error('PerunServiceRunner failed to send IPC message', error) + reject(error) + } else { + logger.info('PerunServiceRunner successfully sent IPC message') + resolve() + } + }) + }) + } + + private handleOpenChannelRequest(_req: Parameters['openChannelRequest']>[0]) { + // Validate the request. + // this.validateOpenChannelRequest(req) + logger.info('PerunServiceRunner received openChannelRequest') + const nonceShare = new Uint8Array(32) + crypto.getRandomValues(nonceShare) + this.ipcReturn('openChannelResponse', { + nonceShare, + }) + } + + private handleUpdateNotificationRequest(req: Parameters['updateNotificationRequest']>[0]) { + logger.info('PerunServiceRunner received updateNotificationRequest', req) + return new Promise((resolve, reject) => { + if ( + !PerunController.emiter.emit('perun-request', { + type: 'UpdateNotification', + request: req, + }) + ) { + return reject(new Error('Failed to send perun request, no listener registered')) + } + + PerunController.emiter.once('perun-response', (res: Controller.Params.RespondPerunRequestParams) => { + console.log('PerunServiceRunner received updateNotificationResponse', res) + if (res.response.rejected) { + this.ipcReturn('updateNotificationResponse', { + rejected: { + reason: res.response.rejected.reason, + }, + }) + return resolve() + } + + this.ipcReturn('updateNotificationResponse', { + accepted: res.response.data, + }) + resolve() + }) + }) + } + + private handleSignMessageRequest(req: Parameters['signMessageRequest']>[0]) { + return new Promise((resolve, reject) => { + if ( + !PerunController.emiter.emit('perun-request', { + type: 'SignMessage', + request: req, + }) + ) { + return reject(new Error('Failed to send perun request, no listener registered')) + } + + PerunController.emiter.once('perun-response', (res: Controller.Params.RespondPerunRequestParams) => { + if (res.response.rejected) { + this.ipcReturn('signMessageResponse', { + rejected: { + reason: res.response.rejected.reason, + }, + }) + return resolve() + } + + // walletSig is a recoverable signature + // + // 0x + <32-byte-r> + <32-byte-s> + <8-byte-recover> + // + // has to be transformed into a DER encoded signature. + const walletSig: string = res.response.data + // Strip `0x` prefix if present. + const sig = walletSig.startsWith('0x') ? walletSig.slice(2) : walletSig + // r and s values are padded with 0 prefix if they are less than 32 bytes. + // We need to remove the padding. + const tmp_r = bytes.bytify('0x' + sig.slice(0, 64).replace(/^(00)+/, '')) + const first_byte = tmp_r[0] + let r: Uint8Array = new Uint8Array() + if ((first_byte & 0x80) >= 0x80) { + logger.info("Padding R with '00'") + r = new Uint8Array([0x00]) + } + r = bytes.concat(r, tmp_r) + + const s = bytes.bytify('0x' + sig.slice(64, 128).replace(/^(00)+/, '')) + logger.info(`full signature: ${sig}`) + logger.info(`r before stripping padding: ${sig.slice(0, 64)}`) + logger.info(`s before stripping padding: ${sig.slice(64, 128)}`) + logger.info(`r after stripping padding: ${bytes.hexify(r)}`) + logger.info(`s after stripping padding: ${bytes.hexify(s)}`) + const numberToHexString = (num: number) => { + const hex = num.toString(16) + return hex.length === 1 ? '0' + hex : hex + } + const derSig = `0x30${numberToHexString(0x04 + r.length + s.length)}02${numberToHexString(r.length)}${bytes + .hexify(r) + .slice(2)}02${numberToHexString(s.length)}${bytes.hexify(s).slice(2)}` + + // Pad the signature to 73 bytes if it is shorter than that. + // MarkerByte = 0xff + // Examples: + // Input: + // Output: | MarkerByte | ZeroByte | ZeroByte + // + // Input: + // Output: | MarkerByte + // + // We always append the marker byte and only pad with zero bytes if the signature is shorter than 72 bytes. + const paddedSig = `${derSig}${'ff'}${'00'.repeat(72 - derSig.slice(2).length / 2)}` + this.ipcReturn('signMessageResponse', { + signature: paddedSig, + }) + resolve() + }) + }) + } + + private handleSignTransactionRequest(req: Parameters['signTransactionRequest']>[0]) { + return new Promise(async (resolve, reject) => { + logger.info('PerunServiceRunner received signTransactionRequest', req) + // Transform IPC malformed request into a valid request. + const snakeCaseToCamelCase = (_: string, value: any): any => { + if (Array.isArray(value)) { + return value.map(item => snakeCaseToCamelCase(_, item)) + } + + const toCamelCase = (str: string) => { + return str.replace(/_([a-z])/g, function (_, group1) { + return group1.toUpperCase() + }) + } + + if (typeof value === 'object' && value !== null) { + const camelCasedObject: any = {} + for (const originalKey in value) { + if (value.hasOwnProperty(originalKey)) { + const camelCasedKey = toCamelCase(originalKey) + const originalValue = value[originalKey] + const newValue = camelCasedKey === 'depType' ? toCamelCase(originalValue) : originalValue + camelCasedObject[camelCasedKey] = newValue + } + } + return camelCasedObject + } + return value + } + const sdkScript = JSON.parse(req.identifier as any, snakeCaseToCamelCase) + // TODO: The transaction here has unresolved inputs, which are only referenced by their outpoints. + // - Transaction.inputs have to be resolved. + // - Transaction.computeHash() has to work. + // -> Rest seems fine. src/models/chain/transaction.ts + let sdkTx = JSON.parse(req.transaction as any, snakeCaseToCamelCase) + // Fetch live cells from txs input-outpoints. + let resolvedInputs = [] + const network = NetworksService.getInstance().getCurrent() + const rpcService = new RpcService(network.remote, network.type) + for (const [idx, input] of sdkTx.inputs.entries()) { + let typedInput = input as { previousOutput: { txHash: string; index: string }; since: string } + logger.info('Fetching live cell', input.previousOutput) + // Try to fetch the live cell from the database multiple times before giving up. + let retries = 0 + const delay = 5_000 // 5 seconds + let liveCell = undefined + while (retries < 25) { + const fetchedCell = await CellsService.getLiveCell(OutPoint.fromObject(input.previousOutput)) + const outputs = await CellsService.getOutputsByTransactionHash(input.previousOutput.txHash) + logger.info('fetched outputs:', outputs) + const tx = await TransactionsService.get(input.previousOutput.txHash) + logger.info('fetched tx:', tx) + if (fetchedCell) { + liveCell = fetchedCell + break + } + logger.info('USING RPC-SERVICE') + const rpcTip = await rpcService.getTipHeader() + logger.info('TIP:', rpcTip) + const rpcTx = await rpcService.getTransaction(input.previousOutput.txHash) + logger.info('RPC-TX:', rpcTx) + if (rpcTx) { + logger.info("rpc output's index:", input.previousOutput.index) + logger.info('rpc outputs', rpcTx.transaction.outputs) + liveCell = rpcTx.transaction.outputs[Number(input.previousOutput.index)] + logger.info("live cell found in rpc-tx's outputs:", liveCell) + break + } + logger.info(`Failed to fetch live cell, retrying in ${delay}ms`) + await new Promise(resolve => setTimeout(resolve, delay)) + retries++ + } + + if (!liveCell) { + return reject(new Error('Failed to fetch live cell')) + } + + const resolvedInput = Input.fromObject({ + previousOutput: OutPoint.fromObject(typedInput.previousOutput), + since: typedInput.since, + capacity: liveCell.capacity, + lock: liveCell.lock, + lockHash: liveCell.lockHash, + multiSignBlake160: liveCell.multiSignBlake160, + type: liveCell.type, + typeHash: liveCell.typeHash, + data: liveCell.data, + }) + resolvedInput.setInputIndex(idx.toString()) + resolvedInputs.push(resolvedInput) + } + logger.info('Resolved inputs', resolvedInputs) + logger.info('Transformed request', { sdkScript, sdkTx }) + const identifier = Script.fromSDK(sdkScript) + const transaction = Transaction.fromSDK(sdkTx) + // Update the transaction with the resolved inputs. + // Has to happen after `fromSDK` call, because `fromSDK` expects less + // data than available and ignores the resolved inputs completely. + transaction.inputs = resolvedInputs + logger.info(`tx hash: ${transaction.computeHash()}`) + let validReq = { identifier, transaction } + logger.info('FINAL INPUTS', validReq.transaction.inputs) + if ( + !PerunController.emiter.emit('perun-request', { + type: 'SignTransaction', + request: validReq, + }) + ) { + return reject(new Error('Failed to send perun request, no listener registered')) + } + + PerunController.emiter.once('perun-response', (res: Controller.Params.RespondPerunRequestParams) => { + if (res.response.rejected) { + this.ipcReturn('signTransactionResponse', { + rejected: { + reason: res.response.rejected.reason, + }, + }) + return resolve() + } + const signedTx = res.response.data + this.ipcReturn('signTransactionResponse', { transaction: signedTx }) + resolve() + }) + }) + } + + private spawnProcess(): ChildProcess { + const grpcModulePath = path.join(__dirname, 'server/index.js') + return fork(grpcModulePath, [], { + stdio: ['ipc', process.stdout, 'pipe'], + }) + } + + async stop() { + this.runnerProcess?.kill() + } + + // TODO: CKBNode probably executes its starting twice. +} diff --git a/packages/neuron-wallet/src/services/perun/service.ts b/packages/neuron-wallet/src/services/perun/service.ts new file mode 100644 index 0000000000..9770cac340 --- /dev/null +++ b/packages/neuron-wallet/src/services/perun/service.ts @@ -0,0 +1,28 @@ +// PerunService is responsible for starting and stopping the Wallet-Backend + +import logger from '../../utils/logger' +import { PerunServiceRunner } from './service-runner' + +// GRPC server. +export default class PerunService { + private static instance: PerunService + + static getInstance(): PerunService { + if (!PerunService.instance) { + logger.info('Creating new PerunService instance') + PerunService.instance = new PerunService() + } + + return PerunService.instance + } + + // Start the GRPC server hosting the wallet-backend API. + public async start() { + logger.info('Starting PerunService runner') + try { + await PerunServiceRunner.getInstance().start() + } catch (error) { + logger.error('Failed to start PerunService GRPC server', error) + } + } +} diff --git a/packages/neuron-wallet/src/services/sign-message.ts b/packages/neuron-wallet/src/services/sign-message.ts index 96787d0ef2..341202c005 100644 --- a/packages/neuron-wallet/src/services/sign-message.ts +++ b/packages/neuron-wallet/src/services/sign-message.ts @@ -7,6 +7,9 @@ import { ec as EC } from 'elliptic' import { AddressNotFound } from '../exceptions' import HardwareWalletService from './hardware' import AddressParser from '../models/address-parser' +import blake2b from 'blake2b' +import { bytes } from '@ckb-lumos/codec' +import logger from '../utils/logger' export default class SignMessage { static GENERATE_COUNT = 100 @@ -38,12 +41,45 @@ export default class SignMessage { return this.signByPrivateKey(privateKey, message) } + // Allows signing messages with custom magic strings. + public static async signRaw(walletID: string, address: string, password: string, message: string): Promise { + const wallet = WalletService.getInstance().get(walletID) + const addresses = await AddressService.getAddressesByWalletId(walletID) + let addr = addresses.find(addr => addr.address === address) + if (!addr) { + throw new AddressNotFound() + } + + if (wallet.isHardware()) { + let device = HardwareWalletService.getInstance().getCurrent() + if (!device) { + const deviceInfo = wallet.getDeviceInfo() + device = await HardwareWalletService.getInstance().initHardware(deviceInfo) + await device.connect() + } + const messagehex = Buffer.from(message, 'utf-8').toString('hex') + return device.signMessage(addr.path, messagehex) + } + + // find private key of address + const privateKey = this.getPrivateKey(wallet, addr.path, password) + + return this.signByPrivateKeyWithMagic(privateKey, message) + } + private static signByPrivateKey(privateKey: string, message: string): string { const digest = SignMessage.signatureHash(message) const signature = hd.key.signRecoverable(digest, privateKey) return signature } + private static async signByPrivateKeyWithMagic(privateKey: string, message: string) { + const digest = await SignMessage.customSignatureHash(message) + logger.info(`Signing Digest: ${digest}`) + const signature = hd.key.signRecoverable(digest, privateKey) + return signature + } + public static verifyOldAndNew( address: string, signature: string, @@ -64,6 +100,16 @@ export default class SignMessage { } } + private static async customSignatureHash(message: string) { + logger.info(`Signing Message: ${message}`) + const buffer = bytes.bytify(message) + const hasher = blake2b(32) // Blake2b-256 + hasher.update(buffer) + const out = new Uint8Array(32) + hasher.digest(out) + return bytes.hexify(out.buffer) + } + private static verify(address: string, signature: string, digest: string): boolean { try { const options = { diff --git a/packages/neuron-wallet/src/services/transaction-sender.ts b/packages/neuron-wallet/src/services/transaction-sender.ts index 208739a2d0..2057f9ccd8 100644 --- a/packages/neuron-wallet/src/services/transaction-sender.ts +++ b/packages/neuron-wallet/src/services/transaction-sender.ts @@ -67,12 +67,27 @@ export default class TransactionSender { walletID: string = '', transaction: Transaction, password: string = '', - skipLastInputs: boolean = true, + skipLastInput: boolean = true, skipSign = false ) { const tx = skipSign ? Transaction.fromObject(transaction) - : await this.sign(walletID, transaction, password, skipLastInputs) + : await this.sign(walletID, transaction, password, skipLastInput).then(({ tx, metadata }) => { + try { + if (metadata.locks.skipped.size) { + throw new Error( + `Fail to send transaction, following lock scripts cannot be identified: ${[ + ...metadata.locks.skipped.values(), + ]} ` + ) + } + } catch (err) { + // FIXME: remove this before being merged into develop branch + logger.debug('Ignore the following error for debugging polycrypt') + logger.error(err) + } + return tx + }) return this.broadcastTx(walletID, tx) } @@ -109,13 +124,21 @@ export default class TransactionSender { walletID: string = '', transaction: Transaction, password: string = '', - skipLastInputs: boolean = true, + skipLastInput: boolean = true, context?: RPC.RawTransaction[] ) { + logger.info('json tx:', JSON.stringify(transaction)) + logger.info(`trying to sign transaction with wallet ${walletID}`) const wallet = this.walletService.get(walletID) const tx = Transaction.fromObject(transaction) + logger.info('tx to sign:', tx) + + logger.info('computing tx hash') const txHash: string = tx.computeHash() + + logger.info(`tx hash is: ${txHash}`) if (wallet.isHardware()) { + logger.info('signing with hardware wallet') let device = HardwareWalletService.getInstance().getCurrent() if (!device) { const wallet = WalletService.getInstance().getCurrent() @@ -124,7 +147,7 @@ export default class TransactionSender { await device.connect() } try { - return await device.signTx(walletID, tx, txHash, skipLastInputs, context) + return await device.signTx(walletID, tx, txHash, skipLastInput, context) } catch (err) { if (err instanceof TypeError) { throw err @@ -132,11 +155,13 @@ export default class TransactionSender { throw new SignTransactionFailed(err.message) } } + logger.info('TX INPUTS', tx.inputs) // Only one multi sign input now. const isMultisig = tx.inputs.length === 1 && tx.inputs[0].lock!.args.length === TransactionSender.MULTI_SIGN_ARGS_LENGTH + logger.info("retrieving addresses' private keys") const addressInfos = await this.getAddressInfos(walletID) const multiSignBlake160s = isMultisig ? addressInfos.map(i => { @@ -149,25 +174,28 @@ export default class TransactionSender { const paths = addressInfos.map(info => info.path) const pathAndPrivateKeys = this.getPrivateKeys(wallet, paths, password) const findPrivateKey = (args: string) => { + logger.log('inside findPrivateKey, args: ', args) let path: string | undefined if (args.length === TransactionSender.MULTI_SIGN_ARGS_LENGTH) { - path = multiSignBlake160s.find(i => args.slice(0, 42) === i.multiSignBlake160)!.path + path = multiSignBlake160s.find(i => args.slice(0, 42) === i.multiSignBlake160)?.path } else if (args.length === 42) { - path = addressInfos.find(i => i.blake160 === args)!.path + path = addressInfos.find(i => i.blake160 === args)?.path } else { - const addressInfo = AssetAccountInfo.findSignPathForCheque(addressInfos, args) - path = addressInfo?.path - } - - const pathAndPrivateKey = pathAndPrivateKeys.find(p => p.path === path) - if (!pathAndPrivateKey) { - throw new Error('no private key found') + // FIXME: should not be a fallback + try { + const addressInfo = AssetAccountInfo.findSignPathForCheque(addressInfos, args) + path = addressInfo?.path + } catch { + logger.error(`Failed to find args: ${args}`) + } } - return pathAndPrivateKey.privateKey + logger.log('inside findPrivateKey, path: ', path) + return pathAndPrivateKeys.find(p => p.path === path)?.privateKey } + logger.info('preparing witness signing entries') const witnessSigningEntries: SignInfo[] = tx.inputs - .slice(0, skipLastInputs ? -1 : tx.inputs.length) + .slice(0, skipLastInput ? -1 : tx.inputs.length) .map((input: Input, index: number) => { const lockArgs: string = input.lock!.args! const wit: WitnessArgs | string = tx.witnesses[index] @@ -181,15 +209,39 @@ export default class TransactionSender { } }) + logger.info('accumulating lock hashes') const lockHashes = new Set(witnessSigningEntries.map(w => w.lockHash)) + const metadata = { + locks: { + skipped: new Set(), + }, + skipLastInput, + } + for (const lockHash of lockHashes) { + logger.info(`processing lock hash: ${lockHash}`) const witnessesArgs = witnessSigningEntries.filter(w => w.lockHash === lockHash) - // A 65-byte empty signature used as placeholder - witnessesArgs[0].witnessArgs.setEmptyLock() + logger.info(`finding private key for args: ${witnessesArgs[0].lockArgs}`) const privateKey = findPrivateKey(witnessesArgs[0].lockArgs) + if (!privateKey) { + logger.info(`no private key found for args: ${witnessesArgs[0].lockArgs}`) + metadata.locks.skipped.add(lockHash) + witnessSigningEntries.forEach((entry, idx) => { + if (entry.lockHash === lockHash) { + const rawWitness = tx.witnesses[idx] + entry.witness = typeof rawWitness === 'string' ? rawWitness : serializeWitnessArgs(rawWitness) + } + }) + continue + } + + // A 65-byte empty signature used as placeholder + witnessesArgs[0].witnessArgs.setEmptyLock() + + logger.info(`serializing witnesses`) const serializedWitnesses: (WitnessArgs | string)[] = witnessesArgs.map((value: SignInfo, index: number) => { const args = value.witnessArgs if (index === 0) { @@ -218,6 +270,7 @@ export default class TransactionSender { wit.lock = serializedMultisig + wit.lock!.slice(2) signed[0] = serializeWitnessArgs(wit.toSDK()) } else { + logger.info('signing witnesses') signed = signWitnesses({ privateKey, transactionHash: txHash, @@ -235,10 +288,11 @@ export default class TransactionSender { } } + logger.info('setting witnesses') tx.witnesses = witnessSigningEntries.map(w => w.witness) tx.hash = txHash - return tx + return { tx, metadata } } public async signMultisig( @@ -266,7 +320,7 @@ export default class TransactionSender { } else { pathAndPrivateKeys = this.getPrivateKeys(wallet, paths, password) } - const findPrivateKeyAndBlake160 = (argsList: string[], signedBlake160s?: string[]) => { + const findPrivateKeyAndBlake160 = (argsList: string[], signedBlake160s?: string[]): [string, string] => { let path: string | undefined let matchArgs: string | undefined argsList.some(args => { @@ -278,13 +332,18 @@ export default class TransactionSender { path = matchAddress?.path matchArgs = matchAddress?.blake160 } else { - const addressInfo = AssetAccountInfo.findSignPathForCheque(addressInfos, args) - path = addressInfo?.path - matchArgs = addressInfo?.blake160 + // FIXME: should not be a fallback + try { + const addressInfo = AssetAccountInfo.findSignPathForCheque(addressInfos, args) + path = addressInfo?.path + matchArgs = addressInfo?.blake160 + } catch { + logger.error(`Failed to find args: ${args}`) + } } return !!path }) - if (!path) { + if (!path || !matchArgs) { throw new NoMatchAddressForSign() } if (!pathAndPrivateKeys) { diff --git a/packages/neuron-wallet/src/services/wallets.ts b/packages/neuron-wallet/src/services/wallets.ts index ac993ce4d0..5e3f6cab9b 100644 --- a/packages/neuron-wallet/src/services/wallets.ts +++ b/packages/neuron-wallet/src/services/wallets.ts @@ -90,7 +90,7 @@ export abstract class Wallet { } public accountExtendedPublicKey = (): AccountExtendedPublicKey => { - throw new WalletFunctionNotSupported(this.accountExtendedPublicKey.name) + return AccountExtendedPublicKey.parse(this.extendedKey) } public update = ({ diff --git a/packages/neuron-wallet/src/types/controller.d.ts b/packages/neuron-wallet/src/types/controller.d.ts index ea54bc7f0f..ffec5305ce 100644 --- a/packages/neuron-wallet/src/types/controller.d.ts +++ b/packages/neuron-wallet/src/types/controller.d.ts @@ -61,12 +61,45 @@ declare namespace Controller { password: string } + interface SignRawParams { + walletID: string + address: string + password: string + message: string + } + interface SignParams { walletID: string address: string password: string message: string } + interface OpenChannelParams { + me: Uint8Array + peer: Uint8Array + balances: [Uint8Array, Uint8Array] + challengeDuration: number + } + interface UpdateChannelParams { + channelId: string + index: number + amount: bigint + } + interface CloseChannelParams { + channelId: Uint8Array + } + interface GetChannelsParams { + requester: Uint8Array + } + interface PerunServiceActionParams { + type: 'open' | 'update' | 'close' | 'get' + payload: OpenChannelParams | UpdateChannelParams | CloseChannelParams | GetChannelParams + } + + interface RespondPerunRequestParams { + type: 'SignMessage' | 'SignTransaction' | 'UpdateNotification' + response: { rejected?: { reason: string }; data: any } + } interface VerifyParams { address: string diff --git a/packages/neuron-wallet/tests/controllers/anyone-can-pay.test.ts b/packages/neuron-wallet/tests/controllers/anyone-can-pay.test.ts index dd906cea2d..bee9d07b2e 100644 --- a/packages/neuron-wallet/tests/controllers/anyone-can-pay.test.ts +++ b/packages/neuron-wallet/tests/controllers/anyone-can-pay.test.ts @@ -96,7 +96,7 @@ describe('anyone-can-pay-controller', () => { walletID: 'string', tx: new Transaction('', [], [], [], [], []), password: 'string', - skipLastInputs: false, + skipLastInput: false, } it('throw exception', async () => { sendTxMock.mockResolvedValueOnce(undefined) diff --git a/packages/neuron-wallet/tests/controllers/offline-sign.test.ts b/packages/neuron-wallet/tests/controllers/offline-sign.test.ts index 7bd987a1c1..5d1d7bfd65 100644 --- a/packages/neuron-wallet/tests/controllers/offline-sign.test.ts +++ b/packages/neuron-wallet/tests/controllers/offline-sign.test.ts @@ -115,6 +115,8 @@ describe('OfflineSignController', () => { }, } + const mockTransactionMetadata = { locks: { skipped: new Set() } } + const mockTxInstance = { toSDKRawTransaction() { return mockTransaction @@ -347,7 +349,9 @@ describe('OfflineSignController', () => { filePath: 'filePath.json', }) - stubbedTransactionSenderSign.mockReturnValue(mockTransaction) + stubbedTransactionSenderSign.mockReturnValue( + Promise.resolve({ tx: mockTransaction, metadata: mockTransactionMetadata }) + ) }) it('sign status should change to `signed`', async () => { @@ -413,7 +417,9 @@ describe('OfflineSignController', () => { filePath: 'filePath.json', }) - stubbedTransactionSenderSign.mockReturnValue(mockTransaction) + stubbedTransactionSenderSign.mockReturnValue( + Promise.resolve({ tx: mockTransaction, metadata: mockTransactionMetadata }) + ) }) it('should signed', async () => { @@ -486,7 +492,7 @@ describe('OfflineSignController', () => { describe('signAndBroadcastTransaction', () => { beforeEach(() => { resetMocks() - signMultisigMock.mockReturnValue(mockTransaction) + signMultisigMock.mockReturnValue({ tx: mockTransaction, metadata: mockTransactionMetadata }) }) it('throw exception', async () => { getMultisigStatusMock.mockReturnValueOnce(SignStatus.PartiallySigned) diff --git a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts index d52372dc3a..62974c8b29 100644 --- a/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts +++ b/packages/neuron-wallet/tests/services/tx/transaction-sender.test.ts @@ -204,6 +204,7 @@ import MultisigConfigModel from '../../../src/models/multisig-config' import Multisig from '../../../src/models/multisig' import { addressToScript } from '../../../src/utils/scriptAndAddress' import { serializeWitnessArgs } from '../../../src/utils/serialization' +import { signWitnesses } from '../../../src/utils/signWitnesses' const fakeScript = new Script( '0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8', @@ -343,7 +344,7 @@ describe('TransactionSender Test', () => { describe('#sign', () => { describe('single sign', () => { - const tx = Transaction.fromObject({ + const tx = { version: '0x0', cellDeps: [ CellDep.fromObject({ @@ -393,13 +394,49 @@ describe('TransactionSender Test', () => { witnesses: [ '0x55000000100000005500000055000000410000003965f54cc684d35d886358ad57214e5f4a5fd13ecc7aba67950495b9be7740267a1d6bb14f1c215e3bc926f9655648b75e173ce6f5fd1e60218383b45503c30301', ], - hash: '0x230ab250ee0ae681e88e462102e5c01a9994ac82bf0effbfb58d6c11a86579f1', - }) + } it('success', async () => { - const ntx = await transactionSender.sign(fakeWallet.id, tx, '1234', false) + const res = await transactionSender.sign(fakeWallet.id, Transaction.fromObject(tx), '1234', false) + expect(res.tx.witnesses[0]).toEqual(tx.witnesses[0]) + expect(res.metadata.locks.skipped.size).toEqual(0) + expect(res.metadata.skipLastInput).toEqual(false) + }) + + it('sign with unidentified lock scripts skipped', async () => { + const FIXTURE = { + lock: Input.fromObject({ + previousOutput: OutPoint.fromObject({ + txHash: '0x1879851943fa686af29bed5c95acd566d0244e7b3ca89cf7c435622a5a5b4cb3', + index: '0x0', + }), + since: '0x0', + lock: Script.fromObject({ + args: '0x0000000000000000000000000000000000000000', + codeHash: '0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8', + hashType: 'type' as ScriptHashType, + }), + }), + witness: new WitnessArgs('0x01'), + } + + const txObj = Transaction.fromObject({ + ...tx, + inputs: [...tx.inputs, FIXTURE.lock], + witnesses: [new WitnessArgs('0x00'), FIXTURE.witness], + }) + + const signedWitnesses = signWitnesses({ + witnesses: txObj.witnesses.slice(0, -1), + transactionHash: txObj.computeHash(), + privateKey: pathAndPrivateKey.privateKey, + }) - expect(ntx.witnesses[0]).toEqual(tx.witnesses[0]) + const res = await transactionSender.sign(fakeWallet.id, txObj, '1234', false) + expect(res.tx.witnesses.slice(0, -1)).toEqual(signedWitnesses) + expect(res.tx.witnesses.slice(-1)).toEqual([serializeWitnessArgs(FIXTURE.witness)]) + expect([...res.metadata.locks.skipped.values()]).toEqual([FIXTURE.lock.lockHash]) + expect(res.metadata.skipLastInput).toEqual(false) }) }) @@ -453,9 +490,11 @@ describe('TransactionSender Test', () => { it('success', async () => { // @ts-ignore: Private method - const ntx = await transactionSender.sign(fakeWallet.id, tx, '1234', false) + const res = await transactionSender.sign(fakeWallet.id, tx, '1234', false) - expect(ntx.witnesses[0]).toEqual(expectedWitness[0]) + expect(res.tx.witnesses[0]).toEqual(expectedWitness[0]) + expect(res.metadata.locks.skipped.size).toEqual(0) + expect(res.metadata.skipLastInput).toEqual(false) }) }) @@ -491,9 +530,11 @@ describe('TransactionSender Test', () => { }) it('success', async () => { // @ts-ignore: Private method - const ntx = await transactionSender.sign(fakeWallet.id, tx, '1234', false) + const res = await transactionSender.sign(fakeWallet.id, tx, '1234', false) - expect(ntx.witnesses[0]).toEqual(tx.witnesses[0]) + expect(res.tx.witnesses[0]).toEqual(tx.witnesses[0]) + expect(res.metadata.locks.skipped.size).toEqual(0) + expect(res.metadata.skipLastInput).toEqual(false) }) }) describe('when not matched receiver lock hash', () => { @@ -546,9 +587,11 @@ describe('TransactionSender Test', () => { tx.inputs[0].lock = chequeLock }) it('success', async () => { - const ntx = await transactionSender.sign(fakeWallet.id, tx, '1234', false) + const res = await transactionSender.sign(fakeWallet.id, tx, '1234', false) - expect(ntx.witnesses[0]).toEqual(tx.witnesses[0]) + expect(res.tx.witnesses[0]).toEqual(tx.witnesses[0]) + expect(res.metadata.locks.skipped.size).toEqual(0) + expect(res.metadata.skipLastInput).toEqual(false) }) }) describe('when not matched sender lock hash', () => { diff --git a/renovate.json b/renovate.json index 44ba1e0835..3407c63b12 100644 --- a/renovate.json +++ b/renovate.json @@ -8,5 +8,5 @@ "addLabels": ["types"] } ], - "reviewers": ["keith-cy", "yanguoyu", "jeffreyma597", "devchenyan", "WhiteMinds"] + "reviewers": ["Magickbase/neuron"] } diff --git a/tsconfig.json b/tsconfig.json index 594cf4c570..bd101f526d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "es2019", "module": "esnext", + "jsx": "react", "moduleResolution": "node", "esModuleInterop": true, "alwaysStrict": true, diff --git a/yarn.lock b/yarn.lock index cc6b605164..87502513c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -61,6 +61,14 @@ "@babel/highlight" "^7.22.13" chalk "^2.4.2" +"@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== + dependencies: + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" + "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.5", "@babel/compat-data@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" @@ -71,21 +79,21 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== -"@babel/core@7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.3.tgz#5ec09c8803b91f51cc887dedc2654a35852849c9" - integrity sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew== +"@babel/core@7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.5.tgz#6e23f2acbcb77ad283c5ed141f824fd9f70101c7" + integrity sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.3" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.5" "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.2" - "@babel/parser" "^7.23.3" + "@babel/helpers" "^7.23.5" + "@babel/parser" "^7.23.5" "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.3" - "@babel/types" "^7.23.3" + "@babel/traverse" "^7.23.5" + "@babel/types" "^7.23.5" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -153,22 +161,12 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/generator@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" - integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== +"@babel/generator@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.5.tgz#17d0a1ea6b62f351d281350a5f80b87a810c4755" + integrity sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA== dependencies: - "@babel/types" "^7.23.0" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.3.tgz#86e6e83d95903fbe7613f448613b8b319f330a8e" - integrity sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg== - dependencies: - "@babel/types" "^7.23.3" + "@babel/types" "^7.23.5" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -444,6 +442,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + "@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" @@ -488,14 +491,14 @@ "@babel/traverse" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/helpers@^7.23.2": - version "7.23.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.2.tgz#2832549a6e37d484286e15ba36a5330483cac767" - integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ== +"@babel/helpers@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.5.tgz#52f522840df8f1a848d06ea6a79b79eefa72401e" + integrity sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg== dependencies: "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.2" - "@babel/types" "^7.23.0" + "@babel/traverse" "^7.23.5" + "@babel/types" "^7.23.5" "@babel/highlight@^7.22.13": version "7.22.13" @@ -515,6 +518,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8", "@babel/parser@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" @@ -525,15 +537,10 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.16.tgz#180aead7f247305cce6551bea2720934e2fa2c95" integrity sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA== -"@babel/parser@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" - integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== - -"@babel/parser@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9" - integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw== +"@babel/parser@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.5.tgz#37dee97c4752af148e1d38c34b856b2507660563" + integrity sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ== "@babel/parser@~7.21.2": version "7.21.9" @@ -1599,7 +1606,7 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/runtime@^7.22.5": +"@babel/runtime@^7.22.5", "@babel/runtime@^7.6.3": version "7.23.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== @@ -1640,35 +1647,19 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.23.2": - version "7.23.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" - integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== - dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.0" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.0" - "@babel/types" "^7.23.0" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.3.tgz#26ee5f252e725aa7aca3474aa5b324eaf7908b5b" - integrity sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ== +"@babel/traverse@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.5.tgz#f546bf9aba9ef2b042c0e00d245990c15508e7ec" + integrity sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w== dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.3" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.5" "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.3" - "@babel/types" "^7.23.3" + "@babel/parser" "^7.23.5" + "@babel/types" "^7.23.5" debug "^4.1.0" globals "^11.1.0" @@ -1715,12 +1706,12 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" -"@babel/types@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.3.tgz#d5ea892c07f2ec371ac704420f4dcdb07b5f9598" - integrity sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw== +"@babel/types@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.5.tgz#48d730a00c95109fa4393352705954d74fb5b602" + integrity sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w== dependencies: - "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-string-parser" "^7.23.4" "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" @@ -2952,32 +2943,84 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@lerna/child-process@7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.1.0.tgz#c3bde37e38822e0506e1ff3cda8b206417b2cf43" - integrity sha512-NmpwxygdVW2xprCNNgZ9d6P/pRlaXX2vfUynYNS+jsv7Q2uDZSdW86kjwEgbWyjSu7quZsmpQLqpl6PnfFl82g== +"@lerna/child-process@7.4.2": + version "7.4.2" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.4.2.tgz#a2fd013ac2150dc288270d3e0d0b850c06bec511" + integrity sha512-je+kkrfcvPcwL5Tg8JRENRqlbzjdlZXyaR88UcnCdNW0AJ1jX9IfHRys1X7AwSroU2ug8ESNC+suoBw1vX833Q== dependencies: chalk "^4.1.0" execa "^5.0.0" strong-log-transformer "^2.1.0" -"@lerna/create@7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-7.1.0.tgz#0bf1911ca1bfd6cea9820aaeefe7fd1136f52a26" - integrity sha512-qmj1c9J9AmMdiZW7akK7PVhUK3rwHnOgOBrz7TL0eoHsCE9kda+5VWiKFKSyDqq2zSai+a5OZU/pfkaPm8FygA== +"@lerna/create@7.4.2": + version "7.4.2" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-7.4.2.tgz#f845fad1480e46555af98bd39af29571605dddc9" + integrity sha512-1wplFbQ52K8E/unnqB0Tq39Z4e+NEoNrpovEnl6GpsTUrC6WDp8+w0Le2uCBV0hXyemxChduCkLz4/y1H1wTeg== dependencies: - "@lerna/child-process" "7.1.0" + "@lerna/child-process" "7.4.2" + "@npmcli/run-script" "6.0.2" + "@nx/devkit" ">=16.5.1 < 17" + "@octokit/plugin-enterprise-rest" "6.0.1" + "@octokit/rest" "19.0.11" + byte-size "8.1.1" + chalk "4.1.0" + clone-deep "4.0.1" + cmd-shim "6.0.1" + columnify "1.6.0" + conventional-changelog-core "5.0.1" + conventional-recommended-bump "7.0.1" + cosmiconfig "^8.2.0" dedent "0.7.0" + execa "5.0.0" fs-extra "^11.1.1" + get-stream "6.0.0" + git-url-parse "13.1.0" + glob-parent "5.1.2" + globby "11.1.0" + graceful-fs "4.2.11" + has-unicode "2.0.1" + ini "^1.3.8" init-package-json "5.0.0" + inquirer "^8.2.4" + is-ci "3.0.1" + is-stream "2.0.0" + js-yaml "4.1.0" + libnpmpublish "7.3.0" + load-json-file "6.2.0" + lodash "^4.17.21" + make-dir "4.0.0" + minimatch "3.0.5" + multimatch "5.0.0" + node-fetch "2.6.7" npm-package-arg "8.1.1" + npm-packlist "5.1.1" + npm-registry-fetch "^14.0.5" + npmlog "^6.0.2" + nx ">=16.5.1 < 17" + p-map "4.0.0" + p-map-series "2.1.0" + p-queue "6.6.2" p-reduce "^2.1.0" pacote "^15.2.0" pify "5.0.0" + read-cmd-shim "4.0.0" + read-package-json "6.0.4" + resolve-from "5.0.0" + rimraf "^4.4.1" semver "^7.3.4" + signal-exit "3.0.7" slash "^3.0.0" + ssri "^9.0.1" + strong-log-transformer "2.1.0" + tar "6.1.11" + temp-dir "1.0.0" + upath "2.0.1" + uuid "^9.0.0" validate-npm-package-license "^3.0.4" validate-npm-package-name "5.0.0" + write-file-atomic "5.0.1" + write-pkg "4.0.0" + yargs "16.2.0" yargs-parser "20.2.4" "@malept/cross-spawn-promise@^1.1.0": @@ -3165,81 +3208,83 @@ read-package-json-fast "^3.0.0" which "^3.0.0" -"@nrwl/devkit@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-16.3.2.tgz#b45393dfd62dcb75554ff0c2dff6715a907e3877" - integrity sha512-EiDwVIvh6AcClXv22Q7auQh7Iy/ONISEFWzTswy/J6ZmVGCQesbiwg4cGV0MKiScr+awdVzqyNey+wD6IR5Lkw== +"@nrwl/devkit@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-16.10.0.tgz#ac8c5b4db00f12c4b817c937be2f7c4eb8f2593c" + integrity sha512-fRloARtsDQoQgQ7HKEy0RJiusg/HSygnmg4gX/0n/Z+SUS+4KoZzvHjXc6T5ZdEiSjvLypJ+HBM8dQzIcVACPQ== dependencies: - "@nx/devkit" "16.3.2" + "@nx/devkit" "16.10.0" -"@nrwl/tao@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-16.3.2.tgz#eefc1974342afbbe48e4e5351d6707ad2f9fb179" - integrity sha512-2Kg7dtv6JcQagCZPSq+okceI81NqmXGGgbKWqS7sOfdmp1otxS9uiUFNXw+Pdtnw38mdRviMtSOXScntu4sUKg== +"@nrwl/tao@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-16.10.0.tgz#94642a0380709b8e387e1e33705a5a9624933375" + integrity sha512-QNAanpINbr+Pod6e1xNgFbzK1x5wmZl+jMocgiEFXZ67KHvmbD6MAQQr0MMz+GPhIu7EE4QCTLTyCEMlAG+K5Q== dependencies: - nx "16.3.2" + nx "16.10.0" + tslib "^2.3.0" -"@nx/devkit@16.3.2", "@nx/devkit@>=16.1.3 < 17": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nx/devkit/-/devkit-16.3.2.tgz#95d58d104449c54bdc276fa1c9166fcad867cfa8" - integrity sha512-1ev3EDm2Sx/ibziZroL1SheqxDR7UgC49tkBgJz1GrQLQnfdhBYroCPSyBSWGPMLHjIuHb3+hyGSV1Bz+BIYOA== +"@nx/devkit@16.10.0", "@nx/devkit@>=16.5.1 < 17": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nx/devkit/-/devkit-16.10.0.tgz#7e466be2dee2dcb1ccaf286786ca2a0a639aa007" + integrity sha512-IvKQqRJFDDiaj33SPfGd3ckNHhHi6ceEoqCbAP4UuMXOPPVOX6H0KVk+9tknkPb48B7jWIw6/AgOeWkBxPRO5w== dependencies: - "@nrwl/devkit" "16.3.2" + "@nrwl/devkit" "16.10.0" ejs "^3.1.7" + enquirer "~2.3.6" ignore "^5.0.4" - semver "7.3.4" + semver "7.5.3" tmp "~0.2.1" tslib "^2.3.0" -"@nx/nx-darwin-arm64@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.3.2.tgz#83b6e78b27d2d7da8f7626560f52070c8735d28a" - integrity sha512-YfYVNfsJBzBcBnJUU4AcA6A4QMkgnVlETfp4KGL36Otq542mRY1ISGHdox63ocI5AKh5gay5AaGcR4wR9PU9Vg== - -"@nx/nx-darwin-x64@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-16.3.2.tgz#0ae2a64356542c5fb73ca8038ce10ec4512e7fcb" - integrity sha512-bJtpozz0zSRVRrcQ76GrlT3TWEGTymLYWrVG51bH5KZ46t6/a4EQBI3uL3vubMmOZ0jR4ywybOcPBBhxmBJ68w== - -"@nx/nx-freebsd-x64@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-16.3.2.tgz#202adf4d6070f47ed46450f006ecd50851147c74" - integrity sha512-ZvufI0bWqT67nLbBo6ejrIGxypdoedRQTP/tudWbs/4isvxLe1uVku1BfKCTQUsJG367SqNOU1H5kzI/MRr3ow== - -"@nx/nx-linux-arm-gnueabihf@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-16.3.2.tgz#62314a82566e3647866b9dd4167a2d0e1397f001" - integrity sha512-IQL4kxdiZLvifar7+SIum3glRuVsxtE0dL8RvteSDXrxDQnaTUrjILC+VGhalRmk7ngBbGKNrhWOeeL7390CzQ== - -"@nx/nx-linux-arm64-gnu@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-16.3.2.tgz#02826400aa55b8f44bac83332dd29647d0e95001" - integrity sha512-f6AWgPVu3mfUEoOBa0rY2/7QY0Or9eR0KtLFpcPh7RUpxPw2EXzIbjD/0RGipdpspSrgiMKbZpsUjo6mXBFsQA== - -"@nx/nx-linux-arm64-musl@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-16.3.2.tgz#a0a81520e0904aa026a7ab0a8a3bf3facec9f14c" - integrity sha512-AvrWcYz7021E3b5P9/0i26p60XMZfw86Epks51L6AhlflarlOH4AcEChc7APMtb1ELAIbDWx2S6oIDRbQ7rtVA== - -"@nx/nx-linux-x64-gnu@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-16.3.2.tgz#e79b5c142ec8d9bfb458ea5803bc4b62abbcf296" - integrity sha512-K2pWGAcbCNm6b7UZI9cc8z4Rb540QcuepBXD7akjPjWerzXriT6VCn4i9mVKsCg2mwSfknTJJVJ1PZwJSmTl/Q== - -"@nx/nx-linux-x64-musl@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-16.3.2.tgz#900aee8f171638b9fb44378e2ac0548cb4aa99a7" - integrity sha512-sY1QDuQlqyYiRPJZanrtV07tU0DOXiCrWb0pDsGiO0qHuUSmW5Vw17GWEY4z3rt0/5U8fJ+/9WQrneviOmsOKg== - -"@nx/nx-win32-arm64-msvc@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-16.3.2.tgz#88db772b3535648e147b1a0206b1a1fe875fa9a5" - integrity sha512-wBfohT2hjrLKn9WFHvG0MFVk7uYhgYNiptnTLdTouziHgFyZ08vyl7XYBq55BwHPMQ5iswVoEfjn/5ZBfCPscg== - -"@nx/nx-win32-x64-msvc@16.3.2": - version "16.3.2" - resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-16.3.2.tgz#2195faaf1fc465c7a89bfdd62323fdd2a5d91f15" - integrity sha512-QC0sWrfQm0/WdvvM//7UAgm+otbak6bznZ0zawTeqmLBh1hLjNeweyzSVKQEtZtlzDMKpzCVuuwkJq+VKBLvmw== +"@nx/nx-darwin-arm64@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-arm64/-/nx-darwin-arm64-16.10.0.tgz#0c73010cac7a502549483b12bad347da9014e6f1" + integrity sha512-YF+MIpeuwFkyvM5OwgY/rTNRpgVAI/YiR0yTYCZR+X3AAvP775IVlusNgQ3oedTBRUzyRnI4Tknj1WniENFsvQ== + +"@nx/nx-darwin-x64@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nx/nx-darwin-x64/-/nx-darwin-x64-16.10.0.tgz#2ccf270418d552fd0a8e0d6089aee4944315adaa" + integrity sha512-ypi6YxwXgb0kg2ixKXE3pwf5myVNUgWf1CsV5OzVccCM8NzheMO51KDXTDmEpXdzUsfT0AkO1sk5GZeCjhVONg== + +"@nx/nx-freebsd-x64@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nx/nx-freebsd-x64/-/nx-freebsd-x64-16.10.0.tgz#c3ee6914256e69493fed9355b0d6661d0e86da44" + integrity sha512-UeEYFDmdbbDkTQamqvtU8ibgu5jQLgFF1ruNb/U4Ywvwutw2d4ruOMl2e0u9hiNja9NFFAnDbvzrDcMo7jYqYw== + +"@nx/nx-linux-arm-gnueabihf@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-16.10.0.tgz#a961eccbb38acb2da7fc125b29d1fead0b39152f" + integrity sha512-WV3XUC2DB6/+bz1sx+d1Ai9q2Cdr+kTZRN50SOkfmZUQyEBaF6DRYpx/a4ahhxH3ktpNfyY8Maa9OEYxGCBkQA== + +"@nx/nx-linux-arm64-gnu@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-16.10.0.tgz#795f20072549d03822b5c4639ef438e473dbb541" + integrity sha512-aWIkOUw995V3ItfpAi5FuxQ+1e9EWLS1cjWM1jmeuo+5WtaKToJn5itgQOkvSlPz+HSLgM3VfXMvOFALNk125g== + +"@nx/nx-linux-arm64-musl@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-16.10.0.tgz#f2428ee6dbe2b2c326e8973f76c97666def33607" + integrity sha512-uO6Gg+irqpVcCKMcEPIQcTFZ+tDI02AZkqkP7koQAjniLEappd8DnUBSQdcn53T086pHpdc264X/ZEpXFfrKWQ== + +"@nx/nx-linux-x64-gnu@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-16.10.0.tgz#d36c2bcf94d49eaa24e3880ddaf6f1f617de539b" + integrity sha512-134PW/u/arNFAQKpqMJniC7irbChMPz+W+qtyKPAUXE0XFKPa7c1GtlI/wK2dvP9qJDZ6bKf0KtA0U/m2HMUOA== + +"@nx/nx-linux-x64-musl@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-16.10.0.tgz#78bd2ab97a583b3d4ea3387b67fd7b136907493c" + integrity sha512-q8sINYLdIJxK/iUx9vRk5jWAWb/2O0PAbOJFwv4qkxBv4rLoN7y+otgCZ5v0xfx/zztFgk/oNY4lg5xYjIso2Q== + +"@nx/nx-win32-arm64-msvc@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-16.10.0.tgz#ef20ec8d0c83d66e73e20df12d2c788b8f866396" + integrity sha512-moJkL9kcqxUdJSRpG7dET3UeLIciwrfP08mzBQ12ewo8K8FzxU8ZUsTIVVdNrwt01CXOdXoweGfdQLjJ4qTURA== + +"@nx/nx-win32-x64-msvc@16.10.0": + version "16.10.0" + resolved "https://registry.yarnpkg.com/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-16.10.0.tgz#7410a51d0f8be631eec9552f01b2e5946285927c" + integrity sha512-5iV2NKZnzxJwZZ4DM5JVbRG/nkhAbzEskKaLBB82PmYGKzaDHuMHP1lcPoD/rtYMlowZgNA/RQndfKvPBPwmXA== "@octokit/auth-token@^3.0.0": version "3.0.4" @@ -3385,11 +3430,45 @@ schema-utils "^3.0.0" source-map "^0.7.3" +"@popperjs/core@^2.11.6": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + +"@react-aria/ssr@^3.5.0": + version "3.8.0" + resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.8.0.tgz#e7f467ac42f72504682724304ce221f785d70d49" + integrity sha512-Y54xs483rglN5DxbwfCPHxnkvZ+gZ0LbSYmR72LyWPGft8hN/lrl1VRS1EW2SMjnkEWlj+Km2mwvA3kEHDUA0A== + dependencies: + "@swc/helpers" "^0.5.0" + "@remix-run/router@1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.0.tgz#550a8d5760b78efc5d60f62b5829b0f74c1fde81" integrity sha512-Eu1V3kz3mV0wUpVTiFHuaT8UD1gj/0VnoFHQYX35xlslQUpe8CuYoKFn9d4WZFHm3yDywz6ALZuGdnUPKrNeAw== +"@restart/hooks@^0.4.9": + version "0.4.11" + resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.11.tgz#8876ccce1d4ad2a4b793a31689d63df36cf56088" + integrity sha512-Ft/ncTULZN6ldGHiF/k5qt72O8JyRMOeg0tApvCni8LkoiEahO+z3TNxfXIVGy890YtWVDvJAl662dVJSJXvMw== + dependencies: + dequal "^2.0.3" + +"@restart/ui@^1.6.6": + version "1.6.6" + resolved "https://registry.yarnpkg.com/@restart/ui/-/ui-1.6.6.tgz#3481e2eaf15d7cae55bb2f518624e10d19c75800" + integrity sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA== + dependencies: + "@babel/runtime" "^7.21.0" + "@popperjs/core" "^2.11.6" + "@react-aria/ssr" "^3.5.0" + "@restart/hooks" "^0.4.9" + "@types/warning" "^3.0.0" + dequal "^2.0.3" + dom-helpers "^5.2.0" + uncontrollable "^8.0.1" + warning "^4.0.3" + "@rollup/plugin-babel@^5.2.0": version "5.3.1" resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283" @@ -4447,6 +4526,13 @@ "@svgr/plugin-svgo" "^5.5.0" loader-utils "^2.0.0" +"@swc/helpers@^0.5.0": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.3.tgz#98c6da1e196f5f08f977658b80d6bd941b5f294f" + integrity sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A== + dependencies: + tslib "^2.4.0" + "@szmarczak/http-timer@^4.0.5": version "4.0.6" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" @@ -4676,10 +4762,10 @@ "@types/cheerio" "*" "@types/react" "^16" -"@types/enzyme@3.10.16": - version "3.10.16" - resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.16.tgz#100c49093f694545fc903266b2eb410df08859a9" - integrity sha512-17uMdJjSKjvdn/MhO/G2lRNPZGvJxFpvgONrsRoS1+khtJ6UcnCwC9v3gk2UqPyAkMZb6a1VYxScc/vOgkDl9w== +"@types/enzyme@3.10.18": + version "3.10.18" + resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.18.tgz#86010e7cb56cf1450dd391b8cc3a788f6a6fadef" + integrity sha512-RaO/TyyHZvXkpzinbMTZmd/S5biU4zxkvDsn22ujC29t9FMSzq8tnn8f2MxQ2P8GVhFRG5jTAL05DXKyTtpEQQ== dependencies: "@types/cheerio" "*" "@types/react" "^16" @@ -4844,10 +4930,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest-when@3.5.2": - version "3.5.2" - resolved "https://registry.yarnpkg.com/@types/jest-when/-/jest-when-3.5.2.tgz#7e6225e827267d26f115dc97da6403a3b37556c5" - integrity sha512-1WP+wJDW7h4TYAVLoIebxRIVv8GPk66Qsq2nU7PkwKZ6usurnDQZgk0DfBNKAJ9gVzapCXSV53Vn/3nBHBNzAw== +"@types/jest-when@3.5.5": + version "3.5.5" + resolved "https://registry.yarnpkg.com/@types/jest-when/-/jest-when-3.5.5.tgz#c23e97945959277946c15eff2a2fe51d18743045" + integrity sha512-H9MDPIrz7NOu6IXP9OHExNN9LnJbGYAzRsGIDKxWr7Fth9vovemNV8yFbkUWLSEmuA8PREvAEvt9yK0PPLmFHA== dependencies: "@types/jest" "*" @@ -5095,7 +5181,14 @@ "@types/history" "^4.7.11" "@types/react" "*" -"@types/react@*", "@types/react@17.0.71", "@types/react@>=16", "@types/react@^16", "@types/react@^17": +"@types/react-transition-group@^4.4.6": + version "4.4.8" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.8.tgz#46f87d80512959cac793ecc610a93d80ef241ccf" + integrity sha512-QmQ22q+Pb+HQSn04NL3HtrqHwYMf4h3QKArOy5F8U5nEVMaihBs3SR10WiOM1iwPz5jIo8x/u11al+iEGZZrvg== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@17.0.71", "@types/react@>=16", "@types/react@>=16.9.11", "@types/react@^16", "@types/react@^17": version "17.0.71" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.71.tgz#3673d446ad482b1564e44bf853b3ab5bcbc942c4" integrity sha512-lfqOu9mp16nmaGRrS8deS2Taqhd5Ih0o92Te5Ws6I1py4ytHBcXLqh0YIqVsViqwVI5f+haiFM6hju814BzcmA== @@ -5210,6 +5303,11 @@ resolved "https://registry.yarnpkg.com/@types/w3c-web-usb/-/w3c-web-usb-1.0.10.tgz#cf89cccd2d93b6245e784c19afe0a9f5038d4528" integrity sha512-CHgUI5kTc/QLMP8hODUHhge0D4vx+9UiAwIGiT0sTy/B2XpdX1U5rJt6JSISgr6ikRT7vxV9EVAFeYZqUnl1gQ== +"@types/warning@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.2.tgz#264f1f93a68f5dcb598db9764e40f14e13b0e630" + integrity sha512-S/2+OjBIcBl8Kur23YLe0hG1e7J5m2bHfB4UuMNoLZjIFhQWhTf1FeS+WFoXHUC6QsCEfk4pftj4J1KIKC1glA== + "@types/ws@^8.5.5": version "8.5.5" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" @@ -5590,7 +5688,7 @@ resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== -"@yarnpkg/parsers@^3.0.0-rc.18": +"@yarnpkg/parsers@3.0.0-rc.46": version "3.0.0-rc.46" resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz#03f8363111efc0ea670e53b0282cd3ef62de4e01" integrity sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q== @@ -6077,6 +6175,17 @@ array-includes@^3.1.5, array-includes@^3.1.6: get-intrinsic "^1.1.3" is-string "^1.0.7" +array-includes@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-string "^1.0.7" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -6108,6 +6217,17 @@ array.prototype.find@^2.1.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +array.prototype.findlastindex@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" + integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + array.prototype.flat@^1.2.3, array.prototype.flat@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" @@ -6118,6 +6238,16 @@ array.prototype.flat@^1.2.3, array.prototype.flat@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + array.prototype.flatmap@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" @@ -6128,6 +6258,16 @@ array.prototype.flatmap@^1.3.1: es-abstract "^1.20.4" es-shim-unscopables "^1.0.0" +array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + array.prototype.reduce@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" @@ -6150,6 +6290,19 @@ array.prototype.tosorted@^1.1.1: es-shim-unscopables "^1.0.0" get-intrinsic "^1.1.3" +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -7062,6 +7215,15 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -7287,7 +7449,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@*, classnames@^2.2.6: +classnames@*, classnames@^2.2.6, classnames@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== @@ -7672,10 +7834,10 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -conventional-changelog-angular@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz#a9a9494c28b7165889144fd5b91573c4aa9ca541" - integrity sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg== +conventional-changelog-angular@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz#5eec8edbff15aa9b1680a8dcfbd53e2d7eb2ba7a" + integrity sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ== dependencies: compare-func "^2.0.0" @@ -8255,6 +8417,15 @@ deferred-leveldown@~5.3.0: abstract-leveldown "~6.2.1" inherits "^2.0.3" +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -8421,6 +8592,11 @@ diff-sequences@^29.4.3: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + dir-compare@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/dir-compare/-/dir-compare-2.4.0.tgz#785c41dc5f645b34343a4eafc50b79bac7f11631" @@ -8514,7 +8690,7 @@ dom-converter@^0.2.0: dependencies: utila "~0.4" -dom-helpers@^5.0.1: +dom-helpers@^5.0.1, dom-helpers@^5.2.0, dom-helpers@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== @@ -8620,7 +8796,7 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" -dotenv-expand@^10.0.0: +dotenv-expand@^10.0.0, dotenv-expand@~10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== @@ -8635,12 +8811,12 @@ dotenv@8.6.0, dotenv@^8.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== -dotenv@^10.0.0, dotenv@~10.0.0: +dotenv@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== -dotenv@^16.0.0: +dotenv@^16.0.0, dotenv@~16.3.1: version "16.3.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== @@ -9035,6 +9211,51 @@ es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21 unbox-primitive "^1.0.2" which-typed-array "^1.1.9" +es-abstract@^1.22.1: + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.5" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.13" + es-array-method-boxes-properly@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" @@ -9225,7 +9446,16 @@ eslint-import-resolver-node@^0.3.7: is-core-module "^2.11.0" resolve "^1.22.1" -eslint-module-utils@^2.7.4: +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== @@ -9240,7 +9470,30 @@ eslint-plugin-flowtype@^8.0.3: lodash "^4.17.21" string-natural-compare "^3.0.1" -eslint-plugin-import@2.27.5, eslint-plugin-import@^2.25.3: +eslint-plugin-import@2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz#8133232e4329ee344f2f612885ac3073b0b7e155" + integrity sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg== + dependencies: + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.14.2" + +eslint-plugin-import@^2.25.3: version "2.27.5" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== @@ -9788,17 +10041,6 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" - integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - fast-glob@^3.2.12, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" @@ -10201,6 +10443,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + function.prototype.name@^1.1.0, function.prototype.name@^1.1.2, function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" @@ -10211,6 +10458,16 @@ function.prototype.name@^1.1.0, function.prototype.name@^1.1.2, function.prototy es-abstract "^1.19.0" functions-have-names "^1.2.2" +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -10265,6 +10522,16 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-proto "^1.0.1" has-symbols "^1.0.3" +get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-npm-tarball-url@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/get-npm-tarball-url/-/get-npm-tarball-url-2.0.3.tgz#67dff908d699e9e2182530ae6e939a93e5f8dfdb" @@ -10740,6 +11007,13 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -11225,6 +11499,13 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + ip@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" @@ -11339,6 +11620,13 @@ is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-mo dependencies: has "^1.0.3" +is-core-module@^2.13.0, is-core-module@^2.13.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -11632,6 +11920,13 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" +is-typed-array@^1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -11884,6 +12179,16 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-diff@^29.4.1: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-docblock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" @@ -11937,6 +12242,11 @@ jest-get-type@^29.4.3: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + jest-haste-map@^25.5.1: version "25.5.1" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.5.1.tgz#1df10f716c1d94e60a1ebf7798c9fb3da2620943" @@ -12248,10 +12558,10 @@ jest-snapshot@^27.5.1: pretty-format "^27.5.1" semver "^7.3.2" -jest-styled-components@7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/jest-styled-components/-/jest-styled-components-7.1.1.tgz#faf19c733e0de4bbef1f9151955b99e839b7df48" - integrity sha512-OUq31R5CivBF8oy81dnegNQrRW13TugMol/Dz6ZnFfEyo03exLASod7YGwyHGuayYlKmCstPtz0RQ1+NrAbIIA== +jest-styled-components@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/jest-styled-components/-/jest-styled-components-7.2.0.tgz#67e768b98d3d58513341a62baca76f3e253a4deb" + integrity sha512-gwyyveNjvuRA0pyhbQoydXZllLZESs2VuL5fXCabzh0buHPAOUfANtW7n5YMPmdC0sH3VB7h2eUGZ23+tjvaBA== dependencies: "@adobe/css-tools" "^4.0.1" @@ -12366,10 +12676,10 @@ jest-watcher@^28.0.0: jest-util "^28.1.3" string-length "^4.0.1" -jest-when@3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/jest-when/-/jest-when-3.5.2.tgz#651d8a73751ab55c29698d388dffd3460cd52bdc" - integrity sha512-4rDvnhaWh08RcPsoEVXgxRnUIE9wVIbZtGqZ5x2Wm9Ziz9aQs89PipQFmOK0ycbEhVAgiV3MUeTXp3Ar4s2FcQ== +jest-when@3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jest-when/-/jest-when-3.6.0.tgz#b46ee408d68f671447b218f2ae6bd93fb5028acf" + integrity sha512-+cZWTy0ekAJo7M9Om0Scdor1jm3wDiYJWmXE8U22UVnkH54YCXAuaqz3P+up/FdtOg8g4wHOxV7Thd7nKhT6Dg== jest-worker@^25.5.0: version "25.5.0" @@ -12737,15 +13047,15 @@ lazystream@^1.0.0: dependencies: readable-stream "^2.0.5" -lerna@7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.1.0.tgz#68855362c8a18f2c9ce14c01793076d2f7aea7c0" - integrity sha512-fY1EctsuP21eR7F9zmnqcdtBRkzvsoAOVYzjrtQQXYt9hlyA14RvjQJIF7R54t+T60As7kFYNgw2PHsC3orV2w== +lerna@7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.4.2.tgz#03497125d7b7c8d463eebfe17a701b16bde2ad09" + integrity sha512-gxavfzHfJ4JL30OvMunmlm4Anw7d7Tq6tdVHzUukLdS9nWnxCN/QB21qR+VJYp5tcyXogHKbdUEGh6qmeyzxSA== dependencies: - "@lerna/child-process" "7.1.0" - "@lerna/create" "7.1.0" + "@lerna/child-process" "7.4.2" + "@lerna/create" "7.4.2" "@npmcli/run-script" "6.0.2" - "@nx/devkit" ">=16.1.3 < 17" + "@nx/devkit" ">=16.5.1 < 17" "@octokit/plugin-enterprise-rest" "6.0.1" "@octokit/rest" "19.0.11" byte-size "8.1.1" @@ -12753,7 +13063,7 @@ lerna@7.1.0: clone-deep "4.0.1" cmd-shim "6.0.1" columnify "1.6.0" - conventional-changelog-angular "6.0.0" + conventional-changelog-angular "7.0.0" conventional-changelog-core "5.0.1" conventional-recommended-bump "7.0.1" cosmiconfig "^8.2.0" @@ -12779,7 +13089,8 @@ lerna@7.1.0: libnpmaccess "7.0.2" libnpmpublish "7.3.0" load-json-file "6.2.0" - make-dir "3.1.0" + lodash "^4.17.21" + make-dir "4.0.0" minimatch "3.0.5" multimatch "5.0.0" node-fetch "2.6.7" @@ -12787,7 +13098,7 @@ lerna@7.1.0: npm-packlist "5.1.1" npm-registry-fetch "^14.0.5" npmlog "^6.0.2" - nx ">=16.1.3 < 17" + nx ">=16.5.1 < 17" p-map "4.0.0" p-map-series "2.1.0" p-pipe "3.1.0" @@ -13152,7 +13463,7 @@ log-update@^5.0.1: strip-ansi "^7.0.1" wrap-ansi "^8.0.1" -loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -13212,12 +13523,12 @@ magic-string@^0.25.0, magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.8" -make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== +make-dir@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== dependencies: - semver "^6.0.0" + semver "^7.5.3" make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" @@ -13227,6 +13538,13 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + make-error@1.x: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -13949,6 +14267,11 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== +node-machine-id@1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/node-machine-id/-/node-machine-id-1.1.12.tgz#37904eee1e59b320bb9c5d6c0a59f3b469cb6267" + integrity sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ== + node-releases@^2.0.12: version "2.0.12" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" @@ -14175,36 +14498,38 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.5.tgz#a52744c61b3889dd44b0a158687add39b8d935e2" integrity sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ== -nx@16.3.2, "nx@>=16.1.3 < 17": - version "16.3.2" - resolved "https://registry.yarnpkg.com/nx/-/nx-16.3.2.tgz#92a2d7ef06d15b3b111b7cf9d35de08de0a22d90" - integrity sha512-fOzCVL7qoCJAcYTJwvJ9j+PSaL791ro4AICWuLxaphZsp2jcLoav4Ev7ONPks2Wlkt8FS9bee3nqQ3w1ya36Og== +nx@16.10.0, "nx@>=16.5.1 < 17": + version "16.10.0" + resolved "https://registry.yarnpkg.com/nx/-/nx-16.10.0.tgz#b070461f7de0a3d7988bd78558ea84cda3543ace" + integrity sha512-gZl4iCC0Hx0Qe1VWmO4Bkeul2nttuXdPpfnlcDKSACGu3ZIo+uySqwOF8yBAxSTIf8xe2JRhgzJN1aFkuezEBg== dependencies: - "@nrwl/tao" "16.3.2" + "@nrwl/tao" "16.10.0" "@parcel/watcher" "2.0.4" "@yarnpkg/lockfile" "^1.1.0" - "@yarnpkg/parsers" "^3.0.0-rc.18" + "@yarnpkg/parsers" "3.0.0-rc.46" "@zkochan/js-yaml" "0.0.6" axios "^1.0.0" chalk "^4.1.0" cli-cursor "3.1.0" cli-spinners "2.6.1" - cliui "^7.0.2" - dotenv "~10.0.0" + cliui "^8.0.1" + dotenv "~16.3.1" + dotenv-expand "~10.0.0" enquirer "~2.3.6" - fast-glob "3.2.7" figures "3.2.0" flat "^5.0.2" fs-extra "^11.1.0" glob "7.1.4" ignore "^5.0.4" + jest-diff "^29.4.1" js-yaml "4.1.0" jsonc-parser "3.2.0" lines-and-columns "~2.0.3" minimatch "3.0.5" + node-machine-id "1.1.12" npm-run-path "^4.0.1" open "^8.4.0" - semver "7.3.4" + semver "7.5.3" string-width "^4.2.3" strong-log-transformer "^2.1.0" tar-stream "~2.2.0" @@ -14215,16 +14540,16 @@ nx@16.3.2, "nx@>=16.1.3 < 17": yargs "^17.6.2" yargs-parser "21.1.1" optionalDependencies: - "@nx/nx-darwin-arm64" "16.3.2" - "@nx/nx-darwin-x64" "16.3.2" - "@nx/nx-freebsd-x64" "16.3.2" - "@nx/nx-linux-arm-gnueabihf" "16.3.2" - "@nx/nx-linux-arm64-gnu" "16.3.2" - "@nx/nx-linux-arm64-musl" "16.3.2" - "@nx/nx-linux-x64-gnu" "16.3.2" - "@nx/nx-linux-x64-musl" "16.3.2" - "@nx/nx-win32-arm64-msvc" "16.3.2" - "@nx/nx-win32-x64-msvc" "16.3.2" + "@nx/nx-darwin-arm64" "16.10.0" + "@nx/nx-darwin-x64" "16.10.0" + "@nx/nx-freebsd-x64" "16.10.0" + "@nx/nx-linux-arm-gnueabihf" "16.10.0" + "@nx/nx-linux-arm64-gnu" "16.10.0" + "@nx/nx-linux-arm64-musl" "16.10.0" + "@nx/nx-linux-x64-gnu" "16.10.0" + "@nx/nx-linux-x64-musl" "16.10.0" + "@nx/nx-win32-arm64-msvc" "16.10.0" + "@nx/nx-win32-x64-msvc" "16.10.0" object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" @@ -14250,6 +14575,11 @@ object-inspect@^1.12.3, object-inspect@^1.7.0, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + object-is@^1.0.1, object-is@^1.0.2, object-is@^1.1.2, object-is@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" @@ -14298,6 +14628,15 @@ object.fromentries@^2.0.0, object.fromentries@^2.0.5, object.fromentries@^2.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +object.fromentries@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + object.getownpropertydescriptors@^2.1.0: version "2.1.6" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz#5e5c384dd209fa4efffead39e3a0512770ccc312" @@ -14309,6 +14648,16 @@ object.getownpropertydescriptors@^2.1.0: es-abstract "^1.21.2" safe-array-concat "^1.0.0" +object.groupby@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" + integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + object.hasown@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" @@ -14333,6 +14682,15 @@ object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.5, object.values@ define-properties "^1.1.4" es-abstract "^1.20.4" +object.values@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + objectorarray@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/objectorarray/-/objectorarray-1.0.5.tgz#2c05248bbefabd8f43ad13b41085951aac5e68a5" @@ -15552,6 +15910,15 @@ pretty-format@^29.0.0, pretty-format@^29.5.0: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -15621,6 +15988,14 @@ prop-types-exact@^1.2.0: object.assign "^4.1.0" reflect.ownkeys "^0.2.0" +prop-types-extra@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b" + integrity sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew== + dependencies: + react-is "^16.3.2" + warning "^4.0.0" + prop-types@^15.6.2, prop-types@^15.7.0, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" @@ -15829,6 +16204,24 @@ react-app-rewired@2.2.1: dependencies: semver "^5.6.0" +react-bootstrap@^2.9.1: + version "2.9.1" + resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-2.9.1.tgz#c1ab48ae2b2cfe6d5ac957c2042eb36fcafdb1d2" + integrity sha512-ezgmh/ARCYp18LbZEqPp0ppvy+ytCmycDORqc8vXSKYV3cer4VH7OReV8uMOoKXmYzivJTxgzGHalGrHamryHA== + dependencies: + "@babel/runtime" "^7.22.5" + "@restart/hooks" "^0.4.9" + "@restart/ui" "^1.6.6" + "@types/react-transition-group" "^4.4.6" + classnames "^2.3.2" + dom-helpers "^5.2.1" + invariant "^2.2.4" + prop-types "^15.8.1" + prop-types-extra "^1.1.0" + react-transition-group "^4.4.5" + uncontrollable "^7.2.1" + warning "^4.0.3" + react-colorful@^5.1.2: version "5.6.1" resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.6.1.tgz#7dc2aed2d7c72fac89694e834d179e32f3da563b" @@ -15909,13 +16302,18 @@ react-error-overlay@^6.0.11: integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== react-i18next@12.1.5, react-i18next@>=11.16.4: - version "13.3.1" - resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-13.3.1.tgz#9b072bf4dd4cafb028e92315a8a1415f8034bdca" - integrity sha512-JAtYREK879JXaN9GdzfBI4yJeo/XyLeXWUsRABvYXiFUakhZJ40l+kaTo+i+A/3cKIED41kS/HAbZ5BzFtq/Og== + version "13.5.0" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-13.5.0.tgz#44198f747628267a115c565f0c736a50a76b1ab0" + integrity sha512-CFJ5NDGJ2MUyBohEHxljOq/39NQ972rh1ajnadG9BjTk+UXbHLq4z5DKEbEQBDoIhUmmbuS/fIMJKo6VOax1HA== dependencies: "@babel/runtime" "^7.22.5" html-parse-stringify "^3.0.1" +react-icons@^4.11.0: + version "4.11.0" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.11.0.tgz#4b0e31c9bfc919608095cc429c4f1846f4d66c65" + integrity sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA== + react-inspector@^6.0.0, react-inspector@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-6.0.2.tgz#aa3028803550cb6dbd7344816d5c80bf39d07e9d" @@ -15931,7 +16329,7 @@ react-is@18.1.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.6: +react-is@^16.13.1, react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.6: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -15941,6 +16339,11 @@ react-is@^17.0.0, react-is@^17.0.1, react-is@^17.0.2: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" @@ -16044,7 +16447,7 @@ react-test-renderer@^17.0.0: react-shallow-renderer "^16.13.1" scheduler "^0.20.2" -react-transition-group@4.4.5: +react-transition-group@4.4.5, react-transition-group@^4.4.5: version "4.4.5" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== @@ -16291,6 +16694,15 @@ regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: define-properties "^1.2.0" functions-have-names "^1.2.3" +regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" + regexpu-core@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" @@ -16428,6 +16840,15 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19. path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.4: version "2.0.0-next.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" @@ -16596,6 +17017,16 @@ safe-array-concat@^1.0.0: has-symbols "^1.0.3" isarray "^2.0.5" +safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -16768,10 +17199,10 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@7.3.4: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== +semver@7.5.3: + version "7.5.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" + integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== dependencies: lru-cache "^6.0.0" @@ -16871,6 +17302,25 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -17450,6 +17900,15 @@ string.prototype.trim@^1.2.1, string.prototype.trim@^1.2.7: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string.prototype.trimend@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" @@ -17459,6 +17918,15 @@ string.prototype.trimend@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string.prototype.trimstart@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" @@ -17468,6 +17936,15 @@ string.prototype.trimstart@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -18057,7 +18534,7 @@ ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== -tsconfig-paths@^3.14.1: +tsconfig-paths@^3.14.1, tsconfig-paths@^3.14.2: version "3.14.2" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== @@ -18191,6 +18668,36 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -18235,10 +18742,10 @@ typeorm@0.2.45: yargs "^17.0.1" zen-observable-ts "^1.0.0" -typescript@5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43" - integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ== +typescript@5.3.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== "typescript@>=3 < 6": version "5.1.3" @@ -18260,6 +18767,21 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +uncontrollable@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-7.2.1.tgz#1fa70ba0c57a14d5f78905d533cf63916dc75738" + integrity sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ== + dependencies: + "@babel/runtime" "^7.6.3" + "@types/react" ">=16.9.11" + invariant "^2.2.4" + react-lifecycles-compat "^3.0.4" + +uncontrollable@^8.0.1: + version "8.0.4" + resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-8.0.4.tgz#a0a8307f638795162fafd0550f4a1efa0f8c5eb6" + integrity sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ== + undici@5.26.2: version "5.26.2" resolved "https://registry.yarnpkg.com/undici/-/undici-5.26.2.tgz#fa61bfe40f732540d15e58b0c1271872d8e3c995" @@ -18630,6 +19152,13 @@ walker@^1.0.7, walker@^1.0.8, walker@~1.0.5: dependencies: makeerror "1.0.12" +warning@^4.0.0, warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + watchpack@^2.2.0, watchpack@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" @@ -18905,6 +19434,17 @@ which-collection@^1.0.1: is-weakmap "^2.0.1" is-weakset "^2.0.1" +which-typed-array@^1.1.11, which-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.4" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + which-typed-array@^1.1.2, which-typed-array@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6"