diff --git a/.eslintrc.json b/.eslintrc.json index cb61834..89b3ee4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -20,5 +20,6 @@ "plugins": ["react", "@typescript-eslint", "unused-imports"], "rules": { "unused-imports/no-unused-imports": "error" - } + }, + "ignorePatterns": ["**/pkg/*"] } diff --git a/config-overrides.js b/config-overrides.js new file mode 100644 index 0000000..a39a4c9 --- /dev/null +++ b/config-overrides.js @@ -0,0 +1,21 @@ +// Refer: https://github.com/lokesh-007/wasm-react-rust +const path = require('path'); +const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin'); +module.exports = function override(config, env) { + config.resolve.extensions.push('.wasm'); + config.module.rules.forEach((rule) => { + (rule.oneOf || []).forEach((oneOf) => { + if (oneOf.loader && oneOf.loader.indexOf('file-loader') >= 0) { + // Make file-loader ignore WASM files + oneOf.exclude.push(/\.wasm$/); + } + }); + }); + config.plugins = (config.plugins || []).concat([ + new WasmPackPlugin({ + crateDirectory: path.resolve(__dirname, './src/Miniscript'), + outDir: path.resolve(__dirname, './src/Miniscript/pkg'), + }), + ]); + return config; +}; diff --git a/desktop/chat.ts b/desktop/chat.ts new file mode 100644 index 0000000..0e6aeda --- /dev/null +++ b/desktop/chat.ts @@ -0,0 +1,110 @@ +import Database from 'better-sqlite3'; +import * as Bitcoin from 'bitcoinjs-lib'; +import * as ed from '@noble/ed25519'; +import { ipcMain } from 'electron'; +import { EnvelopeIn, EnvelopeOut } from '../src/common/chat_interface'; +import fetch from 'node-fetch'; +import { stringify } from 'another-json'; + +let g_chat_server: ChatServer | null = null; +export function setup_chat() { + ipcMain.handle('chat::init', async (event) => { + if (g_chat_server) return; + const privateKey = ed.utils.randomPrivateKey(); + const publicKey = await ed.getPublicKey(privateKey); + g_chat_server = new ChatServer(privateKey, publicKey); + }); + ipcMain.handle('chat::send', async (event, message: EnvelopeIn) => { + if (!g_chat_server) return; + return g_chat_server.send_message(message); + }); + ipcMain.handle('chat::add_user', (event, name: string, key: string) => { + if (!g_chat_server) return; + return g_chat_server.add_user(name, key); + }); + ipcMain.handle('chat::list_users', (event) => { + if (!g_chat_server) return; + return g_chat_server.list_users(); + }); + ipcMain.handle('chat::list_channels', (event) => { + if (!g_chat_server) return; + return g_chat_server.list_channels(); + }); + ipcMain.handle('chat::list_messages_channel', (event, channel, since) => { + if (!g_chat_server) return; + return g_chat_server.list_messages_channel(channel, since); + }); +} + +class ChatServer { + db: Database.Database; + insert: Database.Statement; + list_all_users: Database.Statement; + list_all_channels: Database.Statement; + list_msg_chan: Database.Statement; + my_pk: Uint8Array; + my_sk: Uint8Array; + constructor(privateKey: Uint8Array, publicKey: Uint8Array) { + this.db = new Database( + '/Users/jr/Library/Application Support/org.judica.tor-chat/chat.sqlite3', + { readonly: false } + ); + this.insert = this.db.prepare( + 'INSERT INTO user (nickname, key) VALUES (@name, @key);' + ); + this.list_all_users = this.db.prepare( + 'SELECT nickname, key from user;' + ); + this.list_all_channels = this.db.prepare( + 'SELECT DISTINCT channel_id from messages;' + ); + this.list_msg_chan = this.db.prepare( + ` + SELECT messages.body, user.nickname, messages.received_time + FROM messages + INNER JOIN user ON messages.user = user.userid + where messages.channel_id = ? AND messages.received_time > ?; + ` + ); + this.my_pk = publicKey; + this.my_sk = privateKey; + } + add_user(name: string, key: string) { + this.insert.run({ name, key }); + } + + list_users(): [string, string][] { + let res: any[] = this.list_all_users.all(); + return res; + } + + list_channels(): string[] { + return this.list_all_channels.all(); + } + list_messages_channel(chan: string, since: number) { + return this.list_msg_chan.all(chan, since); + } + + async send_message(m: EnvelopeIn): Promise { + const partial: Partial = m; + partial.key = Array.from(this.my_pk); + const encoded = Buffer.from(stringify(partial), 'utf-8'); + // keys, messages & other inputs can be Uint8Arrays or hex strings + // Uint8Array.from([0xde, 0xad, 0xbe, 0xef]) === 'deadbeef' + const message = Uint8Array.from(encoded); + + const signature = Buffer.from( + await ed.sign(message, this.my_sk) + ).toString('base64'); + // const isValid = await ed.verify(signature, message, publicKey); + partial.signatures = { + [Bitcoin.crypto.sha256(Buffer.from(this.my_pk)).toString('hex')]: { + 'ed25519:1': signature, + }, + }; + await fetch('http://127.0.0.1:46789/msg', { + method: 'POST', + body: JSON.stringify(partial) + '\n', + }); + } +} diff --git a/desktop/declarations.d.ts b/desktop/declarations.d.ts index 20bf93e..269ecde 100644 --- a/desktop/declarations.d.ts +++ b/desktop/declarations.d.ts @@ -6,3 +6,7 @@ declare module 'await-spawn' { ): Promise; export = spawn; } + +declare module 'another-json' { + declare function stringify(js: any): string; +} diff --git a/desktop/handlers.ts b/desktop/handlers.ts index 98b39aa..7912006 100644 --- a/desktop/handlers.ts +++ b/desktop/handlers.ts @@ -3,6 +3,7 @@ import { get_emulator_log, kill_emulator, sapio, + SapioWorkspace, start_sapio_oracle, } from './sapio'; import { readFile, writeFile } from 'fs/promises'; @@ -11,7 +12,9 @@ import { preferences, Prefs } from './settings'; import { get_bitcoin_node } from './bitcoin_rpc'; import RpcError from 'bitcoin-core-ts/dist/src/errors/rpc-error'; import path from 'path'; +import { setup_chat } from './chat'; export default function (window: BrowserWindow) { + setup_chat(); ipcMain.handle('bitcoin::command', async (event, arg) => { const node = await get_bitcoin_node(); try { @@ -41,23 +44,37 @@ export default function (window: BrowserWindow) { const contracts = await sapio.list_contracts(); return contracts; }); - ipcMain.handle('sapio::create_contract', async (event, [which, args]) => { - const result = await sapio.create_contract(which, args); - return result; - }); + ipcMain.handle( + 'sapio::create_contract', + async (event, workspace, [which, psbt, args]) => { + const result = await sapio.create_contract( + workspace, + which, + psbt, + args + ); + return result; + } + ); ipcMain.handle('sapio::show_config', async (event) => { return await sapio.show_config(); }); ipcMain.handle('sapio::load_wasm_plugin', (event) => { - const plugin = dialog.showOpenDialogSync({ - properties: ['openFile'], + const plugins = dialog.showOpenDialogSync({ + properties: ['openFile', 'multiSelections'], filters: [{ extensions: ['wasm'], name: 'WASM' }], }); - if (plugin && plugin.length) - return sapio.load_contract_file_name(plugin[0]!); - return { err: 'No Plugin Selected' }; + const errs = []; + if (!plugins || !plugins.length) return { err: 'No Plugin Selected' }; + for (const plugin of plugins) { + const loaded = sapio.load_contract_file_name(plugin); + if ('err' in loaded) { + return loaded; + } + } + return { ok: null }; }); ipcMain.handle('sapio::open_contract_from_file', (event) => { @@ -77,62 +94,46 @@ export default function (window: BrowserWindow) { return { ok: data }; } }); - ipcMain.handle('sapio::compiled_contracts::list', (event) => { - return sapio.list_compiled_contracts(); - }); - ipcMain.handle('sapio::compiled_contracts::trash', (event, file_name) => { - return sapio.trash_compiled_contract(file_name); - }); + ipcMain.handle( + 'sapio::compiled_contracts::list', + async (event, workspace) => { + return ( + await SapioWorkspace.new(workspace) + ).list_compiled_contracts(); + } + ); + ipcMain.handle( + 'sapio::compiled_contracts::trash', + async (event, workspace, file_name) => { + return ( + await SapioWorkspace.new(workspace) + ).trash_compiled_contract(file_name); + } + ); ipcMain.handle('sapio::psbt::finalize', (event, psbt) => { return sapio.psbt_finalize(psbt); }); - ipcMain.handle( 'sapio::compiled_contracts::open', - async (event, file_name) => { - const data = JSON.parse( - await readFile( - path.join( - app.getPath('userData'), - 'compiled_contracts', - file_name, - 'bound.json' - ), - { - encoding: 'utf-8', - } - ) - ); - const args = JSON.parse( - await readFile( - path.join( - app.getPath('userData'), - 'compiled_contracts', - file_name, - 'args.json' - ), - { - encoding: 'utf-8', - } - ) - ); - const mod = await readFile( - path.join( - app.getPath('userData'), - 'compiled_contracts', - file_name, - 'module.json' - ), - { - encoding: 'utf-8', - } - ); - - const name = JSON.parse(mod).module; - + async (event, workspace_name, file_name) => { + const workspace = await SapioWorkspace.new(workspace_name); + const data = await workspace.read_bound_data_for(file_name); + const name = await workspace.read_module_for(file_name); + const args = await workspace.read_args_for(file_name); return { ok: { data, name, args } }; } ); + + ipcMain.handle('sapio::workspaces::init', async (event, workspace) => { + await SapioWorkspace.new(workspace); + }); + ipcMain.handle('sapio::workspaces::list', async (event) => { + return await SapioWorkspace.list_all(); + }); + ipcMain.handle('sapio::workspaces::trash', async (event, workspace) => { + return (await SapioWorkspace.new(workspace)).trash_workspace(workspace); + }); + ipcMain.handle('write_clipboard', (event, s: string) => { clipboard.writeText(s); }); diff --git a/desktop/main.ts b/desktop/main.ts index a789800..ef1d238 100644 --- a/desktop/main.ts +++ b/desktop/main.ts @@ -5,7 +5,7 @@ import { custom_sapio_config } from './settings'; import { createMenu } from './createMenu'; import register_handlers from './handlers'; -import { start_sapio_oracle } from './sapio'; +import { SapioWorkspace, start_sapio_oracle } from './sapio'; import { register_devtools } from './devtools'; import { get_bitcoin_node } from './bitcoin_rpc'; @@ -13,6 +13,7 @@ let mainWindow: BrowserWindow | null = null; async function createWindow() { await get_bitcoin_node(); + await SapioWorkspace.new('default'); const startUrl = process.env.ELECTRON_START_URL || url.format({ diff --git a/desktop/preload.ts b/desktop/preload.ts index d10b379..1b39aa7 100644 --- a/desktop/preload.ts +++ b/desktop/preload.ts @@ -1,5 +1,4 @@ import { ipcRenderer, contextBridge } from 'electron'; -import { IpcRendererEvent } from 'electron/main'; function bitcoin_command( command: { method: string; parameters: any[] }[] @@ -14,8 +13,17 @@ function bitcoin_command( } type Result = { ok: T } | { err: string }; -function create_contract(which: string, args: string): Promise> { - return ipcRenderer.invoke('sapio::create_contract', [which, args]); +function create_contract( + workspace: string, + which: string, + txn: string | null, + args: string +): Promise> { + return ipcRenderer.invoke('sapio::create_contract', workspace, [ + which, + txn, + args, + ]); } function open_contract_from_file(): Promise> { @@ -39,17 +47,33 @@ const psbt = { }, }; const compiled_contracts = { - list: () => { - return ipcRenderer.invoke('sapio::compiled_contracts::list'); + list: (workspace: string) => { + return ipcRenderer.invoke('sapio::compiled_contracts::list', workspace); }, - trash: (file_name: string) => { + trash: (workspace: string, file_name: string) => { return ipcRenderer.invoke( 'sapio::compiled_contracts::trash', + workspace, + file_name + ); + }, + open: (workspace: string, file_name: string) => { + return ipcRenderer.invoke( + 'sapio::compiled_contracts::open', + workspace, file_name ); }, - open: (file_name: string) => { - return ipcRenderer.invoke('sapio::compiled_contracts::open', file_name); +}; +const workspaces = { + init: (workspace: string) => { + return ipcRenderer.invoke('sapio::workspaces::init', workspace); + }, + list: () => { + return ipcRenderer.invoke('sapio::workspaces::list'); + }, + trash: (workspace: string) => { + return ipcRenderer.invoke('sapio::workspaces::trash', workspace); }, }; @@ -57,7 +81,7 @@ function save_psbt(psbt: string): Promise { return ipcRenderer.invoke('save_psbt', psbt); } -function fetch_psbt(): Promise { +function fetch_psbt(): Promise { return ipcRenderer.invoke('fetch_psbt'); } function save_contract(contract: string): Promise { @@ -71,28 +95,6 @@ function load_settings_sync(which: string): any { return ipcRenderer.invoke('load_settings_sync', which); } -const callbacks = { - simulate: 0, - load_hex: 0, - save_hex: 0, - create_contracts: 0, - load_contract: 0, - 'bitcoin-node-bar': 0, -}; - -type Callback = keyof typeof callbacks; - -function register(msg: Callback, action: (args: any) => void): () => void { - if (callbacks.hasOwnProperty(msg)) { - const listener = (event: IpcRendererEvent, args: any) => { - action(args); - }; - ipcRenderer.on(msg, listener); - return () => ipcRenderer.removeListener(msg, listener); - } - throw 'Unregistered Callback'; -} - function write_clipboard(s: string) { ipcRenderer.invoke('write_clipboard', s); } @@ -112,9 +114,21 @@ function emulator_read_log(): Promise { return ipcRenderer.invoke('emulator::read_log'); } -const api = { +const chat = { + init: () => ipcRenderer.invoke('chat::init'), + send: (message: any /*EnvelopeIn*/) => + ipcRenderer.invoke('chat::send', message), + add_user: (name: string, key: string) => + ipcRenderer.invoke('chat::add_user', name, key), + list_users: () => ipcRenderer.invoke('chat::list_users'), + list_channels: () => ipcRenderer.invoke('chat::list_channels'), + list_messages_channel: (channel: string, since: number) => + ipcRenderer.invoke('chat::list_messages_channel', channel, since), +}; + +// to typecheck, uncomment and import preloads +const api /*:preloads*/ = { bitcoin_command, - register, save_psbt, save_contract, fetch_psbt, @@ -130,11 +144,13 @@ const api = { load_contract_list, compiled_contracts, psbt, + workspaces, }, emulator: { kill: emulator_kill, start: emulator_start, read_log: emulator_read_log, }, + chat, }; contextBridge.exposeInMainWorld('electron', api); diff --git a/desktop/sapio.ts b/desktop/sapio.ts index fc21a9b..6a404ae 100644 --- a/desktop/sapio.ts +++ b/desktop/sapio.ts @@ -11,15 +11,108 @@ import { sys } from 'typescript'; import { preferences, sapio_config_file } from './settings'; import path from 'path'; import * as Bitcoin from 'bitcoinjs-lib'; -import { mkdir, readdir, writeFile } from 'fs/promises'; +import { mkdir, readdir, readFile, writeFile } from 'fs/promises'; +import { API, Result } from '../src/common/preload_interface'; +import { ConstructionRounded } from '@mui/icons-material'; const memo_apis = new Map(); const memo_logos = new Map(); -type Result = { ok: string } | { err: string }; +export class SapioWorkspace { + name: string; + private constructor(name: string) { + this.name = name; + } + static async list_all(): Promise { + const file = path.join(app.getPath('userData'), 'workspaces'); + return readdir(file, { encoding: 'ascii' }); + } + static async new(name: string): Promise { + const file = path.join( + app.getPath('userData'), + 'workspaces', + name, + 'compiled_contracts' + ); + const created = await mkdir(file, { recursive: true }); + return new SapioWorkspace(name); + } + + async list_compiled_contracts(): Promise { + const file = path.join( + app.getPath('userData'), + 'workspaces', + this.name, + 'compiled_contracts' + ); + const contracts = await readdir(file, { encoding: 'ascii' }); + return contracts; + } + async trash_compiled_contract(s: string): Promise { + const file = path.join( + app.getPath('userData'), + 'workspaces', + this.name, + 'compiled_contracts', + s + ); + return shell.trashItem(file); + } + async trash_workspace(s: string): Promise { + const file = path.join( + app.getPath('userData'), + 'workspaces', + this.name + ); + return shell.trashItem(file); + } + + contract_output_path_name(fname: string) { + return path.join( + app.getPath('userData'), + 'workspaces', + this.name, + 'compiled_contracts', + fname + ); + } + async contract_output_path(fname: string) { + const file = this.contract_output_path_name(fname); + await mkdir(file, { recursive: true }); + return file; + } + + async read_bound_data_for(file_name: string) { + const file = this.contract_output_path_name(file_name); + const data = JSON.parse( + await readFile(path.join(file, 'bound.json'), { + encoding: 'utf-8', + }) + ); + return data; + } + async read_args_for(file_name: string) { + const file = this.contract_output_path_name(file_name); + const args = JSON.parse( + await readFile(path.join(file, 'args.json'), { + encoding: 'utf-8', + }) + ); + return args; + } + + async read_module_for(file_name: string): Promise { + const file = this.contract_output_path_name(file_name); + const mod = await readFile(path.join(file, 'module.json'), { + encoding: 'utf-8', + }); + const name = JSON.parse(mod).module; + return name; + } +} + class SapioCompiler { - constructor() {} - static async command(args: string[]): Promise { + static async command(args: string[]): Promise> { const binary = preferences.data.sapio_cli.sapio_cli; const source = preferences.data.sapio_cli.preferences; let new_args: string[] = []; @@ -41,11 +134,11 @@ class SapioCompiler { const ok = (await spawn(binary, new_args)).toString(); return { ok }; } catch (e: any) { - return { err: e.toString() }; + return { err: e.stderr.toString() }; } } - async psbt_finalize(psbt: string): Promise { + async psbt_finalize(psbt: string): Promise> { return await SapioCompiler.command([ 'psbt', 'finalize', @@ -54,18 +147,11 @@ class SapioCompiler { ]); } - async show_config(): Promise { + async show_config(): Promise> { return await SapioCompiler.command(['configure', 'show']); } - async list_contracts(): Promise< - | { - ok: Record< - string, - { name: string; key: string; api: JSONSchema7; logo: string } - >; - } - | { err: string } - > { + + async list_contracts(): Promise> { const res = await SapioCompiler.command(['contract', 'list']); if ('err' in res) return res; const contracts = res.ok; @@ -98,33 +184,34 @@ class SapioCompiler { }) ); const logos_p = Promise.all( - lines.map(([name, key]: [string, string]): Promise => { - if (memo_logos.has(key)) { - return Promise.resolve(memo_logos.get(key)); - } else { - return SapioCompiler.command([ - 'contract', - 'logo', - '--key', - key, - ]) - .then((logo: Result) => { - return 'ok' in logo ? { ok: logo.ok.trim() } : logo; - }) - .then((logo: Result) => { - if ('err' in logo) return logo; - memo_logos.set(key, logo); - return logo; - }); + lines.map( + ([name, key]: [string, string]): Promise> => { + if (memo_logos.has(key)) { + return Promise.resolve(memo_logos.get(key)); + } else { + return SapioCompiler.command([ + 'contract', + 'logo', + '--key', + key, + ]) + .then((logo: Result) => { + return 'ok' in logo + ? { ok: logo.ok.trim() } + : logo; + }) + .then((logo: Result) => { + if ('err' in logo) return logo; + memo_logos.set(key, logo); + return logo; + }); + } } - }) + ) ); const [apis, logos] = await Promise.all([apis_p, logos_p]); - const results: Record< - string, - { name: string; key: string; api: Object; logo: string } - > = {}; + const results: API = {}; equal(lines.length, apis.length); equal(lines.length, logos.length); for (let i = 0; i < lines.length; ++i) { @@ -148,28 +235,17 @@ class SapioCompiler { '--file', file, ]); - console.log(`child stdout:\n${child}`); + console.log(`child stdout:\n${JSON.stringify(child)}`); return { ok: null }; } - async list_compiled_contracts(): Promise { - const file = path.join(app.getPath('userData'), 'compiled_contracts'); - const contracts = await readdir(file, { encoding: 'ascii' }); - return contracts; - } - async trash_compiled_contract(s: string): Promise { - const file = path.join( - app.getPath('userData'), - 'compiled_contracts', - s - ); - return shell.trashItem(file); - } - async create_contract( + workspace_name: string, which: string, + txn: string | null, args: string - ): Promise<{ ok: string | null } | { err: string }> { + ): Promise> { + const workspace = await SapioWorkspace.new(workspace_name); let create, created, bound; const args_h = Bitcoin.crypto.sha256(Buffer.from(args)).toString('hex'); // Unique File Name of Time + Args + Module @@ -177,13 +253,7 @@ class SapioCompiler { 0, 16 )}-${new Date().getTime()}`; - const file = path.join( - app.getPath('userData'), - 'compiled_contracts', - fname - ); - // technically a race condition - await mkdir(file, { recursive: true }); + const file = await workspace.contract_output_path(fname); const write_str = (to: string, data: string) => writeFile(path.join(file, to), data, { encoding: 'utf-8' }); const w_arg = write_str('args.json', args); @@ -218,19 +288,18 @@ class SapioCompiler { Promise.all([w_create]); let bind; try { - bind = await SapioCompiler.command([ - 'contract', - 'bind', - '--base64_psbt', - created, - ]); + const bind_args = ['contract', 'bind', '--base64_psbt']; + if (txn) bind_args.push('--txn', txn); + bind_args.push(created); + bind = await SapioCompiler.command(bind_args); } catch (e: any) { console.debug(created); console.log('Failed to bind', e.toString()); return { ok: null }; } if ('err' in bind) { - write_str('bind_error.json', JSON.stringify(create)); + console.log(['bind'], typeof bind, bind); + write_str('bind_error.json', JSON.stringify(bind)); return bind; } const w_bound = write_str('bound.json', bind.ok); diff --git a/desktop/settings.ts b/desktop/settings.ts index 7124f7f..5f1781a 100644 --- a/desktop/settings.ts +++ b/desktop/settings.ts @@ -3,7 +3,7 @@ const app = electron.app; import path from 'path'; import { writeFileSync } from 'fs'; import { readFile, writeFile } from 'fs/promises'; -import { default_settings } from './settings_gen'; +import { default_settings } from '../src/common/settings_gen'; import { deinit_bitcoin_node, get_bitcoin_node } from './bitcoin_rpc'; import { kill_emulator, start_sapio_oracle } from './sapio'; diff --git a/package.json b/package.json index b7d55bf..e2e73c6 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@mui/material": "^5.5.0", "@mui/styles": "^5.0.0", "@mui/x-data-grid": "^5.2.0", + "@noble/ed25519": "^1.6.0", "@projectstorm/react-canvas-core": "^6.6.1", "@projectstorm/react-diagrams": "^6.6.1", "@projectstorm/react-diagrams-core": "^6.6.1", @@ -23,28 +24,36 @@ "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", + "@types/better-sqlite3": "^7.5.0", "@types/color": "^3.0.1", "@types/jest": "^27.0.1", "@types/lodash": "^4.14.150", "@types/node": "^17.0.21", + "@types/node-fetch": "^2.6.1", "@types/react": "^17.0.18", "@types/react-dom": "^17.0.9", "@types/react-redux": "^7.1.18", + "@wasm-tool/wasm-pack-plugin": "^1.6.0", + "another-json": "^0.2.0", "await-spawn": "^4.0.2", + "better-sqlite3": "^7.5.0", "bitcoin-core-ts": "^3.0.3", "bitcoinjs-lib": "^5.1.6", "bl": "^5.0.0", "closest": "^0.0.1", "color": "^3.1.2", "dagre": "^0.8.5", + "electron-rebuild": "^3.2.7", "eslint-plugin-unused-imports": "^2.0.0", "esm": "^3.2.25", "lodash": "^4.17.15", "mathjs": "^7.5.1", + "node-fetch": "2.6.1", "pathfinding": "^0.4.18", "paths-js": "^0.4.10", "prettier": "^2.6.0", "react": "17.0.2", + "react-app-rewired": "^2.2.1", "react-dom": "17.0.2", "react-redux": "^7.2.4", "react-scripts": "^4.0.3", @@ -56,13 +65,14 @@ "scripts": { "build-electron": "yarn tsc -p ./tsconfig.node.json", "watch": "yarn tsc -w", - "build": "react-scripts build", + "rebuild": "electron-rebuild -f -w desktop/", + "build": "react-app-rewired build", "eject": "react-scripts eject", "prettier": "prettier --write src desktop", "prettier:check": "prettier --check src desktop", "start-electron": "export ELECTRON_START_URL='http://localhost:3000' && yarn build-electron && electron .", - "start-react": "export BROWSER=none REACT_EDITOR=none && react-scripts start", - "test": "react-scripts test" + "start-react": "export BROWSER=none REACT_EDITOR=none && react-app-rewired start", + "test": "react-app-rewired test" }, "eslintConfig": { "extends": "react-app" diff --git a/src/App.css b/src/App.css index 5f94771..5b97d64 100644 --- a/src/App.css +++ b/src/App.css @@ -114,3 +114,15 @@ body { .settings-container { height: 100%; } + +.miniscript-container { + height: 100%; + width: 100%; + overflow: scroll; +} + +.chat-container { + height: 100%; + width: 100%; + overflow: scroll; +} diff --git a/src/App.tsx b/src/App.tsx index 50ffb30..74ef00f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,15 +15,11 @@ import createEngine, { import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import './App.css'; -import { - load_new_model, - selectContract, - selectShowing, - selectStatusBar, -} from './AppSlice'; +import { selectContract, selectShowing, selectStatusBar } from './AppSlice'; +import { Data } from './common/preload_interface'; import { BitcoinNodeManager } from './Data/BitcoinNode'; import { BitcoinStatusBar } from './Data/BitcoinStatusBar'; -import { ContractModel, Data } from './Data/ContractManager'; +import { ContractModel } from './Data/ContractManager'; import { ModelManager } from './Data/ModelManager'; import { SimulationController } from './Data/Simulation'; import { selectSimIsShowing, toggle_showing } from './Data/SimulationSlice'; @@ -33,6 +29,7 @@ import './Glyphs.css'; import { poll_settings } from './Settings/SettingsSlice'; import { TXIDAndWTXIDMap } from './util'; import { AppNavbar } from './UX/AppNavbar'; +import { Chat } from './UX/Chat/Chat'; import { set_continuations } from './UX/ContractCreator/ContractCreatorSlice'; import { CreateContractModal } from './UX/ContractCreator/CreateContractModal'; import { DemoCanvasWidget } from './UX/Diagram/DemoCanvasWidget'; @@ -45,10 +42,10 @@ import { selectShouldViewEntity, } from './UX/Entity/EntitySlice'; import { CurrentlyViewedEntity } from './UX/Entity/EntityViewer'; +import { MiniscriptCompiler } from './UX/Miniscript/Compiler'; import { Modals } from './UX/Modals'; import { Settings } from './UX/Settings/Settings'; import { Wallet } from './Wallet/Wallet'; - export type SelectedEvent = BaseEntityEvent> & { isSelected: boolean; }; @@ -93,11 +90,11 @@ function App() { const model = React.useRef(new DiagramModel()); // TODO: This should go somewhere else :( - React.useEffect(() => { - return window.electron.register('load_contract', (data: string) => { - dispatch(load_new_model(JSON.parse(data))); - }); - }); + //React.useEffect(() => { + // return window.electron.register_callback('load_contract', (data: string) => { + // dispatch(load_new_model(JSON.parse(data))); + // }); + //}); React.useEffect(() => { setTimeout(() => { poll_settings(dispatch); @@ -274,6 +271,18 @@ function Viewing(props: { return ; case 'ContractViewer': return ; + case 'MiniscriptCompiler': + return ( + + + + ); + case 'Chat': + return ( + + + + ); } } function ContractViewer(props: { diff --git a/src/AppSlice.tsx b/src/AppSlice.tsx index 89e67fc..98fc18b 100644 --- a/src/AppSlice.tsx +++ b/src/AppSlice.tsx @@ -1,30 +1,16 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { Data } from './Data/ContractManager'; import { AppDispatch, RootState } from './Store/store'; import { createSelectorCreator, defaultMemoize } from 'reselect'; import _ from 'lodash'; +import { CreatedContract, Data } from './common/preload_interface'; -type ContractArgs = { - arguments: Record; - context: { - amount: number; - network: 'Regtest' | 'Signet' | 'Testnet' | 'Bitcoin'; - effects?: { - effects?: Record< - string, - Record> - >; - }; - }; -}; - -export type CreatedContract = { - name: string; - args: ContractArgs; - data: Data; -}; - -type Pages = 'ContractCreator' | 'ContractViewer' | 'Wallet' | 'Settings'; +type Pages = + | 'ContractCreator' + | 'ContractViewer' + | 'Wallet' + | 'Settings' + | 'MiniscriptCompiler' + | 'Chat'; type StateType = { data: CreatedContract | null; counter: number; @@ -82,10 +68,13 @@ export const { } = appSlice.actions; export const create_contract_of_type = - (type_arg: string, contract: any) => + (type_arg: string, txn: string | null, contract: any) => async (dispatch: AppDispatch, getState: () => RootState) => { + const state = getState(); const compiled_contract = await window.electron.sapio.create_contract( + state.walletReducer.workspace, type_arg, + txn, contract ); if ('ok' in compiled_contract && compiled_contract.ok) @@ -103,6 +92,8 @@ export const recreate_contract = if (s.appReducer.data === null) return; return create_contract_of_type( s.appReducer.data.name, + s.appReducer.data.data.program['funding']?.txs[0]?.linked_psbt + ?.psbt ?? null, JSON.stringify(s.appReducer.data.args) )(dispatch, getState); }; @@ -110,10 +101,13 @@ export const recreate_contract = export const open_contract_directory = (file_name: string) => async (dispatch: AppDispatch, getState: () => RootState) => { - window.electron.sapio.compiled_contracts.open(file_name).then((v) => { - if ('err' in v) return; - return 'ok' in v && dispatch(load_new_model(v.ok)); - }); + const state = getState(); + window.electron.sapio.compiled_contracts + .open(state.walletReducer.workspace, file_name) + .then((v) => { + if ('err' in v) return; + return 'ok' in v && dispatch(load_new_model(v.ok)); + }); }; export const create_contract_from_file = diff --git a/src/Data/BitcoinNode.ts b/src/Data/BitcoinNode.ts index 7d43bd4..ed298f9 100644 --- a/src/Data/BitcoinNode.ts +++ b/src/Data/BitcoinNode.ts @@ -350,7 +350,7 @@ export class BitcoinNodeManager { .map((tm) => tm.get_txid()); const req = txids.map((txid) => { return { - method: 'gettransaction', + method: 'getrawtransaction', parameters: [txid, true], }; }); @@ -375,7 +375,7 @@ export class BitcoinNodeManager { } else { s[1] = { txid: txdata.txid, - confirmations: txdata.confirmations ?? null, + confirmations: txdata.confirmations ?? 0, }; } s[0] = s[1].txid; diff --git a/src/Data/ContractManager.ts b/src/Data/ContractManager.ts index 8f59f52..bcb063d 100644 --- a/src/Data/ContractManager.ts +++ b/src/Data/ContractManager.ts @@ -2,7 +2,6 @@ import * as Bitcoin from 'bitcoinjs-lib'; import { Output } from 'bitcoinjs-lib/types/transaction'; import * as assert from 'assert'; import _, { Collection } from 'lodash'; -import { JSONSchema7 } from 'json-schema'; import { InputMapT, InputMap, @@ -14,6 +13,12 @@ import { import { PhantomTransactionModel, TransactionModel } from './Transaction'; import { UTXOModel } from './UTXO'; import Color from 'color'; +import { + Continuation, + ContinuationTable, + Data, + UTXOFormatData, +} from '../common/preload_interface'; export type NodeColorT = ['NodeColor', string]; export const NodeColor = { new(c: string): NodeColorT { @@ -26,33 +31,6 @@ export const NodeColor = { return NodeColor.new(c[1].slice()); }, }; -export interface UTXOFormatData { - color: string; - label: string; -} -export interface TransactionData { - psbt: string; - hex: string; - metadata: { - color?: string; - label?: string; - }; - output_metadata?: Array; -} -export type ContinuationTable = Record>; - -export type APIPath = string; -export interface Continuation { - schema: JSONSchema7; - path: APIPath; -} -export interface DataItem { - txs: Array<{ linked_psbt: TransactionData }>; - continue_apis: Record; -} -export interface Data { - program: Record; -} type PreProcessedData = { psbts: Array; @@ -281,7 +259,7 @@ function process_utxo_models( m_txn.get_txid(), output_index ) ?? []; - assert.equal(m_txn, utxo_model.txn); + assert.equal(m_txn, utxo_model.getOptions().txn); if ( m_txn instanceof PhantomTransactionModel && spenders.length > 0 @@ -290,7 +268,7 @@ function process_utxo_models( if (spenders[0]?.witness_set.witnesses.length) { const witstack = spenders[0]?.witness_set.witnesses[0]?.[ - utxo_model.utxo.index + utxo_model.getOptions().utxo.index ]; if (witstack) { const program = witstack[witstack.length - 1]; @@ -316,11 +294,12 @@ function process_utxo_models( 0 ); ( - utxo_model.txn.tx.outs[ - utxo_model.utxo.index + utxo_model.getOptions().txn.tx.outs[ + utxo_model.getOptions().utxo.index ] as Output ).value = max_amount; - utxo_model.utxo.amount = max_amount; + utxo_model.getOptions().utxo.amount = + max_amount; const _address = Bitcoin.address.fromOutputScript( script, @@ -342,7 +321,7 @@ function process_utxo_models( } const link = utxo_model.spent_by(spender, spend_idx, idx); spender.input_links.push(link); - utxo_model.utxo.spends.push(spender); + utxo_model.getOptions().utxo.spends.push(spender); }); } ); diff --git a/src/Data/ModelManager.tsx b/src/Data/ModelManager.tsx index 0b57072..017e96d 100644 --- a/src/Data/ModelManager.tsx +++ b/src/Data/ModelManager.tsx @@ -2,7 +2,7 @@ import { DiagramModel, LinkModel } from '@projectstorm/react-diagrams'; import { ContractModel, timing_cache } from './ContractManager'; import { TransactionModel, PhantomTransactionModel } from './Transaction'; import { UTXOModel } from './UTXO'; -import { SpendPortModel } from '../UX/Diagram/DiagramComponents/SpendLink/SpendLink'; +import { SpendPortModel } from '../UX/Diagram/DiagramComponents/SpendLink/SpendPortModel'; export class ModelManager { model: DiagramModel; diff --git a/src/Data/Transaction.ts b/src/Data/Transaction.ts index b3af13f..62e0990 100644 --- a/src/Data/Transaction.ts +++ b/src/Data/Transaction.ts @@ -8,17 +8,13 @@ import { OutputLinkModel } from '../UX/Diagram/DiagramComponents/OutputLink'; import { SpendLinkModel } from '../UX/Diagram/DiagramComponents/SpendLink/SpendLinkModel'; import { TransactionNodeModel } from '../UX/Diagram/DiagramComponents/TransactionNode/TransactionNodeModel'; import { HasKeys, TXID } from '../util'; -import { - NodeColorT, - UTXOFormatData, - SigningDataStore, - NodeColor, -} from './ContractManager'; +import { NodeColorT, SigningDataStore, NodeColor } from './ContractManager'; import './Transaction.css'; -import { UTXOMetaData, UTXOModel } from './UTXO'; -import { TransactionData } from './ContractManager'; +import { new_utxo_inner_data, UTXOModel } from './UTXO'; import { select_txn } from '../UX/Entity/EntitySlice'; import { store } from '../Store/store'; +import { TransactionData, UTXOFormatData } from '../common/preload_interface'; +import _ from 'lodash'; export class TransactionModel extends TransactionNodeModel implements HasKeys { tx: Bitcoin.Transaction; @@ -41,16 +37,18 @@ export class TransactionModel extends TransactionNodeModel implements HasKeys { this.witness_set = all_witnesses; for (let y = 0; y < this.tx.outs.length; ++y) { const subcolor = NodeColor.clone(color); - const metadata = utxo_labels[y] || { - color: NodeColor.get(subcolor), - label: name, - }; + const metadata: UTXOFormatData = _.merge( + { + color: NodeColor.get(subcolor), + label: name, + }, + utxo_labels[y] + ); // TODO: Get rid of assertion const out: Bitcoin.TxOutput = tx.outs[y] as Bitcoin.TxOutput; const utxo = new UTXOModel( - new UTXOMetaData(out.script, out.value, tx, y), - metadata.label, - NodeColor.new(metadata.color), + new_utxo_inner_data(out.script, out.value, tx, y), + metadata, this ); this.utxo_models.push(utxo); @@ -81,6 +79,7 @@ export class TransactionModel extends TransactionNodeModel implements HasKeys { return { color: u.getOptions().color, label: u.getOptions().name, + simp: {}, }; }), }; diff --git a/src/Data/UTXO.ts b/src/Data/UTXO.ts index 50728e0..7734b23 100644 --- a/src/Data/UTXO.ts +++ b/src/Data/UTXO.ts @@ -1,51 +1,47 @@ import { Transaction } from 'bitcoinjs-lib'; -import { NodeColor, NodeColorT } from './ContractManager'; -import { UTXONodeModel } from '../UX/Diagram/DiagramComponents/UTXONode/UTXONodeModel'; +import { + UTXOInnerData, + UTXONodeModel, +} from '../UX/Diagram/DiagramComponents/UTXONode/UTXONodeModel'; import { TransactionModel } from './Transaction'; import { is_mock_outpoint, txid_buf_to_string } from '../util'; -import { SpendLinkModel } from '../UX/Diagram/DiagramComponents/SpendLink/SpendLinkModel'; import { store } from '../Store/store'; import { select_utxo } from '../UX/Entity/EntitySlice'; -export class UTXOMetaData { - index: number; - script: Buffer; - amount: number; - spends: Array; - txid: string; - constructor( - script: Buffer, - amount: number, - txn: Transaction, - index: number - ) { - this.txid = txn.getId(); - this.index = index; - this.script = script; - this.amount = amount; - this.spends = []; - } +import { UTXOFormatData } from '../common/preload_interface'; +import { SpendLinkModel } from '../UX/Diagram/DiagramComponents/SpendLink/SpendLinkModel'; +export function new_utxo_inner_data( + script: Buffer, + amount: number, + txn: Transaction, + index: number +): UTXOInnerData { + return { + txid: txn.getId(), + index: index, + script: script, + amount: amount, + spends: [], + }; } export class UTXOModel extends UTXONodeModel { - txn: TransactionModel; - utxo: UTXOMetaData; constructor( - utxo: UTXOMetaData, - name: string, - color: NodeColorT, + utxo: UTXOInnerData, + metadata: UTXOFormatData, txn: TransactionModel ) { super( { - name, - color: NodeColor.get(color), + name: metadata.label, + color: metadata.color, amount: utxo.amount, confirmed: false, + txn, + utxo, + metadata, }, txn.get_txid(), utxo.index ); - this.utxo = utxo; - this.txn = txn; this.registerListener({ selectionChanged: (event: any) => { // TODO: get store the right way? @@ -57,7 +53,7 @@ export class UTXOModel extends UTXONodeModel { }); } getAmount(): number { - return this.utxo.amount; + return this.getOptions().utxo.amount; } spent_by( @@ -79,13 +75,15 @@ export function update_utxomodel(utxo_in: UTXOModel) { const s = to_iterate.pop()!; for (const utxo of s) { // Pop a node for processing... - utxo.utxo.spends.forEach((spend: TransactionModel) => { + utxo.getOptions().utxo.spends.forEach((spend: TransactionModel) => { spend.tx.ins .filter( - (inp) => txid_buf_to_string(inp.hash) === utxo.utxo.txid + (inp) => + txid_buf_to_string(inp.hash) === + utxo.getOptions().utxo.txid ) .forEach((inp) => { - inp.hash = utxo.txn.tx.getHash(); + inp.hash = utxo.getOptions().txn.tx.getHash(); }); spend.tx.ins .filter((inp) => @@ -96,8 +94,8 @@ export function update_utxomodel(utxo_in: UTXOModel) { ) .forEach((inp) => { // TODO: Only modify the mock that was intended - inp.hash = utxo.txn.tx.getHash(); - inp.index = utxo.utxo.index; + inp.hash = utxo.getOptions().txn.tx.getHash(); + inp.index = utxo.getOptions().utxo.index; }); to_iterate.push(spend.utxo_models); }); diff --git a/src/Miniscript/.appveyor.yml b/src/Miniscript/.appveyor.yml new file mode 100644 index 0000000..8f4ef66 --- /dev/null +++ b/src/Miniscript/.appveyor.yml @@ -0,0 +1,11 @@ +install: + - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly + - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin + - rustc -V + - cargo -V + +build: false + +test_script: + - cargo test --locked diff --git a/src/Miniscript/.cargo-ok b/src/Miniscript/.cargo-ok new file mode 100644 index 0000000..e69de29 diff --git a/src/Miniscript/.gitignore b/src/Miniscript/.gitignore new file mode 100644 index 0000000..4e30131 --- /dev/null +++ b/src/Miniscript/.gitignore @@ -0,0 +1,6 @@ +/target +**/*.rs.bk +Cargo.lock +bin/ +pkg/ +wasm-pack.log diff --git a/src/Miniscript/.travis.yml b/src/Miniscript/.travis.yml new file mode 100644 index 0000000..de0979d --- /dev/null +++ b/src/Miniscript/.travis.yml @@ -0,0 +1,68 @@ +language: rust +sudo: false + +cache: cargo + +matrix: + include: + # Builds with wasm-pack. + - rust: beta + env: RUST_BACKTRACE=1 + addons: + firefox: latest + chrome: stable + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f + script: + - cargo generate --git . --name testing + # Having a broken Cargo.toml (in that it has curlies in fields) anywhere + # in any of our parent dirs is problematic. + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - wasm-pack build + - wasm-pack test --chrome --firefox --headless + + # Builds on nightly. + - rust: nightly + env: RUST_BACKTRACE=1 + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - rustup target add wasm32-unknown-unknown + script: + - cargo generate --git . --name testing + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - cargo check + - cargo check --target wasm32-unknown-unknown + - cargo check --no-default-features + - cargo check --target wasm32-unknown-unknown --no-default-features + - cargo check --no-default-features --features console_error_panic_hook + - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook + - cargo check --no-default-features --features "console_error_panic_hook wee_alloc" + - cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc" + + # Builds on beta. + - rust: beta + env: RUST_BACKTRACE=1 + before_script: + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) + - cargo install-update -a + - rustup target add wasm32-unknown-unknown + script: + - cargo generate --git . --name testing + - mv Cargo.toml Cargo.toml.tmpl + - cd testing + - cargo check + - cargo check --target wasm32-unknown-unknown + - cargo check --no-default-features + - cargo check --target wasm32-unknown-unknown --no-default-features + - cargo check --no-default-features --features console_error_panic_hook + - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook + # Note: no enabling the `wee_alloc` feature here because it requires + # nightly for now. diff --git a/src/Miniscript/Cargo.toml b/src/Miniscript/Cargo.toml new file mode 100644 index 0000000..76858aa --- /dev/null +++ b/src/Miniscript/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "miniscript-shim" +version = "0.1.0" +authors = ["Jeremy Rubin "] +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = ["console_error_panic_hook"] + +[dependencies] +wasm-bindgen = "0.2.63" +sapio-miniscript = {version = "7.0.0-10", features =["use-serde", "compiler", "serde"]} +serde_json = "1.0.59" + + +# The `console_error_panic_hook` crate provides better debugging of panics by +# logging them with `console.error`. This is great for development, but requires +# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for +# code size when deploying. +console_error_panic_hook = { version = "0.1.6", optional = true } + +# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size +# compared to the default allocator's ~10K. It is slower than the default +# allocator, however. +# +# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now. +wee_alloc = { version = "0.4.5", optional = true } + +[dev-dependencies] +wasm-bindgen-test = "0.3.13" + +[profile.release] +# Tell `rustc` to optimize for small code size. +opt-level = "s" diff --git a/src/Miniscript/LICENSE_APACHE b/src/Miniscript/LICENSE_APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/src/Miniscript/LICENSE_APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/src/Miniscript/LICENSE_MIT b/src/Miniscript/LICENSE_MIT new file mode 100644 index 0000000..4f1d53f --- /dev/null +++ b/src/Miniscript/LICENSE_MIT @@ -0,0 +1,25 @@ +Copyright (c) 2018 Jeremy Rubin + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/src/Miniscript/README.md b/src/Miniscript/README.md new file mode 100644 index 0000000..e3717f9 --- /dev/null +++ b/src/Miniscript/README.md @@ -0,0 +1,70 @@ +
+ +

wasm-pack-template

+ +A template for kick starting a Rust and WebAssembly project using wasm-pack. + +

+ Build Status +

+ +

+ Tutorial + | + Chat +

+ +Built with 🦀🕸 by The Rust and WebAssembly Working Group + +
+ +## About + +[**📚 Read this template tutorial! 📚**][template-docs] + +This template is designed for compiling Rust libraries into WebAssembly and +publishing the resulting package to NPM. + +Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other +templates and usages of `wasm-pack`. + +[tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html +[template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html + +## 🚴 Usage + +### 🐑 Use `cargo generate` to Clone this Template + +[Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate) + +``` +cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project +cd my-project +``` + +### 🛠️ Build with `wasm-pack build` + +``` +wasm-pack build +``` + +### 🔬 Test in Headless Browsers with `wasm-pack test` + +``` +wasm-pack test --headless --firefox +``` + +### 🎁 Publish to NPM with `wasm-pack publish` + +``` +wasm-pack publish +``` + +## 🔋 Batteries Included + +- [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating + between WebAssembly and JavaScript. +- [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook) + for logging panic messages to the developer console. +- [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized + for small code size. diff --git a/src/Miniscript/src/lib.rs b/src/Miniscript/src/lib.rs new file mode 100644 index 0000000..d27f7f3 --- /dev/null +++ b/src/Miniscript/src/lib.rs @@ -0,0 +1,133 @@ +#![feature(once_cell)] +#![feature(result_flattening)] +mod utils; + +use crate::bitcoin::Script; +use crate::bitcoin::XOnlyPublicKey; +use std::collections::HashMap; +use std::rc::Rc; +use std::sync::Arc; +use wasm_bindgen::prelude::*; + +// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global +// allocator. +#[cfg(feature = "wee_alloc")] +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +#[wasm_bindgen] +extern "C" { + fn alert(s: &str); +} + +use core::str::FromStr; +use sapio_miniscript::*; +#[wasm_bindgen] +pub fn compile(s: &str) -> Result { + let pol = policy::Concrete::from_str(s).map_err(|e| e.to_string())?; + let ms: Miniscript = pol.compile().map_err(|e| e.to_string())?; + Ok(ms.to_string()) +} +use bitcoin::secp256k1::VerifyOnly; +use sapio_miniscript::bitcoin::secp256k1::Secp256k1; +use std::lazy::SyncLazy; +static SECP: SyncLazy> = SyncLazy::new(|| Secp256k1::verification_only()); +use bitcoin::util::taproot::TaprootSpendInfo; +use sapio_miniscript::TranslatePk; +#[wasm_bindgen] +#[derive(Debug)] +pub struct KeyTab { + v: HashMap, +} + +#[wasm_bindgen] +impl KeyTab { + pub fn new() -> KeyTab { + KeyTab { v: HashMap::new() } + } + pub fn add(&mut self, k: String, v: String) { + self.v.insert(k, v); + } +} + +#[wasm_bindgen] +#[derive(Debug)] +pub struct Fragments { + v: Vec, +} + +#[wasm_bindgen] +impl Fragments { + pub fn new() -> Self { + Fragments { v: vec![] } + } + pub fn add(&mut self, s: String) { + self.v.push(s) + } + pub fn add_all(&mut self, s: Box<[JsValue]>) -> bool { + for v in s.iter() { + if let Some(st) = v.as_string() { + self.v.push(st) + } else { + return false; + } + } + return true; + } +} + +#[wasm_bindgen] +pub fn taproot(frags: Fragments, keytab: &KeyTab) -> Result { + let key = keytab + .v + .iter() + .map(|(k, v)| XOnlyPublicKey::from_str(&v).map(|key| (k, key))) + .collect::, _>>() + .map_err(|e| e.to_string())?; + let ms: Vec> = frags + .v + .iter() + .map(|s| Miniscript::::from_str(&s).map_err(|e| e.to_string())) + .collect::>, _>>() + .map_err(|e| e.to_string())?; + + let fpk: &Fn(&String) -> Result = &|k| { + key.get(&k) + .cloned() + .ok_or_else(|| format!("Missing Key: {}", k)) + }; + let scripts: Vec<(u32, Script)> = ms + .iter() + .map(|s| { + s.translate_pk(fpk, |k| Err(format!("No PKH Support for {}", k))) + .map(|s: Miniscript| (1, s.encode())) + }) + .collect::, _>>()?; + use bitcoin::hashes::Hash; + let nums: XOnlyPublicKey = { + let mut b = bitcoin::hashes::sha256::Hash::hash("Hello".as_bytes()).into_inner(); + loop { + if let Ok(k) = XOnlyPublicKey::from_slice(&b[..]) { + break k; + } else { + b = bitcoin::hashes::sha256::Hash::hash(&b[..]).into_inner(); + } + } + }; + let tsi = + TaprootSpendInfo::with_huffman_tree(&SECP, nums, scripts).map_err(|e| e.to_string())?; + use sapio_miniscript::bitcoin::hashes::hex::ToHex; + let js = serde_json::json! {{ + "tweak": tsi.tap_tweak().as_hash().to_hex(), + "internal_key": tsi.internal_key().to_hex(), + "merkle_root": tsi.merkle_root().map(|m|m.to_hex()), + "scripts": tsi.as_script_map().iter().collect::>(), + "address":{ + "main": sapio_miniscript::bitcoin::Address::p2tr_tweaked(tsi.output_key(), bitcoin::network::constants::Network::Bitcoin), + "test": sapio_miniscript::bitcoin::Address::p2tr_tweaked(tsi.output_key(), bitcoin::network::constants::Network::Testnet), + "regtest": sapio_miniscript::bitcoin::Address::p2tr_tweaked(tsi.output_key(), bitcoin::network::constants::Network::Regtest), + "signet": sapio_miniscript::bitcoin::Address::p2tr_tweaked(tsi.output_key(), bitcoin::network::constants::Network::Signet), + } + }}; + Ok(serde_json::to_string_pretty(&js).map_err(|e| e.to_string())?) +} diff --git a/src/Miniscript/src/utils.rs b/src/Miniscript/src/utils.rs new file mode 100644 index 0000000..b1d7929 --- /dev/null +++ b/src/Miniscript/src/utils.rs @@ -0,0 +1,10 @@ +pub fn set_panic_hook() { + // When the `console_error_panic_hook` feature is enabled, we can call the + // `set_panic_hook` function at least once during initialization, and then + // we will get better error messages if our code ever panics. + // + // For more details see + // https://github.com/rustwasm/console_error_panic_hook#readme + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); +} diff --git a/src/Miniscript/tests/web.rs b/src/Miniscript/tests/web.rs new file mode 100644 index 0000000..de5c1da --- /dev/null +++ b/src/Miniscript/tests/web.rs @@ -0,0 +1,13 @@ +//! Test suite for the Web and headless browsers. + +#![cfg(target_arch = "wasm32")] + +extern crate wasm_bindgen_test; +use wasm_bindgen_test::*; + +wasm_bindgen_test_configure!(run_in_browser); + +#[wasm_bindgen_test] +fn pass() { + assert_eq!(1 + 1, 2); +} diff --git a/src/Store/store.ts b/src/Store/store.ts index a9e53f3..bf7b66f 100644 --- a/src/Store/store.ts +++ b/src/Store/store.ts @@ -5,11 +5,9 @@ import appReducer from '../AppSlice'; import { dataReducer } from '../Data/DataSlice'; import modalReducer from '../UX/ModalSlice'; import { settingsReducer } from '../Settings/SettingsSlice'; -import { - contractCreatorReducer, - register, -} from '../UX/ContractCreator/ContractCreatorSlice'; +import { contractCreatorReducer } from '../UX/ContractCreator/ContractCreatorSlice'; import { simulationReducer } from '../Data/SimulationSlice'; +import { walletReducer } from '../Wallet/Slice/Reducer'; export const store = configureStore({ reducer: { @@ -20,11 +18,11 @@ export const store = configureStore({ settingsReducer, modalReducer, dataReducer, + walletReducer, }, middleware: [thunk], devTools: true, }); -register(store.dispatch); export type AppDispatch = typeof store.dispatch; export type RootState = ReturnType; diff --git a/src/UX/AppNavbar.tsx b/src/UX/AppNavbar.tsx index 4998421..1d33406 100644 --- a/src/UX/AppNavbar.tsx +++ b/src/UX/AppNavbar.tsx @@ -1,3 +1,4 @@ +import { Chat } from '@mui/icons-material'; import SettingsIcon from '@mui/icons-material/Settings'; import { Divider, @@ -32,7 +33,7 @@ export function AppNavbar(props: { return ( - + @@ -127,6 +128,29 @@ function MainScreens() { + { + dispatch(switch_showing('MiniscriptCompiler')); + }} + > + + + + + dispatch(switch_showing('Chat'))} + > + + + + + ); } diff --git a/src/UX/Chat/Channel.css b/src/UX/Chat/Channel.css new file mode 100644 index 0000000..f8fef0b --- /dev/null +++ b/src/UX/Chat/Channel.css @@ -0,0 +1,19 @@ +.Typing { + display: grid; + grid-template-columns: 1fr max-content; + width: 100%; +} + +.ChatBox { + display: grid; + height: 100%; + max-height: 100%; + grid-template-rows: max-content 1fr max-content; +} + +.MessageArea { + max-height: 80vh; + overflow-y: scroll; + overflow-x: hidden; + overflow-wrap: anywhere; +} diff --git a/src/UX/Chat/Channel.tsx b/src/UX/Chat/Channel.tsx new file mode 100644 index 0000000..e8935ce --- /dev/null +++ b/src/UX/Chat/Channel.tsx @@ -0,0 +1,103 @@ +import { Box, Button, FilledInput, FormGroup } from '@mui/material'; +import * as React from 'react'; +import './Channel.css'; +import FormControl, { useFormControl } from '@mui/material/FormControl'; +import _ from 'lodash'; + +export function Channel(props: { channel_id: string; close: () => void }) { + const [messages, set_messages] = React.useState([]); + React.useEffect(() => { + let cancel: ReturnType; + let since = 0; + let all_messages: any[] = []; + async function f() { + const last = since; + /// todo: small concurrency bug here can lead to messages appearing twice + const m = await window.electron.chat.list_messages_channel( + props.channel_id, + last + ); + if (m.length) { + since = Math.max( + _(m) + .map((msg) => msg.received_time) + .max(), + since + ); + all_messages = [...all_messages, ...m]; + set_messages(all_messages); + } + cancel = setTimeout(f, 1000); + } + cancel = setTimeout(f, 0); + return () => { + clearTimeout(cancel); + }; + }, []); + const msg_area = React.useRef(null); + React.useEffect(() => { + if (!msg_area.current) return; + msg_area.current.scrollTop = msg_area.current.scrollHeight; + }, [messages]); + + return ( + + + + {messages.map((m, i) => ( +
+ {m.nickname}: {m.body} +
+ ))} +
+ +
+ +
+
+ ); +} + +function Typing(props: { channel_id: string }) { + const [typed, set_typed] = React.useState(''); + const { focused } = useFormControl() || {}; + return ( + { + ev.preventDefault(); + }} + > + + + set_typed(ev.currentTarget.value)} + value={typed} + autoFocus + type="text" + /> + + + + + ); +} diff --git a/src/UX/Chat/Chat.css b/src/UX/Chat/Chat.css new file mode 100644 index 0000000..e970751 --- /dev/null +++ b/src/UX/Chat/Chat.css @@ -0,0 +1,7 @@ +.Chat { + display: grid; + height: 100%; + width: 100%; + grid-template-columns: 0.5fr 1fr; + column-gap: 2em; +} diff --git a/src/UX/Chat/Chat.tsx b/src/UX/Chat/Chat.tsx new file mode 100644 index 0000000..11a1d79 --- /dev/null +++ b/src/UX/Chat/Chat.tsx @@ -0,0 +1,194 @@ +import { Box, Button } from '@mui/material'; +import * as React from 'react'; +import VisibilityIcon from '@mui/icons-material/Visibility'; +import { + DataGrid, + GridActionsCellItem, + GridColumns, + GridToolbarContainer, +} from '@mui/x-data-grid'; +import './Chat.css'; +import { Channel } from './Channel'; +import { Add } from '@mui/icons-material'; +import { NewNickname } from './NewNickname'; +import { NewChannel } from './NewChannel'; + +export function Chat() { + React.useEffect(() => { + async function f() { + await window.electron.chat.init(); + } + f(); + }); + return ( + + + + + ); +} + +const UserGrid: GridColumns = [ + { + field: 'actions-load', + type: 'actions', + flex: 0.2, + getActions: (params) => [ + } + label="Open" + onClick={() => { + // todo: + }} + />, + ], + }, + { + field: 'nickname', + headerName: 'NickName', + minWidth: 100, + type: 'text', + flex: 1, + }, + { + field: 'key', + headerName: 'Key Hash', + minWidth: 100, + type: 'text', + flex: 1, + }, +]; +function Users() { + const [users, set_users] = React.useState< + { nickname: string; key: string }[] + >([]); + React.useEffect(() => { + let cancel: ReturnType; + async function f() { + await window.electron.chat.init(); + set_users(await window.electron.chat.list_users()); + cancel = setTimeout(f, 5000); + } + cancel = setTimeout(f, 0); + return () => { + clearTimeout(cancel); + }; + }, []); + const [add_new_user, set_add_new_user] = React.useState(false); + function CustomToolbar() { + return ( + + + + ); + } + return ( +
+ set_add_new_user(false)} + > + + { + return { id: v.key, ...v }; + })} + columns={UserGrid} + disableExtendRowFullWidth={false} + columnBuffer={3} + pageSize={10} + rowsPerPageOptions={[5]} + disableColumnSelector + disableSelectionOnClick + /> +
+ ); +} + +function Channels() { + const [channels, set_channels] = React.useState<{ channel_id: string }[]>( + [] + ); + React.useEffect(() => { + let cancel: ReturnType; + async function f() { + set_channels(await window.electron.chat.list_channels()); + cancel = setTimeout(f, 5000); + } + cancel = setTimeout(f, 0); + return () => { + clearTimeout(cancel); + }; + }, []); + const [channel, set_channel] = React.useState(null); + const [add_new_channel, set_add_new_channel] = + React.useState(false); + const ChannelColumns: GridColumns = [ + { + field: 'actions-load', + type: 'actions', + flex: 0.2, + getActions: (params) => [ + } + label="Open" + onClick={() => { + typeof params.id === 'string' && set_channel(params.id); + }} + />, + ], + }, + { + field: 'channel_id', + headerName: 'Channel', + minWidth: 100, + type: 'text', + flex: 1, + }, + ]; + function CustomToolbar() { + return ( + + + + ); + } + return ( +
+ set_add_new_channel(false)} + /> + {channel === null && ( + { + return { id: v.channel_id, ...v }; + })} + columns={ChannelColumns} + disableExtendRowFullWidth={false} + columnBuffer={3} + pageSize={10} + rowsPerPageOptions={[5]} + disableColumnSelector + disableSelectionOnClick + /> + )} + {channel !== null && ( + { + set_channel(null); + }} + > + )} +
+ ); +} diff --git a/src/UX/Chat/NewChannel.tsx b/src/UX/Chat/NewChannel.tsx new file mode 100644 index 0000000..327edca --- /dev/null +++ b/src/UX/Chat/NewChannel.tsx @@ -0,0 +1,54 @@ +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + TextField, +} from '@mui/material'; +import React from 'react'; + +export function NewChannel(props: { show: boolean; hide: () => void }) { + const [value, set_value] = React.useState(null); + return ( + + Create a new channel + + + The name of the new channel to create... + + set_value(ev.currentTarget.value)} + value={value} + autoFocus + margin="dense" + label="Name" + name="name" + type="text" + fullWidth + variant="standard" + /> + + + + + + + ); +} diff --git a/src/UX/Chat/NewNickname.tsx b/src/UX/Chat/NewNickname.tsx new file mode 100644 index 0000000..0aec414 --- /dev/null +++ b/src/UX/Chat/NewNickname.tsx @@ -0,0 +1,65 @@ +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + TextField, +} from '@mui/material'; +import React from 'react'; + +export function NewNickname(props: { show: boolean; hide: () => void }) { + const [value, set_value] = React.useState(null); + const [key_value, set_key_value] = React.useState(null); + return ( + + Create a new User + + + The key and nickname for the new person. Keys must be + unique. + + set_value(ev.currentTarget.value)} + value={value} + autoFocus + margin="dense" + label="Name" + name="name" + type="text" + fullWidth + variant="standard" + /> + set_key_value(ev.currentTarget.value)} + value={key_value} + autoFocus + margin="dense" + label="Key Hash" + name="keyhash" + type="text" + fullWidth + variant="standard" + /> + + + + + + + ); +} diff --git a/src/UX/ContractCreator/ContractCreatorSlice.tsx b/src/UX/ContractCreator/ContractCreatorSlice.tsx index 4ca29fa..2ef4370 100644 --- a/src/UX/ContractCreator/ContractCreatorSlice.tsx +++ b/src/UX/ContractCreator/ContractCreatorSlice.tsx @@ -1,21 +1,17 @@ -import { createSlice, Dispatch, PayloadAction } from '@reduxjs/toolkit'; +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { createSelectorCreator, defaultMemoize } from 'reselect'; import { RootState } from '../../Store/store'; -import { JSONSchema7 } from 'json-schema'; import { + API, APIPath, Continuation, ContinuationTable, -} from '../../Data/ContractManager'; +} from '../../common/preload_interface'; import _ from 'lodash'; -export type APIs = Record< - string, - { name: string; key: string; api: JSONSchema7; logo: string } ->; type CreatorStateType = { - apis: null | APIs; - selected_api: keyof APIs | null; + apis: null | API; + selected_api: keyof API | null; show: boolean; continuations: ContinuationTable; }; @@ -32,10 +28,10 @@ export const contractCreatorSlice = createSlice({ name: 'ContractCreator', initialState: default_state(), reducers: { - set_apis: (state, action: PayloadAction) => { + set_apis: (state, action: PayloadAction) => { state.apis = action.payload; }, - select_api: (state, action: PayloadAction) => { + select_api: (state, action: PayloadAction) => { state.selected_api = action.payload; }, show_apis: (state, action: PayloadAction) => { @@ -52,16 +48,16 @@ export const contractCreatorSlice = createSlice({ export const { show_apis, set_apis, select_api, set_continuations } = contractCreatorSlice.actions; -export const register = (dispatch: Dispatch) => { - window.electron.register('create_contracts', (apis: APIs) => { - dispatch(set_apis(apis)); - dispatch(show_apis(true)); - }); -}; -const selectAPIs = (rs: RootState): APIs | null => { +//export const register = (dispatch: Dispatch) => { +// window.electron.register_callback('create_contracts', (apis: API) => { +// dispatch(set_apis(apis)); +// dispatch(show_apis(true)); +// }); +//}; +const selectAPIs = (rs: RootState): API | null => { return rs.contractCreatorReducer.apis; }; -const selectSelectedAPI = (rs: RootState): keyof APIs | null => { +const selectSelectedAPI = (rs: RootState): keyof API | null => { return rs.contractCreatorReducer.selected_api; }; diff --git a/src/UX/ContractCreator/SapioPluginPicker/PluginForm.tsx b/src/UX/ContractCreator/SapioPluginPicker/PluginForm.tsx index 262f63e..444e77d 100644 --- a/src/UX/ContractCreator/SapioPluginPicker/PluginForm.tsx +++ b/src/UX/ContractCreator/SapioPluginPicker/PluginForm.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import { MuiForm5 as Form } from '@rjsf/material-ui'; -import { ISubmitEvent } from '@rjsf/core'; +import Form, { ISubmitEvent } from '@rjsf/core'; import { logo_image, Plugin } from './PluginTile'; import { create_contract_of_type, switch_showing } from '../../../AppSlice'; import { useDispatch } from 'react-redux'; import { show_apis } from '../ContractCreatorSlice'; import './PluginForm.css'; +import { Button } from '@mui/material'; interface PluginFormProps { app: Plugin; @@ -16,15 +16,24 @@ export function PluginForm(props: PluginFormProps) { const handleSubmit = async (event: ISubmitEvent, type: string) => { const formData = event.formData; dispatch(switch_showing('ContractViewer')); - await dispatch(create_contract_of_type(type, JSON.stringify(formData))); + await dispatch( + create_contract_of_type(type, null, JSON.stringify(formData)) + ); dispatch(show_apis(false)); }; + const [data, set_data] = React.useState({}); + const handleClick = async () => { + const s = await navigator.clipboard.readText(); + set_data(JSON.parse(s)); + }; return (
{logo_image(props.app)} +
) => handleSubmit(e, props.app.key) diff --git a/src/UX/Diagram/DiagramComponents/OutputLink.ts b/src/UX/Diagram/DiagramComponents/OutputLink.ts index 71bf0cb..57208e2 100644 --- a/src/UX/Diagram/DiagramComponents/OutputLink.ts +++ b/src/UX/Diagram/DiagramComponents/OutputLink.ts @@ -1,6 +1,3 @@ -import { PortModel } from '@projectstorm/react-diagrams-core'; -import { TransactionModel } from '../../../Data/Transaction'; -import { SpendPortModel } from './SpendLink/SpendLink'; import { SpendLinkModel } from './SpendLink/SpendLinkModel'; export class OutputLinkModel extends SpendLinkModel { @@ -12,18 +9,3 @@ export class OutputLinkModel extends SpendLinkModel { this.link_type = 'nonexclusive'; } } - -export class OutputPortModel extends SpendPortModel { - createLinkModel(factory: any) { - return new OutputLinkModel(); - } - - create_link(x: OutputPortModel, to: TransactionModel, factory: any) { - const link = this.createLinkModel(factory); - // TODO: fix? - link.setSourcePort(this as unknown as PortModel); - link.setTargetPort(x as unknown as PortModel); - link.linked_to = to; - return link; - } -} diff --git a/src/UX/Diagram/DiagramComponents/OutputPortModel.ts b/src/UX/Diagram/DiagramComponents/OutputPortModel.ts new file mode 100644 index 0000000..1537f90 --- /dev/null +++ b/src/UX/Diagram/DiagramComponents/OutputPortModel.ts @@ -0,0 +1,19 @@ +import { PortModel } from '@projectstorm/react-diagrams-core'; +import { TransactionModel } from '../../../Data/Transaction'; +import { SpendPortModel } from './SpendLink/SpendPortModel'; +import { OutputLinkModel } from './OutputLink'; + +export class OutputPortModel extends SpendPortModel { + createLinkModel(factory: any): OutputLinkModel { + return new OutputLinkModel(); + } + + create_link(x: OutputPortModel, to: TransactionModel, factory: any) { + const link = this.createLinkModel(factory); + // TODO: fix? + link.setSourcePort(this as unknown as PortModel); + link.setTargetPort(x as unknown as PortModel); + link.linked_to = to; + return link; + } +} diff --git a/src/UX/Diagram/DiagramComponents/SpendLink/SpendLink.tsx b/src/UX/Diagram/DiagramComponents/SpendLink/SpendLink.tsx index 827b66a..010f8fb 100644 --- a/src/UX/Diagram/DiagramComponents/SpendLink/SpendLink.tsx +++ b/src/UX/Diagram/DiagramComponents/SpendLink/SpendLink.tsx @@ -1,8 +1,3 @@ -import { - DefaultPortModel, - DefaultPortModelOptions, - PortModel, -} from '@projectstorm/react-diagrams'; import * as React from 'react'; import Color from 'color'; import { SpendLinkModel } from './SpendLinkModel'; @@ -14,24 +9,6 @@ import { TransactionModel } from '../../../../Data/Transaction'; import { useSelector } from 'react-redux'; import { selectIsReachable } from '../../../../Data/SimulationSlice'; -export class SpendPortModel extends DefaultPortModel { - constructor(options: DefaultPortModelOptions) { - super({ - ...options, - }); - } - createLinkModel(factory: any): SpendLinkModel { - return new SpendLinkModel(); - } - spend_link(x: SpendPortModel, to: TransactionModel, factory: any) { - const link = this.createLinkModel(factory); - // TODO: fix? - link.setSourcePort(this as unknown as PortModel); - link.setTargetPort(x as unknown as PortModel); - link.linked_to = to; - return link; - } -} let unique_key = 0; type PathSettings = { circle: MutableRefObject; diff --git a/src/UX/Diagram/DiagramComponents/SpendLink/SpendPortModel.tsx b/src/UX/Diagram/DiagramComponents/SpendLink/SpendPortModel.tsx new file mode 100644 index 0000000..2b9840f --- /dev/null +++ b/src/UX/Diagram/DiagramComponents/SpendLink/SpendPortModel.tsx @@ -0,0 +1,26 @@ +import { + DefaultPortModel, + DefaultPortModelOptions, + PortModel, +} from '@projectstorm/react-diagrams'; +import { SpendLinkModel } from './SpendLinkModel'; +import { TransactionModel } from '../../../../Data/Transaction'; + +export class SpendPortModel extends DefaultPortModel { + constructor(options: DefaultPortModelOptions) { + super({ + ...options, + }); + } + createLinkModel(factory: any): SpendLinkModel { + return new SpendLinkModel(); + } + spend_link(x: SpendPortModel, to: TransactionModel, factory: any) { + const link = this.createLinkModel(factory); + // TODO: fix? + link.setSourcePort(this as unknown as PortModel); + link.setTargetPort(x as unknown as PortModel); + link.linked_to = to; + return link; + } +} diff --git a/src/UX/Diagram/DiagramComponents/TransactionNode/TransactionNodeModel.ts b/src/UX/Diagram/DiagramComponents/TransactionNode/TransactionNodeModel.ts index 34ff3a7..bc3a9ad 100644 --- a/src/UX/Diagram/DiagramComponents/TransactionNode/TransactionNodeModel.ts +++ b/src/UX/Diagram/DiagramComponents/TransactionNode/TransactionNodeModel.ts @@ -4,8 +4,8 @@ import { PortModel, PortModelAlignment, } from '@projectstorm/react-diagrams-core'; -import { OutputPortModel } from '../OutputLink'; -import { SpendPortModel } from '../SpendLink/SpendLink'; +import { OutputPortModel } from '../OutputPortModel'; +import { SpendPortModel } from '../SpendLink/SpendPortModel'; import { Transaction } from 'bitcoinjs-lib'; import { BasePositionModelOptions } from '@projectstorm/react-canvas-core'; import { DefaultPortModel } from '@projectstorm/react-diagrams'; diff --git a/src/UX/Diagram/DiagramComponents/UTXONode/UTXONodeModel.ts b/src/UX/Diagram/DiagramComponents/UTXONode/UTXONodeModel.ts index 8edf62f..a4d0912 100644 --- a/src/UX/Diagram/DiagramComponents/UTXONode/UTXONodeModel.ts +++ b/src/UX/Diagram/DiagramComponents/UTXONode/UTXONodeModel.ts @@ -4,12 +4,21 @@ import { NodeModelGenerics, PortModel, } from '@projectstorm/react-diagrams-core'; -import { SpendPortModel } from '../SpendLink/SpendLink'; +import { SpendPortModel } from '../SpendLink/SpendPortModel'; import { BasePositionModelOptions } from '@projectstorm/react-canvas-core'; import { TXID } from '../../../../util'; -import { OutputPortModel } from '../OutputLink'; +import { OutputPortModel } from '../OutputPortModel'; import { TransactionState } from '../TransactionNode/TransactionNodeModel'; +import { TransactionModel } from '../../../../Data/Transaction'; +import { UTXOFormatData } from '../../../../common/preload_interface'; +export type UTXOInnerData = { + index: number; + script: Buffer; + amount: number; + spends: Array; + txid: string; +}; export interface UTXONodeModelOptions extends BasePositionModelOptions { name: string; color: string; @@ -17,6 +26,10 @@ export interface UTXONodeModelOptions extends BasePositionModelOptions { confirmed: TransactionState; txid: TXID; index: number; + + txn: TransactionModel; + utxo: UTXOInnerData; + metadata: UTXOFormatData; } export interface UTXONodeModelGenerics extends NodeModelGenerics { OPTIONS: UTXONodeModelOptions; diff --git a/src/UX/Entity/Detail/OutputDetail.tsx b/src/UX/Entity/Detail/OutputDetail.tsx index c182a4c..d7c36ed 100644 --- a/src/UX/Entity/Detail/OutputDetail.tsx +++ b/src/UX/Entity/Detail/OutputDetail.tsx @@ -15,12 +15,14 @@ interface OutputDetailProps { } export function OutputDetail(props: OutputDetailProps) { const dispatch = useDispatch(); + const opts = props.txoutput.getOptions(); const decomp = - Bitcoin.script.decompile(props.txoutput.utxo.script) ?? Buffer.from(''); + Bitcoin.script.decompile(opts.utxo.script) ?? Buffer.from(''); const script = Bitcoin.script.toASM(decomp); + return (
- + dispatch( select_utxo({ - hash: props.txoutput.txn.get_txid(), - nIn: props.txoutput.utxo.index, + hash: opts.txn.get_txid(), + nIn: opts.utxo.index, }) ) } diff --git a/src/UX/Entity/Detail/PSBTDetail.tsx b/src/UX/Entity/Detail/PSBTDetail.tsx index 2e9ac55..f490b34 100644 --- a/src/UX/Entity/Detail/PSBTDetail.tsx +++ b/src/UX/Entity/Detail/PSBTDetail.tsx @@ -33,7 +33,7 @@ class PSBTHandler { this.show_flash = show_flash; } async combine_psbt(psbt: Bitcoin.Psbt) { - const psbt_in = Bitcoin.Psbt.fromBase64( + const psbt_in: Bitcoin.Psbt = Bitcoin.Psbt.fromBase64( await window.electron.fetch_psbt() ); try { diff --git a/src/UX/Entity/Detail/TransactionDetail.tsx b/src/UX/Entity/Detail/TransactionDetail.tsx index 0cfc959..87322a0 100644 --- a/src/UX/Entity/Detail/TransactionDetail.tsx +++ b/src/UX/Entity/Detail/TransactionDetail.tsx @@ -14,9 +14,12 @@ import { outpoint_to_id, sequence_convert, time_to_pretty_string, + TXIDAndWTXIDMap, } from '../../../util'; import Color from 'color'; import { + EntityType, + selectEntityToView, selectTXNColor, selectTXNPurpose, set_custom_color, @@ -25,11 +28,36 @@ import { import { useDispatch, useSelector } from 'react-redux'; import { Divider, TextField, Typography } from '@mui/material'; import { PSBTDetail } from './PSBTDetail'; +import { ContractModel } from '../../../Data/ContractManager'; interface TransactionDetailProps { - entity: TransactionModel; + current_contract: ContractModel; find_tx_model: (a: Buffer, b: number) => UTXOModel | null; } export function TransactionDetail(props: TransactionDetailProps) { + const entity_id: EntityType = useSelector(selectEntityToView); + const entity = + entity_id[0] === 'TXN' + ? TXIDAndWTXIDMap.get_by_txid_s( + props.current_contract.txid_map, + entity_id[1] + ) ?? null + : null; + return ( + + ); +} +interface TransactionInnerDetailProps { + entity: TransactionModel; + find_tx_model: (a: Buffer, b: number) => UTXOModel | null; +} +export function TransactionDetailInner(props: TransactionInnerDetailProps) { const dispatch = useDispatch(); const opts = props.entity.getOptions(); const txid = opts.txn.getId(); diff --git a/src/UX/Entity/Detail/UTXODetail.tsx b/src/UX/Entity/Detail/UTXODetail.tsx index 921004a..e90bf0e 100644 --- a/src/UX/Entity/Detail/UTXODetail.tsx +++ b/src/UX/Entity/Detail/UTXODetail.tsx @@ -17,7 +17,7 @@ import DoubleArrowIcon from '@mui/icons-material/DoubleArrow'; import * as Bitcoin from 'bitcoinjs-lib'; import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { Continuation, ContractModel } from '../../../Data/ContractManager'; +import { ContractModel } from '../../../Data/ContractManager'; import { PhantomTransactionModel } from '../../../Data/Transaction'; import { UTXOModel } from '../../../Data/UTXO'; import { @@ -25,10 +25,13 @@ import { hasOwn, is_mock_outpoint, PrettyAmountField, + TXIDAndWTXIDMap, } from '../../../util'; import { create, + EntityType, fetch_utxo, + selectEntityToView, selectUTXO, selectUTXOFlash, select_txn, @@ -37,14 +40,17 @@ import Hex, { ASM } from './Hex'; import { OutpointDetail } from './OutpointDetail'; import './UTXODetail.css'; import { selectContinuation } from '../../ContractCreator/ContractCreatorSlice'; -import { MuiForm5 as Form } from '@rjsf/material-ui'; -import { FormValidation, ISubmitEvent } from '@rjsf/core'; +import Form, { FormValidation, ISubmitEvent } from '@rjsf/core'; import { add_effect_to_contract, recreate_contract, selectHasEffect, } from '../../../AppSlice'; import { RootState } from '../../../Store/store'; +import { + Continuation, + UTXOFormatData, +} from '../../../common/preload_interface'; interface UTXODetailProps { entity: UTXOModel; @@ -56,16 +62,29 @@ const C = React.memo(UTXODetailInner, (prev, next) => { console.log('NEWCHECK?', b); return b; }); -export function UTXODetail(props: UTXODetailProps) { - return ; +export function UTXODetail(props: { contract: ContractModel }) { + const entity_id: EntityType = useSelector(selectEntityToView); + const entity = + entity_id[0] === 'UTXO' + ? TXIDAndWTXIDMap.get_by_txid_s( + props.contract.txid_map, + entity_id[1].hash + )?.utxo_models[entity_id[1].nIn] ?? null + : null; + return ( + + ); } export function UTXODetailInner(props: UTXODetailProps) { const theme = useTheme(); const dispatch = useDispatch(); const select_continuations = useSelector(selectContinuation); - const txid = props.entity.txn.get_txid(); - const idx = props.entity.utxo.index; + const opts = props.entity.getOptions(); + const txid = opts.txn.get_txid(); + const idx = opts.utxo.index; const outpoint = { hash: txid, nIn: idx }; const external_utxo = useSelector(selectUTXO)(outpoint); @@ -75,8 +94,7 @@ export function UTXODetailInner(props: UTXODetailProps) { const decomp = external_utxo?.scriptPubKey.address ?? Bitcoin.script.toASM( - Bitcoin.script.decompile(props.entity.utxo.script) ?? - Buffer.from('') + Bitcoin.script.decompile(opts.utxo.script) ?? Buffer.from('') ); // first attempt to get the address from the extenral utxo if it's present, // otherwise attempt to read if from the utxo model @@ -85,9 +103,9 @@ export function UTXODetailInner(props: UTXODetailProps) { if (!address) { address = 'UNKNOWN'; try { - asm = Bitcoin.script.toASM(props.entity.utxo.script); + asm = Bitcoin.script.toASM(opts.utxo.script); address = Bitcoin.address.fromOutputScript( - props.entity.utxo.script, + opts.utxo.script, /// TODO: Read from preferences? Bitcoin.networks.regtest ); @@ -95,7 +113,7 @@ export function UTXODetailInner(props: UTXODetailProps) { // TODO: Recovery? } } - const spends = props.entity.utxo.spends.map((elt, i) => ( + const spends = opts.utxo.spends.map((elt, i) => (
@@ -115,11 +133,7 @@ export function UTXODetailInner(props: UTXODetailProps) { aria-label="create-contract" onClick={() => dispatch( - create( - props.entity.txn.tx, - props.entity, - props.contract - ) + create(opts.txn.tx, props.entity, props.contract) ) } > @@ -139,10 +153,10 @@ export function UTXODetailInner(props: UTXODetailProps) { ); const title = - props.entity.txn instanceof PhantomTransactionModel ? ( + opts.txn instanceof PhantomTransactionModel ? (

External UTXO

) : ( - + ); const obj = select_continuations(`${txid}:${idx}`); const continuations = obj @@ -168,6 +182,7 @@ export function UTXODetailInner(props: UTXODetailProps) {
{title} {cont} + @@ -180,6 +195,59 @@ export function UTXODetailInner(props: UTXODetailProps) { ); } +function OutputMetadataTable(props: { metadata: UTXOFormatData }) { + return ( + <> + Metadata + + + + + + + + + + {Object.entries(props.metadata) + .filter(([k, _]) => k !== 'simp') + .map(([k, v]) => ( + + + + + ))} + +
KeyValue
+ {k} + + + {typeof v === 'string' + ? v + : JSON.stringify(v)} + +
+ + ); +} + +function SIMPNeg12345(props: { simp_neg_12345?: any }) { + let nft = null; + if (props.simp_neg_12345) { + nft = ( +
+ +
+ ); + } + + return nft; +} + function ContinuationOption(props: { v: Continuation }) { const [is_open, setOpen] = React.useState(false); const name = props.v.path.substr(props.v.path.lastIndexOf('/') + 1); diff --git a/src/UX/Entity/EntityViewer.tsx b/src/UX/Entity/EntityViewer.tsx index 9797a69..af1e094 100644 --- a/src/UX/Entity/EntityViewer.tsx +++ b/src/UX/Entity/EntityViewer.tsx @@ -2,19 +2,11 @@ import { IconButton, Tooltip, useTheme } from '@mui/material'; import { red } from '@mui/material/colors'; import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined'; import React from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { ContractModel } from '../../Data/ContractManager'; -import { TransactionModel } from '../../Data/Transaction'; -import { UTXOModel } from '../../Data/UTXO'; -import { TXIDAndWTXIDMap } from '../../util'; import { TransactionDetail } from './Detail/TransactionDetail'; import { UTXODetail } from './Detail/UTXODetail'; -import { - deselect_entity, - EntityType, - selectEntityToView, - selectShouldViewEntity, -} from './EntitySlice'; +import { deselect_entity } from './EntitySlice'; import './EntityViewer.css'; import Color from 'color'; @@ -24,6 +16,7 @@ interface CurrentylViewedEntityProps { export function CurrentlyViewedEntity(props: CurrentylViewedEntityProps) { const theme = useTheme(); + const dispatch = useDispatch(); const [width, setWidth] = React.useState('20em'); const onMouseUp = (e: MouseEvent) => { e.preventDefault(); @@ -41,50 +34,6 @@ export function CurrentlyViewedEntity(props: CurrentylViewedEntityProps) { document.addEventListener('mouseup', onMouseUp); }; - const show = useSelector(selectShouldViewEntity); - const entity_id: EntityType = useSelector(selectEntityToView); - let guts: JSX.Element | null = null; - if (show) { - switch (entity_id[0]) { - case 'TXN': { - const entity = - TXIDAndWTXIDMap.get_by_txid_s( - props.current_contract.txid_map, - entity_id[1] - ) ?? null; - if (entity) { - guts = ( - - props.current_contract.lookup_utxo_model(a, b) - } - /> - ); - } - break; - } - case 'UTXO': { - const entity = - TXIDAndWTXIDMap.get_by_txid_s( - props.current_contract.txid_map, - entity_id[1].hash - )?.utxo_models[entity_id[1].nIn] ?? null; - if (entity) { - guts = ( - - ); - } - break; - } - case 'NULL': - break; - } - } - const dispatch = useDispatch(); return (
- {guts} + + props.current_contract.lookup_utxo_model(a, b) + } + /> +
diff --git a/src/UX/Miniscript/Compiler.css b/src/UX/Miniscript/Compiler.css new file mode 100644 index 0000000..8ccb1ec --- /dev/null +++ b/src/UX/Miniscript/Compiler.css @@ -0,0 +1,17 @@ +.MiniscriptCompiler { + padding: 50px; + max-width: 100%; + overflow: hidden; +} + +.CompilerOutput { + max-width: 50%; +} + +.PathRow { + white-space: pre-wrap; + word-break: break-word; + padding: 8px; + text-align: left; + border-bottom: 1px solid #ddd; +} diff --git a/src/UX/Miniscript/Compiler.tsx b/src/UX/Miniscript/Compiler.tsx new file mode 100644 index 0000000..03b9fa7 --- /dev/null +++ b/src/UX/Miniscript/Compiler.tsx @@ -0,0 +1,219 @@ +import { + Box, + Table, + TableBody, + TableCell, + TableHead, + TableRow, + TextareaAutosize, + Typography, +} from '@mui/material'; +import * as React from 'react'; +import './Compiler.css'; +import * as Bitcoin from 'bitcoinjs-lib'; +type Compiler = typeof import('../../Miniscript/pkg/'); +export function MiniscriptCompiler() { + const [miniscript, load_miniscript] = React.useState(null); + React.useEffect(() => { + async function load() { + import('../../Miniscript/pkg').then((native) => { + load_miniscript(native); + }); + } + if (miniscript === null) load(); + }, [miniscript]); + if (!miniscript) return

Loading WASM...

; + else return ; +} +type taproot_t = { + address: { + main: string; + regtest: string; + signet: string; + test: string; + }; + internal_key: string; + merkle_root: string; + scripts: Array<[[string, number], string[][]]>; + tweak: string; +}; +const UNICODE_LINE = /\r\n|(?!\r\n)[\n-\r\x85\u2028\u2029]/; +function CompInput(props: { miniscript: Compiler }) { + type ResultT = ['err', string, string] | ['ok', string, string]; + const [compiled, set_compiled] = React.useState([]); + const [keytab_string, set_keytab_string] = React.useState(''); + const updated = ( + event: React.ChangeEvent + ) => { + // some unicode thing + const ret: ResultT[] = event.target.value + .split(UNICODE_LINE) + .flatMap((v: string): ResultT[] => { + if (v.match(UNICODE_LINE)) return []; + try { + /// TODO: Cache based on V + const s = props.miniscript.compile(v); + return [['ok', v, s]]; + } catch (e) { + if (typeof e === 'string') return [['err', v, e]]; + else throw e; + } + }); + set_compiled(ret); + }; + const keytab_updated = ( + event: React.ChangeEvent + ) => { + set_keytab_string(event.target.value); + }; + const [taproot, set_taproot] = React.useState(null); + React.useEffect(() => { + // some unicode thing + const k = props.miniscript.KeyTab.new(); + const ret = keytab_string.split(UNICODE_LINE).forEach((v: string) => { + if (v.match(UNICODE_LINE)) return []; + if (v === '') return []; + const nick_key = v.split(':', 2); + if (nick_key.length !== 2) return []; //throw new Error(`Malformed Keytab Entry: ${v}`); + k.add(nick_key[0]!.trim(), nick_key[1]!.trim()); + }); + const frags = props.miniscript.Fragments.new(); + for (const frag of compiled) { + // eslint-disable-next-line no-constant-condition + if (frag[0] === 'err') { + set_taproot(null); + console.log(frag); + return; + } + frags.add(frag[2]); + } + try { + const compiled = props.miniscript.taproot(frags, k); + set_taproot(JSON.parse(compiled)); + } catch (e) { + set_taproot(null); + console.log(e); + } + }, [keytab_string, compiled]); + + return ( + +

Input

+ + + + + + Input + {'=>'} + Output + + + + {compiled.map((row, i) => ( + + + + {row[1]} + + + {'=>'} + + + {row[2]} + + + + ))} + +
+

Translate Keys

+ +
{taproot && }
+
+ ); +} + +function ShowTaproot(props: taproot_t) { + const paths = props.scripts.flatMap(([[script, depth], paths]) => { + const parsed = Bitcoin.script.decompile(Buffer.from(script, 'hex')); + let asm: string; + if (parsed) asm = Bitcoin.script.toASM(parsed); + else asm = `Unparsable: ${script}`; + return paths.map((path) => { + return ( + + {depth} + + {asm} + + {path} + + ); + }); + }); + + return ( +
+ + + + type + address + + + + + main + {props.address.main} + + + signet + {props.address.main} + + + regtest + {props.address.regtest} + + + testnet + {props.address.test} + + +
+ + + + Script + Depth + Path + + + {paths} +
+
+ ); +} diff --git a/src/UX/Settings/Schemas.ts b/src/UX/Settings/Schemas.ts deleted file mode 100644 index 51e157e..0000000 --- a/src/UX/Settings/Schemas.ts +++ /dev/null @@ -1,425 +0,0 @@ -import { JSONSchema7 } from 'json-schema'; -export type schema_t = { - bitcoin: JSONSchema7; - display: JSONSchema7; - local_oracle: JSONSchema7; - sapio_cli: JSONSchema7; -}; -export const schemas: schema_t = { - bitcoin: { - $schema: 'http://json-schema.org/draft-07/schema#', - definitions: { - Auth: { - oneOf: [ - { - enum: ['None'], - type: 'string', - }, - { - additionalProperties: false, - properties: { - UserPass: { - items: [ - { - type: 'string', - }, - { - $ref: '#/definitions/Password', - }, - ], - maxItems: 2, - minItems: 2, - type: 'array', - }, - }, - required: ['UserPass'], - title: 'Username / Password', - type: 'object', - }, - { - additionalProperties: false, - properties: { - CookieFile: { - $ref: '#/definitions/File', - }, - }, - required: ['CookieFile'], - title: 'Cookie File', - type: 'object', - }, - ], - title: 'Auth File', - }, - File: { - format: 'custom::filename', - type: 'string', - }, - Network: { - enum: ['Bitcoin', 'Testnet', 'Signet', 'Regtest'], - type: 'string', - }, - Password: { - format: 'password', - type: 'string', - }, - }, - properties: { - auth: { - allOf: [ - { - $ref: '#/definitions/Auth', - }, - ], - title: 'Credentials', - }, - host: { - description: 'E.g., 0.0.0.0 or mynode.com', - title: 'Host', - type: 'string', - }, - network: { - allOf: [ - { - $ref: '#/definitions/Network', - }, - ], - title: 'Which Network', - }, - port: { - format: 'uint64', - minimum: 0.0, - title: 'RPC Port', - type: 'integer', - }, - }, - required: ['auth', 'host', 'network', 'port'], - title: 'Bitcoin Settings', - type: 'object', - }, - display: { - $schema: 'http://json-schema.org/draft-07/schema#', - definitions: { - AnimationSpeed: { - oneOf: [ - { - enum: ['Disabled'], - type: 'string', - }, - { - additionalProperties: false, - properties: { - Enabled: { - format: 'uint64', - maximum: 5000.0, - minimum: 50.0, - type: 'integer', - }, - }, - required: ['Enabled'], - title: 'Enable Animation (ms)', - type: 'object', - }, - ], - title: 'Animation Speed', - }, - SatsOrBitcoin: { - oneOf: [ - { - additionalProperties: false, - description: 'min = 0, max = 100000000 (1 btc)', - properties: { - BitcoinAfter: { - format: 'uint64', - maximum: 100000000.0, - minimum: 0.0, - type: 'integer', - }, - }, - required: ['BitcoinAfter'], - title: 'After Threshold', - type: 'object', - }, - { - additionalProperties: false, - properties: { - AlwaysSats: { - type: 'null', - }, - }, - required: ['AlwaysSats'], - title: 'Always Sats', - type: 'object', - }, - { - additionalProperties: false, - properties: { - AlwaysBitcoin: { - type: 'null', - }, - }, - required: ['AlwaysBitcoin'], - title: 'Always Bitcoin', - type: 'object', - }, - ], - title: 'To Show Sats or Bitcoin', - }, - }, - properties: { - animation_speed: { - allOf: [ - { - $ref: '#/definitions/AnimationSpeed', - }, - ], - description: 'The speed (in ms) to animate at', - title: 'Animation Speed', - }, - node_polling_freq: { - format: 'uint64', - minimum: 0.0, - title: 'Bitcoin Node Polling Frequency (seconds)', - type: 'integer', - }, - satoshis: { - allOf: [ - { - $ref: '#/definitions/SatsOrBitcoin', - }, - ], - title: 'Threshold at which to display Bitcoin () or Sats ()', - }, - }, - required: ['animation_speed', 'node_polling_freq', 'satoshis'], - title: 'Display Settings', - type: 'object', - }, - local_oracle: { - $schema: 'http://json-schema.org/draft-07/schema#', - definitions: { - File: { - format: 'custom::filename', - type: 'string', - }, - }, - description: - 'If Sapio Studio should run a local oracle, what seed file and interface to use.', - oneOf: [ - { - enum: ['Disabled'], - type: 'string', - }, - { - additionalProperties: false, - description: 'An oracle will be run & managed by Sapio Studio', - properties: { - Enabled: { - properties: { - file: { - allOf: [ - { - $ref: '#/definitions/File', - }, - ], - title: 'Seed File', - }, - interface: { - description: '(e.g. 0.0.0.0:8080)', - title: 'Interface', - type: 'string', - }, - }, - required: ['file', 'interface'], - type: 'object', - }, - }, - required: ['Enabled'], - title: 'Enabled', - type: 'object', - }, - ], - title: 'Local Oracle', - }, - sapio_cli: { - $schema: 'http://json-schema.org/draft-07/schema#', - definitions: { - File: { - format: 'custom::filename', - type: 'string', - }, - IFace: { - description: 'e.g., hello.com:1010, 0.0.0.0:8080', - title: 'Network Interface', - type: 'string', - }, - ModuleHash: { - description: '(64 char hex)', - title: 'Module Hash', - type: 'string', - }, - Nickname: { - description: 'e.g., my_hot_wallet', - title: 'Human Readable Nickname', - type: 'string', - }, - PK: { - description: 'e.g. xpubD6...', - title: 'Public Key', - type: 'string', - }, - Preferences: { - oneOf: [ - { - enum: ['Default'], - type: 'string', - }, - { - additionalProperties: false, - description: - 'From Sapio-Studio, Configuration parameters done locally.', - properties: { - Here: { - properties: { - emulators: { - description: - 'Which Emulators to query?', - items: { - items: [ - { - $ref: '#/definitions/PK', - }, - { - $ref: '#/definitions/IFace', - }, - ], - maxItems: 2, - minItems: 2, - type: 'array', - }, - title: 'Emulators', - type: 'array', - }, - plugin_map: { - description: - 'Map of nicknames to module hashes', - items: { - items: [ - { - $ref: '#/definitions/Nickname', - }, - { - $ref: '#/definitions/ModuleHash', - }, - ], - maxItems: 2, - minItems: 2, - type: 'array', - }, - title: 'Custom Plugin Mapping', - type: 'array', - }, - threshold: { - description: - 'How many Emulators must sign?', - format: 'uint16', - minimum: 0.0, - title: 'Emulator Threshold (should set to # of emulators usually)', - type: 'integer', - }, - use_emulation: { - description: - 'Whether or not Emulation should be used', - title: 'Use Emulation?', - type: 'boolean', - }, - }, - required: [ - 'emulators', - 'plugin_map', - 'threshold', - 'use_emulation', - ], - type: 'object', - }, - }, - required: ['Here'], - title: 'Here', - type: 'object', - }, - { - additionalProperties: false, - description: 'Use a custom config file', - properties: { - File: { - allOf: [ - { - $ref: '#/definitions/File', - }, - ], - title: 'Configuration File:', - }, - }, - required: ['File'], - title: 'File', - type: 'object', - }, - ], - title: 'Configuration Source', - }, - }, - properties: { - preferences: { - allOf: [ - { - $ref: '#/definitions/Preferences', - }, - ], - description: 'How to configure the options for sapio-cli', - title: 'Preferences', - }, - sapio_cli: { - allOf: [ - { - $ref: '#/definitions/File', - }, - ], - description: 'sapio-cli Binary Location', - title: 'Binary', - }, - }, - required: ['preferences', 'sapio_cli'], - title: 'sapio-cli Studio Configuration', - type: 'object', - }, -}; -export const default_settings = { - bitcoin: { - auth: { - UserPass: ['Put Your Username', 'Put Your Password'], - }, - host: '0.0.0.0', - network: 'Signet', - port: 38332, - }, - display: { - animation_speed: { - Enabled: 500, - }, - node_polling_freq: 10, - satoshis: { - BitcoinAfter: 100000, - }, - }, - local_oracle: 'Disabled', - sapio_cli: { - preferences: { - Here: { - emulators: [], - plugin_map: [], - threshold: 1, - use_emulation: false, - }, - }, - sapio_cli: '', - }, -}; diff --git a/src/UX/Settings/Settings.tsx b/src/UX/Settings/Settings.tsx index bd0e16e..f83ba18 100644 --- a/src/UX/Settings/Settings.tsx +++ b/src/UX/Settings/Settings.tsx @@ -1,4 +1,3 @@ -import { schemas } from './Schemas'; import { Tabs, Tab, @@ -20,6 +19,7 @@ import { Cancel } from '@mui/icons-material'; import { poll_settings } from '../../Settings/SettingsSlice'; import { useDispatch } from 'react-redux'; import './Settings.css'; +import { schemas } from '../../common/settings_gen'; function SettingPane(props: { name: keyof typeof schemas; diff --git a/src/Wallet/AvailableBalance.tsx b/src/Wallet/AvailableBalance.tsx new file mode 100644 index 0000000..35ffaa3 --- /dev/null +++ b/src/Wallet/AvailableBalance.tsx @@ -0,0 +1,29 @@ +import { Typography } from '@mui/material'; +import React from 'react'; +import { BitcoinNodeManager } from '../Data/BitcoinNode'; + +export function AvailableBalance(props: { + bitcoin_node_manager: BitcoinNodeManager; +}) { + const [amount, setAmount] = React.useState(0); + React.useEffect(() => { + let cancel = false; + const update = async () => { + if (cancel) return; + try { + const amt = await props.bitcoin_node_manager.check_balance(); + setAmount(amt); + } catch (err: any) { + console.error(err); + setAmount(0); + } + setTimeout(update, 5000); + }; + + update(); + return () => { + cancel = true; + }; + }, []); + return Amount: {amount}; +} diff --git a/src/Wallet/ContractList.tsx b/src/Wallet/ContractList.tsx new file mode 100644 index 0000000..9361f60 --- /dev/null +++ b/src/Wallet/ContractList.tsx @@ -0,0 +1,137 @@ +import VisibilityIcon from '@mui/icons-material/Visibility'; +import { Delete } from '@mui/icons-material'; +import { DataGrid, GridActionsCellItem, GridColumns } from '@mui/x-data-grid'; +import React from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { open_contract_directory, switch_showing } from '../AppSlice'; +import { DeleteDialog } from './DeleteDialog'; +import { selectWorkspace } from './Slice/Reducer'; +import { Typography } from '@mui/material'; + +export function ContractList(props: { idx: number; value: number }) { + const dispatch = useDispatch(); + const [contracts, set_contracts] = React.useState([]); + const [to_delete, set_to_delete] = React.useState(null); + const [trigger_now, set_trigger_now] = React.useState(0); + const workspace = useSelector(selectWorkspace); + React.useEffect(() => { + let cancel = false; + const update = async () => { + if (cancel) return; + + try { + const list = + await window.electron.sapio.compiled_contracts.list( + workspace + ); + set_contracts(list); + } catch (err) { + console.error(err); + set_contracts([]); + } + setTimeout(update, 5000); + }; + + update(); + return () => { + cancel = true; + }; + }, [trigger_now, workspace]); + const contract_rows = contracts.map((id) => { + const [mod, args, time] = id.split('-'); + return { + id, + mod, + args, + time: new Date(parseInt(time!)), + }; + }); + const delete_contract = (fname: string | number) => { + if (typeof fname === 'number') return; + set_to_delete(fname); + }; + + const columns: GridColumns = [ + { + field: 'actions-load', + type: 'actions', + flex: 0.2, + getActions: (params) => [ + } + label="Open" + onClick={() => { + dispatch(switch_showing('ContractViewer')); + dispatch( + open_contract_directory( + typeof params.id === 'number' ? '' : params.id + ) + ); + }} + />, + ], + }, + { + field: 'time', + headerName: 'Time', + minWidth: 100, + type: 'dateTime', + flex: 1, + }, + { + field: 'args', + headerName: 'Args Hash', + width: 100, + flex: 1, + }, + { + field: 'mod', + headerName: 'Module', + width: 100, + type: 'text', + flex: 1, + }, + { + field: 'actions-delete', + type: 'actions', + flex: 0.2, + getActions: (params) => [ + } + label="Delete" + onClick={() => delete_contract(params.id)} + />, + ], + }, + ]; + return ( + + ); +} diff --git a/src/Wallet/DeleteDialog.tsx b/src/Wallet/DeleteDialog.tsx new file mode 100644 index 0000000..fd56c9d --- /dev/null +++ b/src/Wallet/DeleteDialog.tsx @@ -0,0 +1,57 @@ +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from '@mui/material'; +import React from 'react'; +import { useSelector } from 'react-redux'; +import { selectWorkspace } from './Slice/Reducer'; + +export function DeleteDialog(props: { + set_to_delete: () => void; + to_delete: ['workspace', string] | ['contract', string] | null; + reload: () => void; +}) { + const workspace = useSelector(selectWorkspace); + return ( + + Confirm Deletion + + + Confirm deletion of "{props.to_delete}"? File will + be in your trash folder. + + + + + + + + ); +} diff --git a/src/Wallet/NewWorkspace.tsx b/src/Wallet/NewWorkspace.tsx new file mode 100644 index 0000000..6a01886 --- /dev/null +++ b/src/Wallet/NewWorkspace.tsx @@ -0,0 +1,54 @@ +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + TextField, +} from '@mui/material'; +import React from 'react'; + +export function NewWorkspace(props: { + show: boolean; + hide: () => void; + reload: () => void; +}) { + const [value, set_value] = React.useState(null); + return ( + + Create a new Workspace + + + What should the workspace be called? + + set_value(ev.currentTarget.value)} + value={value} + autoFocus + margin="dense" + label="Name" + name="name" + type="text" + fullWidth + variant="standard" + /> + + + + + + + ); +} diff --git a/src/Wallet/Slice/Reducer.ts b/src/Wallet/Slice/Reducer.ts new file mode 100644 index 0000000..b4eee15 --- /dev/null +++ b/src/Wallet/Slice/Reducer.ts @@ -0,0 +1,38 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { RootState } from '../../Store/store'; + +export type TabIndexes = 0 | 1 | 2 | 3 | 4; +type StateType = { + showing: TabIndexes; + workspace: string; +}; +function default_state(): StateType { + return { + showing: 0, + workspace: 'default', + }; +} + +export const walletSlice = createSlice({ + name: 'Wallet', + initialState: default_state(), + reducers: { + switch_wallet_tab: (state, action: PayloadAction) => { + state.showing = action.payload; + }, + switch_workspace: (state, action: PayloadAction) => { + state.workspace = action.payload; + }, + }, +}); + +export const { switch_wallet_tab, switch_workspace } = walletSlice.actions; + +export const selectWalletTab = (r: RootState) => { + return r.walletReducer.showing; +}; + +export const selectWorkspace = (r: RootState) => { + return r.walletReducer.workspace; +}; +export const walletReducer = walletSlice.reducer; diff --git a/src/Wallet/Wallet.css b/src/Wallet/Wallet.css index 90ce616..285fe87 100644 --- a/src/Wallet/Wallet.css +++ b/src/Wallet/Wallet.css @@ -38,3 +38,15 @@ display: grid; grid-template-columns: 10% 80% 10%; } + +.WorkspaceList { + height: 75%; +} + +.WorkspaceListInner { + padding-top: 3em; + padding-bottom: 30px; + height: 100%; + display: grid; + grid-template-columns: 10% 80% 10%; +} diff --git a/src/Wallet/Wallet.tsx b/src/Wallet/Wallet.tsx index 79cc32c..b8bd53e 100644 --- a/src/Wallet/Wallet.tsx +++ b/src/Wallet/Wallet.tsx @@ -1,35 +1,24 @@ -import { - Button, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, - Tab, - Tabs, - TextField, - Typography, -} from '@mui/material'; -import VisibilityIcon from '@mui/icons-material/Visibility'; -import { Delete } from '@mui/icons-material'; +import { Tab, Tabs } from '@mui/material'; import { Box } from '@mui/system'; -import { - DataGrid, - GridActionsCellItem, - GridColDef, - GridColumns, - GridValueGetterParams, -} from '@mui/x-data-grid'; import React from 'react'; import { BitcoinNodeManager } from '../Data/BitcoinNode'; import './Wallet.css'; -import { useDispatch } from 'react-redux'; -import { open_contract_directory, switch_showing } from '../AppSlice'; +import { ContractList } from './ContractList'; +import { WalletSend } from './WalletSend'; +import { WalletHistory } from './WalletHistory'; +import { Workspaces } from './Workspaces'; +import { useDispatch, useSelector } from 'react-redux'; +import { + selectWalletTab, + switch_wallet_tab, + TabIndexes, +} from './Slice/Reducer'; export function Wallet(props: { bitcoin_node_manager: BitcoinNodeManager }) { - const [idx, set_idx] = React.useState(0); - const handleChange = (_: any, idx: number) => { - set_idx(idx); + const dispatch = useDispatch(); + const idx = useSelector(selectWalletTab); + const handleChange = (_: any, idx: TabIndexes) => { + dispatch(switch_wallet_tab(idx)); }; return (
@@ -41,402 +30,16 @@ export function Wallet(props: { bitcoin_node_manager: BitcoinNodeManager }) { > + - + +
); } - -function ContractList(props: { idx: number; value: number }) { - const dispatch = useDispatch(); - const [contracts, set_contracts] = React.useState([]); - const [to_delete, set_to_delete] = React.useState(null); - const [trigger_now, set_trigger_now] = React.useState(0); - React.useEffect(() => { - let cancel = false; - const update = async () => { - if (cancel) return; - - try { - const list = - await window.electron.sapio.compiled_contracts.list(); - set_contracts(list); - } catch (err) { - console.error(err); - set_contracts([]); - } - setTimeout(update, 5000); - }; - - update(); - return () => { - cancel = true; - }; - }, [trigger_now]); - const contract_rows = contracts.map((id) => { - const [mod, args, time] = id.split('-'); - return { - id, - module, - args, - time: new Date(parseInt(time!)), - }; - }); - const delete_contract = (fname: string | number) => { - if (typeof fname === 'number') return; - set_to_delete(fname); - }; - - const columns: GridColumns = [ - { field: 'mod', headerName: 'Module', width: 130, type: 'text' }, - { field: 'args', headerName: 'Args Hash', width: 130 }, - { field: 'time', headerName: 'Time', width: 130, type: 'date' }, - { - field: 'actions', - type: 'actions', - width: 80, - getActions: (params) => [ - } - label="Delete" - onClick={() => delete_contract(params.id)} - />, - } - label="Open" - onClick={() => { - dispatch(switch_showing('ContractViewer')); - dispatch( - open_contract_directory( - typeof params.id === 'number' ? '' : params.id - ) - ); - }} - />, - ], - }, - ]; - return ( - - ); -} - -function WalletSendDialog(props: { - show: boolean; - amt: number; - to: string; - close: () => void; - bitcoin_node_manager: BitcoinNodeManager; -}) { - return ( - { - props.close(); - }} - > - Confirm Spend - - - Confirm sending - {props.amt} BTC to {props.to} - - - - - - - - ); -} -function WalletSendForm(props: { - bitcoin_node_manager: BitcoinNodeManager; - set_params: (a: number, b: string) => void; -}) { - const [address, setAddress] = React.useState(null); - - const get_address = async () => { - try { - const address = await props.bitcoin_node_manager.get_new_address(); - setAddress(address); - } catch (err) { - // console.error(err); - setAddress(null); - } - }; - const handleSubmit: React.FormEventHandler = async ( - event - ) => { - event.preventDefault(); - const amt = event.currentTarget.amount.value; - const to = event.currentTarget.address.value; - props.set_params(amt, to); - event.currentTarget.reset(); - }; - - return ( -
-
-
- - {address && `New Address: ${address}`} - - - - - - -
-
-
- ); -} -function AvailableBalance(props: { bitcoin_node_manager: BitcoinNodeManager }) { - const [amount, setAmount] = React.useState(0); - React.useEffect(() => { - let cancel = false; - const update = async () => { - if (cancel) return; - try { - const amt = await props.bitcoin_node_manager.check_balance(); - setAmount(amt); - } catch (err: any) { - console.error(err); - setAmount(0); - } - setTimeout(update, 5000); - }; - - update(); - return () => { - cancel = true; - }; - }, []); - return Amount: {amount}; -} -function WalletSend(props: { - bitcoin_node_manager: BitcoinNodeManager; - value: number; - idx: number; -}) { - const [params, set_params] = React.useState({ amt: -1, to: '' }); - return ( - - ); -} - -type TxInfo = { - involvesWatchonly: boolean; // (boolean) Only returns true if imported addresses were involved in transaction. - address: string; // (string) The bitcoin address of the transaction. - category: 'send' | 'receive' | 'generate' | 'immature' | 'orphan'; // (string) The transaction category. - // "send" Transactions sent. - // "receive" Non-coinbase transactions received. - // "generate" Coinbase transactions received with more than 100 confirmations. - // "immature" Coinbase transactions received with 100 or fewer confirmations. - // "orphan" Orphaned coinbase transactions received. - amount: number; // (numeric) The amount in BTC. This is negative for the 'send' category, and is positive - // for all other categories - label: string; // (string) A comment for the address/transaction, if any - vout: number; // (numeric) the vout value - fee: number; // (numeric) The amount of the fee in BTC. This is negative and only available for the - // 'send' category of transactions. - confirmations: number; // (numeric) The number of confirmations for the transaction. Negative confirmations means the - // transaction conflicted that many blocks ago. - generated: boolean; // (boolean) Only present if transaction only input is a coinbase one. - trusted: boolean; // (boolean) Only present if we consider transaction to be trusted and so safe to spend from. - blockhash: string; // (string) The block hash containing the transaction. - blockheight: number; // (numeric) The block height containing the transaction. - blockindex: number; // (numeric) The index of the transaction in the block that includes it. - blocktime: number; // (numeric) The block time expressed in UNIX epoch time. - txid: string; // (string) The transaction id. - id: string; - walletconflicts: string[]; // (json array) Conflicting transaction ids. - // (string) The transaction id. - time: number; // (numeric) The transaction time expressed in UNIX epoch time. - timereceived: number; // (numeric) The time received expressed in UNIX epoch time. - comment: string; // (string) If a comment is associated with the transaction, only present if not empty. - 'bip125-replaceable': string; // (string) ("yes|no|unknown") Whether this transaction could be replaced due to BIP125 (replace-by-fee); - // may be unknown for unconfirmed transactions not in the mempool - abandoned: boolean; // (boolean) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the - // 'send' category of transactions. -}; -function WalletHistory(props: { - bitcoin_node_manager: BitcoinNodeManager; - value: number; - idx: number; -}) { - const [transactions, setTransactions] = React.useState([]); - React.useEffect(() => { - let cancel = false; - const update = async () => { - if (cancel) return; - - try { - const txns = await props.bitcoin_node_manager.list_transactions( - 10 - ); - setTransactions(txns); - } catch (err) { - console.error(err); - setTransactions([]); - } - setTimeout(update, 5000); - }; - - update(); - return () => { - cancel = true; - }; - }, []); - - const columns: GridColDef[] = [ - { field: 'amount', headerName: 'Amount', width: 130, type: 'number' }, - { field: 'category', headerName: 'Category', width: 130 }, - { field: 'txid', headerName: 'TXID', width: 130 }, - { - field: 'blockheight', - headerName: 'Height', - width: 130, - type: 'number', - }, - { - field: 'time', - headerName: 'Time', - width: 130, - type: 'number', - valueGetter: (params: GridValueGetterParams) => { - const d: number = params.row.blocktime; - return d ? new Date(d * 1000).toUTCString() : 'None'; - }, - }, - ]; - - (transactions ?? []).forEach((v) => { - v['id'] = v.txid; - }); - return ( - - ); -} diff --git a/src/Wallet/WalletHistory.tsx b/src/Wallet/WalletHistory.tsx new file mode 100644 index 0000000..9003795 --- /dev/null +++ b/src/Wallet/WalletHistory.tsx @@ -0,0 +1,122 @@ +import { DataGrid, GridColDef, GridValueGetterParams } from '@mui/x-data-grid'; +import React from 'react'; +import { BitcoinNodeManager } from '../Data/BitcoinNode'; + +type TxInfo = { + involvesWatchonly: boolean; // (boolean) Only returns true if imported addresses were involved in transaction. + address: string; // (string) The bitcoin address of the transaction. + category: 'send' | 'receive' | 'generate' | 'immature' | 'orphan'; // (string) The transaction category. + + // "send" Transactions sent. + // "receive" Non-coinbase transactions received. + // "generate" Coinbase transactions received with more than 100 confirmations. + // "immature" Coinbase transactions received with 100 or fewer confirmations. + // "orphan" Orphaned coinbase transactions received. + amount: number; // (numeric) The amount in BTC. This is negative for the 'send' category, and is positive + + // for all other categories + label: string; // (string) A comment for the address/transaction, if any + vout: number; // (numeric) the vout value + fee: number; // (numeric) The amount of the fee in BTC. This is negative and only available for the + + // 'send' category of transactions. + confirmations: number; // (numeric) The number of confirmations for the transaction. Negative confirmations means the + + // transaction conflicted that many blocks ago. + generated: boolean; // (boolean) Only present if transaction only input is a coinbase one. + trusted: boolean; // (boolean) Only present if we consider transaction to be trusted and so safe to spend from. + blockhash: string; // (string) The block hash containing the transaction. + blockheight: number; // (numeric) The block height containing the transaction. + blockindex: number; // (numeric) The index of the transaction in the block that includes it. + blocktime: number; // (numeric) The block time expressed in UNIX epoch time. + txid: string; // (string) The transaction id. + id: string; + walletconflicts: string[]; // (json array) Conflicting transaction ids. + + // (string) The transaction id. + time: number; // (numeric) The transaction time expressed in UNIX epoch time. + timereceived: number; // (numeric) The time received expressed in UNIX epoch time. + comment: string; // (string) If a comment is associated with the transaction, only present if not empty. + 'bip125-replaceable': string; // (string) ("yes|no|unknown") Whether this transaction could be replaced due to BIP125 (replace-by-fee); + + // may be unknown for unconfirmed transactions not in the mempool + abandoned: boolean; // (boolean) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the +}; +export function WalletHistory(props: { + bitcoin_node_manager: BitcoinNodeManager; + value: number; + idx: number; +}) { + const [transactions, setTransactions] = React.useState([]); + React.useEffect(() => { + let cancel = false; + const update = async () => { + if (cancel) return; + + try { + const txns = await props.bitcoin_node_manager.list_transactions( + 10 + ); + setTransactions(txns); + } catch (err) { + console.error(err); + setTransactions([]); + } + setTimeout(update, 5000); + }; + + update(); + return () => { + cancel = true; + }; + }, []); + + const columns: GridColDef[] = [ + { field: 'amount', headerName: 'Amount', width: 130, type: 'number' }, + { field: 'category', headerName: 'Category', width: 130 }, + { field: 'txid', headerName: 'TXID', width: 130 }, + { + field: 'blockheight', + headerName: 'Height', + width: 130, + type: 'number', + }, + { + field: 'time', + headerName: 'Time', + width: 130, + type: 'number', + valueGetter: (params: GridValueGetterParams) => { + const d: number = params.row.blocktime; + return d ? new Date(d * 1000).toUTCString() : 'None'; + }, + }, + ]; + + (transactions ?? []).forEach((v) => { + v['id'] = v.txid; + }); + return ( + + ); +} diff --git a/src/Wallet/WalletSend.tsx b/src/Wallet/WalletSend.tsx new file mode 100644 index 0000000..1adbd96 --- /dev/null +++ b/src/Wallet/WalletSend.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { BitcoinNodeManager } from '../Data/BitcoinNode'; +import { WalletSendDialog } from './WalletSendDialog'; +import { WalletSendForm } from './WalletSendForm'; + +export function WalletSend(props: { + bitcoin_node_manager: BitcoinNodeManager; + value: number; + idx: number; +}) { + const [params, set_params] = React.useState({ amt: -1, to: '' }); + return ( + + ); +} diff --git a/src/Wallet/WalletSendDialog.tsx b/src/Wallet/WalletSendDialog.tsx new file mode 100644 index 0000000..f6c4efa --- /dev/null +++ b/src/Wallet/WalletSendDialog.tsx @@ -0,0 +1,55 @@ +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, +} from '@mui/material'; +import React from 'react'; +import { BitcoinNodeManager } from '../Data/BitcoinNode'; + +export function WalletSendDialog(props: { + show: boolean; + amt: number; + to: string; + close: () => void; + bitcoin_node_manager: BitcoinNodeManager; +}) { + return ( + { + props.close(); + }} + > + Confirm Spend + + + Confirm sending + {props.amt} BTC to {props.to} + + + + + + + + ); +} diff --git a/src/Wallet/WalletSendForm.tsx b/src/Wallet/WalletSendForm.tsx new file mode 100644 index 0000000..2a8b505 --- /dev/null +++ b/src/Wallet/WalletSendForm.tsx @@ -0,0 +1,67 @@ +import { Button, TextField, Typography } from '@mui/material'; +import { Box } from '@mui/system'; +import React from 'react'; +import { BitcoinNodeManager } from '../Data/BitcoinNode'; +import { AvailableBalance } from './AvailableBalance'; + +export function WalletSendForm(props: { + bitcoin_node_manager: BitcoinNodeManager; + set_params: (a: number, b: string) => void; +}) { + const [address, setAddress] = React.useState(null); + + const get_address = async () => { + try { + const address = await props.bitcoin_node_manager.get_new_address(); + setAddress(address); + } catch (err) { + // console.error(err); + setAddress(null); + } + }; + const handleSubmit: React.FormEventHandler = async ( + event + ) => { + event.preventDefault(); + const amt = event.currentTarget.amount.value; + const to = event.currentTarget.address.value; + props.set_params(amt, to); + event.currentTarget.reset(); + }; + + return ( +
+
+
+ + {address && `New Address: ${address}`} + + + + + + +
+
+
+ ); +} diff --git a/src/Wallet/Workspaces.tsx b/src/Wallet/Workspaces.tsx new file mode 100644 index 0000000..b4009e6 --- /dev/null +++ b/src/Wallet/Workspaces.tsx @@ -0,0 +1,142 @@ +import { Add, Delete, FolderOpen } from '@mui/icons-material'; +import { Button } from '@mui/material'; +import { + DataGrid, + GridActionsCellItem, + GridColumns, + GridToolbarContainer, +} from '@mui/x-data-grid'; +import React from 'react'; +import { useDispatch } from 'react-redux'; +import { DeleteDialog } from './DeleteDialog'; +import { NewWorkspace } from './NewWorkspace'; +import { switch_wallet_tab, switch_workspace } from './Slice/Reducer'; + +export function Workspaces(props: { idx: number; value: number }) { + const dispatch = useDispatch(); + const [workspaces, set_workspaces] = React.useState([]); + const [to_delete, set_to_delete] = React.useState(null); + const [trigger_now, set_trigger_now] = React.useState(0); + const [show_new_workspace, set_new_workspace] = React.useState(false); + const hide_new_workspace = () => { + set_new_workspace(false); + }; + const reload = () => { + set_trigger_now(trigger_now + 1); + }; + React.useEffect(() => { + let cancel = false; + const update = async () => { + if (cancel) return; + + try { + const list = await window.electron.sapio.workspaces.list(); + set_workspaces(list); + } catch (err) { + console.error(err); + set_workspaces([]); + } + setTimeout(update, 5000); + }; + + update(); + return () => { + cancel = true; + }; + }, [trigger_now]); + const contract_rows = workspaces.map((id) => { + return { + id, + name: id, + }; + }); + const delete_workspace = (fname: string | number) => { + if (typeof fname === 'number') return; + set_to_delete(fname); + }; + + const columns: GridColumns = [ + { + field: 'actions-load', + type: 'actions', + flex: 0.2, + getActions: (params) => [ + } + label="Open" + onClick={() => { + // TODO: Better tabbing? + dispatch(switch_wallet_tab(3)); + typeof params.id === 'string' && + dispatch(switch_workspace(params.id)); + }} + />, + ], + }, + { + field: 'name', + headerName: 'Name', + width: 100, + type: 'text', + flex: 1, + }, + { + field: 'actions-delete', + type: 'actions', + flex: 0.2, + getActions: (params) => [ + } + label="Delete" + onClick={() => delete_workspace(params.id)} + />, + ], + }, + ]; + function CustomToolbar() { + return ( + + + + ); + } + return ( + + ); +} diff --git a/src/common/chat_interface.d.ts b/src/common/chat_interface.d.ts new file mode 100644 index 0000000..e167980 --- /dev/null +++ b/src/common/chat_interface.d.ts @@ -0,0 +1,17 @@ +export type EnvelopeIn = { + msg: { + Data: string; + }; + channel: string; + sent_time_ms: number; +}; + +export type EnvelopeOut = { + msg: { + Data: string; + }; + channel: string; + key: number[]; + sent_time_ms: number; + signatures: {}; +}; diff --git a/src/common/preload_interface.d.ts b/src/common/preload_interface.d.ts new file mode 100644 index 0000000..8fd11ac --- /dev/null +++ b/src/common/preload_interface.d.ts @@ -0,0 +1,132 @@ +import { JSONSchema7 } from 'json-schema'; +import { schemas } from './settings_gen'; +export type Result = { ok: T } | { err: string }; +import { EnvelopeIn } from './chat_interface'; +export const callbacks = { + simulate: 0, + load_hex: 0, + save_hex: 0, + create_contracts: 0, + load_contract: 0, + 'bitcoin-node-bar': 0, +}; +export type ContractArgs = { + arguments: Record; + context: { + amount: number; + network: 'Regtest' | 'Signet' | 'Testnet' | 'Bitcoin'; + effects?: { + effects?: Record< + string, + Record> + >; + }; + }; +}; + +export type Callbacks = keyof typeof callbacks; +export type API = Record< + string, + { name: string; key: string; api: JSONSchema7; logo: string } +>; +export type CreatedContract = { + name: string; + args: ContractArgs; + data: Data; +}; + +export type APIPath = string; +export type Continuation = { + schema: JSONSchema7; + path: APIPath; +}; +export type DataItem = { + txs: Array<{ linked_psbt: TransactionData }>; + continue_apis: Record; +}; +export type Data = { + program: Record; +}; + +export type TransactionData = { + psbt: string; + hex: string; + metadata: { + color?: string; + label?: string; + }; + output_metadata?: Array; +}; + +export type UTXOFormatData = { + color: string; + label: string; + simp: Record; +} & Record; + +export type ContinuationTable = Record>; +export type preloads = { + bitcoin_command: ( + command: { + method: string; + parameters: any[]; + }[] + ) => Promise; + // register_callback: (msg: Callbacks, action: (args: any) => void) => () => void; + save_psbt: (psbt: string) => Promise; + + save_contract: (contract: string) => Promise; + fetch_psbt: () => Promise; + write_clipboard: (s: string) => void; + save_settings: ( + which: keyof typeof schemas, + data: string + ) => Promise; + load_settings_sync: (which: keyof typeof schemas) => any; + select_filename: () => Promise; + sapio: { + create_contract: ( + workspace: string, + which: string, + txn: string | null, + args: string + ) => Promise>; + show_config: () => Promise>; + load_wasm_plugin: () => Promise>; + open_contract_from_file: () => Promise>; + load_contract_list: () => Promise>; + compiled_contracts: { + list: (workspace: string) => Promise; + trash: (workspace: string, file_name: string) => Promise; + open: ( + workspace: string, + file_name: string + ) => Promise>; + }; + psbt: { + finalize: (psbt: string) => Promise>; + }; + workspaces: { + init: (workspace: string) => Promise; + list: () => Promise; + trash: (workspace) => Promise; + }; + }; + emulator: { + kill: () => Promise; + start: () => Promise; + read_log: () => Promise; + }; + + chat: { + init: () => Promise; + send: (message: EnvelopeIn) => Promise; + add_user: (name: string, key: string) => Promise; + list_users: () => Promise<{ nickname: string; key: string }[]>; + list_channels: () => Promise<{ channel_id: string }[]>; + list_messages_channel: ( + channel: string, + since: number + ) => Promise; + }; +}; diff --git a/desktop/settings_gen.ts b/src/common/settings_gen.ts similarity index 100% rename from desktop/settings_gen.ts rename to src/common/settings_gen.ts diff --git a/src/util.tsx b/src/util.tsx index ef601e8..7259b01 100644 --- a/src/util.tsx +++ b/src/util.tsx @@ -4,67 +4,10 @@ import React from 'react'; import { TextField, InputAdornment } from '@mui/material'; import { useSelector } from 'react-redux'; import { selectMaxSats } from './Settings/SettingsSlice'; -import { APIs } from './UX/ContractCreator/ContractCreatorSlice'; -import { schemas } from './UX/Settings/Schemas'; -import { CreatedContract } from './AppSlice'; -// must manually copy from preload -type Callback = - | 'simulate' - | 'load_hex' - | 'save_hex' - | 'create_contracts' - | 'load_contract' - | 'bitcoin-node-bar'; -export type Result = { ok: T } | { err: E }; +import { preloads } from './common/preload_interface'; declare global { interface Window { - electron: { - bitcoin_command: ( - command: { - method: string; - parameters: any[]; - }[] - ) => Promise; - register: ( - msg: Callback, - action: (args: any) => void - ) => () => void; - save_psbt: (psbt: string) => Promise; - save_contract: (contract: string) => Promise; - fetch_psbt: () => Promise; - write_clipboard: (s: string) => void; - save_settings: ( - which: keyof typeof schemas, - data: string - ) => Promise; - load_settings_sync: (which: keyof typeof schemas) => Promise; - select_filename: () => Promise; - sapio: { - show_config: () => Promise>; - load_wasm_plugin: () => Promise>; - open_contract_from_file: () => Promise>; - load_contract_list: () => Promise>; - create_contract: ( - which: string, - args: string - ) => Promise>; - compiled_contracts: { - list: () => Promise; - trash: (file_name: string) => Promise; - open: ( - file_name: string - ) => Promise>; - }; - psbt: { - finalize: (psbt: string) => Promise>; - }; - }; - emulator: { - kill: () => Promise; - start: () => Promise; - read_log: () => Promise; - }; - }; + electron: preloads; } } diff --git a/yarn.lock b/yarn.lock index 4a5039c..a8f4162 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2190,6 +2190,11 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@gar/promisify@^1.0.1": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" + integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== + "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz" @@ -2444,6 +2449,13 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@malept/cross-spawn-promise@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz#d0772de1aa680a0bfb9ba2f32b4c828c7857cb9d" + integrity sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg== + dependencies: + cross-spawn "^7.0.1" + "@mui/base@5.0.0-alpha.71": version "5.0.0-alpha.71" resolved "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.71.tgz" @@ -2611,6 +2623,11 @@ prop-types "^15.7.2" reselect "^4.1.5" +"@noble/ed25519@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.6.0.tgz#b55f7c9e532b478bf1d7c4f609e1f3a37850b583" + integrity sha512-UKju89WV37IUALIMfKhKW3psO8AqmrE/GvH6QbPKjzolQ98zM7WmGUeY+xdIgSf5tqPFf75ZCYMgym6E9Jsw3Q== + "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz" @@ -2632,6 +2649,14 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@npmcli/fs@^1.0.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" + integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== + dependencies: + "@gar/promisify" "^1.0.1" + semver "^7.3.5" + "@npmcli/move-file@^1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz" @@ -2769,6 +2794,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + "@sinonjs/commons@^1.7.0": version "1.8.1" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz" @@ -2901,6 +2931,13 @@ dependencies: defer-to-connect "^1.0.1" +"@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" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + "@testing-library/dom@*": version "7.28.1" resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-7.28.1.tgz" @@ -2957,6 +2994,11 @@ resolved "https://registry.npmjs.org/@testing-library/user-event/-/user-event-7.2.1.tgz" integrity sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA== +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + "@types/anymatch@*": version "1.3.1" resolved "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz" @@ -3000,6 +3042,13 @@ dependencies: "@babel/types" "^7.3.0" +"@types/better-sqlite3@^7.5.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/better-sqlite3/-/better-sqlite3-7.5.0.tgz#c57f42c76153d070f7673fbad0084ee324905be0" + integrity sha512-G9ZbMjydW2yj1AgiPlUtdgF3a1qNpLJLudc9ynJCeJByS3XFWpmT9LT+VSHrKHFbxb31CvtYwetLTOvG9zdxdg== + dependencies: + "@types/node" "*" + "@types/bl@^5.0.2": version "5.0.2" resolved "https://registry.yarnpkg.com/@types/bl/-/bl-5.0.2.tgz#843331d6c683e9c2d97f50bd8af2b20a614d68a5" @@ -3008,6 +3057,16 @@ "@types/node" "*" "@types/readable-stream" "*" +"@types/cacheable-request@^6.0.1": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" + integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + "@types/color-convert@*": version "2.0.0" resolved "https://registry.npmjs.org/@types/color-convert/-/color-convert-2.0.0.tgz" @@ -3073,6 +3132,11 @@ resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz" integrity sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA== +"@types/http-cache-semantics@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" + integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.3" resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz" @@ -3128,6 +3192,13 @@ resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/keyv@*": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + "@types/lodash@^4.14.150": version "4.14.165" resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.165.tgz" @@ -3143,6 +3214,14 @@ resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/node-fetch@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" + integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + "@types/node@*": version "16.6.1" resolved "https://registry.npmjs.org/@types/node/-/node-16.6.1.tgz" @@ -3259,6 +3338,13 @@ dependencies: "@types/node" "*" +"@types/responselike@*", "@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/scheduler@*": version "0.16.2" resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" @@ -3550,6 +3636,16 @@ dependencies: uuid "^3.0.1" +"@wasm-tool/wasm-pack-plugin@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@wasm-tool/wasm-pack-plugin/-/wasm-pack-plugin-1.6.0.tgz#a2dbec777b317b04f72e13f0080a3f483cd89809" + integrity sha512-Iax4nEgIvVCZqrmuseJm7ln/muWpg7uT5fXMAT0crYo+k5JTuZE58DJvBQoeIAegA3IM9cZgfkcZjAOUCPsT1g== + dependencies: + chalk "^2.4.1" + command-exists "^1.2.7" + watchpack "^2.1.1" + which "^2.0.2" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz" @@ -3710,6 +3806,11 @@ abab@^2.0.3: resolved "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz" @@ -3759,6 +3860,22 @@ adjust-sourcemap-loader@3.0.0: loader-utils "^2.0.0" regex-parser "^2.2.11" +agent-base@6, agent-base@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agentkeepalive@^4.1.3: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" + integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== + dependencies: + debug "^4.1.0" + depd "^1.1.2" + humanize-ms "^1.2.1" + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" @@ -3792,6 +3909,11 @@ alphanum-sort@^1.0.0: resolved "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz" integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= +another-json@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/another-json/-/another-json-0.2.0.tgz#b5f4019c973b6dd5c6506a2d93469cb6d32aeedc" + integrity sha1-tfQBnJc7bdXGUGotk0acttMq7tw= + ansi-colors@^3.0.0: version "3.2.4" resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz" @@ -3829,6 +3951,11 @@ ansi-regex@^5.0.0: resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" @@ -3864,11 +3991,32 @@ anymatch@^3.0.3, anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -aproba@^1.1.1: +aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz#ba20bd6b553e31d62fc8c31bd23d22b95734390d" + integrity sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +are-we-there-yet@~1.1.2: + version "1.1.7" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" + integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" @@ -4369,6 +4517,14 @@ bech32@^1.1.2: resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== +better-sqlite3@^7.5.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-7.5.0.tgz#2a91cb616453f002096743b0e5b66a7021cd1c63" + integrity sha512-6FdG9DoytYGDhLW7VWW1vxjEz7xHkqK6LnaUQYA8d6GHNgZhu9PFX2xwKEEnSBRoT1J4PjTUPeg217ShxNmuPg== + dependencies: + bindings "^1.5.0" + prebuild-install "^7.0.0" + bfj@^7.0.2: version "7.0.2" resolved "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz" @@ -4471,7 +4627,7 @@ bitcoinjs-lib@^5.1.6: varuint-bitcoin "^1.0.4" wif "^2.0.1" -bl@^4.0.3: +bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz" integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== @@ -4829,6 +4985,30 @@ cacache@^15.0.5: tar "^6.0.2" unique-filename "^1.1.1" +cacache@^15.2.0: + version "15.3.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" + integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== + dependencies: + "@npmcli/fs" "^1.0.0" + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz" @@ -4844,6 +5024,11 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -4857,6 +5042,19 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" +cacheable-request@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" + integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + call-bind@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz" @@ -5078,6 +5276,18 @@ clean-stack@^2.0.0: resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + cliui@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz" @@ -5096,6 +5306,15 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" @@ -5103,6 +5322,11 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + closest@^0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/closest/-/closest-0.0.1.tgz" @@ -5129,6 +5353,11 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + collect-v8-coverage@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" @@ -5174,6 +5403,11 @@ color-string@^1.5.4: color-name "^1.0.0" simple-swizzle "^0.2.2" +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + color@^3.0.0, color@^3.1.2: version "3.1.3" resolved "https://registry.npmjs.org/color/-/color-3.1.3.tgz" @@ -5192,13 +5426,18 @@ colorette@^1.2.2: resolved "https://registry.npmjs.org/colorette/-/colorette-1.3.0.tgz" integrity sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w== -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" +command-exists@^1.2.7: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + commander@^2.20.0: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" @@ -5313,6 +5552,11 @@ console-browserify@^1.1.0: resolved "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== +console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz" @@ -5483,7 +5727,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.3, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2: +cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -5823,6 +6067,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: dependencies: ms "2.0.0" +debug@4, debug@^4.3.1, debug@^4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debug@^3.1.1, debug@^3.2.6: version "3.2.7" resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" @@ -5844,13 +6095,6 @@ debug@^4.1.0, debug@^4.1.1: dependencies: ms "2.1.2" -debug@^4.3.2: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - debugnyan@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/debugnyan/-/debugnyan-1.0.0.tgz" @@ -5881,6 +6125,13 @@ decompress-response@^3.3.0: dependencies: mimic-response "^1.0.0" +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + dedent@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" @@ -5898,6 +6149,11 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" @@ -5916,11 +6172,23 @@ default-gateway@^4.2.0: execa "^1.0.0" ip-regex "^2.1.0" +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz" @@ -5968,7 +6236,12 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -depd@~1.1.2: +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@^1.1.2, depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -5986,6 +6259,16 @@ destroy@~1.0.4: resolved "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +detect-libc@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" + integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" @@ -6235,6 +6518,26 @@ electron-devtools-installer@^3.2.0: tslib "^2.1.0" unzip-crx-3 "^0.2.0" +electron-rebuild@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-3.2.7.tgz#0f56c1cc99a6fec0a5b990532283c2a8c838c19b" + integrity sha512-WvaW1EgRinDQ61khHFZfx30rkPQG5ItaOT0wrI7iJv9A3SbghriQGfZQfHZs25fWLBe6/vkv05LOqg6aDw6Wzw== + dependencies: + "@malept/cross-spawn-promise" "^2.0.0" + chalk "^4.0.0" + debug "^4.1.1" + detect-libc "^1.0.3" + fs-extra "^10.0.0" + got "^11.7.0" + lzma-native "^8.0.5" + node-abi "^3.0.0" + node-api-version "^0.1.4" + node-gyp "^8.4.0" + ora "^5.1.0" + semver "^7.3.5" + tar "^6.0.5" + yargs "^17.0.1" + electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.612: version "1.3.615" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.615.tgz" @@ -6307,7 +6610,14 @@ encodeurl@^1.0.2, encodeurl@~1.0.2: resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -end-of-stream@^1.0.0, end-of-stream@^1.1.0: +encoding@^0.1.12: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -6345,6 +6655,11 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz" @@ -6893,6 +7208,11 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + expect@^26.6.0, expect@^26.6.2: version "26.6.2" resolved "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz" @@ -7228,6 +7548,15 @@ fork-ts-checker-webpack-plugin@4.1.6: tapable "^1.0.0" worker-rpc "^0.1.0" +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" @@ -7267,6 +7596,20 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8" + integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^7.0.0: version "7.0.1" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz" @@ -7345,12 +7688,40 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +gauge@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" + integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.3" + console-control-strings "^1.1.0" + has-unicode "^2.0.1" + signal-exit "^3.0.7" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.5" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + gensync@^1.0.0-beta.1: version "1.0.0-beta.2" resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -7417,6 +7788,11 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= + glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz" @@ -7439,6 +7815,11 @@ glob-parent@^5.1.2: dependencies: is-glob "^4.0.1" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + glob@^6.0.1: version "6.0.4" resolved "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz" @@ -7554,6 +7935,23 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +got@^11.7.0: + version "11.8.3" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.3.tgz#f496c8fdda5d729a90b4905d2b07dbd148170770" + integrity sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -7576,7 +7974,7 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.4 resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== -graceful-fs@^4.1.6, graceful-fs@^4.2.0: +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6: version "4.2.9" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== @@ -7656,6 +8054,11 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" +has-unicode@^2.0.0, has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + has-value@^0.3.1: version "0.3.1" resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz" @@ -7834,7 +8237,7 @@ htmlparser2@^3.3.0: inherits "^2.0.1" readable-stream "^3.1.1" -http-cache-semantics@^4.0.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== @@ -7881,6 +8284,15 @@ http-parser-js@>=0.5.1: resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.2.tgz" integrity sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ== +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + http-proxy-middleware@0.19.1: version "0.19.1" resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz" @@ -7909,16 +8321,39 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= + dependencies: + ms "^2.0.0" + hyphenate-style-name@^1.0.3: version "1.0.4" resolved "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz" @@ -7931,6 +8366,13 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + icss-utils@^4.0.0, icss-utils@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz" @@ -8074,7 +8516,7 @@ inherits@2.0.3: resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.4: +ini@^1.3.4, ini@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== @@ -8305,6 +8747,13 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" @@ -8346,6 +8795,16 @@ is-in-browser@^1.0.2, is-in-browser@^1.1.3: resolved "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz" integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= + is-module@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz" @@ -8509,6 +8968,11 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-weakref@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -9153,6 +9617,11 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" @@ -9344,6 +9813,13 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +keyv@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.1.1.tgz#02c538bfdbd2a9308cc932d4096f05ae42bfa06a" + integrity sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ== + dependencies: + json-buffer "3.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz" @@ -9551,6 +10027,14 @@ lodash@^4.0.0, lodash@^4.17.10: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + loglevel@^1.6.8: version "1.7.1" resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.7.1.tgz" @@ -9599,6 +10083,15 @@ lz-string@^1.4.4: resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz" integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= +lzma-native@^8.0.5: + version "8.0.6" + resolved "https://registry.yarnpkg.com/lzma-native/-/lzma-native-8.0.6.tgz#3ea456209d643bafd9b5d911781bdf0b396b2665" + integrity sha512-09xfg67mkL2Lz20PrrDeNYZxzeW7ADtpYFbwSQh9U8+76RIzx5QsJBMy8qikv3hbUPfpy6hqwxt6FcGK81g9AA== + dependencies: + node-addon-api "^3.1.0" + node-gyp-build "^4.2.1" + readable-stream "^3.6.0" + magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.7" resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz" @@ -9621,6 +10114,28 @@ make-dir@^3.0.0, make-dir@^3.0.2: dependencies: semver "^6.0.0" +make-fetch-happen@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" + integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.2.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.2" + promise-retry "^2.0.1" + socks-proxy-agent "^6.0.0" + ssri "^8.0.0" + makeerror@1.0.x: version "1.0.11" resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz" @@ -9816,6 +10331,11 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" @@ -9860,6 +10380,11 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimist@^1.2.3: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz" @@ -9867,6 +10392,17 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" +minipass-fetch@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" + integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== + dependencies: + minipass "^3.1.0" + minipass-sized "^1.0.3" + minizlib "^2.0.0" + optionalDependencies: + encoding "^0.1.12" + minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz" @@ -9874,13 +10410,20 @@ minipass-flush@^1.0.5: dependencies: minipass "^3.0.0" -minipass-pipeline@^1.2.2: +minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== dependencies: minipass "^3.0.0" +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + minipass@^3.0.0, minipass@^3.1.1: version "3.1.3" resolved "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz" @@ -9888,7 +10431,14 @@ minipass@^3.0.0, minipass@^3.1.1: dependencies: yallist "^4.0.0" -minizlib@^2.1.1: +minipass@^3.1.0, minipass@^3.1.3: + version "3.1.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" + integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== + dependencies: + yallist "^4.0.0" + +minizlib@^2.0.0, minizlib@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -9920,6 +10470,11 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" @@ -9964,6 +10519,11 @@ ms@2.1.2, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + multicast-dns-service-types@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz" @@ -10023,6 +10583,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + native-url@^0.2.6: version "0.2.6" resolved "https://registry.npmjs.org/native-url/-/native-url-0.2.6.tgz" @@ -10045,6 +10610,11 @@ negotiator@0.6.2: resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== +negotiator@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + neo-async@^2.5.0, neo-async@^2.6.1, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" @@ -10068,11 +10638,56 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-abi@^3.0.0, node-abi@^3.3.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.8.0.tgz#679957dc8e7aa47b0a02589dbfde4f77b29ccb32" + integrity sha512-tzua9qWWi7iW4I42vUPKM+SfaF0vQSLAm4yO5J83mSwB7GeoWrDKC/K+8YCnYNwqP5duwazbw2X9l4m8SC2cUw== + dependencies: + semver "^7.3.5" + +node-addon-api@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== + +node-api-version@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/node-api-version/-/node-api-version-0.1.4.tgz#1ed46a485e462d55d66b5aa1fe2821720dedf080" + integrity sha512-KGXihXdUChwJAOHO53bv9/vXcLmdUsZ6jIptbvYvkpKfth+r7jw44JkVxQFA3kX5nQjzjmGu1uAu/xNNLNlI5g== + dependencies: + semver "^7.3.5" + +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + node-forge@^0.10.0: version "0.10.0" resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== +node-gyp-build@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" + integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== + +node-gyp@^8.4.0: + version "8.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" + integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.6" + make-fetch-happen "^9.1.0" + nopt "^5.0.0" + npmlog "^6.0.0" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.2" + which "^2.0.2" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" @@ -10139,6 +10754,13 @@ node-releases@^2.0.2: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" @@ -10186,6 +10808,11 @@ normalize-url@^4.1.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + npm-conf@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" @@ -10208,6 +10835,26 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" +npmlog@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +npmlog@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.1.tgz#06f1344a174c06e8de9c6c70834cfba2964bba17" + integrity sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg== + dependencies: + are-we-there-yet "^3.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.0" + set-blocking "^2.0.0" + nth-check@^1.0.2, nth-check@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz" @@ -10220,6 +10867,11 @@ num2fraction@^1.2.2: resolved "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz" integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz" @@ -10443,6 +11095,21 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ora@^5.1.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + original@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/original/-/original-1.0.2.tgz" @@ -10460,6 +11127,11 @@ p-cancelable@^1.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + p-each-series@^2.1.0: version "2.2.0" resolved "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz" @@ -11493,6 +12165,25 @@ postcss@^8.1.0: nanoid "^3.1.20" source-map "^0.6.1" +prebuild-install@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870" + integrity sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + npmlog "^4.0.1" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" @@ -11591,6 +12282,14 @@ promise-inflight@^1.0.1: resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + promise@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz" @@ -11744,6 +12443,11 @@ querystringify@^2.1.1: resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + raf@^3.4.1: version "3.4.1" resolved "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz" @@ -11781,6 +12485,16 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-app-polyfill@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-2.0.0.tgz" @@ -11793,6 +12507,13 @@ react-app-polyfill@^2.0.0: regenerator-runtime "^0.13.7" whatwg-fetch "^3.4.1" +react-app-rewired@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/react-app-rewired/-/react-app-rewired-2.2.1.tgz#84901ee1e3f26add0377ebec0b41bcdfce9fc211" + integrity sha512-uFQWTErXeLDrMzOJHKp0h8P1z0LV9HzPGsJ6adOtGlA/B9WfT6Shh4j2tLTTGlXOfiVx6w6iWpp7SOC5pvk+gA== + dependencies: + semver "^5.6.0" + react-dev-utils@^11.0.3: version "11.0.4" resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz" @@ -11989,7 +12710,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -12249,6 +12970,11 @@ resize-observer-polyfill@^1.5.1: resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz" @@ -12330,6 +13056,21 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" +responselike@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== + dependencies: + lowercase-keys "^2.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz" @@ -12490,7 +13231,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -12692,7 +13433,7 @@ serve-static@1.14.1: parseurl "~1.3.3" send "0.17.1" -set-blocking@^2.0.0: +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -12791,6 +13532,25 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" @@ -12817,6 +13577,11 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz" @@ -12868,6 +13633,23 @@ sockjs@^0.3.21: uuid "^3.4.0" websocket-driver "^0.7.4" +socks-proxy-agent@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87" + integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew== + dependencies: + agent-base "^6.0.2" + debug "^4.3.1" + socks "^2.6.1" + +socks@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.2.tgz#ec042d7960073d40d94268ff3bb727dc685f111a" + integrity sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA== + dependencies: + ip "^1.1.5" + smart-buffer "^4.2.0" + sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz" @@ -13019,6 +13801,13 @@ ssri@^8.0.0: dependencies: minipass "^3.1.1" +ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" + stable@^0.1.8: version "0.1.8" resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" @@ -13109,6 +13898,24 @@ string-natural-compare@^3.0.1: resolved "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" @@ -13230,6 +14037,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" @@ -13270,6 +14084,11 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + style-loader@1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz" @@ -13372,6 +14191,27 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +tar-fs@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + tar@^6.0.2: version "6.0.5" resolved "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz" @@ -13384,6 +14224,18 @@ tar@^6.0.2: mkdirp "^1.0.3" yallist "^4.0.0" +tar@^6.0.5, tar@^6.1.2: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz" @@ -14122,6 +14974,14 @@ watchpack@^1.7.4: chokidar "^3.4.1" watchpack-chokidar2 "^2.0.1" +watchpack@^2.1.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.1.tgz#4200d9447b401156eeca7767ee610f8809bc9d25" + integrity sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + wbuf@^1.1.0, wbuf@^1.7.3: version "1.7.3" resolved "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz" @@ -14129,6 +14989,13 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz" @@ -14314,6 +15181,13 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" +wide-align@^1.1.0, wide-align@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + wif@^2.0.1, wif@^2.0.6: version "2.0.6" resolved "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz" @@ -14511,6 +15385,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -14565,6 +15448,11 @@ y18n@^4.0.0: resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz" integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + yaku@^0.16.6: version "0.16.7" resolved "https://registry.npmjs.org/yaku/-/yaku-0.16.7.tgz" @@ -14601,6 +15489,11 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^21.0.0: + version "21.0.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" + integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== + yargs@^13.3.2: version "13.3.2" resolved "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz" @@ -14634,6 +15527,19 @@ yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" +yargs@^17.0.1: + version "17.4.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.0.tgz#9fc9efc96bd3aa2c1240446af28499f0e7593d00" + integrity sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.0.0" + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"