diff --git a/.eslintrc b/.eslintrc index 0fb6ab98..fc484ae7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,18 +1,17 @@ - { +{ "root": true, "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint", - "prettier" - ], + "plugins": ["@typescript-eslint", "prettier"], "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "prettier" + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "prettier" ], - "rules": { - "no-console": 1, - "prettier/prettier": 2 + "rules": { + "no-console": 0, + "prettier/prettier": 2, + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": ["error"] } - } \ No newline at end of file +} diff --git a/.github/workflows/test_and_build.yml b/.github/workflows/test_and_build.yml new file mode 100644 index 00000000..8717dee7 --- /dev/null +++ b/.github/workflows/test_and_build.yml @@ -0,0 +1,27 @@ +name: Node.js CI + +on: [ push ] +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Read .nvmrc + id: node_version + run: echo ::set-output name=NODE_VERSION::$(cat .nvmrc) + + - name: Set Up node + uses: actions/setup-node@v2 + with: + node-version: ${{ steps.node_version.outputs.NODE_VERSION }} + cache: 'yarn' + + - name: Install dependencies + run: yarn --immutable --immutable-cache + + - name: Test and Build Codebase + run: yarn ci + \ No newline at end of file diff --git a/.gitignore b/.gitignore index fca5e467..b6af2327 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ lib-cov # Coverage directory used by tools like istanbul coverage +.nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt @@ -54,6 +55,7 @@ Sync #Yarn #https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored #zero installs +package-lock.json .yarn/* !.yarn/cache !.yarn/patches @@ -65,3 +67,4 @@ Sync # Misc .idea *.db + diff --git a/.husky/pre-commit b/.husky/pre-commit index 3ab6115d..9c649544 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,3 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -yarn lint-staged \ No newline at end of file + +yarn lint-staged --verbose diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 00000000..35384dd6 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn tsc --noEmit --pretty diff --git a/.lintstagedrc.yaml b/.lintstagedrc.yaml index 97d9a7cd..592be1fd 100644 --- a/.lintstagedrc.yaml +++ b/.lintstagedrc.yaml @@ -3,4 +3,3 @@ src/**/*.{js,ts,json}: - prettier --write - eslint --cache --fix - - tsc-files --noEmit \ No newline at end of file diff --git a/.mocharc.js b/.mocharc.js new file mode 100644 index 00000000..dff6dab9 --- /dev/null +++ b/.mocharc.js @@ -0,0 +1,14 @@ +'use-strict'; + +process.env.NODE_ENV = 'test'; + +// Mocha configuration file +// Reference for options: https://github.com/mochajs/mocha/blob/master/example/config/.mocharc.js +module.exports = { + extension: ['ts'], + spec: ['**/*.test.ts'], + require: ['ts-node/register/transpile-only', 'source-map-support/register', 'tests/testSetup.ts'], + timeout: '3000', + parallel: true, + recursive: true +}; diff --git a/.pnp.js b/.pnp.js index cf616a71..46095fa4 100755 --- a/.pnp.js +++ b/.pnp.js @@ -37,30 +37,46 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { [null, { "packageLocation": "./", "packageDependencies": [ + ["@istanbuljs/nyc-config-typescript", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:1.0.1"], + ["@types/chai", "npm:4.2.21"], ["@types/jwk-to-pem", "npm:2.0.0"], - ["@types/node", "npm:14.14.37"], - ["@types/prompt-sync", "npm:4.1.0"], + ["@types/lodash", "npm:4.14.176"], + ["@types/mocha", "npm:9.0.0"], + ["@types/node", "npm:14.17.15"], + ["@types/node-fetch", "npm:2.5.3"], ["@types/prompts", "npm:2.4.0"], + ["@types/regression", "npm:2.0.2"], + ["@types/sinon", "npm:10.0.2"], + ["@types/source-map-support", "npm:0.5.4"], ["@types/uuid", "npm:8.3.0"], ["@typescript-eslint/eslint-plugin", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:4.20.0"], ["@typescript-eslint/parser", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:4.20.0"], ["ardrive-core-js", "npm:0.5.1"], - ["arweave", "npm:1.10.11"], + ["arweave", "npm:1.10.16"], ["arweave-bundles", "npm:1.0.3"], - ["community-js", "npm:1.1.36"], + ["arweave-mnemonic-keys", "npm:0.0.9"], + ["base64-js", "npm:1.5.1"], + ["chai", "npm:4.3.4"], + ["commander", "npm:8.2.0"], ["eslint", "npm:7.23.0"], ["eslint-config-prettier", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:8.1.0"], ["eslint-plugin-prettier", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:3.3.1"], ["husky", "npm:6.0.0"], + ["jwk-to-pem", "npm:2.0.4"], ["lint-staged", "npm:11.0.0"], + ["lodash", "npm:4.17.21"], + ["mocha", "npm:9.1.1"], + ["node-fetch", "npm:2.6.2"], + ["nyc", "npm:15.1.0"], ["prettier", "npm:2.2.1"], - ["progress", "npm:2.0.3"], - ["prompt-password", "npm:1.2.0"], - ["prompt-sync", "npm:4.2.0"], ["prompts", "npm:2.4.0"], + ["regression", "npm:2.0.1"], ["rimraf", "npm:3.0.2"], - ["ts-node", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:9.1.1"], - ["tsc-files", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:1.1.2"], + ["sinon", "npm:11.1.2"], + ["smartweave", "npm:0.4.45"], + ["source-map-support", "npm:0.5.20"], + ["ts-node", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:10.2.1"], + ["ts-sinon", "npm:2.0.1"], ["typescript", "patch:typescript@npm%3A4.2.3#builtin::version=4.2.3&hash=a45b0e"], ["uuid", "npm:8.3.2"] ], @@ -68,51 +84,325 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["@babel/code-frame", [ - ["npm:7.10.4", { - "packageLocation": "./.yarn/cache/@babel-code-frame-npm-7.10.4-ab1ee3c93e-05245d3b22.zip/node_modules/@babel/code-frame/", + ["npm:7.12.11", { + "packageLocation": "./.yarn/cache/@babel-code-frame-npm-7.12.11-1a9a1b277f-033d3fb3bf.zip/node_modules/@babel/code-frame/", "packageDependencies": [ - ["@babel/code-frame", "npm:7.10.4"], - ["@babel/highlight", "npm:7.10.4"] + ["@babel/code-frame", "npm:7.12.11"], + ["@babel/highlight", "npm:7.14.5"] ], "linkType": "HARD", }], - ["npm:7.12.11", { - "packageLocation": "./.yarn/cache/@babel-code-frame-npm-7.12.11-1a9a1b277f-033d3fb3bf.zip/node_modules/@babel/code-frame/", + ["npm:7.14.5", { + "packageLocation": "./.yarn/cache/@babel-code-frame-npm-7.14.5-4dc9115988-48c584cad9.zip/node_modules/@babel/code-frame/", "packageDependencies": [ - ["@babel/code-frame", "npm:7.12.11"], - ["@babel/highlight", "npm:7.10.4"] + ["@babel/code-frame", "npm:7.14.5"], + ["@babel/highlight", "npm:7.14.5"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/compat-data", [ + ["npm:7.15.0", { + "packageLocation": "./.yarn/cache/@babel-compat-data-npm-7.15.0-48235b743d-76db9ec4fb.zip/node_modules/@babel/compat-data/", + "packageDependencies": [ + ["@babel/compat-data", "npm:7.15.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/core", [ + ["npm:7.15.5", { + "packageLocation": "./.yarn/cache/@babel-core-npm-7.15.5-1d250c9216-84c787c821.zip/node_modules/@babel/core/", + "packageDependencies": [ + ["@babel/core", "npm:7.15.5"], + ["@babel/code-frame", "npm:7.14.5"], + ["@babel/generator", "npm:7.15.4"], + ["@babel/helper-compilation-targets", "virtual:1d250c9216c25e9db076f1a75a12e463816c80da414e0b10bf78965d137b5b888ece2e62fc19eaf2d38cf67baf5687d4b21a2042c3a2819775db1947d83c3002#npm:7.15.4"], + ["@babel/helper-module-transforms", "npm:7.15.4"], + ["@babel/helpers", "npm:7.15.4"], + ["@babel/parser", "npm:7.15.6"], + ["@babel/template", "npm:7.15.4"], + ["@babel/traverse", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"], + ["convert-source-map", "npm:1.7.0"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], + ["gensync", "npm:1.0.0-beta.2"], + ["json5", "npm:2.2.0"], + ["semver", "npm:6.3.0"], + ["source-map", "npm:0.5.7"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/generator", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-generator-npm-7.15.4-11b44cab06-5ee8687d49.zip/node_modules/@babel/generator/", + "packageDependencies": [ + ["@babel/generator", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"], + ["jsesc", "npm:2.5.2"], + ["source-map", "npm:0.5.7"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-compilation-targets", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helper-compilation-targets-npm-7.15.4-8aadf9f3ed-539e4d2093.zip/node_modules/@babel/helper-compilation-targets/", + "packageDependencies": [ + ["@babel/helper-compilation-targets", "npm:7.15.4"] + ], + "linkType": "SOFT", + }], + ["virtual:1d250c9216c25e9db076f1a75a12e463816c80da414e0b10bf78965d137b5b888ece2e62fc19eaf2d38cf67baf5687d4b21a2042c3a2819775db1947d83c3002#npm:7.15.4", { + "packageLocation": "./.yarn/$$virtual/@babel-helper-compilation-targets-virtual-ae0d328453/0/cache/@babel-helper-compilation-targets-npm-7.15.4-8aadf9f3ed-539e4d2093.zip/node_modules/@babel/helper-compilation-targets/", + "packageDependencies": [ + ["@babel/helper-compilation-targets", "virtual:1d250c9216c25e9db076f1a75a12e463816c80da414e0b10bf78965d137b5b888ece2e62fc19eaf2d38cf67baf5687d4b21a2042c3a2819775db1947d83c3002#npm:7.15.4"], + ["@babel/compat-data", "npm:7.15.0"], + ["@babel/core", "npm:7.15.5"], + ["@babel/helper-validator-option", "npm:7.14.5"], + ["@types/babel__core", null], + ["browserslist", "npm:4.17.0"], + ["semver", "npm:6.3.0"] + ], + "packagePeers": [ + "@babel/core", + "@types/babel__core" + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-function-name", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helper-function-name-npm-7.15.4-ef0109c90b-74ec8a86b1.zip/node_modules/@babel/helper-function-name/", + "packageDependencies": [ + ["@babel/helper-function-name", "npm:7.15.4"], + ["@babel/helper-get-function-arity", "npm:7.15.4"], + ["@babel/template", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-get-function-arity", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helper-get-function-arity-npm-7.15.4-0f7c9ab74a-c60ed72a9c.zip/node_modules/@babel/helper-get-function-arity/", + "packageDependencies": [ + ["@babel/helper-get-function-arity", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-hoist-variables", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helper-hoist-variables-npm-7.15.4-1754989aec-b500f154f9.zip/node_modules/@babel/helper-hoist-variables/", + "packageDependencies": [ + ["@babel/helper-hoist-variables", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-member-expression-to-functions", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helper-member-expression-to-functions-npm-7.15.4-212b6361be-7180912838.zip/node_modules/@babel/helper-member-expression-to-functions/", + "packageDependencies": [ + ["@babel/helper-member-expression-to-functions", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-module-imports", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helper-module-imports-npm-7.15.4-b399b49e52-efb5329581.zip/node_modules/@babel/helper-module-imports/", + "packageDependencies": [ + ["@babel/helper-module-imports", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-module-transforms", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helper-module-transforms-npm-7.15.4-2ff12afc8c-f895915ff7.zip/node_modules/@babel/helper-module-transforms/", + "packageDependencies": [ + ["@babel/helper-module-transforms", "npm:7.15.4"], + ["@babel/helper-module-imports", "npm:7.15.4"], + ["@babel/helper-replace-supers", "npm:7.15.4"], + ["@babel/helper-simple-access", "npm:7.15.4"], + ["@babel/helper-split-export-declaration", "npm:7.15.4"], + ["@babel/helper-validator-identifier", "npm:7.14.9"], + ["@babel/template", "npm:7.15.4"], + ["@babel/traverse", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-optimise-call-expression", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helper-optimise-call-expression-npm-7.15.4-20261f745b-96d837b5d2.zip/node_modules/@babel/helper-optimise-call-expression/", + "packageDependencies": [ + ["@babel/helper-optimise-call-expression", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-replace-supers", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helper-replace-supers-npm-7.15.4-2a4bb81d23-44291cc084.zip/node_modules/@babel/helper-replace-supers/", + "packageDependencies": [ + ["@babel/helper-replace-supers", "npm:7.15.4"], + ["@babel/helper-member-expression-to-functions", "npm:7.15.4"], + ["@babel/helper-optimise-call-expression", "npm:7.15.4"], + ["@babel/traverse", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-simple-access", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helper-simple-access-npm-7.15.4-fcd51a651c-ac7f1403b4.zip/node_modules/@babel/helper-simple-access/", + "packageDependencies": [ + ["@babel/helper-simple-access", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-split-export-declaration", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helper-split-export-declaration-npm-7.15.4-ff2895bff2-17179ddcba.zip/node_modules/@babel/helper-split-export-declaration/", + "packageDependencies": [ + ["@babel/helper-split-export-declaration", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"] ], "linkType": "HARD", }] ]], ["@babel/helper-validator-identifier", [ - ["npm:7.10.4", { - "packageLocation": "./.yarn/cache/@babel-helper-validator-identifier-npm-7.10.4-0689d787fa-25098ef842.zip/node_modules/@babel/helper-validator-identifier/", + ["npm:7.14.9", { + "packageLocation": "./.yarn/cache/@babel-helper-validator-identifier-npm-7.14.9-d7bb91b6de-a4825ac127.zip/node_modules/@babel/helper-validator-identifier/", + "packageDependencies": [ + ["@babel/helper-validator-identifier", "npm:7.14.9"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helper-validator-option", [ + ["npm:7.14.5", { + "packageLocation": "./.yarn/cache/@babel-helper-validator-option-npm-7.14.5-fd38dcf0bc-aded46b377.zip/node_modules/@babel/helper-validator-option/", + "packageDependencies": [ + ["@babel/helper-validator-option", "npm:7.14.5"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/helpers", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-helpers-npm-7.15.4-370adba024-b6e700c85b.zip/node_modules/@babel/helpers/", "packageDependencies": [ - ["@babel/helper-validator-identifier", "npm:7.10.4"] + ["@babel/helpers", "npm:7.15.4"], + ["@babel/template", "npm:7.15.4"], + ["@babel/traverse", "npm:7.15.4"], + ["@babel/types", "npm:7.15.6"] ], "linkType": "HARD", }] ]], ["@babel/highlight", [ - ["npm:7.10.4", { - "packageLocation": "./.yarn/cache/@babel-highlight-npm-7.10.4-c7ff18fbba-c167b938af.zip/node_modules/@babel/highlight/", + ["npm:7.14.5", { + "packageLocation": "./.yarn/cache/@babel-highlight-npm-7.14.5-4a18106cbc-a1ed599c26.zip/node_modules/@babel/highlight/", "packageDependencies": [ - ["@babel/highlight", "npm:7.10.4"], - ["@babel/helper-validator-identifier", "npm:7.10.4"], + ["@babel/highlight", "npm:7.14.5"], + ["@babel/helper-validator-identifier", "npm:7.14.9"], ["chalk", "npm:2.4.2"], ["js-tokens", "npm:4.0.0"] ], "linkType": "HARD", }] ]], + ["@babel/parser", [ + ["npm:7.15.6", { + "packageLocation": "./.yarn/cache/@babel-parser-npm-7.15.6-a61d8794ff-2ec5923e17.zip/node_modules/@babel/parser/", + "packageDependencies": [ + ["@babel/parser", "npm:7.15.6"], + ["@babel/types", "npm:7.15.6"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/template", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-template-npm-7.15.4-a024aff24b-d4d366d881.zip/node_modules/@babel/template/", + "packageDependencies": [ + ["@babel/template", "npm:7.15.4"], + ["@babel/code-frame", "npm:7.14.5"], + ["@babel/parser", "npm:7.15.6"], + ["@babel/types", "npm:7.15.6"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/traverse", [ + ["npm:7.15.4", { + "packageLocation": "./.yarn/cache/@babel-traverse-npm-7.15.4-904b3fada4-d023925a46.zip/node_modules/@babel/traverse/", + "packageDependencies": [ + ["@babel/traverse", "npm:7.15.4"], + ["@babel/code-frame", "npm:7.14.5"], + ["@babel/generator", "npm:7.15.4"], + ["@babel/helper-function-name", "npm:7.15.4"], + ["@babel/helper-hoist-variables", "npm:7.15.4"], + ["@babel/helper-split-export-declaration", "npm:7.15.4"], + ["@babel/parser", "npm:7.15.6"], + ["@babel/types", "npm:7.15.6"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], + ["globals", "npm:11.12.0"] + ], + "linkType": "HARD", + }] + ]], + ["@babel/types", [ + ["npm:7.15.6", { + "packageLocation": "./.yarn/cache/@babel-types-npm-7.15.6-330b07a916-34c8048bde.zip/node_modules/@babel/types/", + "packageDependencies": [ + ["@babel/types", "npm:7.15.6"], + ["@babel/helper-validator-identifier", "npm:7.14.9"], + ["to-fast-properties", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@cspotcode/source-map-consumer", [ + ["npm:0.8.0", { + "packageLocation": "./.yarn/cache/@cspotcode-source-map-consumer-npm-0.8.0-1f37e9e72b-fc32215a35.zip/node_modules/@cspotcode/source-map-consumer/", + "packageDependencies": [ + ["@cspotcode/source-map-consumer", "npm:0.8.0"] + ], + "linkType": "HARD", + }] + ]], + ["@cspotcode/source-map-support", [ + ["npm:0.6.1", { + "packageLocation": "./.yarn/cache/@cspotcode-source-map-support-npm-0.6.1-2e72b80534-eda5227576.zip/node_modules/@cspotcode/source-map-support/", + "packageDependencies": [ + ["@cspotcode/source-map-support", "npm:0.6.1"], + ["@cspotcode/source-map-consumer", "npm:0.8.0"] + ], + "linkType": "HARD", + }] + ]], ["@eslint/eslintrc", [ ["npm:0.4.0", { "packageLocation": "./.yarn/cache/@eslint-eslintrc-npm-0.4.0-901e1408cc-d3f51b7419.zip/node_modules/@eslint/eslintrc/", "packageDependencies": [ ["@eslint/eslintrc", "npm:0.4.0"], ["ajv", "npm:6.12.6"], - ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.1.1"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], ["espree", "npm:7.3.1"], ["globals", "npm:12.4.0"], ["ignore", "npm:4.0.6"], @@ -124,15 +414,68 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["@jest/types", [ - ["npm:25.5.0", { - "packageLocation": "./.yarn/cache/@jest-types-npm-25.5.0-45f0640591-33ad68320e.zip/node_modules/@jest/types/", + ["@istanbuljs/load-nyc-config", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@istanbuljs-load-nyc-config-npm-1.1.0-42d17c9cb1-f7f3b1c922.zip/node_modules/@istanbuljs/load-nyc-config/", "packageDependencies": [ - ["@jest/types", "npm:25.5.0"], - ["@types/istanbul-lib-coverage", "npm:2.0.3"], - ["@types/istanbul-reports", "npm:1.1.2"], - ["@types/yargs", "npm:15.0.5"], - ["chalk", "npm:3.0.0"] + ["@istanbuljs/load-nyc-config", "npm:1.1.0"], + ["camelcase", "npm:5.3.1"], + ["find-up", "npm:4.1.0"], + ["get-package-type", "npm:0.1.0"], + ["js-yaml", "npm:3.14.0"], + ["resolve-from", "npm:5.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["@istanbuljs/nyc-config-typescript", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/@istanbuljs-nyc-config-typescript-npm-1.0.1-d1daa3ba46-28c19a10ee.zip/node_modules/@istanbuljs/nyc-config-typescript/", + "packageDependencies": [ + ["@istanbuljs/nyc-config-typescript", "npm:1.0.1"] + ], + "linkType": "SOFT", + }], + ["virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:1.0.1", { + "packageLocation": "./.yarn/$$virtual/@istanbuljs-nyc-config-typescript-virtual-3829775e85/0/cache/@istanbuljs-nyc-config-typescript-npm-1.0.1-d1daa3ba46-28c19a10ee.zip/node_modules/@istanbuljs/nyc-config-typescript/", + "packageDependencies": [ + ["@istanbuljs/nyc-config-typescript", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:1.0.1"], + ["@istanbuljs/schema", "npm:0.1.3"], + ["@types/nyc", null], + ["@types/source-map-support", "npm:0.5.4"], + ["@types/ts-node", null], + ["nyc", "npm:15.1.0"], + ["source-map-support", "npm:0.5.20"], + ["ts-node", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:10.2.1"] + ], + "packagePeers": [ + "@types/nyc", + "@types/source-map-support", + "@types/ts-node", + "nyc", + "source-map-support", + "ts-node" + ], + "linkType": "HARD", + }] + ]], + ["@istanbuljs/schema", [ + ["npm:0.1.3", { + "packageLocation": "./.yarn/cache/@istanbuljs-schema-npm-0.1.3-466bd3eaaa-d84c326335.zip/node_modules/@istanbuljs/schema/", + "packageDependencies": [ + ["@istanbuljs/schema", "npm:0.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["@lordvlad/asn1.js", [ + ["npm:5.1.1", { + "packageLocation": "./.yarn/cache/@lordvlad-asn1.js-npm-5.1.1-4f1ad9d487-806d50b2ea.zip/node_modules/@lordvlad/asn1.js/", + "packageDependencies": [ + ["@lordvlad/asn1.js", "npm:5.1.1"], + ["bn.js", "npm:4.12.0"], + ["inherits", "npm:2.0.4"], + ["minimalistic-assert", "npm:1.0.1"] ], "linkType": "HARD", }] @@ -168,18 +511,198 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["@ponicode/cli", [ - ["npm:0.0.1-18", { - "packageLocation": "./.yarn/cache/@ponicode-cli-npm-0.0.1-18-219ac03cf5-5873f1fb0a.zip/node_modules/@ponicode/cli/", + ["@protobufjs/aspromise", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@protobufjs-aspromise-npm-1.1.2-71d00b938f-83ced0798a.zip/node_modules/@protobufjs/aspromise/", "packageDependencies": [ - ["@ponicode/cli", "npm:0.0.1-18"], - ["@types/connect", "npm:3.4.34"], - ["@types/convert-source-map", "npm:1.5.1"], - ["@types/jest", "npm:25.2.3"], - ["@types/js-base64", "npm:3.0.0"], - ["convert-source-map", "npm:1.7.0"], - ["cross-spawn", "npm:7.0.3"], - ["js-base64", "npm:3.6.0"] + ["@protobufjs/aspromise", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/base64", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@protobufjs-base64-npm-1.1.2-cd8ca6814a-ae9e84aaf6.zip/node_modules/@protobufjs/base64/", + "packageDependencies": [ + ["@protobufjs/base64", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/codegen", [ + ["npm:2.0.4", { + "packageLocation": "./.yarn/cache/@protobufjs-codegen-npm-2.0.4-36e188bbe6-a05d5f8892.zip/node_modules/@protobufjs/codegen/", + "packageDependencies": [ + ["@protobufjs/codegen", "npm:2.0.4"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/eventemitter", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@protobufjs-eventemitter-npm-1.1.0-029cc7d431-32a84e33f1.zip/node_modules/@protobufjs/eventemitter/", + "packageDependencies": [ + ["@protobufjs/eventemitter", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/fetch", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@protobufjs-fetch-npm-1.1.0-ca857b7df4-d682e5d8a1.zip/node_modules/@protobufjs/fetch/", + "packageDependencies": [ + ["@protobufjs/fetch", "npm:1.1.0"], + ["@protobufjs/aspromise", "npm:1.1.2"], + ["@protobufjs/inquire", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/float", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/@protobufjs-float-npm-1.0.2-5678f64d08-eee7278de2.zip/node_modules/@protobufjs/float/", + "packageDependencies": [ + ["@protobufjs/float", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/inquire", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@protobufjs-inquire-npm-1.1.0-3c7759e9ce-3541518cca.zip/node_modules/@protobufjs/inquire/", + "packageDependencies": [ + ["@protobufjs/inquire", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/path", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@protobufjs-path-npm-1.1.2-641d08de76-22f10c5c22.zip/node_modules/@protobufjs/path/", + "packageDependencies": [ + ["@protobufjs/path", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/pool", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@protobufjs-pool-npm-1.1.0-47a76f96a1-5fc4af9e06.zip/node_modules/@protobufjs/pool/", + "packageDependencies": [ + ["@protobufjs/pool", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@protobufjs/utf8", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/@protobufjs-utf8-npm-1.1.0-02c590807c-5b3fa7425f.zip/node_modules/@protobufjs/utf8/", + "packageDependencies": [ + ["@protobufjs/utf8", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["@sinonjs/commons", [ + ["npm:1.8.3", { + "packageLocation": "./.yarn/cache/@sinonjs-commons-npm-1.8.3-30cf78d93f-a7f3181512.zip/node_modules/@sinonjs/commons/", + "packageDependencies": [ + ["@sinonjs/commons", "npm:1.8.3"], + ["type-detect", "npm:4.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["@sinonjs/fake-timers", [ + ["npm:6.0.1", { + "packageLocation": "./.yarn/cache/@sinonjs-fake-timers-npm-6.0.1-cebf4d0bfb-64458b9087.zip/node_modules/@sinonjs/fake-timers/", + "packageDependencies": [ + ["@sinonjs/fake-timers", "npm:6.0.1"], + ["@sinonjs/commons", "npm:1.8.3"] + ], + "linkType": "HARD", + }], + ["npm:7.1.2", { + "packageLocation": "./.yarn/cache/@sinonjs-fake-timers-npm-7.1.2-2a6b119ac7-5ce48e40db.zip/node_modules/@sinonjs/fake-timers/", + "packageDependencies": [ + ["@sinonjs/fake-timers", "npm:7.1.2"], + ["@sinonjs/commons", "npm:1.8.3"] + ], + "linkType": "HARD", + }] + ]], + ["@sinonjs/samsam", [ + ["npm:5.3.1", { + "packageLocation": "./.yarn/cache/@sinonjs-samsam-npm-5.3.1-deedfea087-0277cd0b71.zip/node_modules/@sinonjs/samsam/", + "packageDependencies": [ + ["@sinonjs/samsam", "npm:5.3.1"], + ["@sinonjs/commons", "npm:1.8.3"], + ["lodash.get", "npm:4.4.2"], + ["type-detect", "npm:4.0.8"] + ], + "linkType": "HARD", + }], + ["npm:6.0.2", { + "packageLocation": "./.yarn/cache/@sinonjs-samsam-npm-6.0.2-5e8e8897e2-8113510c5b.zip/node_modules/@sinonjs/samsam/", + "packageDependencies": [ + ["@sinonjs/samsam", "npm:6.0.2"], + ["@sinonjs/commons", "npm:1.8.3"], + ["lodash.get", "npm:4.4.2"], + ["type-detect", "npm:4.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["@sinonjs/text-encoding", [ + ["npm:0.7.1", { + "packageLocation": "./.yarn/cache/@sinonjs-text-encoding-npm-0.7.1-865b0079b5-fbc2abff23.zip/node_modules/@sinonjs/text-encoding/", + "packageDependencies": [ + ["@sinonjs/text-encoding", "npm:0.7.1"] + ], + "linkType": "HARD", + }] + ]], + ["@tsconfig/node10", [ + ["npm:1.0.8", { + "packageLocation": "./.yarn/cache/@tsconfig-node10-npm-1.0.8-90a8cce25d-0336493b89.zip/node_modules/@tsconfig/node10/", + "packageDependencies": [ + ["@tsconfig/node10", "npm:1.0.8"] + ], + "linkType": "HARD", + }] + ]], + ["@tsconfig/node12", [ + ["npm:1.0.9", { + "packageLocation": "./.yarn/cache/@tsconfig-node12-npm-1.0.9-780563856d-5532bfb5df.zip/node_modules/@tsconfig/node12/", + "packageDependencies": [ + ["@tsconfig/node12", "npm:1.0.9"] + ], + "linkType": "HARD", + }] + ]], + ["@tsconfig/node14", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/@tsconfig-node14-npm-1.0.1-3ecac58e68-d0068287db.zip/node_modules/@tsconfig/node14/", + "packageDependencies": [ + ["@tsconfig/node14", "npm:1.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["@tsconfig/node16", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/@tsconfig-node16-npm-1.0.2-1f43ab567a-57310e8885.zip/node_modules/@tsconfig/node16/", + "packageDependencies": [ + ["@tsconfig/node16", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], + ["@types/chai", [ + ["npm:4.2.21", { + "packageLocation": "./.yarn/cache/@types-chai-npm-4.2.21-22c1ed2cef-fc7d32fbae.zip/node_modules/@types/chai/", + "packageDependencies": [ + ["@types/chai", "npm:4.2.21"] ], "linkType": "HARD", }] @@ -212,143 +735,166 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["@types/connect", [ - ["npm:3.4.34", { - "packageLocation": "./.yarn/cache/@types-connect-npm-3.4.34-39e4f7bb55-6f712a0408.zip/node_modules/@types/connect/", + ["@types/inquirer", [ + ["npm:7.3.1", { + "packageLocation": "./.yarn/cache/@types-inquirer-npm-7.3.1-63b5231eee-2db996acf8.zip/node_modules/@types/inquirer/", + "packageDependencies": [ + ["@types/inquirer", "npm:7.3.1"], + ["@types/through", "npm:0.0.30"], + ["rxjs", "npm:6.6.7"] + ], + "linkType": "HARD", + }] + ]], + ["@types/json-schema", [ + ["npm:7.0.7", { + "packageLocation": "./.yarn/cache/@types-json-schema-npm-7.0.7-95fb8178d7-b9d2c509fa.zip/node_modules/@types/json-schema/", "packageDependencies": [ - ["@types/connect", "npm:3.4.34"], - ["@types/node", "npm:14.6.4"] + ["@types/json-schema", "npm:7.0.7"] ], "linkType": "HARD", }] ]], - ["@types/convert-source-map", [ - ["npm:1.5.1", { - "packageLocation": "./.yarn/cache/@types-convert-source-map-npm-1.5.1-819f48f4ab-36cd50ea42.zip/node_modules/@types/convert-source-map/", + ["@types/jwk-to-pem", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/@types-jwk-to-pem-npm-2.0.0-85a9aabd67-647e62d347.zip/node_modules/@types/jwk-to-pem/", "packageDependencies": [ - ["@types/convert-source-map", "npm:1.5.1"] + ["@types/jwk-to-pem", "npm:2.0.0"] ], "linkType": "HARD", }] ]], - ["@types/inquirer", [ - ["npm:7.3.1", { - "packageLocation": "./.yarn/cache/@types-inquirer-npm-7.3.1-63b5231eee-2db996acf8.zip/node_modules/@types/inquirer/", + ["@types/lodash", [ + ["npm:4.14.176", { + "packageLocation": "./.yarn/cache/@types-lodash-npm-4.14.176-34dca4acb8-cc3c9d7522.zip/node_modules/@types/lodash/", "packageDependencies": [ - ["@types/inquirer", "npm:7.3.1"], - ["@types/through", "npm:0.0.30"], - ["rxjs", "npm:6.6.7"] + ["@types/lodash", "npm:4.14.176"] ], "linkType": "HARD", }] ]], - ["@types/istanbul-lib-coverage", [ - ["npm:2.0.3", { - "packageLocation": "./.yarn/cache/@types-istanbul-lib-coverage-npm-2.0.3-67a37eb00a-d6f6dbf66d.zip/node_modules/@types/istanbul-lib-coverage/", + ["@types/long", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/@types-long-npm-4.0.1-022c8b6e77-ed2a125330.zip/node_modules/@types/long/", "packageDependencies": [ - ["@types/istanbul-lib-coverage", "npm:2.0.3"] + ["@types/long", "npm:4.0.1"] ], "linkType": "HARD", }] ]], - ["@types/istanbul-lib-report", [ - ["npm:3.0.0", { - "packageLocation": "./.yarn/cache/@types-istanbul-lib-report-npm-3.0.0-50de3e6b3b-78aa9f859b.zip/node_modules/@types/istanbul-lib-report/", + ["@types/mocha", [ + ["npm:9.0.0", { + "packageLocation": "./.yarn/cache/@types-mocha-npm-9.0.0-cd77a42cf3-dac70c24e5.zip/node_modules/@types/mocha/", "packageDependencies": [ - ["@types/istanbul-lib-report", "npm:3.0.0"], - ["@types/istanbul-lib-coverage", "npm:2.0.3"] + ["@types/mocha", "npm:9.0.0"] ], "linkType": "HARD", }] ]], - ["@types/istanbul-reports", [ - ["npm:1.1.2", { - "packageLocation": "./.yarn/cache/@types-istanbul-reports-npm-1.1.2-4f435a3d0f-92bd1f76a4.zip/node_modules/@types/istanbul-reports/", + ["@types/node", [ + ["npm:11.11.6", { + "packageLocation": "./.yarn/cache/@types-node-npm-11.11.6-40abad0842-2b64bfc234.zip/node_modules/@types/node/", "packageDependencies": [ - ["@types/istanbul-reports", "npm:1.1.2"], - ["@types/istanbul-lib-coverage", "npm:2.0.3"], - ["@types/istanbul-lib-report", "npm:3.0.0"] + ["@types/node", "npm:11.11.6"] + ], + "linkType": "HARD", + }], + ["npm:14.17.15", { + "packageLocation": "./.yarn/cache/@types-node-npm-14.17.15-9cfdb3e8eb-c9f5b6fa6a.zip/node_modules/@types/node/", + "packageDependencies": [ + ["@types/node", "npm:14.17.15"] + ], + "linkType": "HARD", + }], + ["npm:16.7.13", { + "packageLocation": "./.yarn/cache/@types-node-npm-16.7.13-08a1515fa1-e22d3b58e5.zip/node_modules/@types/node/", + "packageDependencies": [ + ["@types/node", "npm:16.7.13"] ], "linkType": "HARD", }] ]], - ["@types/jest", [ - ["npm:25.2.3", { - "packageLocation": "./.yarn/cache/@types-jest-npm-25.2.3-41912c4b23-fe92624fd7.zip/node_modules/@types/jest/", + ["@types/node-fetch", [ + ["npm:2.5.3", { + "packageLocation": "./.yarn/cache/@types-node-fetch-npm-2.5.3-63cbf4aef6-c85f183722.zip/node_modules/@types/node-fetch/", "packageDependencies": [ - ["@types/jest", "npm:25.2.3"], - ["jest-diff", "npm:25.5.0"], - ["pretty-format", "npm:25.5.0"] + ["@types/node-fetch", "npm:2.5.3"], + ["@types/node", "npm:16.7.13"] ], "linkType": "HARD", }] ]], - ["@types/js-base64", [ - ["npm:3.0.0", { - "packageLocation": "./.yarn/cache/@types-js-base64-npm-3.0.0-00b29e1112-7513271c3f.zip/node_modules/@types/js-base64/", + ["@types/parse-json", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/@types-parse-json-npm-4.0.0-298522afa6-4a8f720afa.zip/node_modules/@types/parse-json/", "packageDependencies": [ - ["@types/js-base64", "npm:3.0.0"] + ["@types/parse-json", "npm:4.0.0"] ], "linkType": "HARD", }] ]], - ["@types/json-schema", [ - ["npm:7.0.7", { - "packageLocation": "./.yarn/cache/@types-json-schema-npm-7.0.7-95fb8178d7-b9d2c509fa.zip/node_modules/@types/json-schema/", + ["@types/prompts", [ + ["npm:2.4.0", { + "packageLocation": "./.yarn/cache/@types-prompts-npm-2.4.0-5b5caba459-9d5b490dba.zip/node_modules/@types/prompts/", "packageDependencies": [ - ["@types/json-schema", "npm:7.0.7"] + ["@types/prompts", "npm:2.4.0"] ], "linkType": "HARD", }] ]], - ["@types/jwk-to-pem", [ - ["npm:2.0.0", { - "packageLocation": "./.yarn/cache/@types-jwk-to-pem-npm-2.0.0-85a9aabd67-647e62d347.zip/node_modules/@types/jwk-to-pem/", + ["@types/regression", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/@types-regression-npm-2.0.2-a0bd52194b-fa577d48c5.zip/node_modules/@types/regression/", "packageDependencies": [ - ["@types/jwk-to-pem", "npm:2.0.0"] + ["@types/regression", "npm:2.0.2"] ], "linkType": "HARD", }] ]], - ["@types/node", [ - ["npm:14.14.37", { - "packageLocation": "./.yarn/cache/@types-node-npm-14.14.37-6783f920bd-5e2d9baf75.zip/node_modules/@types/node/", + ["@types/sinon", [ + ["npm:10.0.2", { + "packageLocation": "./.yarn/cache/@types-sinon-npm-10.0.2-2a54516cee-44d149b0ba.zip/node_modules/@types/sinon/", "packageDependencies": [ - ["@types/node", "npm:14.14.37"] + ["@types/sinon", "npm:10.0.2"], + ["@sinonjs/fake-timers", "npm:7.1.2"] ], "linkType": "HARD", }], - ["npm:14.6.4", { - "packageLocation": "./.yarn/cache/@types-node-npm-14.6.4-5ce5afae9a-bff274e362.zip/node_modules/@types/node/", + ["npm:9.0.11", { + "packageLocation": "./.yarn/cache/@types-sinon-npm-9.0.11-231734b808-c84ccbd5ac.zip/node_modules/@types/sinon/", "packageDependencies": [ - ["@types/node", "npm:14.6.4"] + ["@types/sinon", "npm:9.0.11"], + ["@types/sinonjs__fake-timers", "npm:6.0.3"] ], "linkType": "HARD", }] ]], - ["@types/parse-json", [ - ["npm:4.0.0", { - "packageLocation": "./.yarn/cache/@types-parse-json-npm-4.0.0-298522afa6-4a8f720afa.zip/node_modules/@types/parse-json/", + ["@types/sinon-chai", [ + ["npm:3.2.5", { + "packageLocation": "./.yarn/cache/@types-sinon-chai-npm-3.2.5-1d6490532a-0e482de52b.zip/node_modules/@types/sinon-chai/", "packageDependencies": [ - ["@types/parse-json", "npm:4.0.0"] + ["@types/sinon-chai", "npm:3.2.5"], + ["@types/chai", "npm:4.2.21"], + ["@types/sinon", "npm:10.0.2"] ], "linkType": "HARD", }] ]], - ["@types/prompt-sync", [ - ["npm:4.1.0", { - "packageLocation": "./.yarn/cache/@types-prompt-sync-npm-4.1.0-5d6d03c0bb-21bc6832fb.zip/node_modules/@types/prompt-sync/", + ["@types/sinonjs__fake-timers", [ + ["npm:6.0.3", { + "packageLocation": "./.yarn/cache/@types-sinonjs__fake-timers-npm-6.0.3-7a45cf3bad-2f2accb87e.zip/node_modules/@types/sinonjs__fake-timers/", "packageDependencies": [ - ["@types/prompt-sync", "npm:4.1.0"] + ["@types/sinonjs__fake-timers", "npm:6.0.3"] ], "linkType": "HARD", }] ]], - ["@types/prompts", [ - ["npm:2.4.0", { - "packageLocation": "./.yarn/cache/@types-prompts-npm-2.4.0-5b5caba459-9d5b490dba.zip/node_modules/@types/prompts/", + ["@types/source-map-support", [ + ["npm:0.5.4", { + "packageLocation": "./.yarn/cache/@types-source-map-support-npm-0.5.4-1c4eb2be20-448a3aa4fa.zip/node_modules/@types/source-map-support/", "packageDependencies": [ - ["@types/prompts", "npm:2.4.0"] + ["@types/source-map-support", "npm:0.5.4"], + ["source-map", "npm:0.6.1"] ], "linkType": "HARD", }] @@ -358,7 +904,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./.yarn/cache/@types-through-npm-0.0.30-59be01cca3-67bae5cefe.zip/node_modules/@types/through/", "packageDependencies": [ ["@types/through", "npm:0.0.30"], - ["@types/node", "npm:14.6.4"] + ["@types/node", "npm:16.7.13"] ], "linkType": "HARD", }] @@ -372,25 +918,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["@types/yargs", [ - ["npm:15.0.5", { - "packageLocation": "./.yarn/cache/@types-yargs-npm-15.0.5-18a2128a57-2133c8cb58.zip/node_modules/@types/yargs/", - "packageDependencies": [ - ["@types/yargs", "npm:15.0.5"], - ["@types/yargs-parser", "npm:15.0.0"] - ], - "linkType": "HARD", - }] - ]], - ["@types/yargs-parser", [ - ["npm:15.0.0", { - "packageLocation": "./.yarn/cache/@types-yargs-parser-npm-15.0.0-db1d59832c-74bfaefde9.zip/node_modules/@types/yargs-parser/", - "packageDependencies": [ - ["@types/yargs-parser", "npm:15.0.0"] - ], - "linkType": "HARD", - }] - ]], ["@typescript-eslint/eslint-plugin", [ ["npm:4.20.0", { "packageLocation": "./.yarn/cache/@typescript-eslint-eslint-plugin-npm-4.20.0-71542df847-b458d6a83d.zip/node_modules/@typescript-eslint/eslint-plugin/", @@ -408,12 +935,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@typescript-eslint/experimental-utils", "virtual:1c16952a6f9ff3d31a30bd98354e284eb46c7d43e97d0ad7096aaa6132e3bb1b945d2671dd15d2d2f3d9aec0fb21038daeef60baa697e632704d1ce504a133e8#npm:4.20.0"], ["@typescript-eslint/parser", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:4.20.0"], ["@typescript-eslint/scope-manager", "npm:4.20.0"], - ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.1.1"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], ["eslint", "npm:7.23.0"], ["functional-red-black-tree", "npm:1.0.1"], ["lodash", "npm:4.17.21"], ["regexpp", "npm:3.1.0"], - ["semver", "npm:7.3.2"], + ["semver", "npm:7.3.5"], ["tsutils", "virtual:1c16952a6f9ff3d31a30bd98354e284eb46c7d43e97d0ad7096aaa6132e3bb1b945d2671dd15d2d2f3d9aec0fb21038daeef60baa697e632704d1ce504a133e8#npm:3.21.0"], ["typescript", "patch:typescript@npm%3A4.2.3#builtin::version=4.2.3&hash=a45b0e"] ], @@ -471,7 +998,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@typescript-eslint/scope-manager", "npm:4.20.0"], ["@typescript-eslint/types", "npm:4.20.0"], ["@typescript-eslint/typescript-estree", "virtual:25e1b21bada4d97cbe7fb0b32c85e197135b7ad033e22748123bb86e92ae56380815e20d8fd1c215680f18199f84cdf076b831414c75ab2af8b9cc2dfc2fd5da#npm:4.20.0"], - ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.1.1"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], ["eslint", "npm:7.23.0"], ["typescript", "patch:typescript@npm%3A4.2.3#builtin::version=4.2.3&hash=a45b0e"] ], @@ -517,10 +1044,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@typescript-eslint/typescript-estree", "virtual:25e1b21bada4d97cbe7fb0b32c85e197135b7ad033e22748123bb86e92ae56380815e20d8fd1c215680f18199f84cdf076b831414c75ab2af8b9cc2dfc2fd5da#npm:4.20.0"], ["@typescript-eslint/types", "npm:4.20.0"], ["@typescript-eslint/visitor-keys", "npm:4.20.0"], - ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.1.1"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], ["globby", "npm:11.0.3"], ["is-glob", "npm:4.0.1"], - ["semver", "npm:7.3.2"], + ["semver", "npm:7.3.5"], ["tsutils", "virtual:1c16952a6f9ff3d31a30bd98354e284eb46c7d43e97d0ad7096aaa6132e3bb1b945d2671dd15d2d2f3d9aec0fb21038daeef60baa697e632704d1ce504a133e8#npm:3.21.0"], ["typescript", "patch:typescript@npm%3A4.2.3#builtin::version=4.2.3&hash=a45b0e"] ], @@ -535,10 +1062,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@typescript-eslint/typescript-estree", "virtual:43eed5deb2597af4389c392a6789258e1914c5797b580c163e65213533c927ec4d9bca2be2c4d178480b5715da6eef7a798d56538c4cd6f6412113e735ea5d5e#npm:4.20.0"], ["@typescript-eslint/types", "npm:4.20.0"], ["@typescript-eslint/visitor-keys", "npm:4.20.0"], - ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.1.1"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], ["globby", "npm:11.0.3"], ["is-glob", "npm:4.0.1"], - ["semver", "npm:7.3.2"], + ["semver", "npm:7.3.5"], ["tsutils", "virtual:282286d55deb4847b728ad67048ae7437cacaffb83a6c16650f3a18b960544a6f9300739f54d72b19b63ddd101ae40578da80b88cd9ff45d15fca2cf37f6e9ef#npm:3.21.0"], ["typescript", null] ], @@ -559,6 +1086,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["@ungap/promise-all-settled", [ + ["npm:1.1.2", { + "packageLocation": "./.yarn/cache/@ungap-promise-all-settled-npm-1.1.2-c0f42e147b-be6c80a2fc.zip/node_modules/@ungap/promise-all-settled/", + "packageDependencies": [ + ["@ungap/promise-all-settled", "npm:1.1.2"] + ], + "linkType": "HARD", + }] + ]], ["@weavery/clarity", [ ["npm:0.1.5", { "packageLocation": "./.yarn/cache/@weavery-clarity-npm-0.1.5-4059231b5b-41eafc56b7.zip/node_modules/@weavery/clarity/", @@ -584,6 +1120,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["acorn", "npm:7.4.1"] ], "linkType": "HARD", + }], + ["npm:8.5.0", { + "packageLocation": "./.yarn/cache/acorn-npm-8.5.0-faed0ea119-989ff8bf4b.zip/node_modules/acorn/", + "packageDependencies": [ + ["acorn", "npm:8.5.0"] + ], + "linkType": "HARD", }] ]], ["acorn-jsx", [ @@ -608,6 +1151,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["acorn-walk", [ + ["npm:8.2.0", { + "packageLocation": "./.yarn/cache/acorn-walk-npm-8.2.0-2f2cac3177-93ffbdb8b3.zip/node_modules/acorn-walk/", + "packageDependencies": [ + ["acorn-walk", "npm:8.2.0"] + ], + "linkType": "HARD", + }] + ]], ["aggregate-error", [ ["npm:3.1.0", { "packageLocation": "./.yarn/cache/aggregate-error-npm-3.1.0-415a406f4e-704d2001a3.zip/node_modules/aggregate-error/", @@ -620,17 +1172,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["ajv", [ - ["npm:6.12.4", { - "packageLocation": "./.yarn/cache/ajv-npm-6.12.4-c4dcb5109f-50d72b0a10.zip/node_modules/ajv/", - "packageDependencies": [ - ["ajv", "npm:6.12.4"], - ["fast-deep-equal", "npm:3.1.3"], - ["fast-json-stable-stringify", "npm:2.1.0"], - ["json-schema-traverse", "npm:0.4.1"], - ["uri-js", "npm:4.4.0"] - ], - "linkType": "HARD", - }], ["npm:6.12.6", { "packageLocation": "./.yarn/cache/ajv-npm-6.12.6-4b5105e2b2-19a8f3b0a0.zip/node_modules/ajv/", "packageDependencies": [ @@ -828,14 +1369,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["ansi-escapes", [ - ["npm:4.3.1", { - "packageLocation": "./.yarn/cache/ansi-escapes-npm-4.3.1-f4aad61b5b-bcb39e57bd.zip/node_modules/ansi-escapes/", - "packageDependencies": [ - ["ansi-escapes", "npm:4.3.1"], - ["type-fest", "npm:0.11.0"] - ], - "linkType": "HARD", - }], ["npm:4.3.2", { "packageLocation": "./.yarn/cache/ansi-escapes-npm-4.3.2-3ad173702f-eca4d4e15b.zip/node_modules/ansi-escapes/", "packageDependencies": [ @@ -1034,12 +1567,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["anymatch", [ - ["npm:3.1.1", { - "packageLocation": "./.yarn/cache/anymatch-npm-3.1.1-7dcfa6178a-cf61bbaf7f.zip/node_modules/anymatch/", + ["npm:3.1.2", { + "packageLocation": "./.yarn/cache/anymatch-npm-3.1.2-1d5471acfa-cd6c08eb8d.zip/node_modules/anymatch/", "packageDependencies": [ - ["anymatch", "npm:3.1.1"], + ["anymatch", "npm:3.1.2"], ["normalize-path", "npm:3.0.0"], - ["picomatch", "npm:2.2.2"] + ["picomatch", "npm:2.3.0"] ], "linkType": "HARD", }] @@ -1054,6 +1587,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["append-transform", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/append-transform-npm-2.0.0-99bd7d69ed-7fd09e1d1f.zip/node_modules/append-transform/", + "packageDependencies": [ + ["append-transform", "npm:2.0.0"], + ["default-require-extensions", "npm:3.0.0"] + ], + "linkType": "HARD", + }] + ]], ["aproba", [ ["npm:1.2.0", { "packageLocation": "./.yarn/cache/aproba-npm-1.2.0-34129f0778-d4bac3e640.zip/node_modules/aproba/", @@ -1063,6 +1606,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["archy", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/archy-npm-1.0.0-7db8bfdc3b-fed06a0487.zip/node_modules/archy/", + "packageDependencies": [ + ["archy", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], ["arconnect", [ ["npm:0.2.9", { "packageLocation": "./.yarn/cache/arconnect-npm-0.2.9-a276136014-7871621da2.zip/node_modules/arconnect/", @@ -1078,30 +1630,46 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./", "packageDependencies": [ ["ardrive-cli", "workspace:."], + ["@istanbuljs/nyc-config-typescript", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:1.0.1"], + ["@types/chai", "npm:4.2.21"], ["@types/jwk-to-pem", "npm:2.0.0"], - ["@types/node", "npm:14.14.37"], - ["@types/prompt-sync", "npm:4.1.0"], + ["@types/lodash", "npm:4.14.176"], + ["@types/mocha", "npm:9.0.0"], + ["@types/node", "npm:14.17.15"], + ["@types/node-fetch", "npm:2.5.3"], ["@types/prompts", "npm:2.4.0"], + ["@types/regression", "npm:2.0.2"], + ["@types/sinon", "npm:10.0.2"], + ["@types/source-map-support", "npm:0.5.4"], ["@types/uuid", "npm:8.3.0"], ["@typescript-eslint/eslint-plugin", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:4.20.0"], ["@typescript-eslint/parser", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:4.20.0"], ["ardrive-core-js", "npm:0.5.1"], - ["arweave", "npm:1.10.11"], + ["arweave", "npm:1.10.16"], ["arweave-bundles", "npm:1.0.3"], - ["community-js", "npm:1.1.36"], + ["arweave-mnemonic-keys", "npm:0.0.9"], + ["base64-js", "npm:1.5.1"], + ["chai", "npm:4.3.4"], + ["commander", "npm:8.2.0"], ["eslint", "npm:7.23.0"], ["eslint-config-prettier", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:8.1.0"], ["eslint-plugin-prettier", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:3.3.1"], ["husky", "npm:6.0.0"], + ["jwk-to-pem", "npm:2.0.4"], ["lint-staged", "npm:11.0.0"], + ["lodash", "npm:4.17.21"], + ["mocha", "npm:9.1.1"], + ["node-fetch", "npm:2.6.2"], + ["nyc", "npm:15.1.0"], ["prettier", "npm:2.2.1"], - ["progress", "npm:2.0.3"], - ["prompt-password", "npm:1.2.0"], - ["prompt-sync", "npm:4.2.0"], ["prompts", "npm:2.4.0"], + ["regression", "npm:2.0.1"], ["rimraf", "npm:3.0.2"], - ["ts-node", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:9.1.1"], - ["tsc-files", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:1.1.2"], + ["sinon", "npm:11.1.2"], + ["smartweave", "npm:0.4.45"], + ["source-map-support", "npm:0.5.20"], + ["ts-node", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:10.2.1"], + ["ts-sinon", "npm:2.0.1"], ["typescript", "patch:typescript@npm%3A4.2.3#builtin::version=4.2.3&hash=a45b0e"], ["uuid", "npm:8.3.2"] ], @@ -1117,14 +1685,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["arweave-bundles", "npm:1.0.3"], ["axios", "npm:0.21.1"], ["better-sqlite3", "npm:7.4.0"], - ["chokidar", "npm:3.5.1"], + ["chokidar", "npm:3.5.2"], ["concat-stream", "npm:2.0.0"], ["folder-hash", "npm:4.0.1"], ["futoin-hkdf", "npm:1.3.3"], ["jwk-to-pem", "npm:2.0.4"], ["md5-file", "npm:5.0.0"], ["mime-types", "npm:2.1.29"], - ["node-fetch", "npm:2.6.1"], + ["node-fetch", "npm:2.6.2"], ["progress", "npm:2.0.3"], ["prompt-password", "npm:1.2.0"], ["prompt-sync", "npm:4.2.0"], @@ -1165,6 +1733,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["sprintf-js", "npm:1.0.3"] ], "linkType": "HARD", + }], + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/argparse-npm-2.0.1-faff7999e6-160b7a25d2.zip/node_modules/argparse/", + "packageDependencies": [ + ["argparse", "npm:2.0.1"] + ], + "linkType": "HARD", }] ]], ["arr-flatten", [ @@ -1205,28 +1780,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["arweave", [ - ["npm:1.10.11", { - "packageLocation": "./.yarn/cache/arweave-npm-1.10.11-a663c3e819-40d32d1a68.zip/node_modules/arweave/", - "packageDependencies": [ - ["arweave", "npm:1.10.11"], - ["asn1.js", "npm:5.4.1"], - ["axios", "npm:0.21.1"], - ["base64-js", "npm:1.3.1"], - ["bignumber.js", "npm:9.0.1"] - ], - "linkType": "HARD", - }], - ["npm:1.10.13", { - "packageLocation": "./.yarn/cache/arweave-npm-1.10.13-28daec638a-5da8a3f5cc.zip/node_modules/arweave/", - "packageDependencies": [ - ["arweave", "npm:1.10.13"], - ["asn1.js", "npm:5.4.1"], - ["axios", "npm:0.21.1"], - ["base64-js", "npm:1.3.1"], - ["bignumber.js", "npm:9.0.1"] - ], - "linkType": "HARD", - }], ["npm:1.10.16", { "packageLocation": "./.yarn/cache/arweave-npm-1.10.16-559acf76f7-2baa240095.zip/node_modules/arweave/", "packageDependencies": [ @@ -1234,7 +1787,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["arconnect", "npm:0.2.9"], ["asn1.js", "npm:5.4.1"], ["axios", "npm:0.21.1"], - ["base64-js", "npm:1.3.1"], + ["base64-js", "npm:1.5.1"], ["bignumber.js", "npm:9.0.1"] ], "linkType": "HARD", @@ -1245,7 +1798,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./.yarn/cache/arweave-bundles-npm-1.0.3-2bdcb9d1ed-fe920c8448.zip/node_modules/arweave-bundles/", "packageDependencies": [ ["arweave-bundles", "npm:1.0.3"], - ["arweave", "npm:1.10.13"] + ["arweave", "npm:1.10.16"] + ], + "linkType": "HARD", + }] + ]], + ["arweave-mnemonic-keys", [ + ["npm:0.0.9", { + "packageLocation": "./.yarn/cache/arweave-mnemonic-keys-npm-0.0.9-d1e53ee8b7-4e04c4726b.zip/node_modules/arweave-mnemonic-keys/", + "packageDependencies": [ + ["arweave-mnemonic-keys", "npm:0.0.9"], + ["human-crypto-keys", "npm:0.1.4"], + ["libp2p-crypto", "npm:0.19.7"] ], "linkType": "HARD", }] @@ -1265,7 +1829,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./.yarn/cache/asn1.js-npm-5.4.1-37c7edbcb0-4aa368fce1.zip/node_modules/asn1.js/", "packageDependencies": [ ["asn1.js", "npm:5.4.1"], - ["bn.js", "npm:4.11.9"], + ["bn.js", "npm:4.12.0"], ["inherits", "npm:2.0.4"], ["minimalistic-assert", "npm:1.0.1"], ["safer-buffer", "npm:2.1.2"] @@ -1282,6 +1846,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["assertion-error", [ + ["npm:1.1.0", { + "packageLocation": "./.yarn/cache/assertion-error-npm-1.1.0-66b893015e-7bbc9fa2ff.zip/node_modules/assertion-error/", + "packageDependencies": [ + ["assertion-error", "npm:1.1.0"] + ], + "linkType": "HARD", + }] + ]], ["astral-regex", [ ["npm:2.0.0", { "packageLocation": "./.yarn/cache/astral-regex-npm-2.0.0-f30d866aab-bf049ee704.zip/node_modules/astral-regex/", @@ -1338,10 +1911,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["base64-js", [ - ["npm:1.3.1", { - "packageLocation": "./.yarn/cache/base64-js-npm-1.3.1-8625be908e-8a0cc69d7c.zip/node_modules/base64-js/", + ["npm:1.5.1", { + "packageLocation": "./.yarn/cache/base64-js-npm-1.5.1-b2f7275641-c1b41a26dd.zip/node_modules/base64-js/", "packageDependencies": [ - ["base64-js", "npm:1.3.1"] + ["base64-js", "npm:1.5.1"] ], "linkType": "HARD", }] @@ -1397,6 +1970,19 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["bip39", [ + ["npm:3.0.4", { + "packageLocation": "./.yarn/cache/bip39-npm-3.0.4-7c69c9182f-24359ef61f.zip/node_modules/bip39/", + "packageDependencies": [ + ["bip39", "npm:3.0.4"], + ["@types/node", "npm:11.11.6"], + ["create-hash", "npm:1.2.0"], + ["pbkdf2", "npm:3.1.2"], + ["randombytes", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], ["bl", [ ["npm:4.1.0", { "packageLocation": "./.yarn/cache/bl-npm-4.1.0-7f94cdcf3f-15d009339c.zip/node_modules/bl/", @@ -1410,13 +1996,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["bn.js", [ - ["npm:4.11.9", { - "packageLocation": "./.yarn/cache/bn.js-npm-4.11.9-c739f92b89-31630d3560.zip/node_modules/bn.js/", - "packageDependencies": [ - ["bn.js", "npm:4.11.9"] - ], - "linkType": "HARD", - }], ["npm:4.12.0", { "packageLocation": "./.yarn/cache/bn.js-npm-4.12.0-3ec6c884f6-cfe7494de9.zip/node_modules/bn.js/", "packageDependencies": [ @@ -1455,12 +2034,35 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["browser-stdout", [ + ["npm:1.3.1", { + "packageLocation": "./.yarn/cache/browser-stdout-npm-1.3.1-6b2376bf3f-2f91b1ad26.zip/node_modules/browser-stdout/", + "packageDependencies": [ + ["browser-stdout", "npm:1.3.1"] + ], + "linkType": "HARD", + }] + ]], + ["browserslist", [ + ["npm:4.17.0", { + "packageLocation": "./.yarn/cache/browserslist-npm-4.17.0-98801cc7f5-e7c4b78520.zip/node_modules/browserslist/", + "packageDependencies": [ + ["browserslist", "npm:4.17.0"], + ["caniuse-lite", "npm:1.0.30001257"], + ["colorette", "npm:1.4.0"], + ["electron-to-chromium", "npm:1.3.836"], + ["escalade", "npm:3.1.1"], + ["node-releases", "npm:1.1.75"] + ], + "linkType": "HARD", + }] + ]], ["buffer", [ ["npm:5.7.1", { "packageLocation": "./.yarn/cache/buffer-npm-5.7.1-513ef8259e-1750ac396e.zip/node_modules/buffer/", "packageDependencies": [ ["buffer", "npm:5.7.1"], - ["base64-js", "npm:1.3.1"], + ["base64-js", "npm:1.5.1"], ["ieee754", "npm:1.2.1"] ], "linkType": "HARD", @@ -1475,6 +2077,19 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["caching-transform", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/caching-transform-npm-4.0.0-d619d562ea-0a9e0c317e.zip/node_modules/caching-transform/", + "packageDependencies": [ + ["caching-transform", "npm:4.0.0"], + ["hasha", "npm:5.2.2"], + ["make-dir", "npm:3.1.0"], + ["package-hash", "npm:4.0.0"], + ["write-file-atomic", "npm:3.0.3"] + ], + "linkType": "HARD", + }] + ]], ["call-bind", [ ["npm:1.0.2", { "packageLocation": "./.yarn/cache/call-bind-npm-1.0.2-c957124861-18cc6107a1.zip/node_modules/call-bind/", @@ -1495,6 +2110,31 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["camelcase", [ + ["npm:5.3.1", { + "packageLocation": "./.yarn/cache/camelcase-npm-5.3.1-5db8af62c5-6a3350c4ea.zip/node_modules/camelcase/", + "packageDependencies": [ + ["camelcase", "npm:5.3.1"] + ], + "linkType": "HARD", + }], + ["npm:6.2.0", { + "packageLocation": "./.yarn/cache/camelcase-npm-6.2.0-69f8c130ac-654700600a.zip/node_modules/camelcase/", + "packageDependencies": [ + ["camelcase", "npm:6.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["caniuse-lite", [ + ["npm:1.0.30001257", { + "packageLocation": "./.yarn/cache/caniuse-lite-npm-1.0.30001257-10e3a8c80e-2d39f401d7.zip/node_modules/caniuse-lite/", + "packageDependencies": [ + ["caniuse-lite", "npm:1.0.30001257"] + ], + "linkType": "HARD", + }] + ]], ["caseless", [ ["npm:0.12.0", { "packageLocation": "./.yarn/cache/caseless-npm-0.12.0-e83bc5df83-147f48bff9.zip/node_modules/caseless/", @@ -1504,6 +2144,21 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["chai", [ + ["npm:4.3.4", { + "packageLocation": "./.yarn/cache/chai-npm-4.3.4-808f3b5355-ea3e6547b9.zip/node_modules/chai/", + "packageDependencies": [ + ["chai", "npm:4.3.4"], + ["assertion-error", "npm:1.1.0"], + ["check-error", "npm:1.0.2"], + ["deep-eql", "npm:3.0.1"], + ["get-func-name", "npm:2.0.0"], + ["pathval", "npm:1.1.1"], + ["type-detect", "npm:4.0.8"] + ], + "linkType": "HARD", + }] + ]], ["chalk", [ ["npm:2.4.2", { "packageLocation": "./.yarn/cache/chalk-npm-2.4.2-3ea16dd91e-22c7b7b5bc.zip/node_modules/chalk/", @@ -1515,33 +2170,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], "linkType": "HARD", }], - ["npm:3.0.0", { - "packageLocation": "./.yarn/cache/chalk-npm-3.0.0-e813208025-4018b0c812.zip/node_modules/chalk/", - "packageDependencies": [ - ["chalk", "npm:3.0.0"], - ["ansi-styles", "npm:4.2.1"], - ["supports-color", "npm:7.2.0"] - ], - "linkType": "HARD", - }], - ["npm:4.1.0", { - "packageLocation": "./.yarn/cache/chalk-npm-4.1.0-c746e252ba-f860285b41.zip/node_modules/chalk/", - "packageDependencies": [ - ["chalk", "npm:4.1.0"], - ["ansi-styles", "npm:4.2.1"], - ["supports-color", "npm:7.2.0"] - ], - "linkType": "HARD", - }], - ["npm:4.1.1", { - "packageLocation": "./.yarn/cache/chalk-npm-4.1.1-f1ce6bae57-445c12db7a.zip/node_modules/chalk/", - "packageDependencies": [ - ["chalk", "npm:4.1.1"], - ["ansi-styles", "npm:4.2.1"], - ["supports-color", "npm:7.2.0"] - ], - "linkType": "HARD", - }], ["npm:4.1.2", { "packageLocation": "./.yarn/cache/chalk-npm-4.1.2-ba8b67ab80-e3901b97d9.zip/node_modules/chalk/", "packageDependencies": [ @@ -1561,6 +2189,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["check-error", [ + ["npm:1.0.2", { + "packageLocation": "./.yarn/cache/check-error-npm-1.0.2-00c540c6e9-1460ad12da.zip/node_modules/check-error/", + "packageDependencies": [ + ["check-error", "npm:1.0.2"] + ], + "linkType": "HARD", + }] + ]], ["choices-separator", [ ["npm:2.0.0", { "packageLocation": "./.yarn/cache/choices-separator-npm-2.0.0-bfc7473442-c04f4957c4.zip/node_modules/choices-separator/", @@ -1574,18 +2211,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["chokidar", [ - ["npm:3.5.1", { - "packageLocation": "./.yarn/cache/chokidar-npm-3.5.1-205217279e-61b3f710f9.zip/node_modules/chokidar/", + ["npm:3.5.2", { + "packageLocation": "./.yarn/cache/chokidar-npm-3.5.2-6752340fec-52fbff3ace.zip/node_modules/chokidar/", "packageDependencies": [ - ["chokidar", "npm:3.5.1"], - ["anymatch", "npm:3.1.1"], + ["chokidar", "npm:3.5.2"], + ["anymatch", "npm:3.1.2"], ["braces", "npm:3.0.2"], ["fsevents", "patch:fsevents@npm%3A2.3.2#builtin::version=2.3.2&hash=11e9ea"], - ["glob-parent", "npm:5.1.1"], + ["glob-parent", "npm:5.1.2"], ["is-binary-path", "npm:2.1.0"], ["is-glob", "npm:4.0.1"], ["normalize-path", "npm:3.0.0"], - ["readdirp", "npm:3.5.0"] + ["readdirp", "npm:3.6.0"] ], "linkType": "HARD", }] @@ -1606,6 +2243,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["cipher-base", [ + ["npm:1.0.4", { + "packageLocation": "./.yarn/cache/cipher-base-npm-1.0.4-2e98b97140-ec80001ec9.zip/node_modules/cipher-base/", + "packageDependencies": [ + ["cipher-base", "npm:1.0.4"], + ["inherits", "npm:2.0.4"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], ["clean-stack", [ ["npm:2.2.0", { "packageLocation": "./.yarn/cache/clean-stack-npm-2.2.0-a8ce435a5c-e291ce2b8c.zip/node_modules/clean-stack/", @@ -1668,6 +2316,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["cliui", [ + ["npm:6.0.0", { + "packageLocation": "./.yarn/cache/cliui-npm-6.0.0-488b2414c6-e59d064294.zip/node_modules/cliui/", + "packageDependencies": [ + ["cliui", "npm:6.0.0"], + ["string-width", "npm:4.2.0"], + ["strip-ansi", "npm:6.0.0"], + ["wrap-ansi", "npm:6.2.0"] + ], + "linkType": "HARD", + }], ["npm:7.0.4", { "packageLocation": "./.yarn/cache/cliui-npm-7.0.4-d6b8a9edb6-c49ac1d13f.zip/node_modules/cliui/", "packageDependencies": [ @@ -1776,10 +2434,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["colorette", [ - ["npm:1.2.2", { - "packageLocation": "./.yarn/cache/colorette-npm-1.2.2-da75bd0b32-e240f0c94b.zip/node_modules/colorette/", + ["npm:1.4.0", { + "packageLocation": "./.yarn/cache/colorette-npm-1.4.0-7e94b44dc3-7ef8e1ca16.zip/node_modules/colorette/", "packageDependencies": [ - ["colorette", "npm:1.2.2"] + ["colorette", "npm:1.4.0"] ], "linkType": "HARD", }] @@ -1801,17 +2459,20 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["commander", "npm:7.2.0"] ], "linkType": "HARD", + }], + ["npm:8.2.0", { + "packageLocation": "./.yarn/cache/commander-npm-8.2.0-c925691796-e41e680f2a.zip/node_modules/commander/", + "packageDependencies": [ + ["commander", "npm:8.2.0"] + ], + "linkType": "HARD", }] ]], - ["community-js", [ - ["npm:1.1.36", { - "packageLocation": "./.yarn/cache/community-js-npm-1.1.36-edb2db2761-940cd0d27e.zip/node_modules/community-js/", + ["commondir", [ + ["npm:1.0.1", { + "packageLocation": "./.yarn/cache/commondir-npm-1.0.1-291b790340-98f18ad14f.zip/node_modules/commondir/", "packageDependencies": [ - ["community-js", "npm:1.1.36"], - ["@ponicode/cli", "npm:0.0.1-18"], - ["arweave", "npm:1.10.13"], - ["axios", "npm:0.21.1"], - ["smartweave", "npm:0.4.27"] + ["commondir", "npm:1.0.1"] ], "linkType": "HARD", }] @@ -1898,6 +2559,35 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["create-hash", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/create-hash-npm-1.2.0-afd048e1ce-5565182efc.zip/node_modules/create-hash/", + "packageDependencies": [ + ["create-hash", "npm:1.2.0"], + ["cipher-base", "npm:1.0.4"], + ["inherits", "npm:2.0.4"], + ["md5.js", "npm:1.3.5"], + ["ripemd160", "npm:2.0.2"], + ["sha.js", "npm:2.4.11"] + ], + "linkType": "HARD", + }] + ]], + ["create-hmac", [ + ["npm:1.1.7", { + "packageLocation": "./.yarn/cache/create-hmac-npm-1.1.7-b4ef32668a-98957676a9.zip/node_modules/create-hmac/", + "packageDependencies": [ + ["create-hmac", "npm:1.1.7"], + ["cipher-base", "npm:1.0.4"], + ["create-hash", "npm:1.2.0"], + ["inherits", "npm:2.0.4"], + ["ripemd160", "npm:2.0.2"], + ["safe-buffer", "npm:5.2.1"], + ["sha.js", "npm:2.4.11"] + ], + "linkType": "HARD", + }] + ]], ["create-require", [ ["npm:1.1.1", { "packageLocation": "./.yarn/cache/create-require-npm-1.1.1-839884ca2e-babd307893.zip/node_modules/create-require/", @@ -1919,6 +2609,22 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["crypto-key-composer", [ + ["npm:0.1.3", { + "packageLocation": "./.yarn/cache/crypto-key-composer-npm-0.1.3-8dd1e39879-48f6d45d2a.zip/node_modules/crypto-key-composer/", + "packageDependencies": [ + ["crypto-key-composer", "npm:0.1.3"], + ["@lordvlad/asn1.js", "npm:5.1.1"], + ["buffer", "npm:5.7.1"], + ["clone-deep", "npm:4.0.1"], + ["deep-for-each", "npm:3.0.0"], + ["es6-error", "npm:4.1.1"], + ["matcher", "npm:2.1.0"], + ["node-forge", "npm:0.8.5"] + ], + "linkType": "HARD", + }] + ]], ["d", [ ["npm:0.1.1", { "packageLocation": "./.yarn/cache/d-npm-0.1.1-79160303bc-9a79c0e4d5.zip/node_modules/d/", @@ -1963,10 +2669,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], "linkType": "SOFT", }], - ["npm:4.1.1", { - "packageLocation": "./.yarn/cache/debug-npm-4.1.1-540248b3aa-3601a6ce96.zip/node_modules/debug/", + ["npm:4.3.1", { + "packageLocation": "./.yarn/cache/debug-npm-4.3.1-22e08d605e-0d41ba5177.zip/node_modules/debug/", "packageDependencies": [ - ["debug", "npm:4.1.1"] + ["debug", "npm:4.3.1"] ], "linkType": "SOFT", }], @@ -1977,10 +2683,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], "linkType": "SOFT", }], - ["virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.1.1", { - "packageLocation": "./.yarn/$$virtual/debug-virtual-4d1d517f9b/0/cache/debug-npm-4.1.1-540248b3aa-3601a6ce96.zip/node_modules/debug/", + ["virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2", { + "packageLocation": "./.yarn/$$virtual/debug-virtual-bdcc63c535/0/cache/debug-npm-4.3.2-f0148b6afe-5543570879.zip/node_modules/debug/", "packageDependencies": [ - ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.1.1"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], ["ms", "npm:2.1.2"], ["supports-color", null] ], @@ -1989,23 +2695,23 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], "linkType": "HARD", }], - ["virtual:4e2e2c452f0c35a5a10c57e943518259fa97a001cda495d8fda79efd23a450f837aa9efafc71c03bcfc80ee40a84218ca4ede934c2d4a454ece51f9ad06a2685#npm:3.2.6", { - "packageLocation": "./.yarn/$$virtual/debug-virtual-0db0091604/0/cache/debug-npm-3.2.6-6214e40f12-619feb53b1.zip/node_modules/debug/", + ["virtual:48f601891217fc93366476dd2adefe8eec19b6ef0d837d3de7622a1230dd3d0d2040b2e36145558b91d7a18aa61d8905063fb8a24c603be355eeca7ba6fface5#npm:4.3.1", { + "packageLocation": "./.yarn/$$virtual/debug-virtual-51c2235c2e/0/cache/debug-npm-4.3.1-22e08d605e-0d41ba5177.zip/node_modules/debug/", "packageDependencies": [ - ["debug", "virtual:4e2e2c452f0c35a5a10c57e943518259fa97a001cda495d8fda79efd23a450f837aa9efafc71c03bcfc80ee40a84218ca4ede934c2d4a454ece51f9ad06a2685#npm:3.2.6"], + ["debug", "virtual:48f601891217fc93366476dd2adefe8eec19b6ef0d837d3de7622a1230dd3d0d2040b2e36145558b91d7a18aa61d8905063fb8a24c603be355eeca7ba6fface5#npm:4.3.1"], ["ms", "npm:2.1.2"], - ["supports-color", null] + ["supports-color", "npm:8.1.1"] ], "packagePeers": [ "supports-color" ], "linkType": "HARD", }], - ["virtual:899360d658a6566626efa634457187645bf30bfc405d8695b0e1ccc1653296afe8e5e5b09977bdee91d5fc1e3e0d54a299eb775f180e3fc3ef462478be69f50f#npm:4.3.2", { - "packageLocation": "./.yarn/$$virtual/debug-virtual-d0a2f46b5b/0/cache/debug-npm-4.3.2-f0148b6afe-5543570879.zip/node_modules/debug/", + ["virtual:4e2e2c452f0c35a5a10c57e943518259fa97a001cda495d8fda79efd23a450f837aa9efafc71c03bcfc80ee40a84218ca4ede934c2d4a454ece51f9ad06a2685#npm:3.2.6", { + "packageLocation": "./.yarn/$$virtual/debug-virtual-0db0091604/0/cache/debug-npm-3.2.6-6214e40f12-619feb53b1.zip/node_modules/debug/", "packageDependencies": [ - ["debug", "virtual:899360d658a6566626efa634457187645bf30bfc405d8695b0e1ccc1653296afe8e5e5b09977bdee91d5fc1e3e0d54a299eb775f180e3fc3ef462478be69f50f#npm:4.3.2"], - ["ms", "npm:2.1.2"], + ["debug", "virtual:4e2e2c452f0c35a5a10c57e943518259fa97a001cda495d8fda79efd23a450f837aa9efafc71c03bcfc80ee40a84218ca4ede934c2d4a454ece51f9ad06a2685#npm:3.2.6"], + ["ms", "npm:2.1.3"], ["supports-color", null] ], "packagePeers": [ @@ -2026,6 +2732,22 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["decamelize", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/decamelize-npm-1.2.0-c5a2fdc622-8ca9d03ea8.zip/node_modules/decamelize/", + "packageDependencies": [ + ["decamelize", "npm:1.2.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/decamelize-npm-4.0.0-12410e3409-3846161a3b.zip/node_modules/decamelize/", + "packageDependencies": [ + ["decamelize", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], ["decompress-response", [ ["npm:4.2.1", { "packageLocation": "./.yarn/cache/decompress-response-npm-4.2.1-abe5b4ebe4-d854171a10.zip/node_modules/decompress-response/", @@ -2045,6 +2767,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["deep-eql", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/deep-eql-npm-3.0.1-9a66c09c65-eff42bc2d4.zip/node_modules/deep-eql/", + "packageDependencies": [ + ["deep-eql", "npm:3.0.1"], + ["type-detect", "npm:4.0.8"] + ], + "linkType": "HARD", + }] + ]], ["deep-extend", [ ["npm:0.6.0", { "packageLocation": "./.yarn/cache/deep-extend-npm-0.6.0-e182924219-856d7f52db.zip/node_modules/deep-extend/", @@ -2054,6 +2786,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["deep-for-each", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/deep-for-each-npm-3.0.0-5aa901f30c-88c1dc9f15.zip/node_modules/deep-for-each/", + "packageDependencies": [ + ["deep-for-each", "npm:3.0.0"], + ["lodash.isplainobject", "npm:4.0.6"] + ], + "linkType": "HARD", + }] + ]], ["deep-is", [ ["npm:0.1.3", { "packageLocation": "./.yarn/cache/deep-is-npm-0.1.3-0941784645-3de58f86af.zip/node_modules/deep-is/", @@ -2063,6 +2805,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["default-require-extensions", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/default-require-extensions-npm-3.0.0-40586718d6-c53a7ddfa1.zip/node_modules/default-require-extensions/", + "packageDependencies": [ + ["default-require-extensions", "npm:3.0.0"], + ["strip-bom", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], ["defaults", [ ["npm:1.0.3", { "packageLocation": "./.yarn/cache/defaults-npm-1.0.3-e829107b9e-974f63dd0a.zip/node_modules/defaults/", @@ -2134,13 +2886,11 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["diff", "npm:4.0.2"] ], "linkType": "HARD", - }] - ]], - ["diff-sequences", [ - ["npm:25.2.6", { - "packageLocation": "./.yarn/cache/diff-sequences-npm-25.2.6-d72e0e66bc-332484fc00.zip/node_modules/diff-sequences/", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/diff-npm-5.0.0-ad6900db18-ef241d3b20.zip/node_modules/diff/", "packageDependencies": [ - ["diff-sequences", "npm:25.2.6"] + ["diff", "npm:5.0.0"] ], "linkType": "HARD", }] @@ -2176,6 +2926,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["electron-to-chromium", [ + ["npm:1.3.836", { + "packageLocation": "./.yarn/cache/electron-to-chromium-npm-1.3.836-0df58045fd-4cb506937e.zip/node_modules/electron-to-chromium/", + "packageDependencies": [ + ["electron-to-chromium", "npm:1.3.836"] + ], + "linkType": "HARD", + }] + ]], ["elliptic", [ ["npm:6.5.4", { "packageLocation": "./.yarn/cache/elliptic-npm-6.5.4-0ca8204a86-e0fb360fb6.zip/node_modules/elliptic/", @@ -2230,6 +2989,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["err-code", [ + ["npm:3.0.1", { + "packageLocation": "./.yarn/cache/err-code-npm-3.0.1-3a0dc5fc51-ddae1b8c69.zip/node_modules/err-code/", + "packageDependencies": [ + ["err-code", "npm:3.0.1"] + ], + "linkType": "HARD", + }] + ]], ["error-ex", [ ["npm:1.3.2", { "packageLocation": "./.yarn/cache/error-ex-npm-1.3.2-5654f80c0f-6c6c918742.zip/node_modules/error-ex/", @@ -2261,6 +3029,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["es6-error", [ + ["npm:4.1.1", { + "packageLocation": "./.yarn/cache/es6-error-npm-4.1.1-5e8c22b20f-d7343d3f47.zip/node_modules/es6-error/", + "packageDependencies": [ + ["es6-error", "npm:4.1.1"] + ], + "linkType": "HARD", + }] + ]], ["es6-iterator", [ ["npm:0.1.3", { "packageLocation": "./.yarn/cache/es6-iterator-npm-0.1.3-a720f17fd9-7f28d2bb62.zip/node_modules/es6-iterator/", @@ -2332,6 +3109,20 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["escape-string-regexp", "npm:1.0.5"] ], "linkType": "HARD", + }], + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/escape-string-regexp-npm-2.0.0-aef69d2a25-f3500f264e.zip/node_modules/escape-string-regexp/", + "packageDependencies": [ + ["escape-string-regexp", "npm:2.0.0"] + ], + "linkType": "HARD", + }], + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/escape-string-regexp-npm-4.0.0-4b531d8d59-c747be8d5f.zip/node_modules/escape-string-regexp/", + "packageDependencies": [ + ["escape-string-regexp", "npm:4.0.0"] + ], + "linkType": "HARD", }] ]], ["eslint", [ @@ -2342,9 +3133,9 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@babel/code-frame", "npm:7.12.11"], ["@eslint/eslintrc", "npm:0.4.0"], ["ajv", "npm:6.12.6"], - ["chalk", "npm:4.1.0"], + ["chalk", "npm:4.1.2"], ["cross-spawn", "npm:7.0.3"], - ["debug", "virtual:899360d658a6566626efa634457187645bf30bfc405d8695b0e1ccc1653296afe8e5e5b09977bdee91d5fc1e3e0d54a299eb775f180e3fc3ef462478be69f50f#npm:4.3.2"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], ["doctrine", "npm:3.0.0"], ["enquirer", "npm:2.3.6"], ["eslint-scope", "npm:5.1.1"], @@ -2545,6 +3336,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["events", [ + ["npm:3.3.0", { + "packageLocation": "./.yarn/cache/events-npm-3.3.0-c280bc7e48-56fa125670.zip/node_modules/events/", + "packageDependencies": [ + ["events", "npm:3.3.0"] + ], + "linkType": "HARD", + }] + ]], ["execa", [ ["npm:5.0.0", { "packageLocation": "./.yarn/cache/execa-npm-5.0.0-4ee568fb49-bf9664702c.zip/node_modules/execa/", @@ -2656,8 +3456,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@nodelib/fs.walk", "npm:1.2.6"], ["glob-parent", "npm:5.1.2"], ["merge2", "npm:1.4.1"], - ["micromatch", "npm:4.0.2"], - ["picomatch", "npm:2.2.2"] + ["micromatch", "npm:4.0.4"], + ["picomatch", "npm:2.3.0"] ], "linkType": "HARD", }] @@ -2691,13 +3491,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["figlet", [ - ["npm:1.5.0", { - "packageLocation": "./.yarn/cache/figlet-npm-1.5.0-8014ee3b57-49839d8179.zip/node_modules/figlet/", - "packageDependencies": [ - ["figlet", "npm:1.5.0"] - ], - "linkType": "HARD", - }], ["npm:1.5.2", { "packageLocation": "./.yarn/cache/figlet-npm-1.5.2-5f7d8f3af4-64b6932ac1.zip/node_modules/figlet/", "packageDependencies": [ @@ -2745,6 +3538,47 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["find-cache-dir", [ + ["npm:3.3.2", { + "packageLocation": "./.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-5330b81922.zip/node_modules/find-cache-dir/", + "packageDependencies": [ + ["find-cache-dir", "npm:3.3.2"], + ["commondir", "npm:1.0.1"], + ["make-dir", "npm:3.1.0"], + ["pkg-dir", "npm:4.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["find-up", [ + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/find-up-npm-4.1.0-c3ccf8d855-d612d28e02.zip/node_modules/find-up/", + "packageDependencies": [ + ["find-up", "npm:4.1.0"], + ["locate-path", "npm:5.0.0"], + ["path-exists", "npm:4.0.0"] + ], + "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/find-up-npm-5.0.0-e03e9b796d-cd0b77415b.zip/node_modules/find-up/", + "packageDependencies": [ + ["find-up", "npm:5.0.0"], + ["locate-path", "npm:6.0.0"], + ["path-exists", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["flat", [ + ["npm:5.0.2", { + "packageLocation": "./.yarn/cache/flat-npm-5.0.2-12748102a5-549b3012e9.zip/node_modules/flat/", + "packageDependencies": [ + ["flat", "npm:5.0.2"] + ], + "linkType": "HARD", + }] + ]], ["flat-cache", [ ["npm:3.0.4", { "packageLocation": "./.yarn/cache/flat-cache-npm-3.0.4-ee77e5911e-72d86ccdf8.zip/node_modules/flat-cache/", @@ -2770,8 +3604,8 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./.yarn/cache/folder-hash-npm-4.0.1-20665a6bba-a48b73df55.zip/node_modules/folder-hash/", "packageDependencies": [ ["folder-hash", "npm:4.0.1"], - ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.1.1"], - ["graceful-fs", "npm:4.2.6"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], + ["graceful-fs", "npm:4.2.8"], ["minimatch", "npm:3.0.4"] ], "linkType": "HARD", @@ -2823,6 +3657,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["foreground-child", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/foreground-child-npm-2.0.0-80c976b61e-661c7adbc1.zip/node_modules/foreground-child/", + "packageDependencies": [ + ["foreground-child", "npm:2.0.0"], + ["cross-spawn", "npm:7.0.3"], + ["signal-exit", "npm:3.0.3"] + ], + "linkType": "HARD", + }] + ]], ["forever-agent", [ ["npm:0.6.1", { "packageLocation": "./.yarn/cache/forever-agent-npm-0.6.1-01dae53bf9-9cc0054dd4.zip/node_modules/forever-agent/", @@ -2839,7 +3684,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["form-data", "npm:2.3.3"], ["asynckit", "npm:0.4.0"], ["combined-stream", "npm:1.0.8"], - ["mime-types", "npm:2.1.27"] + ["mime-types", "npm:2.1.29"] + ], + "linkType": "HARD", + }] + ]], + ["fromentries", [ + ["npm:1.3.2", { + "packageLocation": "./.yarn/cache/fromentries-npm-1.3.2-f5392090b8-5cc722e4e3.zip/node_modules/fromentries/", + "packageDependencies": [ + ["fromentries", "npm:1.3.2"] ], "linkType": "HARD", }] @@ -2926,6 +3780,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["gensync", [ + ["npm:1.0.0-beta.2", { + "packageLocation": "./.yarn/cache/gensync-npm-1.0.0-beta.2-224666d72f-d523437689.zip/node_modules/gensync/", + "packageDependencies": [ + ["gensync", "npm:1.0.0-beta.2"] + ], + "linkType": "HARD", + }] + ]], ["get-caller-file", [ ["npm:2.0.5", { "packageLocation": "./.yarn/cache/get-caller-file-npm-2.0.5-80e8a86305-9dd9e1e259.zip/node_modules/get-caller-file/", @@ -2935,6 +3798,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["get-func-name", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/get-func-name-npm-2.0.0-afbf363765-c72d3857cd.zip/node_modules/get-func-name/", + "packageDependencies": [ + ["get-func-name", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], ["get-intrinsic", [ ["npm:1.1.1", { "packageLocation": "./.yarn/cache/get-intrinsic-npm-1.1.1-7e868745da-acf1506f25.zip/node_modules/get-intrinsic/", @@ -2956,6 +3828,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["get-package-type", [ + ["npm:0.1.0", { + "packageLocation": "./.yarn/cache/get-package-type-npm-0.1.0-6c70cdc8ab-a5b8beaf68.zip/node_modules/get-package-type/", + "packageDependencies": [ + ["get-package-type", "npm:0.1.0"] + ], + "linkType": "HARD", + }] + ]], ["get-stream", [ ["npm:6.0.1", { "packageLocation": "./.yarn/cache/get-stream-npm-6.0.1-83e51a4642-83de1fde5b.zip/node_modules/get-stream/", @@ -2985,10 +3866,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["glob", [ - ["npm:7.1.6", { - "packageLocation": "./.yarn/cache/glob-npm-7.1.6-1ce3a5189a-789977b524.zip/node_modules/glob/", + ["npm:7.1.7", { + "packageLocation": "./.yarn/cache/glob-npm-7.1.7-5698ad9c48-352f74f082.zip/node_modules/glob/", "packageDependencies": [ - ["glob", "npm:7.1.6"], + ["glob", "npm:7.1.7"], ["fs.realpath", "npm:1.0.0"], ["inflight", "npm:1.0.6"], ["inherits", "npm:2.0.4"], @@ -3000,14 +3881,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["glob-parent", [ - ["npm:5.1.1", { - "packageLocation": "./.yarn/cache/glob-parent-npm-5.1.1-57b061cd88-2af6e196fb.zip/node_modules/glob-parent/", - "packageDependencies": [ - ["glob-parent", "npm:5.1.1"], - ["is-glob", "npm:4.0.1"] - ], - "linkType": "HARD", - }], ["npm:5.1.2", { "packageLocation": "./.yarn/cache/glob-parent-npm-5.1.2-021ab32634-82fcaa4ce1.zip/node_modules/glob-parent/", "packageDependencies": [ @@ -3018,6 +3891,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["globals", [ + ["npm:11.12.0", { + "packageLocation": "./.yarn/cache/globals-npm-11.12.0-1fa7f41a6c-2563d3306a.zip/node_modules/globals/", + "packageDependencies": [ + ["globals", "npm:11.12.0"] + ], + "linkType": "HARD", + }], ["npm:12.4.0", { "packageLocation": "./.yarn/cache/globals-npm-12.4.0-02b5a6ba9c-0b9764bdea.zip/node_modules/globals/", "packageDependencies": [ @@ -3051,10 +3931,19 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["graceful-fs", [ - ["npm:4.2.6", { - "packageLocation": "./.yarn/cache/graceful-fs-npm-4.2.6-535b2234f1-84d39c7756.zip/node_modules/graceful-fs/", + ["npm:4.2.8", { + "packageLocation": "./.yarn/cache/graceful-fs-npm-4.2.8-37c16fc3d3-b07e032c0a.zip/node_modules/graceful-fs/", + "packageDependencies": [ + ["graceful-fs", "npm:4.2.8"] + ], + "linkType": "HARD", + }] + ]], + ["growl", [ + ["npm:1.10.5", { + "packageLocation": "./.yarn/cache/growl-npm-1.10.5-2d1da54198-e1dae8dde6.zip/node_modules/growl/", "packageDependencies": [ - ["graceful-fs", "npm:4.2.6"] + ["growl", "npm:1.10.5"] ], "linkType": "HARD", }] @@ -3073,7 +3962,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./.yarn/cache/har-validator-npm-5.1.5-bd9ac162f5-01b905cdaa.zip/node_modules/har-validator/", "packageDependencies": [ ["har-validator", "npm:5.1.5"], - ["ajv", "npm:6.12.4"], + ["ajv", "npm:6.12.6"], ["har-schema", "npm:2.0.0"] ], "linkType": "HARD", @@ -3123,6 +4012,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["hash-base", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/hash-base-npm-3.1.0-26fc5711dd-9f4b0d183d.zip/node_modules/hash-base/", + "packageDependencies": [ + ["hash-base", "npm:3.1.0"], + ["inherits", "npm:2.0.4"], + ["readable-stream", "npm:3.6.0"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], ["hash.js", [ ["npm:1.1.7", { "packageLocation": "./.yarn/cache/hash.js-npm-1.1.7-f1ad187358-fceb7fb87e.zip/node_modules/hash.js/", @@ -3134,6 +4035,26 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["hasha", [ + ["npm:5.2.2", { + "packageLocation": "./.yarn/cache/hasha-npm-5.2.2-d171116d12-50adf6a312.zip/node_modules/hasha/", + "packageDependencies": [ + ["hasha", "npm:5.2.2"], + ["is-stream", "npm:2.0.0"], + ["type-fest", "npm:0.8.1"] + ], + "linkType": "HARD", + }] + ]], + ["he", [ + ["npm:1.2.0", { + "packageLocation": "./.yarn/cache/he-npm-1.2.0-3b73a2ff07-212122003c.zip/node_modules/he/", + "packageDependencies": [ + ["he", "npm:1.2.0"] + ], + "linkType": "HARD", + }] + ]], ["hmac-drbg", [ ["npm:1.0.1", { "packageLocation": "./.yarn/cache/hmac-drbg-npm-1.0.1-3499ad31cd-729d5a55bf.zip/node_modules/hmac-drbg/", @@ -3146,6 +4067,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["html-escaper", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-a216ae96fa.zip/node_modules/html-escaper/", + "packageDependencies": [ + ["html-escaper", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], ["http-signature", [ ["npm:1.2.0", { "packageLocation": "./.yarn/cache/http-signature-npm-1.2.0-ee92426f34-d28227eed3.zip/node_modules/http-signature/", @@ -3158,6 +4088,21 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["human-crypto-keys", [ + ["npm:0.1.4", { + "packageLocation": "./.yarn/cache/human-crypto-keys-npm-0.1.4-352fe0a6a6-594ae91b0f.zip/node_modules/human-crypto-keys/", + "packageDependencies": [ + ["human-crypto-keys", "npm:0.1.4"], + ["bip39", "npm:3.0.4"], + ["crypto-key-composer", "npm:0.1.3"], + ["hash.js", "npm:1.1.7"], + ["hmac-drbg", "npm:1.0.1"], + ["node-forge", "npm:0.8.5"], + ["pify", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], ["human-signals", [ ["npm:2.1.0", { "packageLocation": "./.yarn/cache/human-signals-npm-2.1.0-f75815481d-70bfd94d27.zip/node_modules/human-signals/", @@ -3279,32 +4224,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["inquirer", [ - ["npm:7.3.3", { - "packageLocation": "./.yarn/cache/inquirer-npm-7.3.3-9e86782610-fa0cbd9594.zip/node_modules/inquirer/", - "packageDependencies": [ - ["inquirer", "npm:7.3.3"], - ["ansi-escapes", "npm:4.3.1"], - ["chalk", "npm:4.1.0"], - ["cli-cursor", "npm:3.1.0"], - ["cli-width", "npm:3.0.0"], - ["external-editor", "npm:3.1.0"], - ["figures", "npm:3.2.0"], - ["lodash", "npm:4.17.20"], - ["mute-stream", "npm:0.0.8"], - ["run-async", "npm:2.4.1"], - ["rxjs", "npm:6.6.7"], - ["string-width", "npm:4.2.0"], - ["strip-ansi", "npm:6.0.0"], - ["through", "npm:2.3.8"] - ], - "linkType": "HARD", - }], ["npm:8.1.5", { "packageLocation": "./.yarn/cache/inquirer-npm-8.1.5-6673ea3bbe-f697814321.zip/node_modules/inquirer/", "packageDependencies": [ ["inquirer", "npm:8.1.5"], - ["ansi-escapes", "npm:4.3.1"], - ["chalk", "npm:4.1.1"], + ["ansi-escapes", "npm:4.3.2"], + ["chalk", "npm:4.1.2"], ["cli-cursor", "npm:3.1.0"], ["cli-width", "npm:3.0.0"], ["external-editor", "npm:3.1.0"], @@ -3520,6 +4445,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["is-plain-obj", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/is-plain-obj-npm-2.1.0-8dffd7ae9c-2314302f91.zip/node_modules/is-plain-obj/", + "packageDependencies": [ + ["is-plain-obj", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], ["is-plain-object", [ ["npm:2.0.4", { "packageLocation": "./.yarn/cache/is-plain-object-npm-2.0.4-da3265d804-2f32322673.zip/node_modules/is-plain-object/", @@ -3585,6 +4519,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["isarray", [ + ["npm:0.0.1", { + "packageLocation": "./.yarn/cache/isarray-npm-0.0.1-92e37e0a70-daeda3c236.zip/node_modules/isarray/", + "packageDependencies": [ + ["isarray", "npm:0.0.1"] + ], + "linkType": "HARD", + }], ["npm:1.0.0", { "packageLocation": "./.yarn/cache/isarray-npm-1.0.0-db4f547720-b0ff31a290.zip/node_modules/isarray/", "packageDependencies": [ @@ -3602,6 +4543,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["iso-random-stream", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/iso-random-stream-npm-2.0.0-bbb11744dc-5dc5c37ecb.zip/node_modules/iso-random-stream/", + "packageDependencies": [ + ["iso-random-stream", "npm:2.0.0"], + ["events", "npm:3.3.0"], + ["readable-stream", "npm:3.6.0"] + ], + "linkType": "HARD", + }] + ]], ["isobject", [ ["npm:3.0.1", { "packageLocation": "./.yarn/cache/isobject-npm-3.0.1-8145901fd2-b537a9ccdd.zip/node_modules/isobject/", @@ -3620,33 +4572,85 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["jest-diff", [ - ["npm:25.5.0", { - "packageLocation": "./.yarn/cache/jest-diff-npm-25.5.0-dfbc320001-14a2634ecb.zip/node_modules/jest-diff/", + ["istanbul-lib-coverage", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/istanbul-lib-coverage-npm-3.0.0-654bb0146d-c8effc09ae.zip/node_modules/istanbul-lib-coverage/", "packageDependencies": [ - ["jest-diff", "npm:25.5.0"], - ["chalk", "npm:3.0.0"], - ["diff-sequences", "npm:25.2.6"], - ["jest-get-type", "npm:25.2.6"], - ["pretty-format", "npm:25.5.0"] + ["istanbul-lib-coverage", "npm:3.0.0"] ], "linkType": "HARD", }] ]], - ["jest-get-type", [ - ["npm:25.2.6", { - "packageLocation": "./.yarn/cache/jest-get-type-npm-25.2.6-7abd790493-6051fcb75c.zip/node_modules/jest-get-type/", + ["istanbul-lib-hook", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/istanbul-lib-hook-npm-3.0.0-be73f95173-7b5fa35741.zip/node_modules/istanbul-lib-hook/", "packageDependencies": [ - ["jest-get-type", "npm:25.2.6"] + ["istanbul-lib-hook", "npm:3.0.0"], + ["append-transform", "npm:2.0.0"] ], "linkType": "HARD", }] ]], - ["js-base64", [ - ["npm:3.6.0", { - "packageLocation": "./.yarn/cache/js-base64-npm-3.6.0-987f8cbc88-6959097159.zip/node_modules/js-base64/", + ["istanbul-lib-instrument", [ + ["npm:4.0.3", { + "packageLocation": "./.yarn/cache/istanbul-lib-instrument-npm-4.0.3-4d4c2263f8-478e43e75d.zip/node_modules/istanbul-lib-instrument/", + "packageDependencies": [ + ["istanbul-lib-instrument", "npm:4.0.3"], + ["@babel/core", "npm:7.15.5"], + ["@istanbuljs/schema", "npm:0.1.3"], + ["istanbul-lib-coverage", "npm:3.0.0"], + ["semver", "npm:6.3.0"] + ], + "linkType": "HARD", + }] + ]], + ["istanbul-lib-processinfo", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/istanbul-lib-processinfo-npm-2.0.2-74916fa6cb-ab1e7cb67f.zip/node_modules/istanbul-lib-processinfo/", + "packageDependencies": [ + ["istanbul-lib-processinfo", "npm:2.0.2"], + ["archy", "npm:1.0.0"], + ["cross-spawn", "npm:7.0.3"], + ["istanbul-lib-coverage", "npm:3.0.0"], + ["make-dir", "npm:3.1.0"], + ["p-map", "npm:3.0.0"], + ["rimraf", "npm:3.0.2"], + ["uuid", "npm:3.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["istanbul-lib-report", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/istanbul-lib-report-npm-3.0.0-660f97340a-aada59dfce.zip/node_modules/istanbul-lib-report/", + "packageDependencies": [ + ["istanbul-lib-report", "npm:3.0.0"], + ["istanbul-lib-coverage", "npm:3.0.0"], + ["make-dir", "npm:3.1.0"], + ["supports-color", "npm:7.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["istanbul-lib-source-maps", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/istanbul-lib-source-maps-npm-4.0.0-def3895674-018b5feeb4.zip/node_modules/istanbul-lib-source-maps/", + "packageDependencies": [ + ["istanbul-lib-source-maps", "npm:4.0.0"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], + ["istanbul-lib-coverage", "npm:3.0.0"], + ["source-map", "npm:0.6.1"] + ], + "linkType": "HARD", + }] + ]], + ["istanbul-reports", [ + ["npm:3.0.2", { + "packageLocation": "./.yarn/cache/istanbul-reports-npm-3.0.2-6ccd67e17e-d4ed416e13.zip/node_modules/istanbul-reports/", "packageDependencies": [ - ["js-base64", "npm:3.6.0"] + ["istanbul-reports", "npm:3.0.2"], + ["html-escaper", "npm:2.0.2"], + ["istanbul-lib-report", "npm:3.0.0"] ], "linkType": "HARD", }] @@ -3664,9 +4668,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["npm:3.14.0", { "packageLocation": "./.yarn/cache/js-yaml-npm-3.14.0-7ecf74b3d2-2eb95464e5.zip/node_modules/js-yaml/", "packageDependencies": [ - ["js-yaml", "npm:3.14.0"], - ["argparse", "npm:1.0.10"], - ["esprima", "npm:4.0.1"] + ["js-yaml", "npm:3.14.0"], + ["argparse", "npm:1.0.10"], + ["esprima", "npm:4.0.1"] + ], + "linkType": "HARD", + }], + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/js-yaml-npm-4.1.0-3606f32312-8973cf4296.zip/node_modules/js-yaml/", + "packageDependencies": [ + ["js-yaml", "npm:4.1.0"], + ["argparse", "npm:2.0.1"] ], "linkType": "HARD", }] @@ -3680,6 +4692,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["jsesc", [ + ["npm:2.5.2", { + "packageLocation": "./.yarn/cache/jsesc-npm-2.5.2-c5acb78804-ca91ec33d7.zip/node_modules/jsesc/", + "packageDependencies": [ + ["jsesc", "npm:2.5.2"] + ], + "linkType": "HARD", + }] + ]], ["json-beautify", [ ["npm:1.1.1", { "packageLocation": "./.yarn/cache/json-beautify-npm-1.1.1-b1e61370d7-76294ff33b.zip/node_modules/json-beautify/", @@ -3741,6 +4762,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["json5", [ + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/json5-npm-2.2.0-da49dc7cb5-07b1f90c28.zip/node_modules/json5/", + "packageDependencies": [ + ["json5", "npm:2.2.0"], + ["minimist", "npm:1.2.5"] + ], + "linkType": "HARD", + }] + ]], ["jsprim", [ ["npm:1.4.1", { "packageLocation": "./.yarn/cache/jsprim-npm-1.4.1-948d2c9ec3-ee0177b7ef.zip/node_modules/jsprim/", @@ -3754,6 +4785,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["just-extend", [ + ["npm:4.2.1", { + "packageLocation": "./.yarn/cache/just-extend-npm-4.2.1-ccc4201277-4fb49b328a.zip/node_modules/just-extend/", + "packageDependencies": [ + ["just-extend", "npm:4.2.1"] + ], + "linkType": "HARD", + }] + ]], ["jwk-to-pem", [ ["npm:2.0.4", { "packageLocation": "./.yarn/cache/jwk-to-pem-npm-2.0.4-e0a071d74c-eca90c18c8.zip/node_modules/jwk-to-pem/", @@ -3766,6 +4806,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["keypair", [ + ["npm:1.0.3", { + "packageLocation": "./.yarn/cache/keypair-npm-1.0.3-f098145e16-1be4b14da7.zip/node_modules/keypair/", + "packageDependencies": [ + ["keypair", "npm:1.0.3"] + ], + "linkType": "HARD", + }] + ]], ["kind-of", [ ["npm:3.2.2", { "packageLocation": "./.yarn/cache/kind-of-npm-3.2.2-7deaffa5f9-e8a1835c4b.zip/node_modules/kind-of/", @@ -3829,6 +4878,26 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["libp2p-crypto", [ + ["npm:0.19.7", { + "packageLocation": "./.yarn/cache/libp2p-crypto-npm-0.19.7-6963e32764-f4964e1214.zip/node_modules/libp2p-crypto/", + "packageDependencies": [ + ["libp2p-crypto", "npm:0.19.7"], + ["err-code", "npm:3.0.1"], + ["is-typedarray", "npm:1.0.0"], + ["iso-random-stream", "npm:2.0.0"], + ["keypair", "npm:1.0.3"], + ["multiformats", "npm:9.4.7"], + ["node-forge", "npm:0.10.0"], + ["pem-jwk", "npm:2.0.0"], + ["protobufjs", "npm:6.11.2"], + ["secp256k1", "npm:4.0.2"], + ["uint8arrays", "npm:3.0.0"], + ["ursa-optional", "npm:0.10.2"] + ], + "linkType": "HARD", + }] + ]], ["lines-and-columns", [ ["npm:1.1.6", { "packageLocation": "./.yarn/cache/lines-and-columns-npm-1.1.6-23e74fab67-798b80ed7a.zip/node_modules/lines-and-columns/", @@ -3843,11 +4912,11 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./.yarn/cache/lint-staged-npm-11.0.0-4b0eea214d-e5d9c38f46.zip/node_modules/lint-staged/", "packageDependencies": [ ["lint-staged", "npm:11.0.0"], - ["chalk", "npm:4.1.1"], + ["chalk", "npm:4.1.2"], ["cli-truncate", "npm:2.1.0"], ["commander", "npm:7.2.0"], ["cosmiconfig", "npm:7.0.0"], - ["debug", "virtual:899360d658a6566626efa634457187645bf30bfc405d8695b0e1ccc1653296afe8e5e5b09977bdee91d5fc1e3e0d54a299eb775f180e3fc3ef462478be69f50f#npm:4.3.2"], + ["debug", "virtual:20665a6bba701f8eef2f279f16262723a301f7a3b45c6d90e539bd7673dfafcd63e8c306b5c9432dcde6b0c5c6d351279fd53de433fc8f77187bc3053cb113b3#npm:4.3.2"], ["dedent", "npm:0.7.0"], ["enquirer", "npm:2.3.6"], ["execa", "npm:5.0.0"], @@ -3876,7 +4945,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["listr2", "virtual:4b0eea214d83e6e05cbe9674d69cb7f553c930fbd95cf27b023ecb1d9ff23bce32ab3afc880c65a5b91bd3abeccea112435944b80702879b8337532cf8f5b466#npm:3.9.0"], ["@types/enquirer", null], ["cli-truncate", "npm:2.1.0"], - ["colorette", "npm:1.2.2"], + ["colorette", "npm:1.4.0"], ["enquirer", "npm:2.3.6"], ["log-update", "npm:4.0.0"], ["p-map", "npm:4.0.0"], @@ -3891,14 +4960,25 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["lodash", [ - ["npm:4.17.20", { - "packageLocation": "./.yarn/cache/lodash-npm-4.17.20-c0db62021c-c62101d250.zip/node_modules/lodash/", + ["locate-path", [ + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/locate-path-npm-5.0.0-46580c43e4-c58f49d45c.zip/node_modules/locate-path/", "packageDependencies": [ - ["lodash", "npm:4.17.20"] + ["locate-path", "npm:5.0.0"], + ["p-locate", "npm:4.1.0"] ], "linkType": "HARD", }], + ["npm:6.0.0", { + "packageLocation": "./.yarn/cache/locate-path-npm-6.0.0-06a1e4c528-4c37963815.zip/node_modules/locate-path/", + "packageDependencies": [ + ["locate-path", "npm:6.0.0"], + ["p-locate", "npm:5.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash", [ ["npm:4.17.21", { "packageLocation": "./.yarn/cache/lodash-npm-4.17.21-6382451519-4983720b9a.zip/node_modules/lodash/", "packageDependencies": [ @@ -3925,6 +5005,33 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["lodash.flattendeep", [ + ["npm:4.4.0", { + "packageLocation": "./.yarn/cache/lodash.flattendeep-npm-4.4.0-26b2b4cbd7-941b709524.zip/node_modules/lodash.flattendeep/", + "packageDependencies": [ + ["lodash.flattendeep", "npm:4.4.0"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.get", [ + ["npm:4.4.2", { + "packageLocation": "./.yarn/cache/lodash.get-npm-4.4.2-7bda64ed87-447e575e3c.zip/node_modules/lodash.get/", + "packageDependencies": [ + ["lodash.get", "npm:4.4.2"] + ], + "linkType": "HARD", + }] + ]], + ["lodash.isplainobject", [ + ["npm:4.0.6", { + "packageLocation": "./.yarn/cache/lodash.isplainobject-npm-4.0.6-d73937742f-72a114b610.zip/node_modules/lodash.isplainobject/", + "packageDependencies": [ + ["lodash.isplainobject", "npm:4.0.6"] + ], + "linkType": "HARD", + }] + ]], ["lodash.truncate", [ ["npm:4.4.2", { "packageLocation": "./.yarn/cache/lodash.truncate-npm-4.4.2-bc50fe1663-b1b0d7d993.zip/node_modules/lodash.truncate/", @@ -3950,7 +5057,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./.yarn/cache/log-symbols-npm-4.1.0-0a13492d8b-57be4aeb6a.zip/node_modules/log-symbols/", "packageDependencies": [ ["log-symbols", "npm:4.1.0"], - ["chalk", "npm:4.1.0"], + ["chalk", "npm:4.1.2"], ["is-unicode-supported", "npm:0.1.0"] ], "linkType": "HARD", @@ -3994,6 +5101,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["long", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/long-npm-4.0.0-ecd96a31ed-9cebc1ee8b.zip/node_modules/long/", + "packageDependencies": [ + ["long", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], ["lru-cache", [ ["npm:6.0.0", { "packageLocation": "./.yarn/cache/lru-cache-npm-6.0.0-b4c8668fe1-b8b78353d2.zip/node_modules/lru-cache/", @@ -4014,6 +5130,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["make-dir", [ + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/make-dir-npm-3.1.0-d1d7505142-54b6f186c2.zip/node_modules/make-dir/", + "packageDependencies": [ + ["make-dir", "npm:3.1.0"], + ["semver", "npm:6.3.0"] + ], + "linkType": "HARD", + }] + ]], ["make-error", [ ["npm:1.3.6", { "packageLocation": "./.yarn/cache/make-error-npm-1.3.6-ccb85d9458-2c780bab84.zip/node_modules/make-error/", @@ -4033,6 +5159,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["matcher", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/matcher-npm-2.1.0-5ab18ff7b6-dc05a170ef.zip/node_modules/matcher/", + "packageDependencies": [ + ["matcher", "npm:2.1.0"], + ["escape-string-regexp", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], ["md5-file", [ ["npm:5.0.0", { "packageLocation": "./.yarn/cache/md5-file-npm-5.0.0-e5f59abc62-7d6e43bd53.zip/node_modules/md5-file/", @@ -4042,6 +5178,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["md5.js", [ + ["npm:1.3.5", { + "packageLocation": "./.yarn/cache/md5.js-npm-1.3.5-130901125a-ca0b260ea2.zip/node_modules/md5.js/", + "packageDependencies": [ + ["md5.js", "npm:1.3.5"], + ["hash-base", "npm:3.1.0"], + ["inherits", "npm:2.0.4"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], ["memoizee", [ ["npm:0.3.10", { "packageLocation": "./.yarn/cache/memoizee-npm-0.3.10-c4e165e81d-86be841655.zip/node_modules/memoizee/", @@ -4077,15 +5225,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["micromatch", [ - ["npm:4.0.2", { - "packageLocation": "./.yarn/cache/micromatch-npm-4.0.2-f059c00e51-0cb0e11d64.zip/node_modules/micromatch/", - "packageDependencies": [ - ["micromatch", "npm:4.0.2"], - ["braces", "npm:3.0.2"], - ["picomatch", "npm:2.2.2"] - ], - "linkType": "HARD", - }], ["npm:4.0.4", { "packageLocation": "./.yarn/cache/micromatch-npm-4.0.4-9fdcbb7a0e-bc522ad93c.zip/node_modules/micromatch/", "packageDependencies": [ @@ -4097,13 +5236,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["mime-db", [ - ["npm:1.44.0", { - "packageLocation": "./.yarn/cache/mime-db-npm-1.44.0-d6ab7b4e20-b4e3b21414.zip/node_modules/mime-db/", - "packageDependencies": [ - ["mime-db", "npm:1.44.0"] - ], - "linkType": "HARD", - }], ["npm:1.46.0", { "packageLocation": "./.yarn/cache/mime-db-npm-1.46.0-46f8800b47-4e137ac502.zip/node_modules/mime-db/", "packageDependencies": [ @@ -4113,14 +5245,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["mime-types", [ - ["npm:2.1.27", { - "packageLocation": "./.yarn/cache/mime-types-npm-2.1.27-bbebca8e17-51fe2f2c08.zip/node_modules/mime-types/", - "packageDependencies": [ - ["mime-types", "npm:2.1.27"], - ["mime-db", "npm:1.44.0"] - ], - "linkType": "HARD", - }], ["npm:2.1.29", { "packageLocation": "./.yarn/cache/mime-types-npm-2.1.29-18d18d60ed-744d72b2a2.zip/node_modules/mime-types/", "packageDependencies": [ @@ -4235,6 +5359,40 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["mocha", [ + ["npm:9.1.1", { + "packageLocation": "./.yarn/cache/mocha-npm-9.1.1-48f6018912-c316e4e396.zip/node_modules/mocha/", + "packageDependencies": [ + ["mocha", "npm:9.1.1"], + ["@ungap/promise-all-settled", "npm:1.1.2"], + ["ansi-colors", "npm:4.1.1"], + ["browser-stdout", "npm:1.3.1"], + ["chokidar", "npm:3.5.2"], + ["debug", "virtual:48f601891217fc93366476dd2adefe8eec19b6ef0d837d3de7622a1230dd3d0d2040b2e36145558b91d7a18aa61d8905063fb8a24c603be355eeca7ba6fface5#npm:4.3.1"], + ["diff", "npm:5.0.0"], + ["escape-string-regexp", "npm:4.0.0"], + ["find-up", "npm:5.0.0"], + ["glob", "npm:7.1.7"], + ["growl", "npm:1.10.5"], + ["he", "npm:1.2.0"], + ["js-yaml", "npm:4.1.0"], + ["log-symbols", "npm:4.1.0"], + ["minimatch", "npm:3.0.4"], + ["ms", "npm:2.1.3"], + ["nanoid", "npm:3.1.23"], + ["serialize-javascript", "npm:6.0.0"], + ["strip-json-comments", "npm:3.1.1"], + ["supports-color", "npm:8.1.1"], + ["which", "npm:2.0.2"], + ["wide-align", "npm:1.1.3"], + ["workerpool", "npm:6.1.5"], + ["yargs", "npm:16.2.0"], + ["yargs-parser", "npm:20.2.4"], + ["yargs-unparser", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], ["ms", [ ["npm:2.0.0", { "packageLocation": "./.yarn/cache/ms-npm-2.0.0-9e1101a471-1a230340cc.zip/node_modules/ms/", @@ -4249,6 +5407,22 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["ms", "npm:2.1.2"] ], "linkType": "HARD", + }], + ["npm:2.1.3", { + "packageLocation": "./.yarn/cache/ms-npm-2.1.3-81ff3cfac1-6e721e648a.zip/node_modules/ms/", + "packageDependencies": [ + ["ms", "npm:2.1.3"] + ], + "linkType": "HARD", + }] + ]], + ["multiformats", [ + ["npm:9.4.7", { + "packageLocation": "./.yarn/cache/multiformats-npm-9.4.7-4a414eb83b-370ed783ca.zip/node_modules/multiformats/", + "packageDependencies": [ + ["multiformats", "npm:9.4.7"] + ], + "linkType": "HARD", }] ]], ["mute-stream", [ @@ -4267,6 +5441,25 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["nan", [ + ["npm:2.15.0", { + "packageLocation": "./.yarn/unplugged/nan-npm-2.15.0-505c98ef4d/node_modules/nan/", + "packageDependencies": [ + ["nan", "npm:2.15.0"], + ["node-gyp", "npm:7.1.2"] + ], + "linkType": "HARD", + }] + ]], + ["nanoid", [ + ["npm:3.1.23", { + "packageLocation": "./.yarn/cache/nanoid-npm-3.1.23-5f6acb650d-e6dea1da5a.zip/node_modules/nanoid/", + "packageDependencies": [ + ["nanoid", "npm:3.1.23"] + ], + "linkType": "HARD", + }] + ]], ["napi-build-utils", [ ["npm:1.0.2", { "packageLocation": "./.yarn/cache/napi-build-utils-npm-1.0.2-892e4bba56-e4dfbec94d.zip/node_modules/napi-build-utils/", @@ -4320,6 +5513,32 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["nise", [ + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/nise-npm-4.1.0-bca1f3b90c-87b37725d9.zip/node_modules/nise/", + "packageDependencies": [ + ["nise", "npm:4.1.0"], + ["@sinonjs/commons", "npm:1.8.3"], + ["@sinonjs/fake-timers", "npm:6.0.1"], + ["@sinonjs/text-encoding", "npm:0.7.1"], + ["just-extend", "npm:4.2.1"], + ["path-to-regexp", "npm:1.8.0"] + ], + "linkType": "HARD", + }], + ["npm:5.1.0", { + "packageLocation": "./.yarn/cache/nise-npm-5.1.0-8fc543b66e-09c3ad4165.zip/node_modules/nise/", + "packageDependencies": [ + ["nise", "npm:5.1.0"], + ["@sinonjs/commons", "npm:1.8.3"], + ["@sinonjs/fake-timers", "npm:7.1.2"], + ["@sinonjs/text-encoding", "npm:0.7.1"], + ["just-extend", "npm:4.2.1"], + ["path-to-regexp", "npm:1.8.0"] + ], + "linkType": "HARD", + }] + ]], ["node-abi", [ ["npm:2.30.0", { "packageLocation": "./.yarn/cache/node-abi-npm-2.30.0-d6aca91d32-d048604a9f.zip/node_modules/node-abi/", @@ -4330,11 +5549,37 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["node-addon-api", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/unplugged/node-addon-api-npm-2.0.2-8c2c1e9782/node_modules/node-addon-api/", + "packageDependencies": [ + ["node-addon-api", "npm:2.0.2"], + ["node-gyp", "npm:7.1.2"] + ], + "linkType": "HARD", + }] + ]], ["node-fetch", [ - ["npm:2.6.1", { - "packageLocation": "./.yarn/cache/node-fetch-npm-2.6.1-46c670dbc1-cbb171635e.zip/node_modules/node-fetch/", + ["npm:2.6.2", { + "packageLocation": "./.yarn/cache/node-fetch-npm-2.6.2-2820154539-f48d84c89c.zip/node_modules/node-fetch/", + "packageDependencies": [ + ["node-fetch", "npm:2.6.2"] + ], + "linkType": "HARD", + }] + ]], + ["node-forge", [ + ["npm:0.10.0", { + "packageLocation": "./.yarn/cache/node-forge-npm-0.10.0-605ba7b28b-c7a729933a.zip/node_modules/node-forge/", + "packageDependencies": [ + ["node-forge", "npm:0.10.0"] + ], + "linkType": "HARD", + }], + ["npm:0.8.5", { + "packageLocation": "./.yarn/cache/node-forge-npm-0.8.5-f18eb3b9a2-4bfa3755c2.zip/node_modules/node-forge/", "packageDependencies": [ - ["node-fetch", "npm:2.6.1"] + ["node-forge", "npm:0.8.5"] ], "linkType": "HARD", }] @@ -4345,19 +5590,47 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageDependencies": [ ["node-gyp", "npm:7.1.2"], ["env-paths", "npm:2.2.1"], - ["glob", "npm:7.1.6"], - ["graceful-fs", "npm:4.2.6"], + ["glob", "npm:7.1.7"], + ["graceful-fs", "npm:4.2.8"], ["nopt", "npm:5.0.0"], ["npmlog", "npm:4.1.2"], ["request", "npm:2.88.2"], ["rimraf", "npm:3.0.2"], - ["semver", "npm:7.3.2"], + ["semver", "npm:7.3.5"], ["tar", "npm:6.1.0"], ["which", "npm:2.0.2"] ], "linkType": "HARD", }] ]], + ["node-gyp-build", [ + ["npm:4.2.3", { + "packageLocation": "./.yarn/cache/node-gyp-build-npm-4.2.3-051c80c95f-8512c25498.zip/node_modules/node-gyp-build/", + "packageDependencies": [ + ["node-gyp-build", "npm:4.2.3"] + ], + "linkType": "HARD", + }] + ]], + ["node-preload", [ + ["npm:0.2.1", { + "packageLocation": "./.yarn/cache/node-preload-npm-0.2.1-5b6aef1c8e-72d4cccb33.zip/node_modules/node-preload/", + "packageDependencies": [ + ["node-preload", "npm:0.2.1"], + ["process-on-spawn", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["node-releases", [ + ["npm:1.1.75", { + "packageLocation": "./.yarn/cache/node-releases-npm-1.1.75-3d5ac48148-0ea9d84f2c.zip/node_modules/node-releases/", + "packageDependencies": [ + ["node-releases", "npm:1.1.75"] + ], + "linkType": "HARD", + }] + ]], ["noop-logger", [ ["npm:0.1.1", { "packageLocation": "./.yarn/cache/noop-logger-npm-0.1.1-c88441172d-353d31cd08.zip/node_modules/noop-logger/", @@ -4418,6 +5691,42 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["nyc", [ + ["npm:15.1.0", { + "packageLocation": "./.yarn/cache/nyc-npm-15.1.0-f134b19668-f7f71b2b7f.zip/node_modules/nyc/", + "packageDependencies": [ + ["nyc", "npm:15.1.0"], + ["@istanbuljs/load-nyc-config", "npm:1.1.0"], + ["@istanbuljs/schema", "npm:0.1.3"], + ["caching-transform", "npm:4.0.0"], + ["convert-source-map", "npm:1.7.0"], + ["decamelize", "npm:1.2.0"], + ["find-cache-dir", "npm:3.3.2"], + ["find-up", "npm:4.1.0"], + ["foreground-child", "npm:2.0.0"], + ["get-package-type", "npm:0.1.0"], + ["glob", "npm:7.1.7"], + ["istanbul-lib-coverage", "npm:3.0.0"], + ["istanbul-lib-hook", "npm:3.0.0"], + ["istanbul-lib-instrument", "npm:4.0.3"], + ["istanbul-lib-processinfo", "npm:2.0.2"], + ["istanbul-lib-report", "npm:3.0.0"], + ["istanbul-lib-source-maps", "npm:4.0.0"], + ["istanbul-reports", "npm:3.0.2"], + ["make-dir", "npm:3.1.0"], + ["node-preload", "npm:0.2.1"], + ["p-map", "npm:3.0.0"], + ["process-on-spawn", "npm:1.0.0"], + ["resolve-from", "npm:5.0.0"], + ["rimraf", "npm:3.0.2"], + ["signal-exit", "npm:3.0.3"], + ["spawn-wrap", "npm:2.0.0"], + ["test-exclude", "npm:6.0.0"], + ["yargs", "npm:15.4.1"] + ], + "linkType": "HARD", + }] + ]], ["oauth-sign", [ ["npm:0.9.0", { "packageLocation": "./.yarn/cache/oauth-sign-npm-0.9.0-7aa9422221-af1ab60297.zip/node_modules/oauth-sign/", @@ -4499,7 +5808,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageDependencies": [ ["ora", "npm:5.4.1"], ["bl", "npm:4.1.0"], - ["chalk", "npm:4.1.0"], + ["chalk", "npm:4.1.2"], ["cli-cursor", "npm:3.1.0"], ["cli-spinners", "npm:2.6.0"], ["is-interactive", "npm:1.0.0"], @@ -4520,7 +5829,51 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["p-limit", [ + ["npm:2.3.0", { + "packageLocation": "./.yarn/cache/p-limit-npm-2.3.0-94a0310039-5f20492a25.zip/node_modules/p-limit/", + "packageDependencies": [ + ["p-limit", "npm:2.3.0"], + ["p-try", "npm:2.2.0"] + ], + "linkType": "HARD", + }], + ["npm:3.1.0", { + "packageLocation": "./.yarn/cache/p-limit-npm-3.1.0-05d2ede37f-5301db6a34.zip/node_modules/p-limit/", + "packageDependencies": [ + ["p-limit", "npm:3.1.0"], + ["yocto-queue", "npm:0.1.0"] + ], + "linkType": "HARD", + }] + ]], + ["p-locate", [ + ["npm:4.1.0", { + "packageLocation": "./.yarn/cache/p-locate-npm-4.1.0-eec6872537-57f9abef0b.zip/node_modules/p-locate/", + "packageDependencies": [ + ["p-locate", "npm:4.1.0"], + ["p-limit", "npm:2.3.0"] + ], + "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/p-locate-npm-5.0.0-92cc7c7a3e-a233d775c8.zip/node_modules/p-locate/", + "packageDependencies": [ + ["p-locate", "npm:5.0.0"], + ["p-limit", "npm:3.1.0"] + ], + "linkType": "HARD", + }] + ]], ["p-map", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/p-map-npm-3.0.0-e4f17c4167-f7ce4709f4.zip/node_modules/p-map/", + "packageDependencies": [ + ["p-map", "npm:3.0.0"], + ["aggregate-error", "npm:3.1.0"] + ], + "linkType": "HARD", + }], ["npm:4.0.0", { "packageLocation": "./.yarn/cache/p-map-npm-4.0.0-4677ae07c7-d51e630d72.zip/node_modules/p-map/", "packageDependencies": [ @@ -4530,6 +5883,28 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["p-try", [ + ["npm:2.2.0", { + "packageLocation": "./.yarn/cache/p-try-npm-2.2.0-e0390dbaf8-20983f3765.zip/node_modules/p-try/", + "packageDependencies": [ + ["p-try", "npm:2.2.0"] + ], + "linkType": "HARD", + }] + ]], + ["package-hash", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/package-hash-npm-4.0.0-1e83d2429d-f64c22b5d6.zip/node_modules/package-hash/", + "packageDependencies": [ + ["package-hash", "npm:4.0.0"], + ["graceful-fs", "npm:4.2.8"], + ["hasha", "npm:5.2.2"], + ["lodash.flattendeep", "npm:4.4.0"], + ["release-zalgo", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], ["parent-module", [ ["npm:1.0.1", { "packageLocation": "./.yarn/cache/parent-module-npm-1.0.1-1fae11b095-58714b9699.zip/node_modules/parent-module/", @@ -4545,7 +5920,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./.yarn/cache/parse-json-npm-5.2.0-00a63b1199-65b1e494a5.zip/node_modules/parse-json/", "packageDependencies": [ ["parse-json", "npm:5.2.0"], - ["@babel/code-frame", "npm:7.10.4"], + ["@babel/code-frame", "npm:7.14.5"], ["error-ex", "npm:1.3.2"], ["json-parse-even-better-errors", "npm:2.3.1"], ["lines-and-columns", "npm:1.1.6"] @@ -4553,6 +5928,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["path-exists", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/path-exists-npm-4.0.0-e9e4f63eb0-6ab15000c5.zip/node_modules/path-exists/", + "packageDependencies": [ + ["path-exists", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], ["path-is-absolute", [ ["npm:1.0.1", { "packageLocation": "./.yarn/cache/path-is-absolute-npm-1.0.1-31bc695ffd-907e1e3e6a.zip/node_modules/path-is-absolute/", @@ -4571,11 +5955,54 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["path-to-regexp", [ + ["npm:1.8.0", { + "packageLocation": "./.yarn/cache/path-to-regexp-npm-1.8.0-a1904f5c44-4c0d9aaf3f.zip/node_modules/path-to-regexp/", + "packageDependencies": [ + ["path-to-regexp", "npm:1.8.0"], + ["isarray", "npm:0.0.1"] + ], + "linkType": "HARD", + }] + ]], ["path-type", [ ["npm:4.0.0", { "packageLocation": "./.yarn/cache/path-type-npm-4.0.0-10d47fc86a-ef5835f2eb.zip/node_modules/path-type/", "packageDependencies": [ - ["path-type", "npm:4.0.0"] + ["path-type", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], + ["pathval", [ + ["npm:1.1.1", { + "packageLocation": "./.yarn/cache/pathval-npm-1.1.1-ce0311d7e0-81cd01d46c.zip/node_modules/pathval/", + "packageDependencies": [ + ["pathval", "npm:1.1.1"] + ], + "linkType": "HARD", + }] + ]], + ["pbkdf2", [ + ["npm:3.1.2", { + "packageLocation": "./.yarn/cache/pbkdf2-npm-3.1.2-d67bbb584f-12ac46c71d.zip/node_modules/pbkdf2/", + "packageDependencies": [ + ["pbkdf2", "npm:3.1.2"], + ["create-hash", "npm:1.2.0"], + ["create-hmac", "npm:1.1.7"], + ["ripemd160", "npm:2.0.2"], + ["safe-buffer", "npm:5.2.1"], + ["sha.js", "npm:2.4.11"] + ], + "linkType": "HARD", + }] + ]], + ["pem-jwk", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/pem-jwk-npm-2.0.0-4f6af502ec-c3d2248e11.zip/node_modules/pem-jwk/", + "packageDependencies": [ + ["pem-jwk", "npm:2.0.0"], + ["asn1.js", "npm:5.4.1"] ], "linkType": "HARD", }] @@ -4590,13 +6017,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["picomatch", [ - ["npm:2.2.2", { - "packageLocation": "./.yarn/cache/picomatch-npm-2.2.2-1ce736a913-20fa75e0a5.zip/node_modules/picomatch/", - "packageDependencies": [ - ["picomatch", "npm:2.2.2"] - ], - "linkType": "HARD", - }], ["npm:2.3.0", { "packageLocation": "./.yarn/cache/picomatch-npm-2.3.0-5e60e6c82d-80113a0fb7.zip/node_modules/picomatch/", "packageDependencies": [ @@ -4605,6 +6025,25 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["pify", [ + ["npm:4.0.1", { + "packageLocation": "./.yarn/cache/pify-npm-4.0.1-062756097b-786486a8c9.zip/node_modules/pify/", + "packageDependencies": [ + ["pify", "npm:4.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["pkg-dir", [ + ["npm:4.2.0", { + "packageLocation": "./.yarn/cache/pkg-dir-npm-4.2.0-2b5d0a8d32-1956ebf3cf.zip/node_modules/pkg-dir/", + "packageDependencies": [ + ["pkg-dir", "npm:4.2.0"], + ["find-up", "npm:4.1.0"] + ], + "linkType": "HARD", + }] + ]], ["please-upgrade-node", [ ["npm:3.2.0", { "packageLocation": "./.yarn/cache/please-upgrade-node-npm-3.2.0-3f653350ed-34cf86f6d5.zip/node_modules/please-upgrade-node/", @@ -4675,19 +6114,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["pretty-format", [ - ["npm:25.5.0", { - "packageLocation": "./.yarn/cache/pretty-format-npm-25.5.0-9def2180a5-f7cc631d51.zip/node_modules/pretty-format/", - "packageDependencies": [ - ["pretty-format", "npm:25.5.0"], - ["@jest/types", "npm:25.5.0"], - ["ansi-regex", "npm:5.0.0"], - ["ansi-styles", "npm:4.2.1"], - ["react-is", "npm:16.13.1"] - ], - "linkType": "HARD", - }] - ]], ["prng-well1024a", [ ["npm:1.0.1", { "packageLocation": "./.yarn/cache/prng-well1024a-npm-1.0.1-bb37056cf9-d618b77f55.zip/node_modules/prng-well1024a/", @@ -4706,6 +6132,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["process-on-spawn", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/process-on-spawn-npm-1.0.0-676960b4dd-91b18f68bb.zip/node_modules/process-on-spawn/", + "packageDependencies": [ + ["process-on-spawn", "npm:1.0.0"], + ["fromentries", "npm:1.3.2"] + ], + "linkType": "HARD", + }] + ]], ["progress", [ ["npm:2.0.3", { "packageLocation": "./.yarn/cache/progress-npm-2.0.3-d1f87e2ac6-c46ef5a1de.zip/node_modules/progress/", @@ -4816,6 +6252,28 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["protobufjs", [ + ["npm:6.11.2", { + "packageLocation": "./.yarn/unplugged/protobufjs-npm-6.11.2-9b422ce98e/node_modules/protobufjs/", + "packageDependencies": [ + ["protobufjs", "npm:6.11.2"], + ["@protobufjs/aspromise", "npm:1.1.2"], + ["@protobufjs/base64", "npm:1.1.2"], + ["@protobufjs/codegen", "npm:2.0.4"], + ["@protobufjs/eventemitter", "npm:1.1.0"], + ["@protobufjs/fetch", "npm:1.1.0"], + ["@protobufjs/float", "npm:1.0.2"], + ["@protobufjs/inquire", "npm:1.1.0"], + ["@protobufjs/path", "npm:1.1.2"], + ["@protobufjs/pool", "npm:1.1.0"], + ["@protobufjs/utf8", "npm:1.1.0"], + ["@types/long", "npm:4.0.1"], + ["@types/node", "npm:16.7.13"], + ["long", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], ["psl", [ ["npm:1.8.0", { "packageLocation": "./.yarn/cache/psl-npm-1.8.0-226099d70e-92d47c6257.zip/node_modules/psl/", @@ -4875,6 +6333,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["randombytes", [ + ["npm:2.1.0", { + "packageLocation": "./.yarn/cache/randombytes-npm-2.1.0-e3da76bccf-ede2693af0.zip/node_modules/randombytes/", + "packageDependencies": [ + ["randombytes", "npm:2.1.0"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], ["randy", [ ["npm:1.5.1", { "packageLocation": "./.yarn/cache/randy-npm-1.5.1-bc3d269266-1e0201ac1c.zip/node_modules/randy/", @@ -4898,15 +6366,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["react-is", [ - ["npm:16.13.1", { - "packageLocation": "./.yarn/cache/react-is-npm-16.13.1-a9b9382b4f-11bcf1267a.zip/node_modules/react-is/", - "packageDependencies": [ - ["react-is", "npm:16.13.1"] - ], - "linkType": "HARD", - }] - ]], ["readable-stream", [ ["npm:2.3.7", { "packageLocation": "./.yarn/cache/readable-stream-npm-2.3.7-77b22a9818-6e38265606.zip/node_modules/readable-stream/", @@ -4934,11 +6393,11 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["readdirp", [ - ["npm:3.5.0", { - "packageLocation": "./.yarn/cache/readdirp-npm-3.5.0-a1b1568d32-a64fe56069.zip/node_modules/readdirp/", + ["npm:3.6.0", { + "packageLocation": "./.yarn/cache/readdirp-npm-3.6.0-f950cc74ab-7da2fe8d5a.zip/node_modules/readdirp/", "packageDependencies": [ - ["readdirp", "npm:3.5.0"], - ["picomatch", "npm:2.2.2"] + ["readdirp", "npm:3.6.0"], + ["picomatch", "npm:2.3.0"] ], "linkType": "HARD", }] @@ -4983,6 +6442,25 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["regression", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/regression-npm-2.0.1-50bfc2587a-61feb6f23b.zip/node_modules/regression/", + "packageDependencies": [ + ["regression", "npm:2.0.1"] + ], + "linkType": "HARD", + }] + ]], + ["release-zalgo", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/release-zalgo-npm-1.0.0-aa3e59962f-db2e7567a9.zip/node_modules/release-zalgo/", + "packageDependencies": [ + ["release-zalgo", "npm:1.0.0"], + ["es6-error", "npm:4.1.1"] + ], + "linkType": "HARD", + }] + ]], ["request", [ ["npm:2.88.2", { "packageLocation": "./.yarn/cache/request-npm-2.88.2-f4a57c72c4-7a74841f30.zip/node_modules/request/", @@ -5000,7 +6478,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["is-typedarray", "npm:1.0.0"], ["isstream", "npm:0.1.2"], ["json-stringify-safe", "npm:5.0.1"], - ["mime-types", "npm:2.1.27"], + ["mime-types", "npm:2.1.29"], ["oauth-sign", "npm:0.9.0"], ["performance-now", "npm:2.1.0"], ["qs", "npm:6.5.2"], @@ -5030,6 +6508,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["require-main-filename", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/require-main-filename-npm-2.0.0-03eef65c84-8d3633149a.zip/node_modules/require-main-filename/", + "packageDependencies": [ + ["require-main-filename", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], ["resolve-from", [ ["npm:4.0.0", { "packageLocation": "./.yarn/cache/resolve-from-npm-4.0.0-f758ec21bf-87a4357c0c.zip/node_modules/resolve-from/", @@ -5037,6 +6524,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["resolve-from", "npm:4.0.0"] ], "linkType": "HARD", + }], + ["npm:5.0.0", { + "packageLocation": "./.yarn/cache/resolve-from-npm-5.0.0-15c9db4d33-0d29fc7012.zip/node_modules/resolve-from/", + "packageDependencies": [ + ["resolve-from", "npm:5.0.0"] + ], + "linkType": "HARD", }] ]], ["restore-cursor", [ @@ -5064,7 +6558,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageLocation": "./.yarn/cache/rimraf-npm-3.0.2-2cb7dac69a-f0de3e4455.zip/node_modules/rimraf/", "packageDependencies": [ ["rimraf", "npm:3.0.2"], - ["glob", "npm:7.1.6"] + ["glob", "npm:7.1.7"] + ], + "linkType": "HARD", + }] + ]], + ["ripemd160", [ + ["npm:2.0.2", { + "packageLocation": "./.yarn/cache/ripemd160-npm-2.0.2-7b1fb8dc76-e0370fbe77.zip/node_modules/ripemd160/", + "packageDependencies": [ + ["ripemd160", "npm:2.0.2"], + ["hash-base", "npm:3.1.0"], + ["inherits", "npm:2.0.4"] ], "linkType": "HARD", }] @@ -5131,6 +6636,19 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["secp256k1", [ + ["npm:4.0.2", { + "packageLocation": "./.yarn/unplugged/secp256k1-npm-4.0.2-80b0224eff/node_modules/secp256k1/", + "packageDependencies": [ + ["secp256k1", "npm:4.0.2"], + ["elliptic", "npm:6.5.4"], + ["node-addon-api", "npm:2.0.2"], + ["node-gyp", "npm:7.1.2"], + ["node-gyp-build", "npm:4.2.3"] + ], + "linkType": "HARD", + }] + ]], ["semver", [ ["npm:5.7.1", { "packageLocation": "./.yarn/cache/semver-npm-5.7.1-40bcea106b-06ff0ed753.zip/node_modules/semver/", @@ -5139,10 +6657,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], "linkType": "HARD", }], - ["npm:7.3.2", { - "packageLocation": "./.yarn/cache/semver-npm-7.3.2-161b023bbb-bceb46d396.zip/node_modules/semver/", + ["npm:6.3.0", { + "packageLocation": "./.yarn/cache/semver-npm-6.3.0-b3eace8bfd-f0d155c06a.zip/node_modules/semver/", "packageDependencies": [ - ["semver", "npm:7.3.2"] + ["semver", "npm:6.3.0"] ], "linkType": "HARD", }], @@ -5177,6 +6695,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["serialize-javascript", [ + ["npm:6.0.0", { + "packageLocation": "./.yarn/cache/serialize-javascript-npm-6.0.0-0bb8a3c88d-e086a40bfc.zip/node_modules/serialize-javascript/", + "packageDependencies": [ + ["serialize-javascript", "npm:6.0.0"], + ["randombytes", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], ["set-blocking", [ ["npm:2.0.0", { "packageLocation": "./.yarn/cache/set-blocking-npm-2.0.0-49e2cffa24-0ac2403b0c.zip/node_modules/set-blocking/", @@ -5206,6 +6734,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["sha.js", [ + ["npm:2.4.11", { + "packageLocation": "./.yarn/cache/sha.js-npm-2.4.11-14868df4ca-7554240ab7.zip/node_modules/sha.js/", + "packageDependencies": [ + ["sha.js", "npm:2.4.11"], + ["inherits", "npm:2.0.4"], + ["safe-buffer", "npm:5.2.1"] + ], + "linkType": "HARD", + }] + ]], ["shallow-clone", [ ["npm:1.0.0", { "packageLocation": "./.yarn/cache/shallow-clone-npm-1.0.0-936fb13dbd-456b856473.zip/node_modules/shallow-clone/", @@ -5275,6 +6814,34 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["sinon", [ + ["npm:11.1.2", { + "packageLocation": "./.yarn/cache/sinon-npm-11.1.2-5325724cb2-dca6f9cbc7.zip/node_modules/sinon/", + "packageDependencies": [ + ["sinon", "npm:11.1.2"], + ["@sinonjs/commons", "npm:1.8.3"], + ["@sinonjs/fake-timers", "npm:7.1.2"], + ["@sinonjs/samsam", "npm:6.0.2"], + ["diff", "npm:5.0.0"], + ["nise", "npm:5.1.0"], + ["supports-color", "npm:7.2.0"] + ], + "linkType": "HARD", + }], + ["npm:9.2.4", { + "packageLocation": "./.yarn/cache/sinon-npm-9.2.4-3c6e379b87-c285c3750a.zip/node_modules/sinon/", + "packageDependencies": [ + ["sinon", "npm:9.2.4"], + ["@sinonjs/commons", "npm:1.8.3"], + ["@sinonjs/fake-timers", "npm:6.0.1"], + ["@sinonjs/samsam", "npm:5.3.1"], + ["diff", "npm:4.0.2"], + ["nise", "npm:4.1.0"], + ["supports-color", "npm:7.2.0"] + ], + "linkType": "HARD", + }] + ]], ["sisteransi", [ ["npm:1.0.5", { "packageLocation": "./.yarn/cache/sisteransi-npm-1.0.5-af60cc0cfa-6554debe10.zip/node_modules/sisteransi/", @@ -5316,26 +6883,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["smartweave", [ - ["npm:0.4.27", { - "packageLocation": "./.yarn/cache/smartweave-npm-0.4.27-8ada3fca84-ea910348e0.zip/node_modules/smartweave/", - "packageDependencies": [ - ["smartweave", "npm:0.4.27"], - ["@types/clui", "npm:0.3.0"], - ["@types/inquirer", "npm:7.3.1"], - ["@weavery/clarity", "npm:0.1.5"], - ["arweave", "npm:1.10.13"], - ["bignumber.js", "npm:9.0.1"], - ["chalk", "npm:4.1.0"], - ["clui", "npm:0.3.6"], - ["figlet", "npm:1.5.0"], - ["inquirer", "npm:7.3.3"], - ["json-beautify", "npm:1.1.1"], - ["loglevel", "npm:1.7.1"], - ["sentencer", "npm:0.2.1"], - ["yargs", "npm:16.2.0"] - ], - "linkType": "HARD", - }], ["npm:0.4.45", { "packageLocation": "./.yarn/cache/smartweave-npm-0.4.45-96bc502da0-d8d925df0b.zip/node_modules/smartweave/", "packageDependencies": [ @@ -5358,6 +6905,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["source-map", [ + ["npm:0.5.7", { + "packageLocation": "./.yarn/cache/source-map-npm-0.5.7-7c3f035429-737face965.zip/node_modules/source-map/", + "packageDependencies": [ + ["source-map", "npm:0.5.7"] + ], + "linkType": "HARD", + }], ["npm:0.6.1", { "packageLocation": "./.yarn/cache/source-map-npm-0.6.1-1a3621db16-8647829a06.zip/node_modules/source-map/", "packageDependencies": [ @@ -5367,16 +6921,31 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["source-map-support", [ - ["npm:0.5.19", { - "packageLocation": "./.yarn/cache/source-map-support-npm-0.5.19-65b33ae61e-59d4efaae9.zip/node_modules/source-map-support/", + ["npm:0.5.20", { + "packageLocation": "./.yarn/cache/source-map-support-npm-0.5.20-edfc5ce275-2c5821ee94.zip/node_modules/source-map-support/", "packageDependencies": [ - ["source-map-support", "npm:0.5.19"], + ["source-map-support", "npm:0.5.20"], ["buffer-from", "npm:1.1.1"], ["source-map", "npm:0.6.1"] ], "linkType": "HARD", }] ]], + ["spawn-wrap", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/spawn-wrap-npm-2.0.0-368c0a5bad-463a408825.zip/node_modules/spawn-wrap/", + "packageDependencies": [ + ["spawn-wrap", "npm:2.0.0"], + ["foreground-child", "npm:2.0.0"], + ["is-windows", "npm:1.0.2"], + ["make-dir", "npm:3.1.0"], + ["rimraf", "npm:3.0.2"], + ["signal-exit", "npm:3.0.3"], + ["which", "npm:2.0.2"] + ], + "linkType": "HARD", + }] + ]], ["sprintf-js", [ ["npm:1.0.3", { "packageLocation": "./.yarn/cache/sprintf-js-npm-1.0.3-73f0a322fa-51df1bce9e.zip/node_modules/sprintf-js/", @@ -5519,6 +7088,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["strip-bom", [ + ["npm:4.0.0", { + "packageLocation": "./.yarn/cache/strip-bom-npm-4.0.0-97d367a64d-25a231aacb.zip/node_modules/strip-bom/", + "packageDependencies": [ + ["strip-bom", "npm:4.0.0"] + ], + "linkType": "HARD", + }] + ]], ["strip-color", [ ["npm:0.1.0", { "packageLocation": "./.yarn/cache/strip-color-npm-0.1.0-7c0f52536e-23ddaa9ada.zip/node_modules/strip-color/", @@ -5578,6 +7156,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["has-flag", "npm:4.0.0"] ], "linkType": "HARD", + }], + ["npm:8.1.1", { + "packageLocation": "./.yarn/cache/supports-color-npm-8.1.1-289e937149-0219f5c917.zip/node_modules/supports-color/", + "packageDependencies": [ + ["supports-color", "npm:8.1.1"], + ["has-flag", "npm:4.0.0"] + ], + "linkType": "HARD", }] ]], ["sylvester", [ @@ -5661,6 +7247,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["test-exclude", [ + ["npm:6.0.0", { + "packageLocation": "./.yarn/cache/test-exclude-npm-6.0.0-3fb03d69df-68294d1006.zip/node_modules/test-exclude/", + "packageDependencies": [ + ["test-exclude", "npm:6.0.0"], + ["@istanbuljs/schema", "npm:0.1.3"], + ["glob", "npm:7.1.7"], + ["minimatch", "npm:3.0.4"] + ], + "linkType": "HARD", + }] + ]], ["text-table", [ ["npm:0.2.0", { "packageLocation": "./.yarn/cache/text-table-npm-0.2.0-d92a778b59-373904ce70.zip/node_modules/text-table/", @@ -5709,6 +7307,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["to-fast-properties", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/to-fast-properties-npm-2.0.0-0dc60cc481-40e6198424.zip/node_modules/to-fast-properties/", + "packageDependencies": [ + ["to-fast-properties", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], ["to-object-path", [ ["npm:0.3.0", { "packageLocation": "./.yarn/cache/to-object-path-npm-0.3.0-241b5ffa9c-a6a5a50225.zip/node_modules/to-object-path/", @@ -5751,63 +7358,65 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["ts-node", [ - ["npm:9.1.1", { - "packageLocation": "./.yarn/cache/ts-node-npm-9.1.1-4ad31da228-a90db4a342.zip/node_modules/ts-node/", + ["npm:10.2.1", { + "packageLocation": "./.yarn/cache/ts-node-npm-10.2.1-18dc22b42d-528e79c827.zip/node_modules/ts-node/", "packageDependencies": [ - ["ts-node", "npm:9.1.1"] + ["ts-node", "npm:10.2.1"] ], "linkType": "SOFT", }], - ["virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:9.1.1", { - "packageLocation": "./.yarn/$$virtual/ts-node-virtual-49241a1dd4/0/cache/ts-node-npm-9.1.1-4ad31da228-a90db4a342.zip/node_modules/ts-node/", - "packageDependencies": [ - ["ts-node", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:9.1.1"], + ["virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:10.2.1", { + "packageLocation": "./.yarn/$$virtual/ts-node-virtual-85f370d6b8/0/cache/ts-node-npm-10.2.1-18dc22b42d-528e79c827.zip/node_modules/ts-node/", + "packageDependencies": [ + ["ts-node", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:10.2.1"], + ["@cspotcode/source-map-support", "npm:0.6.1"], + ["@swc/core", null], + ["@swc/wasm", null], + ["@tsconfig/node10", "npm:1.0.8"], + ["@tsconfig/node12", "npm:1.0.9"], + ["@tsconfig/node14", "npm:1.0.1"], + ["@tsconfig/node16", "npm:1.0.2"], + ["@types/node", "npm:14.17.15"], + ["@types/swc__core", null], + ["@types/swc__wasm", null], + ["@types/types__node", null], ["@types/typescript", null], + ["acorn", "npm:8.5.0"], + ["acorn-walk", "npm:8.2.0"], ["arg", "npm:4.1.3"], ["create-require", "npm:1.1.1"], ["diff", "npm:4.0.2"], ["make-error", "npm:1.3.6"], - ["source-map-support", "npm:0.5.19"], ["typescript", "patch:typescript@npm%3A4.2.3#builtin::version=4.2.3&hash=a45b0e"], ["yn", "npm:3.1.1"] ], "packagePeers": [ + "@swc/core", + "@swc/wasm", + "@types/node", + "@types/swc__core", + "@types/swc__wasm", + "@types/types__node", "@types/typescript", "typescript" ], "linkType": "HARD", }] ]], - ["tsc-files", [ - ["npm:1.1.2", { - "packageLocation": "./.yarn/cache/tsc-files-npm-1.1.2-5dbe902f60-6e5a3a53f5.zip/node_modules/tsc-files/", - "packageDependencies": [ - ["tsc-files", "npm:1.1.2"] - ], - "linkType": "SOFT", - }], - ["virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:1.1.2", { - "packageLocation": "./.yarn/$$virtual/tsc-files-virtual-79c092f09d/0/cache/tsc-files-npm-1.1.2-5dbe902f60-6e5a3a53f5.zip/node_modules/tsc-files/", + ["ts-sinon", [ + ["npm:2.0.1", { + "packageLocation": "./.yarn/cache/ts-sinon-npm-2.0.1-dececf902a-c274652c3e.zip/node_modules/ts-sinon/", "packageDependencies": [ - ["tsc-files", "virtual:6f50bb9424c73c7612c66dab5cf8914d8ec79550c84d8ca5e4888e80022682c708b4b5a1c510d282a03285cc9bb19002b477ae70d15882aa995ea1d5d6bf24ab#npm:1.1.2"], - ["@types/typescript", null], - ["typescript", "patch:typescript@npm%3A4.2.3#builtin::version=4.2.3&hash=a45b0e"] - ], - "packagePeers": [ - "@types/typescript", - "typescript" + ["ts-sinon", "npm:2.0.1"], + ["@types/node", "npm:14.17.15"], + ["@types/sinon", "npm:9.0.11"], + ["@types/sinon-chai", "npm:3.2.5"], + ["sinon", "npm:9.2.4"] ], "linkType": "HARD", }] ]], ["tslib", [ - ["npm:1.13.0", { - "packageLocation": "./.yarn/cache/tslib-npm-1.13.0-f5e9ea9b66-5dc3bdaea3.zip/node_modules/tslib/", - "packageDependencies": [ - ["tslib", "npm:1.13.0"] - ], - "linkType": "HARD", - }], ["npm:1.14.1", { "packageLocation": "./.yarn/cache/tslib-npm-1.14.1-102499115e-f44fe7f216.zip/node_modules/tslib/", "packageDependencies": [ @@ -5836,7 +7445,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageDependencies": [ ["tsutils", "virtual:1c16952a6f9ff3d31a30bd98354e284eb46c7d43e97d0ad7096aaa6132e3bb1b945d2671dd15d2d2f3d9aec0fb21038daeef60baa697e632704d1ce504a133e8#npm:3.21.0"], ["@types/typescript", null], - ["tslib", "npm:1.13.0"], + ["tslib", "npm:1.14.1"], ["typescript", "patch:typescript@npm%3A4.2.3#builtin::version=4.2.3&hash=a45b0e"] ], "packagePeers": [ @@ -5850,7 +7459,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "packageDependencies": [ ["tsutils", "virtual:282286d55deb4847b728ad67048ae7437cacaffb83a6c16650f3a18b960544a6f9300739f54d72b19b63ddd101ae40578da80b88cd9ff45d15fca2cf37f6e9ef#npm:3.21.0"], ["@types/typescript", null], - ["tslib", "npm:1.13.0"], + ["tslib", "npm:1.14.1"], ["typescript", null] ], "packagePeers": [ @@ -5905,14 +7514,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], - ["type-fest", [ - ["npm:0.11.0", { - "packageLocation": "./.yarn/cache/type-fest-npm-0.11.0-81410fe889-02e5cadf13.zip/node_modules/type-fest/", + ["type-detect", [ + ["npm:4.0.8", { + "packageLocation": "./.yarn/cache/type-detect-npm-4.0.8-8d8127b901-e01dc6ac90.zip/node_modules/type-detect/", "packageDependencies": [ - ["type-fest", "npm:0.11.0"] + ["type-detect", "npm:4.0.8"] ], "linkType": "HARD", - }], + }] + ]], + ["type-fest", [ ["npm:0.20.2", { "packageLocation": "./.yarn/cache/type-fest-npm-0.20.2-b36432617f-1f887bc615.zip/node_modules/type-fest/", "packageDependencies": [ @@ -5944,6 +7555,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["typedarray-to-buffer", [ + ["npm:3.1.5", { + "packageLocation": "./.yarn/cache/typedarray-to-buffer-npm-3.1.5-aadc11995e-e6e0e6812a.zip/node_modules/typedarray-to-buffer/", + "packageDependencies": [ + ["typedarray-to-buffer", "npm:3.1.5"], + ["is-typedarray", "npm:1.0.0"] + ], + "linkType": "HARD", + }] + ]], ["typescript", [ ["patch:typescript@npm%3A4.2.3#builtin::version=4.2.3&hash=a45b0e", { "packageLocation": "./.yarn/cache/typescript-patch-99fe32b02b-a8956044aa.zip/node_modules/typescript/", @@ -5953,6 +7574,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["uint8arrays", [ + ["npm:3.0.0", { + "packageLocation": "./.yarn/cache/uint8arrays-npm-3.0.0-8a9076adb2-75361a84ae.zip/node_modules/uint8arrays/", + "packageDependencies": [ + ["uint8arrays", "npm:3.0.0"], + ["multiformats", "npm:9.4.7"] + ], + "linkType": "HARD", + }] + ]], ["underscore", [ ["npm:1.12.1", { "packageLocation": "./.yarn/cache/underscore-npm-1.12.1-f5ca0889f5-b53ae924fe.zip/node_modules/underscore/", @@ -5972,6 +7603,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["ursa-optional", [ + ["npm:0.10.2", { + "packageLocation": "./.yarn/unplugged/ursa-optional-npm-0.10.2-2e11b3a5da/node_modules/ursa-optional/", + "packageDependencies": [ + ["ursa-optional", "npm:0.10.2"], + ["bindings", "npm:1.5.0"], + ["nan", "npm:2.15.0"], + ["node-gyp", "npm:7.1.2"] + ], + "linkType": "HARD", + }] + ]], ["utf8", [ ["npm:3.0.0", { "packageLocation": "./.yarn/cache/utf8-npm-3.0.0-7c39b5994a-5a82761c09.zip/node_modules/utf8/", @@ -6056,6 +7699,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["which-module", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/which-module-npm-2.0.0-daf3daa08d-3d2107ab18.zip/node_modules/which-module/", + "packageDependencies": [ + ["which-module", "npm:2.0.0"] + ], + "linkType": "HARD", + }] + ]], ["wide-align", [ ["npm:1.1.3", { "packageLocation": "./.yarn/cache/wide-align-npm-1.1.3-48c7d4953c-4f850f84da.zip/node_modules/wide-align/", @@ -6086,6 +7738,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["workerpool", [ + ["npm:6.1.5", { + "packageLocation": "./.yarn/cache/workerpool-npm-6.1.5-61adb98c59-aaf220c463.zip/node_modules/workerpool/", + "packageDependencies": [ + ["workerpool", "npm:6.1.5"] + ], + "linkType": "HARD", + }] + ]], ["wrap-ansi", [ ["npm:6.2.0", { "packageLocation": "./.yarn/cache/wrap-ansi-npm-6.2.0-439a7246d8-ee4ed8b299.zip/node_modules/wrap-ansi/", @@ -6117,7 +7778,27 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["write-file-atomic", [ + ["npm:3.0.3", { + "packageLocation": "./.yarn/cache/write-file-atomic-npm-3.0.3-d948a237da-a26a8699c3.zip/node_modules/write-file-atomic/", + "packageDependencies": [ + ["write-file-atomic", "npm:3.0.3"], + ["imurmurhash", "npm:0.1.4"], + ["is-typedarray", "npm:1.0.0"], + ["signal-exit", "npm:3.0.3"], + ["typedarray-to-buffer", "npm:3.1.5"] + ], + "linkType": "HARD", + }] + ]], ["y18n", [ + ["npm:4.0.3", { + "packageLocation": "./.yarn/cache/y18n-npm-4.0.3-ced95acdbc-e6d08e9d14.zip/node_modules/y18n/", + "packageDependencies": [ + ["y18n", "npm:4.0.3"] + ], + "linkType": "HARD", + }], ["npm:5.0.5", { "packageLocation": "./.yarn/cache/y18n-npm-5.0.5-1fa41a2023-a7d41b0ccc.zip/node_modules/y18n/", "packageDependencies": [ @@ -6145,6 +7826,24 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["yargs", [ + ["npm:15.4.1", { + "packageLocation": "./.yarn/cache/yargs-npm-15.4.1-ca1c444de1-dbf687d6b9.zip/node_modules/yargs/", + "packageDependencies": [ + ["yargs", "npm:15.4.1"], + ["cliui", "npm:6.0.0"], + ["decamelize", "npm:1.2.0"], + ["find-up", "npm:4.1.0"], + ["get-caller-file", "npm:2.0.5"], + ["require-directory", "npm:2.1.1"], + ["require-main-filename", "npm:2.0.0"], + ["set-blocking", "npm:2.0.0"], + ["string-width", "npm:4.2.0"], + ["which-module", "npm:2.0.0"], + ["y18n", "npm:4.0.3"], + ["yargs-parser", "npm:18.1.3"] + ], + "linkType": "HARD", + }], ["npm:16.2.0", { "packageLocation": "./.yarn/cache/yargs-npm-16.2.0-547873d425-a79ce1f043.zip/node_modules/yargs/", "packageDependencies": [ @@ -6175,6 +7874,22 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { }] ]], ["yargs-parser", [ + ["npm:18.1.3", { + "packageLocation": "./.yarn/cache/yargs-parser-npm-18.1.3-0ba9c4f088-3387172167.zip/node_modules/yargs-parser/", + "packageDependencies": [ + ["yargs-parser", "npm:18.1.3"], + ["camelcase", "npm:5.3.1"], + ["decamelize", "npm:1.2.0"] + ], + "linkType": "HARD", + }], + ["npm:20.2.4", { + "packageLocation": "./.yarn/cache/yargs-parser-npm-20.2.4-1de20916a6-00dd0f23b6.zip/node_modules/yargs-parser/", + "packageDependencies": [ + ["yargs-parser", "npm:20.2.4"] + ], + "linkType": "HARD", + }], ["npm:20.2.7", { "packageLocation": "./.yarn/cache/yargs-parser-npm-20.2.7-5ab0b83136-124e7f1c24.zip/node_modules/yargs-parser/", "packageDependencies": [ @@ -6183,6 +7898,19 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD", }] ]], + ["yargs-unparser", [ + ["npm:2.0.0", { + "packageLocation": "./.yarn/cache/yargs-unparser-npm-2.0.0-930f3ff3f6-afa83ec3fe.zip/node_modules/yargs-unparser/", + "packageDependencies": [ + ["yargs-unparser", "npm:2.0.0"], + ["camelcase", "npm:6.2.0"], + ["decamelize", "npm:4.0.0"], + ["flat", "npm:5.0.2"], + ["is-plain-obj", "npm:2.1.0"] + ], + "linkType": "HARD", + }] + ]], ["yn", [ ["npm:3.1.1", { "packageLocation": "./.yarn/cache/yn-npm-3.1.1-8ad4259784-bff63b8056.zip/node_modules/yn/", @@ -6191,6 +7919,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ], "linkType": "HARD", }] + ]], + ["yocto-queue", [ + ["npm:0.1.0", { + "packageLocation": "./.yarn/cache/yocto-queue-npm-0.1.0-c6c9a7db29-096c3b40be.zip/node_modules/yocto-queue/", + "packageDependencies": [ + ["yocto-queue", "npm:0.1.0"] + ], + "linkType": "HARD", + }] ]] ] }, {basePath: basePath || __dirname}); diff --git a/.yarn/cache/@babel-code-frame-npm-7.10.4-ab1ee3c93e-05245d3b22.zip b/.yarn/cache/@babel-code-frame-npm-7.10.4-ab1ee3c93e-05245d3b22.zip deleted file mode 100644 index 9b4f6a2a..00000000 Binary files a/.yarn/cache/@babel-code-frame-npm-7.10.4-ab1ee3c93e-05245d3b22.zip and /dev/null differ diff --git a/.yarn/cache/@babel-code-frame-npm-7.14.5-4dc9115988-48c584cad9.zip b/.yarn/cache/@babel-code-frame-npm-7.14.5-4dc9115988-48c584cad9.zip new file mode 100644 index 00000000..21d72a35 Binary files /dev/null and b/.yarn/cache/@babel-code-frame-npm-7.14.5-4dc9115988-48c584cad9.zip differ diff --git a/.yarn/cache/@babel-compat-data-npm-7.15.0-48235b743d-76db9ec4fb.zip b/.yarn/cache/@babel-compat-data-npm-7.15.0-48235b743d-76db9ec4fb.zip new file mode 100644 index 00000000..7c541ba4 Binary files /dev/null and b/.yarn/cache/@babel-compat-data-npm-7.15.0-48235b743d-76db9ec4fb.zip differ diff --git a/.yarn/cache/@babel-core-npm-7.15.5-1d250c9216-84c787c821.zip b/.yarn/cache/@babel-core-npm-7.15.5-1d250c9216-84c787c821.zip new file mode 100644 index 00000000..94ad82c0 Binary files /dev/null and b/.yarn/cache/@babel-core-npm-7.15.5-1d250c9216-84c787c821.zip differ diff --git a/.yarn/cache/@babel-generator-npm-7.15.4-11b44cab06-5ee8687d49.zip b/.yarn/cache/@babel-generator-npm-7.15.4-11b44cab06-5ee8687d49.zip new file mode 100644 index 00000000..aad076b7 Binary files /dev/null and b/.yarn/cache/@babel-generator-npm-7.15.4-11b44cab06-5ee8687d49.zip differ diff --git a/.yarn/cache/@babel-helper-compilation-targets-npm-7.15.4-8aadf9f3ed-539e4d2093.zip b/.yarn/cache/@babel-helper-compilation-targets-npm-7.15.4-8aadf9f3ed-539e4d2093.zip new file mode 100644 index 00000000..60296a49 Binary files /dev/null and b/.yarn/cache/@babel-helper-compilation-targets-npm-7.15.4-8aadf9f3ed-539e4d2093.zip differ diff --git a/.yarn/cache/@babel-helper-function-name-npm-7.15.4-ef0109c90b-74ec8a86b1.zip b/.yarn/cache/@babel-helper-function-name-npm-7.15.4-ef0109c90b-74ec8a86b1.zip new file mode 100644 index 00000000..b2d3d80d Binary files /dev/null and b/.yarn/cache/@babel-helper-function-name-npm-7.15.4-ef0109c90b-74ec8a86b1.zip differ diff --git a/.yarn/cache/@babel-helper-get-function-arity-npm-7.15.4-0f7c9ab74a-c60ed72a9c.zip b/.yarn/cache/@babel-helper-get-function-arity-npm-7.15.4-0f7c9ab74a-c60ed72a9c.zip new file mode 100644 index 00000000..a9ee645c Binary files /dev/null and b/.yarn/cache/@babel-helper-get-function-arity-npm-7.15.4-0f7c9ab74a-c60ed72a9c.zip differ diff --git a/.yarn/cache/@babel-helper-hoist-variables-npm-7.15.4-1754989aec-b500f154f9.zip b/.yarn/cache/@babel-helper-hoist-variables-npm-7.15.4-1754989aec-b500f154f9.zip new file mode 100644 index 00000000..f4e62300 Binary files /dev/null and b/.yarn/cache/@babel-helper-hoist-variables-npm-7.15.4-1754989aec-b500f154f9.zip differ diff --git a/.yarn/cache/@babel-helper-member-expression-to-functions-npm-7.15.4-212b6361be-7180912838.zip b/.yarn/cache/@babel-helper-member-expression-to-functions-npm-7.15.4-212b6361be-7180912838.zip new file mode 100644 index 00000000..3b842544 Binary files /dev/null and b/.yarn/cache/@babel-helper-member-expression-to-functions-npm-7.15.4-212b6361be-7180912838.zip differ diff --git a/.yarn/cache/@babel-helper-module-imports-npm-7.15.4-b399b49e52-efb5329581.zip b/.yarn/cache/@babel-helper-module-imports-npm-7.15.4-b399b49e52-efb5329581.zip new file mode 100644 index 00000000..46bb83e6 Binary files /dev/null and b/.yarn/cache/@babel-helper-module-imports-npm-7.15.4-b399b49e52-efb5329581.zip differ diff --git a/.yarn/cache/@babel-helper-module-transforms-npm-7.15.4-2ff12afc8c-f895915ff7.zip b/.yarn/cache/@babel-helper-module-transforms-npm-7.15.4-2ff12afc8c-f895915ff7.zip new file mode 100644 index 00000000..3a07531c Binary files /dev/null and b/.yarn/cache/@babel-helper-module-transforms-npm-7.15.4-2ff12afc8c-f895915ff7.zip differ diff --git a/.yarn/cache/@babel-helper-optimise-call-expression-npm-7.15.4-20261f745b-96d837b5d2.zip b/.yarn/cache/@babel-helper-optimise-call-expression-npm-7.15.4-20261f745b-96d837b5d2.zip new file mode 100644 index 00000000..6f56c3cf Binary files /dev/null and b/.yarn/cache/@babel-helper-optimise-call-expression-npm-7.15.4-20261f745b-96d837b5d2.zip differ diff --git a/.yarn/cache/@babel-helper-replace-supers-npm-7.15.4-2a4bb81d23-44291cc084.zip b/.yarn/cache/@babel-helper-replace-supers-npm-7.15.4-2a4bb81d23-44291cc084.zip new file mode 100644 index 00000000..883e1627 Binary files /dev/null and b/.yarn/cache/@babel-helper-replace-supers-npm-7.15.4-2a4bb81d23-44291cc084.zip differ diff --git a/.yarn/cache/@babel-helper-simple-access-npm-7.15.4-fcd51a651c-ac7f1403b4.zip b/.yarn/cache/@babel-helper-simple-access-npm-7.15.4-fcd51a651c-ac7f1403b4.zip new file mode 100644 index 00000000..87fae04d Binary files /dev/null and b/.yarn/cache/@babel-helper-simple-access-npm-7.15.4-fcd51a651c-ac7f1403b4.zip differ diff --git a/.yarn/cache/@babel-helper-split-export-declaration-npm-7.15.4-ff2895bff2-17179ddcba.zip b/.yarn/cache/@babel-helper-split-export-declaration-npm-7.15.4-ff2895bff2-17179ddcba.zip new file mode 100644 index 00000000..13cb69d4 Binary files /dev/null and b/.yarn/cache/@babel-helper-split-export-declaration-npm-7.15.4-ff2895bff2-17179ddcba.zip differ diff --git a/.yarn/cache/@babel-helper-validator-identifier-npm-7.10.4-0689d787fa-25098ef842.zip b/.yarn/cache/@babel-helper-validator-identifier-npm-7.10.4-0689d787fa-25098ef842.zip deleted file mode 100644 index a20680af..00000000 Binary files a/.yarn/cache/@babel-helper-validator-identifier-npm-7.10.4-0689d787fa-25098ef842.zip and /dev/null differ diff --git a/.yarn/cache/@babel-helper-validator-identifier-npm-7.14.9-d7bb91b6de-a4825ac127.zip b/.yarn/cache/@babel-helper-validator-identifier-npm-7.14.9-d7bb91b6de-a4825ac127.zip new file mode 100644 index 00000000..427e19ad Binary files /dev/null and b/.yarn/cache/@babel-helper-validator-identifier-npm-7.14.9-d7bb91b6de-a4825ac127.zip differ diff --git a/.yarn/cache/@babel-helper-validator-option-npm-7.14.5-fd38dcf0bc-aded46b377.zip b/.yarn/cache/@babel-helper-validator-option-npm-7.14.5-fd38dcf0bc-aded46b377.zip new file mode 100644 index 00000000..04c61afd Binary files /dev/null and b/.yarn/cache/@babel-helper-validator-option-npm-7.14.5-fd38dcf0bc-aded46b377.zip differ diff --git a/.yarn/cache/@babel-helpers-npm-7.15.4-370adba024-b6e700c85b.zip b/.yarn/cache/@babel-helpers-npm-7.15.4-370adba024-b6e700c85b.zip new file mode 100644 index 00000000..c2f72dc7 Binary files /dev/null and b/.yarn/cache/@babel-helpers-npm-7.15.4-370adba024-b6e700c85b.zip differ diff --git a/.yarn/cache/@babel-highlight-npm-7.10.4-c7ff18fbba-c167b938af.zip b/.yarn/cache/@babel-highlight-npm-7.10.4-c7ff18fbba-c167b938af.zip deleted file mode 100644 index 8dac0b56..00000000 Binary files a/.yarn/cache/@babel-highlight-npm-7.10.4-c7ff18fbba-c167b938af.zip and /dev/null differ diff --git a/.yarn/cache/@babel-highlight-npm-7.14.5-4a18106cbc-a1ed599c26.zip b/.yarn/cache/@babel-highlight-npm-7.14.5-4a18106cbc-a1ed599c26.zip new file mode 100644 index 00000000..e929f3c6 Binary files /dev/null and b/.yarn/cache/@babel-highlight-npm-7.14.5-4a18106cbc-a1ed599c26.zip differ diff --git a/.yarn/cache/@babel-parser-npm-7.15.6-a61d8794ff-2ec5923e17.zip b/.yarn/cache/@babel-parser-npm-7.15.6-a61d8794ff-2ec5923e17.zip new file mode 100644 index 00000000..09ac958e Binary files /dev/null and b/.yarn/cache/@babel-parser-npm-7.15.6-a61d8794ff-2ec5923e17.zip differ diff --git a/.yarn/cache/@babel-template-npm-7.15.4-a024aff24b-d4d366d881.zip b/.yarn/cache/@babel-template-npm-7.15.4-a024aff24b-d4d366d881.zip new file mode 100644 index 00000000..f621962e Binary files /dev/null and b/.yarn/cache/@babel-template-npm-7.15.4-a024aff24b-d4d366d881.zip differ diff --git a/.yarn/cache/@babel-traverse-npm-7.15.4-904b3fada4-d023925a46.zip b/.yarn/cache/@babel-traverse-npm-7.15.4-904b3fada4-d023925a46.zip new file mode 100644 index 00000000..11f6aef6 Binary files /dev/null and b/.yarn/cache/@babel-traverse-npm-7.15.4-904b3fada4-d023925a46.zip differ diff --git a/.yarn/cache/@babel-types-npm-7.15.6-330b07a916-34c8048bde.zip b/.yarn/cache/@babel-types-npm-7.15.6-330b07a916-34c8048bde.zip new file mode 100644 index 00000000..794e7769 Binary files /dev/null and b/.yarn/cache/@babel-types-npm-7.15.6-330b07a916-34c8048bde.zip differ diff --git a/.yarn/cache/@cspotcode-source-map-consumer-npm-0.8.0-1f37e9e72b-fc32215a35.zip b/.yarn/cache/@cspotcode-source-map-consumer-npm-0.8.0-1f37e9e72b-fc32215a35.zip new file mode 100644 index 00000000..7bc7bcd7 Binary files /dev/null and b/.yarn/cache/@cspotcode-source-map-consumer-npm-0.8.0-1f37e9e72b-fc32215a35.zip differ diff --git a/.yarn/cache/@cspotcode-source-map-support-npm-0.6.1-2e72b80534-eda5227576.zip b/.yarn/cache/@cspotcode-source-map-support-npm-0.6.1-2e72b80534-eda5227576.zip new file mode 100644 index 00000000..f6fcf617 Binary files /dev/null and b/.yarn/cache/@cspotcode-source-map-support-npm-0.6.1-2e72b80534-eda5227576.zip differ diff --git a/.yarn/cache/@istanbuljs-load-nyc-config-npm-1.1.0-42d17c9cb1-f7f3b1c922.zip b/.yarn/cache/@istanbuljs-load-nyc-config-npm-1.1.0-42d17c9cb1-f7f3b1c922.zip new file mode 100644 index 00000000..cf24ee85 Binary files /dev/null and b/.yarn/cache/@istanbuljs-load-nyc-config-npm-1.1.0-42d17c9cb1-f7f3b1c922.zip differ diff --git a/.yarn/cache/@istanbuljs-nyc-config-typescript-npm-1.0.1-d1daa3ba46-28c19a10ee.zip b/.yarn/cache/@istanbuljs-nyc-config-typescript-npm-1.0.1-d1daa3ba46-28c19a10ee.zip new file mode 100644 index 00000000..8679bcaa Binary files /dev/null and b/.yarn/cache/@istanbuljs-nyc-config-typescript-npm-1.0.1-d1daa3ba46-28c19a10ee.zip differ diff --git a/.yarn/cache/@istanbuljs-schema-npm-0.1.3-466bd3eaaa-d84c326335.zip b/.yarn/cache/@istanbuljs-schema-npm-0.1.3-466bd3eaaa-d84c326335.zip new file mode 100644 index 00000000..2c3735a9 Binary files /dev/null and b/.yarn/cache/@istanbuljs-schema-npm-0.1.3-466bd3eaaa-d84c326335.zip differ diff --git a/.yarn/cache/@jest-types-npm-25.5.0-45f0640591-33ad68320e.zip b/.yarn/cache/@jest-types-npm-25.5.0-45f0640591-33ad68320e.zip deleted file mode 100644 index 6ee8a5e4..00000000 Binary files a/.yarn/cache/@jest-types-npm-25.5.0-45f0640591-33ad68320e.zip and /dev/null differ diff --git a/.yarn/cache/@lordvlad-asn1.js-npm-5.1.1-4f1ad9d487-806d50b2ea.zip b/.yarn/cache/@lordvlad-asn1.js-npm-5.1.1-4f1ad9d487-806d50b2ea.zip new file mode 100644 index 00000000..33b7f415 Binary files /dev/null and b/.yarn/cache/@lordvlad-asn1.js-npm-5.1.1-4f1ad9d487-806d50b2ea.zip differ diff --git a/.yarn/cache/@ponicode-cli-npm-0.0.1-18-219ac03cf5-5873f1fb0a.zip b/.yarn/cache/@ponicode-cli-npm-0.0.1-18-219ac03cf5-5873f1fb0a.zip deleted file mode 100644 index 55c8ca93..00000000 Binary files a/.yarn/cache/@ponicode-cli-npm-0.0.1-18-219ac03cf5-5873f1fb0a.zip and /dev/null differ diff --git a/.yarn/cache/@protobufjs-aspromise-npm-1.1.2-71d00b938f-83ced0798a.zip b/.yarn/cache/@protobufjs-aspromise-npm-1.1.2-71d00b938f-83ced0798a.zip new file mode 100644 index 00000000..abe94ffa Binary files /dev/null and b/.yarn/cache/@protobufjs-aspromise-npm-1.1.2-71d00b938f-83ced0798a.zip differ diff --git a/.yarn/cache/@protobufjs-base64-npm-1.1.2-cd8ca6814a-ae9e84aaf6.zip b/.yarn/cache/@protobufjs-base64-npm-1.1.2-cd8ca6814a-ae9e84aaf6.zip new file mode 100644 index 00000000..5104a89f Binary files /dev/null and b/.yarn/cache/@protobufjs-base64-npm-1.1.2-cd8ca6814a-ae9e84aaf6.zip differ diff --git a/.yarn/cache/@protobufjs-codegen-npm-2.0.4-36e188bbe6-a05d5f8892.zip b/.yarn/cache/@protobufjs-codegen-npm-2.0.4-36e188bbe6-a05d5f8892.zip new file mode 100644 index 00000000..804acfd9 Binary files /dev/null and b/.yarn/cache/@protobufjs-codegen-npm-2.0.4-36e188bbe6-a05d5f8892.zip differ diff --git a/.yarn/cache/@protobufjs-eventemitter-npm-1.1.0-029cc7d431-32a84e33f1.zip b/.yarn/cache/@protobufjs-eventemitter-npm-1.1.0-029cc7d431-32a84e33f1.zip new file mode 100644 index 00000000..ed0bef18 Binary files /dev/null and b/.yarn/cache/@protobufjs-eventemitter-npm-1.1.0-029cc7d431-32a84e33f1.zip differ diff --git a/.yarn/cache/@protobufjs-fetch-npm-1.1.0-ca857b7df4-d682e5d8a1.zip b/.yarn/cache/@protobufjs-fetch-npm-1.1.0-ca857b7df4-d682e5d8a1.zip new file mode 100644 index 00000000..99f0e43d Binary files /dev/null and b/.yarn/cache/@protobufjs-fetch-npm-1.1.0-ca857b7df4-d682e5d8a1.zip differ diff --git a/.yarn/cache/@protobufjs-float-npm-1.0.2-5678f64d08-eee7278de2.zip b/.yarn/cache/@protobufjs-float-npm-1.0.2-5678f64d08-eee7278de2.zip new file mode 100644 index 00000000..47a455ff Binary files /dev/null and b/.yarn/cache/@protobufjs-float-npm-1.0.2-5678f64d08-eee7278de2.zip differ diff --git a/.yarn/cache/@protobufjs-inquire-npm-1.1.0-3c7759e9ce-3541518cca.zip b/.yarn/cache/@protobufjs-inquire-npm-1.1.0-3c7759e9ce-3541518cca.zip new file mode 100644 index 00000000..324669d3 Binary files /dev/null and b/.yarn/cache/@protobufjs-inquire-npm-1.1.0-3c7759e9ce-3541518cca.zip differ diff --git a/.yarn/cache/@protobufjs-path-npm-1.1.2-641d08de76-22f10c5c22.zip b/.yarn/cache/@protobufjs-path-npm-1.1.2-641d08de76-22f10c5c22.zip new file mode 100644 index 00000000..42e0dd23 Binary files /dev/null and b/.yarn/cache/@protobufjs-path-npm-1.1.2-641d08de76-22f10c5c22.zip differ diff --git a/.yarn/cache/@protobufjs-pool-npm-1.1.0-47a76f96a1-5fc4af9e06.zip b/.yarn/cache/@protobufjs-pool-npm-1.1.0-47a76f96a1-5fc4af9e06.zip new file mode 100644 index 00000000..5964f23a Binary files /dev/null and b/.yarn/cache/@protobufjs-pool-npm-1.1.0-47a76f96a1-5fc4af9e06.zip differ diff --git a/.yarn/cache/@protobufjs-utf8-npm-1.1.0-02c590807c-5b3fa7425f.zip b/.yarn/cache/@protobufjs-utf8-npm-1.1.0-02c590807c-5b3fa7425f.zip new file mode 100644 index 00000000..84bbc1f1 Binary files /dev/null and b/.yarn/cache/@protobufjs-utf8-npm-1.1.0-02c590807c-5b3fa7425f.zip differ diff --git a/.yarn/cache/@sinonjs-commons-npm-1.8.3-30cf78d93f-a7f3181512.zip b/.yarn/cache/@sinonjs-commons-npm-1.8.3-30cf78d93f-a7f3181512.zip new file mode 100644 index 00000000..a1af66dd Binary files /dev/null and b/.yarn/cache/@sinonjs-commons-npm-1.8.3-30cf78d93f-a7f3181512.zip differ diff --git a/.yarn/cache/@sinonjs-fake-timers-npm-6.0.1-cebf4d0bfb-64458b9087.zip b/.yarn/cache/@sinonjs-fake-timers-npm-6.0.1-cebf4d0bfb-64458b9087.zip new file mode 100644 index 00000000..c60ed9ff Binary files /dev/null and b/.yarn/cache/@sinonjs-fake-timers-npm-6.0.1-cebf4d0bfb-64458b9087.zip differ diff --git a/.yarn/cache/@sinonjs-fake-timers-npm-7.1.2-2a6b119ac7-5ce48e40db.zip b/.yarn/cache/@sinonjs-fake-timers-npm-7.1.2-2a6b119ac7-5ce48e40db.zip new file mode 100644 index 00000000..5c6dbc28 Binary files /dev/null and b/.yarn/cache/@sinonjs-fake-timers-npm-7.1.2-2a6b119ac7-5ce48e40db.zip differ diff --git a/.yarn/cache/@sinonjs-samsam-npm-5.3.1-deedfea087-0277cd0b71.zip b/.yarn/cache/@sinonjs-samsam-npm-5.3.1-deedfea087-0277cd0b71.zip new file mode 100644 index 00000000..5e3a8e12 Binary files /dev/null and b/.yarn/cache/@sinonjs-samsam-npm-5.3.1-deedfea087-0277cd0b71.zip differ diff --git a/.yarn/cache/@sinonjs-samsam-npm-6.0.2-5e8e8897e2-8113510c5b.zip b/.yarn/cache/@sinonjs-samsam-npm-6.0.2-5e8e8897e2-8113510c5b.zip new file mode 100644 index 00000000..837b87e0 Binary files /dev/null and b/.yarn/cache/@sinonjs-samsam-npm-6.0.2-5e8e8897e2-8113510c5b.zip differ diff --git a/.yarn/cache/@sinonjs-text-encoding-npm-0.7.1-865b0079b5-fbc2abff23.zip b/.yarn/cache/@sinonjs-text-encoding-npm-0.7.1-865b0079b5-fbc2abff23.zip new file mode 100644 index 00000000..b04f14be Binary files /dev/null and b/.yarn/cache/@sinonjs-text-encoding-npm-0.7.1-865b0079b5-fbc2abff23.zip differ diff --git a/.yarn/cache/@tsconfig-node10-npm-1.0.8-90a8cce25d-0336493b89.zip b/.yarn/cache/@tsconfig-node10-npm-1.0.8-90a8cce25d-0336493b89.zip new file mode 100644 index 00000000..60bad844 Binary files /dev/null and b/.yarn/cache/@tsconfig-node10-npm-1.0.8-90a8cce25d-0336493b89.zip differ diff --git a/.yarn/cache/@tsconfig-node12-npm-1.0.9-780563856d-5532bfb5df.zip b/.yarn/cache/@tsconfig-node12-npm-1.0.9-780563856d-5532bfb5df.zip new file mode 100644 index 00000000..ca367049 Binary files /dev/null and b/.yarn/cache/@tsconfig-node12-npm-1.0.9-780563856d-5532bfb5df.zip differ diff --git a/.yarn/cache/@tsconfig-node14-npm-1.0.1-3ecac58e68-d0068287db.zip b/.yarn/cache/@tsconfig-node14-npm-1.0.1-3ecac58e68-d0068287db.zip new file mode 100644 index 00000000..1ff5b8f1 Binary files /dev/null and b/.yarn/cache/@tsconfig-node14-npm-1.0.1-3ecac58e68-d0068287db.zip differ diff --git a/.yarn/cache/@tsconfig-node16-npm-1.0.2-1f43ab567a-57310e8885.zip b/.yarn/cache/@tsconfig-node16-npm-1.0.2-1f43ab567a-57310e8885.zip new file mode 100644 index 00000000..e6d28d1a Binary files /dev/null and b/.yarn/cache/@tsconfig-node16-npm-1.0.2-1f43ab567a-57310e8885.zip differ diff --git a/.yarn/cache/@types-chai-npm-4.2.21-22c1ed2cef-fc7d32fbae.zip b/.yarn/cache/@types-chai-npm-4.2.21-22c1ed2cef-fc7d32fbae.zip new file mode 100644 index 00000000..130652ec Binary files /dev/null and b/.yarn/cache/@types-chai-npm-4.2.21-22c1ed2cef-fc7d32fbae.zip differ diff --git a/.yarn/cache/@types-connect-npm-3.4.34-39e4f7bb55-6f712a0408.zip b/.yarn/cache/@types-connect-npm-3.4.34-39e4f7bb55-6f712a0408.zip deleted file mode 100644 index 23c6ee59..00000000 Binary files a/.yarn/cache/@types-connect-npm-3.4.34-39e4f7bb55-6f712a0408.zip and /dev/null differ diff --git a/.yarn/cache/@types-convert-source-map-npm-1.5.1-819f48f4ab-36cd50ea42.zip b/.yarn/cache/@types-convert-source-map-npm-1.5.1-819f48f4ab-36cd50ea42.zip deleted file mode 100644 index c8b50191..00000000 Binary files a/.yarn/cache/@types-convert-source-map-npm-1.5.1-819f48f4ab-36cd50ea42.zip and /dev/null differ diff --git a/.yarn/cache/@types-istanbul-lib-coverage-npm-2.0.3-67a37eb00a-d6f6dbf66d.zip b/.yarn/cache/@types-istanbul-lib-coverage-npm-2.0.3-67a37eb00a-d6f6dbf66d.zip deleted file mode 100644 index 5d5df1c2..00000000 Binary files a/.yarn/cache/@types-istanbul-lib-coverage-npm-2.0.3-67a37eb00a-d6f6dbf66d.zip and /dev/null differ diff --git a/.yarn/cache/@types-istanbul-lib-report-npm-3.0.0-50de3e6b3b-78aa9f859b.zip b/.yarn/cache/@types-istanbul-lib-report-npm-3.0.0-50de3e6b3b-78aa9f859b.zip deleted file mode 100644 index 78fb004c..00000000 Binary files a/.yarn/cache/@types-istanbul-lib-report-npm-3.0.0-50de3e6b3b-78aa9f859b.zip and /dev/null differ diff --git a/.yarn/cache/@types-istanbul-reports-npm-1.1.2-4f435a3d0f-92bd1f76a4.zip b/.yarn/cache/@types-istanbul-reports-npm-1.1.2-4f435a3d0f-92bd1f76a4.zip deleted file mode 100644 index ca0d4c1b..00000000 Binary files a/.yarn/cache/@types-istanbul-reports-npm-1.1.2-4f435a3d0f-92bd1f76a4.zip and /dev/null differ diff --git a/.yarn/cache/@types-jest-npm-25.2.3-41912c4b23-fe92624fd7.zip b/.yarn/cache/@types-jest-npm-25.2.3-41912c4b23-fe92624fd7.zip deleted file mode 100644 index c738c4dd..00000000 Binary files a/.yarn/cache/@types-jest-npm-25.2.3-41912c4b23-fe92624fd7.zip and /dev/null differ diff --git a/.yarn/cache/@types-js-base64-npm-3.0.0-00b29e1112-7513271c3f.zip b/.yarn/cache/@types-js-base64-npm-3.0.0-00b29e1112-7513271c3f.zip deleted file mode 100644 index 174b2aaa..00000000 Binary files a/.yarn/cache/@types-js-base64-npm-3.0.0-00b29e1112-7513271c3f.zip and /dev/null differ diff --git a/.yarn/cache/@types-lodash-npm-4.14.176-34dca4acb8-cc3c9d7522.zip b/.yarn/cache/@types-lodash-npm-4.14.176-34dca4acb8-cc3c9d7522.zip new file mode 100644 index 00000000..cf4c5abd Binary files /dev/null and b/.yarn/cache/@types-lodash-npm-4.14.176-34dca4acb8-cc3c9d7522.zip differ diff --git a/.yarn/cache/@types-long-npm-4.0.1-022c8b6e77-ed2a125330.zip b/.yarn/cache/@types-long-npm-4.0.1-022c8b6e77-ed2a125330.zip new file mode 100644 index 00000000..e7c2e8c4 Binary files /dev/null and b/.yarn/cache/@types-long-npm-4.0.1-022c8b6e77-ed2a125330.zip differ diff --git a/.yarn/cache/@types-mocha-npm-9.0.0-cd77a42cf3-dac70c24e5.zip b/.yarn/cache/@types-mocha-npm-9.0.0-cd77a42cf3-dac70c24e5.zip new file mode 100644 index 00000000..5ccba0f7 Binary files /dev/null and b/.yarn/cache/@types-mocha-npm-9.0.0-cd77a42cf3-dac70c24e5.zip differ diff --git a/.yarn/cache/@types-node-fetch-npm-2.5.3-63cbf4aef6-c85f183722.zip b/.yarn/cache/@types-node-fetch-npm-2.5.3-63cbf4aef6-c85f183722.zip new file mode 100644 index 00000000..22f86619 Binary files /dev/null and b/.yarn/cache/@types-node-fetch-npm-2.5.3-63cbf4aef6-c85f183722.zip differ diff --git a/.yarn/cache/@types-node-npm-11.11.6-40abad0842-2b64bfc234.zip b/.yarn/cache/@types-node-npm-11.11.6-40abad0842-2b64bfc234.zip new file mode 100644 index 00000000..93e671ef Binary files /dev/null and b/.yarn/cache/@types-node-npm-11.11.6-40abad0842-2b64bfc234.zip differ diff --git a/.yarn/cache/@types-node-npm-14.14.37-6783f920bd-5e2d9baf75.zip b/.yarn/cache/@types-node-npm-14.14.37-6783f920bd-5e2d9baf75.zip deleted file mode 100644 index 0d1ba38d..00000000 Binary files a/.yarn/cache/@types-node-npm-14.14.37-6783f920bd-5e2d9baf75.zip and /dev/null differ diff --git a/.yarn/cache/@types-node-npm-14.17.15-9cfdb3e8eb-c9f5b6fa6a.zip b/.yarn/cache/@types-node-npm-14.17.15-9cfdb3e8eb-c9f5b6fa6a.zip new file mode 100644 index 00000000..72c98de4 Binary files /dev/null and b/.yarn/cache/@types-node-npm-14.17.15-9cfdb3e8eb-c9f5b6fa6a.zip differ diff --git a/.yarn/cache/@types-node-npm-14.6.4-5ce5afae9a-bff274e362.zip b/.yarn/cache/@types-node-npm-14.6.4-5ce5afae9a-bff274e362.zip deleted file mode 100644 index 635d41a3..00000000 Binary files a/.yarn/cache/@types-node-npm-14.6.4-5ce5afae9a-bff274e362.zip and /dev/null differ diff --git a/.yarn/cache/@types-node-npm-16.7.13-08a1515fa1-e22d3b58e5.zip b/.yarn/cache/@types-node-npm-16.7.13-08a1515fa1-e22d3b58e5.zip new file mode 100644 index 00000000..af01add6 Binary files /dev/null and b/.yarn/cache/@types-node-npm-16.7.13-08a1515fa1-e22d3b58e5.zip differ diff --git a/.yarn/cache/@types-prompt-sync-npm-4.1.0-5d6d03c0bb-21bc6832fb.zip b/.yarn/cache/@types-prompt-sync-npm-4.1.0-5d6d03c0bb-21bc6832fb.zip deleted file mode 100644 index c8d85a7d..00000000 Binary files a/.yarn/cache/@types-prompt-sync-npm-4.1.0-5d6d03c0bb-21bc6832fb.zip and /dev/null differ diff --git a/.yarn/cache/@types-regression-npm-2.0.2-a0bd52194b-fa577d48c5.zip b/.yarn/cache/@types-regression-npm-2.0.2-a0bd52194b-fa577d48c5.zip new file mode 100644 index 00000000..c8737b2b Binary files /dev/null and b/.yarn/cache/@types-regression-npm-2.0.2-a0bd52194b-fa577d48c5.zip differ diff --git a/.yarn/cache/@types-sinon-chai-npm-3.2.5-1d6490532a-0e482de52b.zip b/.yarn/cache/@types-sinon-chai-npm-3.2.5-1d6490532a-0e482de52b.zip new file mode 100644 index 00000000..8b561cd2 Binary files /dev/null and b/.yarn/cache/@types-sinon-chai-npm-3.2.5-1d6490532a-0e482de52b.zip differ diff --git a/.yarn/cache/@types-sinon-npm-10.0.2-2a54516cee-44d149b0ba.zip b/.yarn/cache/@types-sinon-npm-10.0.2-2a54516cee-44d149b0ba.zip new file mode 100644 index 00000000..e8b65e1b Binary files /dev/null and b/.yarn/cache/@types-sinon-npm-10.0.2-2a54516cee-44d149b0ba.zip differ diff --git a/.yarn/cache/@types-sinon-npm-9.0.11-231734b808-c84ccbd5ac.zip b/.yarn/cache/@types-sinon-npm-9.0.11-231734b808-c84ccbd5ac.zip new file mode 100644 index 00000000..20aaa793 Binary files /dev/null and b/.yarn/cache/@types-sinon-npm-9.0.11-231734b808-c84ccbd5ac.zip differ diff --git a/.yarn/cache/@types-sinonjs__fake-timers-npm-6.0.3-7a45cf3bad-2f2accb87e.zip b/.yarn/cache/@types-sinonjs__fake-timers-npm-6.0.3-7a45cf3bad-2f2accb87e.zip new file mode 100644 index 00000000..8e1c4ffe Binary files /dev/null and b/.yarn/cache/@types-sinonjs__fake-timers-npm-6.0.3-7a45cf3bad-2f2accb87e.zip differ diff --git a/.yarn/cache/@types-source-map-support-npm-0.5.4-1c4eb2be20-448a3aa4fa.zip b/.yarn/cache/@types-source-map-support-npm-0.5.4-1c4eb2be20-448a3aa4fa.zip new file mode 100644 index 00000000..310d1742 Binary files /dev/null and b/.yarn/cache/@types-source-map-support-npm-0.5.4-1c4eb2be20-448a3aa4fa.zip differ diff --git a/.yarn/cache/@types-yargs-npm-15.0.5-18a2128a57-2133c8cb58.zip b/.yarn/cache/@types-yargs-npm-15.0.5-18a2128a57-2133c8cb58.zip deleted file mode 100644 index ab30d170..00000000 Binary files a/.yarn/cache/@types-yargs-npm-15.0.5-18a2128a57-2133c8cb58.zip and /dev/null differ diff --git a/.yarn/cache/@types-yargs-parser-npm-15.0.0-db1d59832c-74bfaefde9.zip b/.yarn/cache/@types-yargs-parser-npm-15.0.0-db1d59832c-74bfaefde9.zip deleted file mode 100644 index 7dbfe25a..00000000 Binary files a/.yarn/cache/@types-yargs-parser-npm-15.0.0-db1d59832c-74bfaefde9.zip and /dev/null differ diff --git a/.yarn/cache/@ungap-promise-all-settled-npm-1.1.2-c0f42e147b-be6c80a2fc.zip b/.yarn/cache/@ungap-promise-all-settled-npm-1.1.2-c0f42e147b-be6c80a2fc.zip new file mode 100644 index 00000000..ee29c2e8 Binary files /dev/null and b/.yarn/cache/@ungap-promise-all-settled-npm-1.1.2-c0f42e147b-be6c80a2fc.zip differ diff --git a/.yarn/cache/acorn-npm-8.5.0-faed0ea119-989ff8bf4b.zip b/.yarn/cache/acorn-npm-8.5.0-faed0ea119-989ff8bf4b.zip new file mode 100644 index 00000000..3d1432bc Binary files /dev/null and b/.yarn/cache/acorn-npm-8.5.0-faed0ea119-989ff8bf4b.zip differ diff --git a/.yarn/cache/acorn-walk-npm-8.2.0-2f2cac3177-93ffbdb8b3.zip b/.yarn/cache/acorn-walk-npm-8.2.0-2f2cac3177-93ffbdb8b3.zip new file mode 100644 index 00000000..2d58781a Binary files /dev/null and b/.yarn/cache/acorn-walk-npm-8.2.0-2f2cac3177-93ffbdb8b3.zip differ diff --git a/.yarn/cache/ajv-npm-6.12.4-c4dcb5109f-50d72b0a10.zip b/.yarn/cache/ajv-npm-6.12.4-c4dcb5109f-50d72b0a10.zip deleted file mode 100644 index eb5d2f76..00000000 Binary files a/.yarn/cache/ajv-npm-6.12.4-c4dcb5109f-50d72b0a10.zip and /dev/null differ diff --git a/.yarn/cache/ansi-escapes-npm-4.3.1-f4aad61b5b-bcb39e57bd.zip b/.yarn/cache/ansi-escapes-npm-4.3.1-f4aad61b5b-bcb39e57bd.zip deleted file mode 100644 index 2019ab41..00000000 Binary files a/.yarn/cache/ansi-escapes-npm-4.3.1-f4aad61b5b-bcb39e57bd.zip and /dev/null differ diff --git a/.yarn/cache/anymatch-npm-3.1.1-7dcfa6178a-cf61bbaf7f.zip b/.yarn/cache/anymatch-npm-3.1.2-1d5471acfa-cd6c08eb8d.zip similarity index 64% rename from .yarn/cache/anymatch-npm-3.1.1-7dcfa6178a-cf61bbaf7f.zip rename to .yarn/cache/anymatch-npm-3.1.2-1d5471acfa-cd6c08eb8d.zip index cfe679e3..87d2af76 100644 Binary files a/.yarn/cache/anymatch-npm-3.1.1-7dcfa6178a-cf61bbaf7f.zip and b/.yarn/cache/anymatch-npm-3.1.2-1d5471acfa-cd6c08eb8d.zip differ diff --git a/.yarn/cache/append-transform-npm-2.0.0-99bd7d69ed-7fd09e1d1f.zip b/.yarn/cache/append-transform-npm-2.0.0-99bd7d69ed-7fd09e1d1f.zip new file mode 100644 index 00000000..1165bb4f Binary files /dev/null and b/.yarn/cache/append-transform-npm-2.0.0-99bd7d69ed-7fd09e1d1f.zip differ diff --git a/.yarn/cache/archy-npm-1.0.0-7db8bfdc3b-fed06a0487.zip b/.yarn/cache/archy-npm-1.0.0-7db8bfdc3b-fed06a0487.zip new file mode 100644 index 00000000..19ed6718 Binary files /dev/null and b/.yarn/cache/archy-npm-1.0.0-7db8bfdc3b-fed06a0487.zip differ diff --git a/.yarn/cache/argparse-npm-2.0.1-faff7999e6-160b7a25d2.zip b/.yarn/cache/argparse-npm-2.0.1-faff7999e6-160b7a25d2.zip new file mode 100644 index 00000000..02d76b10 Binary files /dev/null and b/.yarn/cache/argparse-npm-2.0.1-faff7999e6-160b7a25d2.zip differ diff --git a/.yarn/cache/arweave-mnemonic-keys-npm-0.0.9-d1e53ee8b7-4e04c4726b.zip b/.yarn/cache/arweave-mnemonic-keys-npm-0.0.9-d1e53ee8b7-4e04c4726b.zip new file mode 100644 index 00000000..090bcaae Binary files /dev/null and b/.yarn/cache/arweave-mnemonic-keys-npm-0.0.9-d1e53ee8b7-4e04c4726b.zip differ diff --git a/.yarn/cache/arweave-npm-1.10.11-a663c3e819-40d32d1a68.zip b/.yarn/cache/arweave-npm-1.10.11-a663c3e819-40d32d1a68.zip deleted file mode 100644 index 139944d1..00000000 Binary files a/.yarn/cache/arweave-npm-1.10.11-a663c3e819-40d32d1a68.zip and /dev/null differ diff --git a/.yarn/cache/arweave-npm-1.10.13-28daec638a-5da8a3f5cc.zip b/.yarn/cache/arweave-npm-1.10.13-28daec638a-5da8a3f5cc.zip deleted file mode 100644 index aa86f5b4..00000000 Binary files a/.yarn/cache/arweave-npm-1.10.13-28daec638a-5da8a3f5cc.zip and /dev/null differ diff --git a/.yarn/cache/assertion-error-npm-1.1.0-66b893015e-7bbc9fa2ff.zip b/.yarn/cache/assertion-error-npm-1.1.0-66b893015e-7bbc9fa2ff.zip new file mode 100644 index 00000000..e2533b5f Binary files /dev/null and b/.yarn/cache/assertion-error-npm-1.1.0-66b893015e-7bbc9fa2ff.zip differ diff --git a/.yarn/cache/base64-js-npm-1.3.1-8625be908e-8a0cc69d7c.zip b/.yarn/cache/base64-js-npm-1.3.1-8625be908e-8a0cc69d7c.zip deleted file mode 100644 index c51a1415..00000000 Binary files a/.yarn/cache/base64-js-npm-1.3.1-8625be908e-8a0cc69d7c.zip and /dev/null differ diff --git a/.yarn/cache/base64-js-npm-1.5.1-b2f7275641-c1b41a26dd.zip b/.yarn/cache/base64-js-npm-1.5.1-b2f7275641-c1b41a26dd.zip new file mode 100644 index 00000000..84be2c49 Binary files /dev/null and b/.yarn/cache/base64-js-npm-1.5.1-b2f7275641-c1b41a26dd.zip differ diff --git a/.yarn/cache/bip39-npm-3.0.4-7c69c9182f-24359ef61f.zip b/.yarn/cache/bip39-npm-3.0.4-7c69c9182f-24359ef61f.zip new file mode 100644 index 00000000..2d5b0dfc Binary files /dev/null and b/.yarn/cache/bip39-npm-3.0.4-7c69c9182f-24359ef61f.zip differ diff --git a/.yarn/cache/bn.js-npm-4.11.9-c739f92b89-31630d3560.zip b/.yarn/cache/bn.js-npm-4.11.9-c739f92b89-31630d3560.zip deleted file mode 100644 index 9baf186a..00000000 Binary files a/.yarn/cache/bn.js-npm-4.11.9-c739f92b89-31630d3560.zip and /dev/null differ diff --git a/.yarn/cache/browser-stdout-npm-1.3.1-6b2376bf3f-2f91b1ad26.zip b/.yarn/cache/browser-stdout-npm-1.3.1-6b2376bf3f-2f91b1ad26.zip new file mode 100644 index 00000000..f8129a23 Binary files /dev/null and b/.yarn/cache/browser-stdout-npm-1.3.1-6b2376bf3f-2f91b1ad26.zip differ diff --git a/.yarn/cache/browserslist-npm-4.17.0-98801cc7f5-e7c4b78520.zip b/.yarn/cache/browserslist-npm-4.17.0-98801cc7f5-e7c4b78520.zip new file mode 100644 index 00000000..081abca2 Binary files /dev/null and b/.yarn/cache/browserslist-npm-4.17.0-98801cc7f5-e7c4b78520.zip differ diff --git a/.yarn/cache/caching-transform-npm-4.0.0-d619d562ea-0a9e0c317e.zip b/.yarn/cache/caching-transform-npm-4.0.0-d619d562ea-0a9e0c317e.zip new file mode 100644 index 00000000..7f93b03f Binary files /dev/null and b/.yarn/cache/caching-transform-npm-4.0.0-d619d562ea-0a9e0c317e.zip differ diff --git a/.yarn/cache/camelcase-npm-5.3.1-5db8af62c5-6a3350c4ea.zip b/.yarn/cache/camelcase-npm-5.3.1-5db8af62c5-6a3350c4ea.zip new file mode 100644 index 00000000..7a8b5604 Binary files /dev/null and b/.yarn/cache/camelcase-npm-5.3.1-5db8af62c5-6a3350c4ea.zip differ diff --git a/.yarn/cache/camelcase-npm-6.2.0-69f8c130ac-654700600a.zip b/.yarn/cache/camelcase-npm-6.2.0-69f8c130ac-654700600a.zip new file mode 100644 index 00000000..23ed3a6d Binary files /dev/null and b/.yarn/cache/camelcase-npm-6.2.0-69f8c130ac-654700600a.zip differ diff --git a/.yarn/cache/caniuse-lite-npm-1.0.30001257-10e3a8c80e-2d39f401d7.zip b/.yarn/cache/caniuse-lite-npm-1.0.30001257-10e3a8c80e-2d39f401d7.zip new file mode 100644 index 00000000..d47b2689 Binary files /dev/null and b/.yarn/cache/caniuse-lite-npm-1.0.30001257-10e3a8c80e-2d39f401d7.zip differ diff --git a/.yarn/cache/chai-npm-4.3.4-808f3b5355-ea3e6547b9.zip b/.yarn/cache/chai-npm-4.3.4-808f3b5355-ea3e6547b9.zip new file mode 100644 index 00000000..1b658a69 Binary files /dev/null and b/.yarn/cache/chai-npm-4.3.4-808f3b5355-ea3e6547b9.zip differ diff --git a/.yarn/cache/chalk-npm-3.0.0-e813208025-4018b0c812.zip b/.yarn/cache/chalk-npm-3.0.0-e813208025-4018b0c812.zip deleted file mode 100644 index bd1f5c64..00000000 Binary files a/.yarn/cache/chalk-npm-3.0.0-e813208025-4018b0c812.zip and /dev/null differ diff --git a/.yarn/cache/chalk-npm-4.1.0-c746e252ba-f860285b41.zip b/.yarn/cache/chalk-npm-4.1.0-c746e252ba-f860285b41.zip deleted file mode 100644 index 36efe276..00000000 Binary files a/.yarn/cache/chalk-npm-4.1.0-c746e252ba-f860285b41.zip and /dev/null differ diff --git a/.yarn/cache/chalk-npm-4.1.1-f1ce6bae57-445c12db7a.zip b/.yarn/cache/chalk-npm-4.1.1-f1ce6bae57-445c12db7a.zip deleted file mode 100644 index 7422fb61..00000000 Binary files a/.yarn/cache/chalk-npm-4.1.1-f1ce6bae57-445c12db7a.zip and /dev/null differ diff --git a/.yarn/cache/check-error-npm-1.0.2-00c540c6e9-1460ad12da.zip b/.yarn/cache/check-error-npm-1.0.2-00c540c6e9-1460ad12da.zip new file mode 100644 index 00000000..643a1501 Binary files /dev/null and b/.yarn/cache/check-error-npm-1.0.2-00c540c6e9-1460ad12da.zip differ diff --git a/.yarn/cache/chokidar-npm-3.5.1-205217279e-61b3f710f9.zip b/.yarn/cache/chokidar-npm-3.5.1-205217279e-61b3f710f9.zip deleted file mode 100644 index 90033e96..00000000 Binary files a/.yarn/cache/chokidar-npm-3.5.1-205217279e-61b3f710f9.zip and /dev/null differ diff --git a/.yarn/cache/chokidar-npm-3.5.2-6752340fec-52fbff3ace.zip b/.yarn/cache/chokidar-npm-3.5.2-6752340fec-52fbff3ace.zip new file mode 100644 index 00000000..beba3d2b Binary files /dev/null and b/.yarn/cache/chokidar-npm-3.5.2-6752340fec-52fbff3ace.zip differ diff --git a/.yarn/cache/cipher-base-npm-1.0.4-2e98b97140-ec80001ec9.zip b/.yarn/cache/cipher-base-npm-1.0.4-2e98b97140-ec80001ec9.zip new file mode 100644 index 00000000..ecddbfc5 Binary files /dev/null and b/.yarn/cache/cipher-base-npm-1.0.4-2e98b97140-ec80001ec9.zip differ diff --git a/.yarn/cache/cliui-npm-6.0.0-488b2414c6-e59d064294.zip b/.yarn/cache/cliui-npm-6.0.0-488b2414c6-e59d064294.zip new file mode 100644 index 00000000..d3f326ac Binary files /dev/null and b/.yarn/cache/cliui-npm-6.0.0-488b2414c6-e59d064294.zip differ diff --git a/.yarn/cache/colorette-npm-1.2.2-da75bd0b32-e240f0c94b.zip b/.yarn/cache/colorette-npm-1.2.2-da75bd0b32-e240f0c94b.zip deleted file mode 100644 index 4cb43f6e..00000000 Binary files a/.yarn/cache/colorette-npm-1.2.2-da75bd0b32-e240f0c94b.zip and /dev/null differ diff --git a/.yarn/cache/colorette-npm-1.4.0-7e94b44dc3-7ef8e1ca16.zip b/.yarn/cache/colorette-npm-1.4.0-7e94b44dc3-7ef8e1ca16.zip new file mode 100644 index 00000000..02fa1350 Binary files /dev/null and b/.yarn/cache/colorette-npm-1.4.0-7e94b44dc3-7ef8e1ca16.zip differ diff --git a/.yarn/cache/commander-npm-8.2.0-c925691796-e41e680f2a.zip b/.yarn/cache/commander-npm-8.2.0-c925691796-e41e680f2a.zip new file mode 100644 index 00000000..82823c8e Binary files /dev/null and b/.yarn/cache/commander-npm-8.2.0-c925691796-e41e680f2a.zip differ diff --git a/.yarn/cache/commondir-npm-1.0.1-291b790340-98f18ad14f.zip b/.yarn/cache/commondir-npm-1.0.1-291b790340-98f18ad14f.zip new file mode 100644 index 00000000..5e61d38a Binary files /dev/null and b/.yarn/cache/commondir-npm-1.0.1-291b790340-98f18ad14f.zip differ diff --git a/.yarn/cache/community-js-npm-1.1.36-edb2db2761-940cd0d27e.zip b/.yarn/cache/community-js-npm-1.1.36-edb2db2761-940cd0d27e.zip deleted file mode 100644 index 3955a4d6..00000000 Binary files a/.yarn/cache/community-js-npm-1.1.36-edb2db2761-940cd0d27e.zip and /dev/null differ diff --git a/.yarn/cache/create-hash-npm-1.2.0-afd048e1ce-5565182efc.zip b/.yarn/cache/create-hash-npm-1.2.0-afd048e1ce-5565182efc.zip new file mode 100644 index 00000000..6783989f Binary files /dev/null and b/.yarn/cache/create-hash-npm-1.2.0-afd048e1ce-5565182efc.zip differ diff --git a/.yarn/cache/create-hmac-npm-1.1.7-b4ef32668a-98957676a9.zip b/.yarn/cache/create-hmac-npm-1.1.7-b4ef32668a-98957676a9.zip new file mode 100644 index 00000000..20407f29 Binary files /dev/null and b/.yarn/cache/create-hmac-npm-1.1.7-b4ef32668a-98957676a9.zip differ diff --git a/.yarn/cache/crypto-key-composer-npm-0.1.3-8dd1e39879-48f6d45d2a.zip b/.yarn/cache/crypto-key-composer-npm-0.1.3-8dd1e39879-48f6d45d2a.zip new file mode 100644 index 00000000..3c8f322e Binary files /dev/null and b/.yarn/cache/crypto-key-composer-npm-0.1.3-8dd1e39879-48f6d45d2a.zip differ diff --git a/.yarn/cache/debug-npm-4.1.1-540248b3aa-3601a6ce96.zip b/.yarn/cache/debug-npm-4.1.1-540248b3aa-3601a6ce96.zip deleted file mode 100644 index 68f7ff84..00000000 Binary files a/.yarn/cache/debug-npm-4.1.1-540248b3aa-3601a6ce96.zip and /dev/null differ diff --git a/.yarn/cache/debug-npm-4.3.1-22e08d605e-0d41ba5177.zip b/.yarn/cache/debug-npm-4.3.1-22e08d605e-0d41ba5177.zip new file mode 100644 index 00000000..9024dcff Binary files /dev/null and b/.yarn/cache/debug-npm-4.3.1-22e08d605e-0d41ba5177.zip differ diff --git a/.yarn/cache/decamelize-npm-1.2.0-c5a2fdc622-8ca9d03ea8.zip b/.yarn/cache/decamelize-npm-1.2.0-c5a2fdc622-8ca9d03ea8.zip new file mode 100644 index 00000000..29f34713 Binary files /dev/null and b/.yarn/cache/decamelize-npm-1.2.0-c5a2fdc622-8ca9d03ea8.zip differ diff --git a/.yarn/cache/decamelize-npm-4.0.0-12410e3409-3846161a3b.zip b/.yarn/cache/decamelize-npm-4.0.0-12410e3409-3846161a3b.zip new file mode 100644 index 00000000..fec9da3a Binary files /dev/null and b/.yarn/cache/decamelize-npm-4.0.0-12410e3409-3846161a3b.zip differ diff --git a/.yarn/cache/deep-eql-npm-3.0.1-9a66c09c65-eff42bc2d4.zip b/.yarn/cache/deep-eql-npm-3.0.1-9a66c09c65-eff42bc2d4.zip new file mode 100644 index 00000000..2f9dbde6 Binary files /dev/null and b/.yarn/cache/deep-eql-npm-3.0.1-9a66c09c65-eff42bc2d4.zip differ diff --git a/.yarn/cache/deep-for-each-npm-3.0.0-5aa901f30c-88c1dc9f15.zip b/.yarn/cache/deep-for-each-npm-3.0.0-5aa901f30c-88c1dc9f15.zip new file mode 100644 index 00000000..5f67026f Binary files /dev/null and b/.yarn/cache/deep-for-each-npm-3.0.0-5aa901f30c-88c1dc9f15.zip differ diff --git a/.yarn/cache/default-require-extensions-npm-3.0.0-40586718d6-c53a7ddfa1.zip b/.yarn/cache/default-require-extensions-npm-3.0.0-40586718d6-c53a7ddfa1.zip new file mode 100644 index 00000000..d09bb173 Binary files /dev/null and b/.yarn/cache/default-require-extensions-npm-3.0.0-40586718d6-c53a7ddfa1.zip differ diff --git a/.yarn/cache/diff-npm-5.0.0-ad6900db18-ef241d3b20.zip b/.yarn/cache/diff-npm-5.0.0-ad6900db18-ef241d3b20.zip new file mode 100644 index 00000000..621a659a Binary files /dev/null and b/.yarn/cache/diff-npm-5.0.0-ad6900db18-ef241d3b20.zip differ diff --git a/.yarn/cache/diff-sequences-npm-25.2.6-d72e0e66bc-332484fc00.zip b/.yarn/cache/diff-sequences-npm-25.2.6-d72e0e66bc-332484fc00.zip deleted file mode 100644 index 14e061e8..00000000 Binary files a/.yarn/cache/diff-sequences-npm-25.2.6-d72e0e66bc-332484fc00.zip and /dev/null differ diff --git a/.yarn/cache/electron-to-chromium-npm-1.3.836-0df58045fd-4cb506937e.zip b/.yarn/cache/electron-to-chromium-npm-1.3.836-0df58045fd-4cb506937e.zip new file mode 100644 index 00000000..2d65b9a5 Binary files /dev/null and b/.yarn/cache/electron-to-chromium-npm-1.3.836-0df58045fd-4cb506937e.zip differ diff --git a/.yarn/cache/err-code-npm-3.0.1-3a0dc5fc51-ddae1b8c69.zip b/.yarn/cache/err-code-npm-3.0.1-3a0dc5fc51-ddae1b8c69.zip new file mode 100644 index 00000000..21e99892 Binary files /dev/null and b/.yarn/cache/err-code-npm-3.0.1-3a0dc5fc51-ddae1b8c69.zip differ diff --git a/.yarn/cache/es6-error-npm-4.1.1-5e8c22b20f-d7343d3f47.zip b/.yarn/cache/es6-error-npm-4.1.1-5e8c22b20f-d7343d3f47.zip new file mode 100644 index 00000000..492b9994 Binary files /dev/null and b/.yarn/cache/es6-error-npm-4.1.1-5e8c22b20f-d7343d3f47.zip differ diff --git a/.yarn/cache/escape-string-regexp-npm-2.0.0-aef69d2a25-f3500f264e.zip b/.yarn/cache/escape-string-regexp-npm-2.0.0-aef69d2a25-f3500f264e.zip new file mode 100644 index 00000000..d01792c0 Binary files /dev/null and b/.yarn/cache/escape-string-regexp-npm-2.0.0-aef69d2a25-f3500f264e.zip differ diff --git a/.yarn/cache/escape-string-regexp-npm-4.0.0-4b531d8d59-c747be8d5f.zip b/.yarn/cache/escape-string-regexp-npm-4.0.0-4b531d8d59-c747be8d5f.zip new file mode 100644 index 00000000..07a458b1 Binary files /dev/null and b/.yarn/cache/escape-string-regexp-npm-4.0.0-4b531d8d59-c747be8d5f.zip differ diff --git a/.yarn/cache/events-npm-3.3.0-c280bc7e48-56fa125670.zip b/.yarn/cache/events-npm-3.3.0-c280bc7e48-56fa125670.zip new file mode 100644 index 00000000..879b7b50 Binary files /dev/null and b/.yarn/cache/events-npm-3.3.0-c280bc7e48-56fa125670.zip differ diff --git a/.yarn/cache/figlet-npm-1.5.0-8014ee3b57-49839d8179.zip b/.yarn/cache/figlet-npm-1.5.0-8014ee3b57-49839d8179.zip deleted file mode 100644 index 747c3240..00000000 Binary files a/.yarn/cache/figlet-npm-1.5.0-8014ee3b57-49839d8179.zip and /dev/null differ diff --git a/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-5330b81922.zip b/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-5330b81922.zip new file mode 100644 index 00000000..d9ba1213 Binary files /dev/null and b/.yarn/cache/find-cache-dir-npm-3.3.2-836e68dd83-5330b81922.zip differ diff --git a/.yarn/cache/find-up-npm-4.1.0-c3ccf8d855-d612d28e02.zip b/.yarn/cache/find-up-npm-4.1.0-c3ccf8d855-d612d28e02.zip new file mode 100644 index 00000000..b2b527b8 Binary files /dev/null and b/.yarn/cache/find-up-npm-4.1.0-c3ccf8d855-d612d28e02.zip differ diff --git a/.yarn/cache/find-up-npm-5.0.0-e03e9b796d-cd0b77415b.zip b/.yarn/cache/find-up-npm-5.0.0-e03e9b796d-cd0b77415b.zip new file mode 100644 index 00000000..8783897b Binary files /dev/null and b/.yarn/cache/find-up-npm-5.0.0-e03e9b796d-cd0b77415b.zip differ diff --git a/.yarn/cache/flat-npm-5.0.2-12748102a5-549b3012e9.zip b/.yarn/cache/flat-npm-5.0.2-12748102a5-549b3012e9.zip new file mode 100644 index 00000000..3f06ab3a Binary files /dev/null and b/.yarn/cache/flat-npm-5.0.2-12748102a5-549b3012e9.zip differ diff --git a/.yarn/cache/foreground-child-npm-2.0.0-80c976b61e-661c7adbc1.zip b/.yarn/cache/foreground-child-npm-2.0.0-80c976b61e-661c7adbc1.zip new file mode 100644 index 00000000..9f158093 Binary files /dev/null and b/.yarn/cache/foreground-child-npm-2.0.0-80c976b61e-661c7adbc1.zip differ diff --git a/.yarn/cache/fromentries-npm-1.3.2-f5392090b8-5cc722e4e3.zip b/.yarn/cache/fromentries-npm-1.3.2-f5392090b8-5cc722e4e3.zip new file mode 100644 index 00000000..8e54c7a6 Binary files /dev/null and b/.yarn/cache/fromentries-npm-1.3.2-f5392090b8-5cc722e4e3.zip differ diff --git a/.yarn/cache/gensync-npm-1.0.0-beta.2-224666d72f-d523437689.zip b/.yarn/cache/gensync-npm-1.0.0-beta.2-224666d72f-d523437689.zip new file mode 100644 index 00000000..b72f6611 Binary files /dev/null and b/.yarn/cache/gensync-npm-1.0.0-beta.2-224666d72f-d523437689.zip differ diff --git a/.yarn/cache/get-func-name-npm-2.0.0-afbf363765-c72d3857cd.zip b/.yarn/cache/get-func-name-npm-2.0.0-afbf363765-c72d3857cd.zip new file mode 100644 index 00000000..faeeb3d1 Binary files /dev/null and b/.yarn/cache/get-func-name-npm-2.0.0-afbf363765-c72d3857cd.zip differ diff --git a/.yarn/cache/get-package-type-npm-0.1.0-6c70cdc8ab-a5b8beaf68.zip b/.yarn/cache/get-package-type-npm-0.1.0-6c70cdc8ab-a5b8beaf68.zip new file mode 100644 index 00000000..bc6bfbc6 Binary files /dev/null and b/.yarn/cache/get-package-type-npm-0.1.0-6c70cdc8ab-a5b8beaf68.zip differ diff --git a/.yarn/cache/glob-npm-7.1.6-1ce3a5189a-789977b524.zip b/.yarn/cache/glob-npm-7.1.6-1ce3a5189a-789977b524.zip deleted file mode 100644 index aa993581..00000000 Binary files a/.yarn/cache/glob-npm-7.1.6-1ce3a5189a-789977b524.zip and /dev/null differ diff --git a/.yarn/cache/glob-npm-7.1.7-5698ad9c48-352f74f082.zip b/.yarn/cache/glob-npm-7.1.7-5698ad9c48-352f74f082.zip new file mode 100644 index 00000000..a7e40da6 Binary files /dev/null and b/.yarn/cache/glob-npm-7.1.7-5698ad9c48-352f74f082.zip differ diff --git a/.yarn/cache/glob-parent-npm-5.1.1-57b061cd88-2af6e196fb.zip b/.yarn/cache/glob-parent-npm-5.1.1-57b061cd88-2af6e196fb.zip deleted file mode 100644 index 1cdaca19..00000000 Binary files a/.yarn/cache/glob-parent-npm-5.1.1-57b061cd88-2af6e196fb.zip and /dev/null differ diff --git a/.yarn/cache/globals-npm-11.12.0-1fa7f41a6c-2563d3306a.zip b/.yarn/cache/globals-npm-11.12.0-1fa7f41a6c-2563d3306a.zip new file mode 100644 index 00000000..ebf18b4a Binary files /dev/null and b/.yarn/cache/globals-npm-11.12.0-1fa7f41a6c-2563d3306a.zip differ diff --git a/.yarn/cache/graceful-fs-npm-4.2.6-535b2234f1-84d39c7756.zip b/.yarn/cache/graceful-fs-npm-4.2.6-535b2234f1-84d39c7756.zip deleted file mode 100644 index 25705787..00000000 Binary files a/.yarn/cache/graceful-fs-npm-4.2.6-535b2234f1-84d39c7756.zip and /dev/null differ diff --git a/.yarn/cache/graceful-fs-npm-4.2.8-37c16fc3d3-b07e032c0a.zip b/.yarn/cache/graceful-fs-npm-4.2.8-37c16fc3d3-b07e032c0a.zip new file mode 100644 index 00000000..831c6fc8 Binary files /dev/null and b/.yarn/cache/graceful-fs-npm-4.2.8-37c16fc3d3-b07e032c0a.zip differ diff --git a/.yarn/cache/growl-npm-1.10.5-2d1da54198-e1dae8dde6.zip b/.yarn/cache/growl-npm-1.10.5-2d1da54198-e1dae8dde6.zip new file mode 100644 index 00000000..a9d956a5 Binary files /dev/null and b/.yarn/cache/growl-npm-1.10.5-2d1da54198-e1dae8dde6.zip differ diff --git a/.yarn/cache/hash-base-npm-3.1.0-26fc5711dd-9f4b0d183d.zip b/.yarn/cache/hash-base-npm-3.1.0-26fc5711dd-9f4b0d183d.zip new file mode 100644 index 00000000..a6c3916c Binary files /dev/null and b/.yarn/cache/hash-base-npm-3.1.0-26fc5711dd-9f4b0d183d.zip differ diff --git a/.yarn/cache/hasha-npm-5.2.2-d171116d12-50adf6a312.zip b/.yarn/cache/hasha-npm-5.2.2-d171116d12-50adf6a312.zip new file mode 100644 index 00000000..05f1dc2b Binary files /dev/null and b/.yarn/cache/hasha-npm-5.2.2-d171116d12-50adf6a312.zip differ diff --git a/.yarn/cache/he-npm-1.2.0-3b73a2ff07-212122003c.zip b/.yarn/cache/he-npm-1.2.0-3b73a2ff07-212122003c.zip new file mode 100644 index 00000000..ea7fa80c Binary files /dev/null and b/.yarn/cache/he-npm-1.2.0-3b73a2ff07-212122003c.zip differ diff --git a/.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-a216ae96fa.zip b/.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-a216ae96fa.zip new file mode 100644 index 00000000..33b9bfe5 Binary files /dev/null and b/.yarn/cache/html-escaper-npm-2.0.2-38e51ef294-a216ae96fa.zip differ diff --git a/.yarn/cache/human-crypto-keys-npm-0.1.4-352fe0a6a6-594ae91b0f.zip b/.yarn/cache/human-crypto-keys-npm-0.1.4-352fe0a6a6-594ae91b0f.zip new file mode 100644 index 00000000..f223f361 Binary files /dev/null and b/.yarn/cache/human-crypto-keys-npm-0.1.4-352fe0a6a6-594ae91b0f.zip differ diff --git a/.yarn/cache/inquirer-npm-7.3.3-9e86782610-fa0cbd9594.zip b/.yarn/cache/inquirer-npm-7.3.3-9e86782610-fa0cbd9594.zip deleted file mode 100644 index a3a275b2..00000000 Binary files a/.yarn/cache/inquirer-npm-7.3.3-9e86782610-fa0cbd9594.zip and /dev/null differ diff --git a/.yarn/cache/is-plain-obj-npm-2.1.0-8dffd7ae9c-2314302f91.zip b/.yarn/cache/is-plain-obj-npm-2.1.0-8dffd7ae9c-2314302f91.zip new file mode 100644 index 00000000..aa8f20d9 Binary files /dev/null and b/.yarn/cache/is-plain-obj-npm-2.1.0-8dffd7ae9c-2314302f91.zip differ diff --git a/.yarn/cache/isarray-npm-0.0.1-92e37e0a70-daeda3c236.zip b/.yarn/cache/isarray-npm-0.0.1-92e37e0a70-daeda3c236.zip new file mode 100644 index 00000000..ff4758ec Binary files /dev/null and b/.yarn/cache/isarray-npm-0.0.1-92e37e0a70-daeda3c236.zip differ diff --git a/.yarn/cache/iso-random-stream-npm-2.0.0-bbb11744dc-5dc5c37ecb.zip b/.yarn/cache/iso-random-stream-npm-2.0.0-bbb11744dc-5dc5c37ecb.zip new file mode 100644 index 00000000..7b1c1a7a Binary files /dev/null and b/.yarn/cache/iso-random-stream-npm-2.0.0-bbb11744dc-5dc5c37ecb.zip differ diff --git a/.yarn/cache/istanbul-lib-coverage-npm-3.0.0-654bb0146d-c8effc09ae.zip b/.yarn/cache/istanbul-lib-coverage-npm-3.0.0-654bb0146d-c8effc09ae.zip new file mode 100644 index 00000000..1f2b7f1e Binary files /dev/null and b/.yarn/cache/istanbul-lib-coverage-npm-3.0.0-654bb0146d-c8effc09ae.zip differ diff --git a/.yarn/cache/istanbul-lib-hook-npm-3.0.0-be73f95173-7b5fa35741.zip b/.yarn/cache/istanbul-lib-hook-npm-3.0.0-be73f95173-7b5fa35741.zip new file mode 100644 index 00000000..fdf6e4cd Binary files /dev/null and b/.yarn/cache/istanbul-lib-hook-npm-3.0.0-be73f95173-7b5fa35741.zip differ diff --git a/.yarn/cache/istanbul-lib-instrument-npm-4.0.3-4d4c2263f8-478e43e75d.zip b/.yarn/cache/istanbul-lib-instrument-npm-4.0.3-4d4c2263f8-478e43e75d.zip new file mode 100644 index 00000000..91a51718 Binary files /dev/null and b/.yarn/cache/istanbul-lib-instrument-npm-4.0.3-4d4c2263f8-478e43e75d.zip differ diff --git a/.yarn/cache/istanbul-lib-processinfo-npm-2.0.2-74916fa6cb-ab1e7cb67f.zip b/.yarn/cache/istanbul-lib-processinfo-npm-2.0.2-74916fa6cb-ab1e7cb67f.zip new file mode 100644 index 00000000..498a4ee1 Binary files /dev/null and b/.yarn/cache/istanbul-lib-processinfo-npm-2.0.2-74916fa6cb-ab1e7cb67f.zip differ diff --git a/.yarn/cache/istanbul-lib-report-npm-3.0.0-660f97340a-aada59dfce.zip b/.yarn/cache/istanbul-lib-report-npm-3.0.0-660f97340a-aada59dfce.zip new file mode 100644 index 00000000..f2bdc930 Binary files /dev/null and b/.yarn/cache/istanbul-lib-report-npm-3.0.0-660f97340a-aada59dfce.zip differ diff --git a/.yarn/cache/istanbul-lib-source-maps-npm-4.0.0-def3895674-018b5feeb4.zip b/.yarn/cache/istanbul-lib-source-maps-npm-4.0.0-def3895674-018b5feeb4.zip new file mode 100644 index 00000000..3edea62b Binary files /dev/null and b/.yarn/cache/istanbul-lib-source-maps-npm-4.0.0-def3895674-018b5feeb4.zip differ diff --git a/.yarn/cache/istanbul-reports-npm-3.0.2-6ccd67e17e-d4ed416e13.zip b/.yarn/cache/istanbul-reports-npm-3.0.2-6ccd67e17e-d4ed416e13.zip new file mode 100644 index 00000000..f2801f9d Binary files /dev/null and b/.yarn/cache/istanbul-reports-npm-3.0.2-6ccd67e17e-d4ed416e13.zip differ diff --git a/.yarn/cache/jest-diff-npm-25.5.0-dfbc320001-14a2634ecb.zip b/.yarn/cache/jest-diff-npm-25.5.0-dfbc320001-14a2634ecb.zip deleted file mode 100644 index 5c85933b..00000000 Binary files a/.yarn/cache/jest-diff-npm-25.5.0-dfbc320001-14a2634ecb.zip and /dev/null differ diff --git a/.yarn/cache/jest-get-type-npm-25.2.6-7abd790493-6051fcb75c.zip b/.yarn/cache/jest-get-type-npm-25.2.6-7abd790493-6051fcb75c.zip deleted file mode 100644 index 6ccbbb34..00000000 Binary files a/.yarn/cache/jest-get-type-npm-25.2.6-7abd790493-6051fcb75c.zip and /dev/null differ diff --git a/.yarn/cache/js-base64-npm-3.6.0-987f8cbc88-6959097159.zip b/.yarn/cache/js-base64-npm-3.6.0-987f8cbc88-6959097159.zip deleted file mode 100644 index 7ed26ca9..00000000 Binary files a/.yarn/cache/js-base64-npm-3.6.0-987f8cbc88-6959097159.zip and /dev/null differ diff --git a/.yarn/cache/js-yaml-npm-4.1.0-3606f32312-8973cf4296.zip b/.yarn/cache/js-yaml-npm-4.1.0-3606f32312-8973cf4296.zip new file mode 100644 index 00000000..f06e0048 Binary files /dev/null and b/.yarn/cache/js-yaml-npm-4.1.0-3606f32312-8973cf4296.zip differ diff --git a/.yarn/cache/jsesc-npm-2.5.2-c5acb78804-ca91ec33d7.zip b/.yarn/cache/jsesc-npm-2.5.2-c5acb78804-ca91ec33d7.zip new file mode 100644 index 00000000..bb6eeb71 Binary files /dev/null and b/.yarn/cache/jsesc-npm-2.5.2-c5acb78804-ca91ec33d7.zip differ diff --git a/.yarn/cache/json5-npm-2.2.0-da49dc7cb5-07b1f90c28.zip b/.yarn/cache/json5-npm-2.2.0-da49dc7cb5-07b1f90c28.zip new file mode 100644 index 00000000..4163029d Binary files /dev/null and b/.yarn/cache/json5-npm-2.2.0-da49dc7cb5-07b1f90c28.zip differ diff --git a/.yarn/cache/just-extend-npm-4.2.1-ccc4201277-4fb49b328a.zip b/.yarn/cache/just-extend-npm-4.2.1-ccc4201277-4fb49b328a.zip new file mode 100644 index 00000000..5ff665fa Binary files /dev/null and b/.yarn/cache/just-extend-npm-4.2.1-ccc4201277-4fb49b328a.zip differ diff --git a/.yarn/cache/keypair-npm-1.0.3-f098145e16-1be4b14da7.zip b/.yarn/cache/keypair-npm-1.0.3-f098145e16-1be4b14da7.zip new file mode 100644 index 00000000..ca0d4ef8 Binary files /dev/null and b/.yarn/cache/keypair-npm-1.0.3-f098145e16-1be4b14da7.zip differ diff --git a/.yarn/cache/libp2p-crypto-npm-0.19.7-6963e32764-f4964e1214.zip b/.yarn/cache/libp2p-crypto-npm-0.19.7-6963e32764-f4964e1214.zip new file mode 100644 index 00000000..5d1fb123 Binary files /dev/null and b/.yarn/cache/libp2p-crypto-npm-0.19.7-6963e32764-f4964e1214.zip differ diff --git a/.yarn/cache/locate-path-npm-5.0.0-46580c43e4-c58f49d45c.zip b/.yarn/cache/locate-path-npm-5.0.0-46580c43e4-c58f49d45c.zip new file mode 100644 index 00000000..4629bfb3 Binary files /dev/null and b/.yarn/cache/locate-path-npm-5.0.0-46580c43e4-c58f49d45c.zip differ diff --git a/.yarn/cache/locate-path-npm-6.0.0-06a1e4c528-4c37963815.zip b/.yarn/cache/locate-path-npm-6.0.0-06a1e4c528-4c37963815.zip new file mode 100644 index 00000000..5dbbdb0e Binary files /dev/null and b/.yarn/cache/locate-path-npm-6.0.0-06a1e4c528-4c37963815.zip differ diff --git a/.yarn/cache/lodash-npm-4.17.20-c0db62021c-c62101d250.zip b/.yarn/cache/lodash-npm-4.17.20-c0db62021c-c62101d250.zip deleted file mode 100644 index 3289d857..00000000 Binary files a/.yarn/cache/lodash-npm-4.17.20-c0db62021c-c62101d250.zip and /dev/null differ diff --git a/.yarn/cache/lodash.flattendeep-npm-4.4.0-26b2b4cbd7-941b709524.zip b/.yarn/cache/lodash.flattendeep-npm-4.4.0-26b2b4cbd7-941b709524.zip new file mode 100644 index 00000000..5d448dfd Binary files /dev/null and b/.yarn/cache/lodash.flattendeep-npm-4.4.0-26b2b4cbd7-941b709524.zip differ diff --git a/.yarn/cache/lodash.get-npm-4.4.2-7bda64ed87-447e575e3c.zip b/.yarn/cache/lodash.get-npm-4.4.2-7bda64ed87-447e575e3c.zip new file mode 100644 index 00000000..30748cc1 Binary files /dev/null and b/.yarn/cache/lodash.get-npm-4.4.2-7bda64ed87-447e575e3c.zip differ diff --git a/.yarn/cache/lodash.isplainobject-npm-4.0.6-d73937742f-72a114b610.zip b/.yarn/cache/lodash.isplainobject-npm-4.0.6-d73937742f-72a114b610.zip new file mode 100644 index 00000000..42d2c2e7 Binary files /dev/null and b/.yarn/cache/lodash.isplainobject-npm-4.0.6-d73937742f-72a114b610.zip differ diff --git a/.yarn/cache/long-npm-4.0.0-ecd96a31ed-9cebc1ee8b.zip b/.yarn/cache/long-npm-4.0.0-ecd96a31ed-9cebc1ee8b.zip new file mode 100644 index 00000000..08108476 Binary files /dev/null and b/.yarn/cache/long-npm-4.0.0-ecd96a31ed-9cebc1ee8b.zip differ diff --git a/.yarn/cache/make-dir-npm-3.1.0-d1d7505142-54b6f186c2.zip b/.yarn/cache/make-dir-npm-3.1.0-d1d7505142-54b6f186c2.zip new file mode 100644 index 00000000..7c78ec09 Binary files /dev/null and b/.yarn/cache/make-dir-npm-3.1.0-d1d7505142-54b6f186c2.zip differ diff --git a/.yarn/cache/matcher-npm-2.1.0-5ab18ff7b6-dc05a170ef.zip b/.yarn/cache/matcher-npm-2.1.0-5ab18ff7b6-dc05a170ef.zip new file mode 100644 index 00000000..6203c84e Binary files /dev/null and b/.yarn/cache/matcher-npm-2.1.0-5ab18ff7b6-dc05a170ef.zip differ diff --git a/.yarn/cache/md5.js-npm-1.3.5-130901125a-ca0b260ea2.zip b/.yarn/cache/md5.js-npm-1.3.5-130901125a-ca0b260ea2.zip new file mode 100644 index 00000000..16584a10 Binary files /dev/null and b/.yarn/cache/md5.js-npm-1.3.5-130901125a-ca0b260ea2.zip differ diff --git a/.yarn/cache/micromatch-npm-4.0.2-f059c00e51-0cb0e11d64.zip b/.yarn/cache/micromatch-npm-4.0.2-f059c00e51-0cb0e11d64.zip deleted file mode 100644 index 619b9534..00000000 Binary files a/.yarn/cache/micromatch-npm-4.0.2-f059c00e51-0cb0e11d64.zip and /dev/null differ diff --git a/.yarn/cache/mime-db-npm-1.44.0-d6ab7b4e20-b4e3b21414.zip b/.yarn/cache/mime-db-npm-1.44.0-d6ab7b4e20-b4e3b21414.zip deleted file mode 100644 index ab34aaea..00000000 Binary files a/.yarn/cache/mime-db-npm-1.44.0-d6ab7b4e20-b4e3b21414.zip and /dev/null differ diff --git a/.yarn/cache/mime-types-npm-2.1.27-bbebca8e17-51fe2f2c08.zip b/.yarn/cache/mime-types-npm-2.1.27-bbebca8e17-51fe2f2c08.zip deleted file mode 100644 index 5ff37681..00000000 Binary files a/.yarn/cache/mime-types-npm-2.1.27-bbebca8e17-51fe2f2c08.zip and /dev/null differ diff --git a/.yarn/cache/mocha-npm-9.1.1-48f6018912-c316e4e396.zip b/.yarn/cache/mocha-npm-9.1.1-48f6018912-c316e4e396.zip new file mode 100644 index 00000000..0c7ae68d Binary files /dev/null and b/.yarn/cache/mocha-npm-9.1.1-48f6018912-c316e4e396.zip differ diff --git a/.yarn/cache/ms-npm-2.1.3-81ff3cfac1-6e721e648a.zip b/.yarn/cache/ms-npm-2.1.3-81ff3cfac1-6e721e648a.zip new file mode 100644 index 00000000..1398f6a2 Binary files /dev/null and b/.yarn/cache/ms-npm-2.1.3-81ff3cfac1-6e721e648a.zip differ diff --git a/.yarn/cache/multiformats-npm-9.4.7-4a414eb83b-370ed783ca.zip b/.yarn/cache/multiformats-npm-9.4.7-4a414eb83b-370ed783ca.zip new file mode 100644 index 00000000..27958557 Binary files /dev/null and b/.yarn/cache/multiformats-npm-9.4.7-4a414eb83b-370ed783ca.zip differ diff --git a/.yarn/cache/nan-npm-2.15.0-505c98ef4d-85bc21e90b.zip b/.yarn/cache/nan-npm-2.15.0-505c98ef4d-85bc21e90b.zip new file mode 100644 index 00000000..8eb8f009 Binary files /dev/null and b/.yarn/cache/nan-npm-2.15.0-505c98ef4d-85bc21e90b.zip differ diff --git a/.yarn/cache/nanoid-npm-3.1.23-5f6acb650d-e6dea1da5a.zip b/.yarn/cache/nanoid-npm-3.1.23-5f6acb650d-e6dea1da5a.zip new file mode 100644 index 00000000..03dbe308 Binary files /dev/null and b/.yarn/cache/nanoid-npm-3.1.23-5f6acb650d-e6dea1da5a.zip differ diff --git a/.yarn/cache/nise-npm-4.1.0-bca1f3b90c-87b37725d9.zip b/.yarn/cache/nise-npm-4.1.0-bca1f3b90c-87b37725d9.zip new file mode 100644 index 00000000..7a39a912 Binary files /dev/null and b/.yarn/cache/nise-npm-4.1.0-bca1f3b90c-87b37725d9.zip differ diff --git a/.yarn/cache/nise-npm-5.1.0-8fc543b66e-09c3ad4165.zip b/.yarn/cache/nise-npm-5.1.0-8fc543b66e-09c3ad4165.zip new file mode 100644 index 00000000..1885c4fd Binary files /dev/null and b/.yarn/cache/nise-npm-5.1.0-8fc543b66e-09c3ad4165.zip differ diff --git a/.yarn/cache/node-addon-api-npm-2.0.2-8c2c1e9782-c407489489.zip b/.yarn/cache/node-addon-api-npm-2.0.2-8c2c1e9782-c407489489.zip new file mode 100644 index 00000000..0cd020d4 Binary files /dev/null and b/.yarn/cache/node-addon-api-npm-2.0.2-8c2c1e9782-c407489489.zip differ diff --git a/.yarn/cache/node-fetch-npm-2.6.1-46c670dbc1-cbb171635e.zip b/.yarn/cache/node-fetch-npm-2.6.2-2820154539-f48d84c89c.zip similarity index 87% rename from .yarn/cache/node-fetch-npm-2.6.1-46c670dbc1-cbb171635e.zip rename to .yarn/cache/node-fetch-npm-2.6.2-2820154539-f48d84c89c.zip index 31a07c8a..df01d641 100644 Binary files a/.yarn/cache/node-fetch-npm-2.6.1-46c670dbc1-cbb171635e.zip and b/.yarn/cache/node-fetch-npm-2.6.2-2820154539-f48d84c89c.zip differ diff --git a/.yarn/cache/node-forge-npm-0.10.0-605ba7b28b-c7a729933a.zip b/.yarn/cache/node-forge-npm-0.10.0-605ba7b28b-c7a729933a.zip new file mode 100644 index 00000000..a5738907 Binary files /dev/null and b/.yarn/cache/node-forge-npm-0.10.0-605ba7b28b-c7a729933a.zip differ diff --git a/.yarn/cache/node-forge-npm-0.8.5-f18eb3b9a2-4bfa3755c2.zip b/.yarn/cache/node-forge-npm-0.8.5-f18eb3b9a2-4bfa3755c2.zip new file mode 100644 index 00000000..b844bcd1 Binary files /dev/null and b/.yarn/cache/node-forge-npm-0.8.5-f18eb3b9a2-4bfa3755c2.zip differ diff --git a/.yarn/cache/node-gyp-build-npm-4.2.3-051c80c95f-8512c25498.zip b/.yarn/cache/node-gyp-build-npm-4.2.3-051c80c95f-8512c25498.zip new file mode 100644 index 00000000..78ffc620 Binary files /dev/null and b/.yarn/cache/node-gyp-build-npm-4.2.3-051c80c95f-8512c25498.zip differ diff --git a/.yarn/cache/node-preload-npm-0.2.1-5b6aef1c8e-72d4cccb33.zip b/.yarn/cache/node-preload-npm-0.2.1-5b6aef1c8e-72d4cccb33.zip new file mode 100644 index 00000000..0f18fdac Binary files /dev/null and b/.yarn/cache/node-preload-npm-0.2.1-5b6aef1c8e-72d4cccb33.zip differ diff --git a/.yarn/cache/node-releases-npm-1.1.75-3d5ac48148-0ea9d84f2c.zip b/.yarn/cache/node-releases-npm-1.1.75-3d5ac48148-0ea9d84f2c.zip new file mode 100644 index 00000000..386a8636 Binary files /dev/null and b/.yarn/cache/node-releases-npm-1.1.75-3d5ac48148-0ea9d84f2c.zip differ diff --git a/.yarn/cache/nyc-npm-15.1.0-f134b19668-f7f71b2b7f.zip b/.yarn/cache/nyc-npm-15.1.0-f134b19668-f7f71b2b7f.zip new file mode 100644 index 00000000..a870c0a7 Binary files /dev/null and b/.yarn/cache/nyc-npm-15.1.0-f134b19668-f7f71b2b7f.zip differ diff --git a/.yarn/cache/p-limit-npm-2.3.0-94a0310039-5f20492a25.zip b/.yarn/cache/p-limit-npm-2.3.0-94a0310039-5f20492a25.zip new file mode 100644 index 00000000..79830762 Binary files /dev/null and b/.yarn/cache/p-limit-npm-2.3.0-94a0310039-5f20492a25.zip differ diff --git a/.yarn/cache/p-limit-npm-3.1.0-05d2ede37f-5301db6a34.zip b/.yarn/cache/p-limit-npm-3.1.0-05d2ede37f-5301db6a34.zip new file mode 100644 index 00000000..12c6ab42 Binary files /dev/null and b/.yarn/cache/p-limit-npm-3.1.0-05d2ede37f-5301db6a34.zip differ diff --git a/.yarn/cache/p-locate-npm-4.1.0-eec6872537-57f9abef0b.zip b/.yarn/cache/p-locate-npm-4.1.0-eec6872537-57f9abef0b.zip new file mode 100644 index 00000000..74eb761f Binary files /dev/null and b/.yarn/cache/p-locate-npm-4.1.0-eec6872537-57f9abef0b.zip differ diff --git a/.yarn/cache/p-locate-npm-5.0.0-92cc7c7a3e-a233d775c8.zip b/.yarn/cache/p-locate-npm-5.0.0-92cc7c7a3e-a233d775c8.zip new file mode 100644 index 00000000..0725bb3c Binary files /dev/null and b/.yarn/cache/p-locate-npm-5.0.0-92cc7c7a3e-a233d775c8.zip differ diff --git a/.yarn/cache/p-map-npm-3.0.0-e4f17c4167-f7ce4709f4.zip b/.yarn/cache/p-map-npm-3.0.0-e4f17c4167-f7ce4709f4.zip new file mode 100644 index 00000000..bc2d3541 Binary files /dev/null and b/.yarn/cache/p-map-npm-3.0.0-e4f17c4167-f7ce4709f4.zip differ diff --git a/.yarn/cache/p-try-npm-2.2.0-e0390dbaf8-20983f3765.zip b/.yarn/cache/p-try-npm-2.2.0-e0390dbaf8-20983f3765.zip new file mode 100644 index 00000000..cf703cf2 Binary files /dev/null and b/.yarn/cache/p-try-npm-2.2.0-e0390dbaf8-20983f3765.zip differ diff --git a/.yarn/cache/package-hash-npm-4.0.0-1e83d2429d-f64c22b5d6.zip b/.yarn/cache/package-hash-npm-4.0.0-1e83d2429d-f64c22b5d6.zip new file mode 100644 index 00000000..a570c8a9 Binary files /dev/null and b/.yarn/cache/package-hash-npm-4.0.0-1e83d2429d-f64c22b5d6.zip differ diff --git a/.yarn/cache/path-exists-npm-4.0.0-e9e4f63eb0-6ab15000c5.zip b/.yarn/cache/path-exists-npm-4.0.0-e9e4f63eb0-6ab15000c5.zip new file mode 100644 index 00000000..68d93f89 Binary files /dev/null and b/.yarn/cache/path-exists-npm-4.0.0-e9e4f63eb0-6ab15000c5.zip differ diff --git a/.yarn/cache/path-to-regexp-npm-1.8.0-a1904f5c44-4c0d9aaf3f.zip b/.yarn/cache/path-to-regexp-npm-1.8.0-a1904f5c44-4c0d9aaf3f.zip new file mode 100644 index 00000000..a4089f52 Binary files /dev/null and b/.yarn/cache/path-to-regexp-npm-1.8.0-a1904f5c44-4c0d9aaf3f.zip differ diff --git a/.yarn/cache/pathval-npm-1.1.1-ce0311d7e0-81cd01d46c.zip b/.yarn/cache/pathval-npm-1.1.1-ce0311d7e0-81cd01d46c.zip new file mode 100644 index 00000000..2d7544b7 Binary files /dev/null and b/.yarn/cache/pathval-npm-1.1.1-ce0311d7e0-81cd01d46c.zip differ diff --git a/.yarn/cache/pbkdf2-npm-3.1.2-d67bbb584f-12ac46c71d.zip b/.yarn/cache/pbkdf2-npm-3.1.2-d67bbb584f-12ac46c71d.zip new file mode 100644 index 00000000..bc50c8c2 Binary files /dev/null and b/.yarn/cache/pbkdf2-npm-3.1.2-d67bbb584f-12ac46c71d.zip differ diff --git a/.yarn/cache/pem-jwk-npm-2.0.0-4f6af502ec-c3d2248e11.zip b/.yarn/cache/pem-jwk-npm-2.0.0-4f6af502ec-c3d2248e11.zip new file mode 100644 index 00000000..d96cc8e3 Binary files /dev/null and b/.yarn/cache/pem-jwk-npm-2.0.0-4f6af502ec-c3d2248e11.zip differ diff --git a/.yarn/cache/picomatch-npm-2.2.2-1ce736a913-20fa75e0a5.zip b/.yarn/cache/picomatch-npm-2.2.2-1ce736a913-20fa75e0a5.zip deleted file mode 100644 index 92b5c4a9..00000000 Binary files a/.yarn/cache/picomatch-npm-2.2.2-1ce736a913-20fa75e0a5.zip and /dev/null differ diff --git a/.yarn/cache/pify-npm-4.0.1-062756097b-786486a8c9.zip b/.yarn/cache/pify-npm-4.0.1-062756097b-786486a8c9.zip new file mode 100644 index 00000000..0e4a8514 Binary files /dev/null and b/.yarn/cache/pify-npm-4.0.1-062756097b-786486a8c9.zip differ diff --git a/.yarn/cache/pkg-dir-npm-4.2.0-2b5d0a8d32-1956ebf3cf.zip b/.yarn/cache/pkg-dir-npm-4.2.0-2b5d0a8d32-1956ebf3cf.zip new file mode 100644 index 00000000..e25bbf10 Binary files /dev/null and b/.yarn/cache/pkg-dir-npm-4.2.0-2b5d0a8d32-1956ebf3cf.zip differ diff --git a/.yarn/cache/pretty-format-npm-25.5.0-9def2180a5-f7cc631d51.zip b/.yarn/cache/pretty-format-npm-25.5.0-9def2180a5-f7cc631d51.zip deleted file mode 100644 index 036333eb..00000000 Binary files a/.yarn/cache/pretty-format-npm-25.5.0-9def2180a5-f7cc631d51.zip and /dev/null differ diff --git a/.yarn/cache/process-on-spawn-npm-1.0.0-676960b4dd-91b18f68bb.zip b/.yarn/cache/process-on-spawn-npm-1.0.0-676960b4dd-91b18f68bb.zip new file mode 100644 index 00000000..c6d6601c Binary files /dev/null and b/.yarn/cache/process-on-spawn-npm-1.0.0-676960b4dd-91b18f68bb.zip differ diff --git a/.yarn/cache/protobufjs-npm-6.11.2-9b422ce98e-3c7c6875ca.zip b/.yarn/cache/protobufjs-npm-6.11.2-9b422ce98e-3c7c6875ca.zip new file mode 100644 index 00000000..18412715 Binary files /dev/null and b/.yarn/cache/protobufjs-npm-6.11.2-9b422ce98e-3c7c6875ca.zip differ diff --git a/.yarn/cache/randombytes-npm-2.1.0-e3da76bccf-ede2693af0.zip b/.yarn/cache/randombytes-npm-2.1.0-e3da76bccf-ede2693af0.zip new file mode 100644 index 00000000..a8762ec1 Binary files /dev/null and b/.yarn/cache/randombytes-npm-2.1.0-e3da76bccf-ede2693af0.zip differ diff --git a/.yarn/cache/react-is-npm-16.13.1-a9b9382b4f-11bcf1267a.zip b/.yarn/cache/react-is-npm-16.13.1-a9b9382b4f-11bcf1267a.zip deleted file mode 100644 index 095cd7b3..00000000 Binary files a/.yarn/cache/react-is-npm-16.13.1-a9b9382b4f-11bcf1267a.zip and /dev/null differ diff --git a/.yarn/cache/readdirp-npm-3.5.0-a1b1568d32-a64fe56069.zip b/.yarn/cache/readdirp-npm-3.5.0-a1b1568d32-a64fe56069.zip deleted file mode 100644 index c575d6a4..00000000 Binary files a/.yarn/cache/readdirp-npm-3.5.0-a1b1568d32-a64fe56069.zip and /dev/null differ diff --git a/.yarn/cache/readdirp-npm-3.6.0-f950cc74ab-7da2fe8d5a.zip b/.yarn/cache/readdirp-npm-3.6.0-f950cc74ab-7da2fe8d5a.zip new file mode 100644 index 00000000..973cfd1f Binary files /dev/null and b/.yarn/cache/readdirp-npm-3.6.0-f950cc74ab-7da2fe8d5a.zip differ diff --git a/.yarn/cache/regression-npm-2.0.1-50bfc2587a-61feb6f23b.zip b/.yarn/cache/regression-npm-2.0.1-50bfc2587a-61feb6f23b.zip new file mode 100644 index 00000000..6e676f05 Binary files /dev/null and b/.yarn/cache/regression-npm-2.0.1-50bfc2587a-61feb6f23b.zip differ diff --git a/.yarn/cache/release-zalgo-npm-1.0.0-aa3e59962f-db2e7567a9.zip b/.yarn/cache/release-zalgo-npm-1.0.0-aa3e59962f-db2e7567a9.zip new file mode 100644 index 00000000..00e33e09 Binary files /dev/null and b/.yarn/cache/release-zalgo-npm-1.0.0-aa3e59962f-db2e7567a9.zip differ diff --git a/.yarn/cache/require-main-filename-npm-2.0.0-03eef65c84-8d3633149a.zip b/.yarn/cache/require-main-filename-npm-2.0.0-03eef65c84-8d3633149a.zip new file mode 100644 index 00000000..0d829065 Binary files /dev/null and b/.yarn/cache/require-main-filename-npm-2.0.0-03eef65c84-8d3633149a.zip differ diff --git a/.yarn/cache/resolve-from-npm-5.0.0-15c9db4d33-0d29fc7012.zip b/.yarn/cache/resolve-from-npm-5.0.0-15c9db4d33-0d29fc7012.zip new file mode 100644 index 00000000..fffc8ded Binary files /dev/null and b/.yarn/cache/resolve-from-npm-5.0.0-15c9db4d33-0d29fc7012.zip differ diff --git a/.yarn/cache/ripemd160-npm-2.0.2-7b1fb8dc76-e0370fbe77.zip b/.yarn/cache/ripemd160-npm-2.0.2-7b1fb8dc76-e0370fbe77.zip new file mode 100644 index 00000000..7a78bb92 Binary files /dev/null and b/.yarn/cache/ripemd160-npm-2.0.2-7b1fb8dc76-e0370fbe77.zip differ diff --git a/.yarn/cache/secp256k1-npm-4.0.2-80b0224eff-5bf0e0aa11.zip b/.yarn/cache/secp256k1-npm-4.0.2-80b0224eff-5bf0e0aa11.zip new file mode 100644 index 00000000..5eeb0e3a Binary files /dev/null and b/.yarn/cache/secp256k1-npm-4.0.2-80b0224eff-5bf0e0aa11.zip differ diff --git a/.yarn/cache/semver-npm-6.3.0-b3eace8bfd-f0d155c06a.zip b/.yarn/cache/semver-npm-6.3.0-b3eace8bfd-f0d155c06a.zip new file mode 100644 index 00000000..f2b4177f Binary files /dev/null and b/.yarn/cache/semver-npm-6.3.0-b3eace8bfd-f0d155c06a.zip differ diff --git a/.yarn/cache/semver-npm-7.3.2-161b023bbb-bceb46d396.zip b/.yarn/cache/semver-npm-7.3.2-161b023bbb-bceb46d396.zip deleted file mode 100644 index 4b1976f4..00000000 Binary files a/.yarn/cache/semver-npm-7.3.2-161b023bbb-bceb46d396.zip and /dev/null differ diff --git a/.yarn/cache/serialize-javascript-npm-6.0.0-0bb8a3c88d-e086a40bfc.zip b/.yarn/cache/serialize-javascript-npm-6.0.0-0bb8a3c88d-e086a40bfc.zip new file mode 100644 index 00000000..a4aa8875 Binary files /dev/null and b/.yarn/cache/serialize-javascript-npm-6.0.0-0bb8a3c88d-e086a40bfc.zip differ diff --git a/.yarn/cache/sha.js-npm-2.4.11-14868df4ca-7554240ab7.zip b/.yarn/cache/sha.js-npm-2.4.11-14868df4ca-7554240ab7.zip new file mode 100644 index 00000000..07bf477b Binary files /dev/null and b/.yarn/cache/sha.js-npm-2.4.11-14868df4ca-7554240ab7.zip differ diff --git a/.yarn/cache/sinon-npm-11.1.2-5325724cb2-dca6f9cbc7.zip b/.yarn/cache/sinon-npm-11.1.2-5325724cb2-dca6f9cbc7.zip new file mode 100644 index 00000000..9b7864e3 Binary files /dev/null and b/.yarn/cache/sinon-npm-11.1.2-5325724cb2-dca6f9cbc7.zip differ diff --git a/.yarn/cache/sinon-npm-9.2.4-3c6e379b87-c285c3750a.zip b/.yarn/cache/sinon-npm-9.2.4-3c6e379b87-c285c3750a.zip new file mode 100644 index 00000000..efe3e7b4 Binary files /dev/null and b/.yarn/cache/sinon-npm-9.2.4-3c6e379b87-c285c3750a.zip differ diff --git a/.yarn/cache/smartweave-npm-0.4.27-8ada3fca84-ea910348e0.zip b/.yarn/cache/smartweave-npm-0.4.27-8ada3fca84-ea910348e0.zip deleted file mode 100644 index 54d0bde6..00000000 Binary files a/.yarn/cache/smartweave-npm-0.4.27-8ada3fca84-ea910348e0.zip and /dev/null differ diff --git a/.yarn/cache/source-map-npm-0.5.7-7c3f035429-737face965.zip b/.yarn/cache/source-map-npm-0.5.7-7c3f035429-737face965.zip new file mode 100644 index 00000000..f43e9055 Binary files /dev/null and b/.yarn/cache/source-map-npm-0.5.7-7c3f035429-737face965.zip differ diff --git a/.yarn/cache/source-map-support-npm-0.5.19-65b33ae61e-59d4efaae9.zip b/.yarn/cache/source-map-support-npm-0.5.19-65b33ae61e-59d4efaae9.zip deleted file mode 100644 index 1ca7518a..00000000 Binary files a/.yarn/cache/source-map-support-npm-0.5.19-65b33ae61e-59d4efaae9.zip and /dev/null differ diff --git a/.yarn/cache/source-map-support-npm-0.5.20-edfc5ce275-2c5821ee94.zip b/.yarn/cache/source-map-support-npm-0.5.20-edfc5ce275-2c5821ee94.zip new file mode 100644 index 00000000..ad056523 Binary files /dev/null and b/.yarn/cache/source-map-support-npm-0.5.20-edfc5ce275-2c5821ee94.zip differ diff --git a/.yarn/cache/spawn-wrap-npm-2.0.0-368c0a5bad-463a408825.zip b/.yarn/cache/spawn-wrap-npm-2.0.0-368c0a5bad-463a408825.zip new file mode 100644 index 00000000..eb0d1ebd Binary files /dev/null and b/.yarn/cache/spawn-wrap-npm-2.0.0-368c0a5bad-463a408825.zip differ diff --git a/.yarn/cache/strip-bom-npm-4.0.0-97d367a64d-25a231aacb.zip b/.yarn/cache/strip-bom-npm-4.0.0-97d367a64d-25a231aacb.zip new file mode 100644 index 00000000..2cdddcc3 Binary files /dev/null and b/.yarn/cache/strip-bom-npm-4.0.0-97d367a64d-25a231aacb.zip differ diff --git a/.yarn/cache/supports-color-npm-8.1.1-289e937149-0219f5c917.zip b/.yarn/cache/supports-color-npm-8.1.1-289e937149-0219f5c917.zip new file mode 100644 index 00000000..017f21ac Binary files /dev/null and b/.yarn/cache/supports-color-npm-8.1.1-289e937149-0219f5c917.zip differ diff --git a/.yarn/cache/test-exclude-npm-6.0.0-3fb03d69df-68294d1006.zip b/.yarn/cache/test-exclude-npm-6.0.0-3fb03d69df-68294d1006.zip new file mode 100644 index 00000000..cd4c384e Binary files /dev/null and b/.yarn/cache/test-exclude-npm-6.0.0-3fb03d69df-68294d1006.zip differ diff --git a/.yarn/cache/to-fast-properties-npm-2.0.0-0dc60cc481-40e6198424.zip b/.yarn/cache/to-fast-properties-npm-2.0.0-0dc60cc481-40e6198424.zip new file mode 100644 index 00000000..8c8b2753 Binary files /dev/null and b/.yarn/cache/to-fast-properties-npm-2.0.0-0dc60cc481-40e6198424.zip differ diff --git a/.yarn/cache/ts-node-npm-10.2.1-18dc22b42d-528e79c827.zip b/.yarn/cache/ts-node-npm-10.2.1-18dc22b42d-528e79c827.zip new file mode 100644 index 00000000..888b9ccf Binary files /dev/null and b/.yarn/cache/ts-node-npm-10.2.1-18dc22b42d-528e79c827.zip differ diff --git a/.yarn/cache/ts-node-npm-9.1.1-4ad31da228-a90db4a342.zip b/.yarn/cache/ts-node-npm-9.1.1-4ad31da228-a90db4a342.zip deleted file mode 100644 index 6f41af0c..00000000 Binary files a/.yarn/cache/ts-node-npm-9.1.1-4ad31da228-a90db4a342.zip and /dev/null differ diff --git a/.yarn/cache/ts-sinon-npm-2.0.1-dececf902a-c274652c3e.zip b/.yarn/cache/ts-sinon-npm-2.0.1-dececf902a-c274652c3e.zip new file mode 100644 index 00000000..f2b770ce Binary files /dev/null and b/.yarn/cache/ts-sinon-npm-2.0.1-dececf902a-c274652c3e.zip differ diff --git a/.yarn/cache/tsc-files-npm-1.1.2-5dbe902f60-6e5a3a53f5.zip b/.yarn/cache/tsc-files-npm-1.1.2-5dbe902f60-6e5a3a53f5.zip deleted file mode 100644 index fdd0f905..00000000 Binary files a/.yarn/cache/tsc-files-npm-1.1.2-5dbe902f60-6e5a3a53f5.zip and /dev/null differ diff --git a/.yarn/cache/tslib-npm-1.13.0-f5e9ea9b66-5dc3bdaea3.zip b/.yarn/cache/tslib-npm-1.13.0-f5e9ea9b66-5dc3bdaea3.zip deleted file mode 100644 index bafb23a9..00000000 Binary files a/.yarn/cache/tslib-npm-1.13.0-f5e9ea9b66-5dc3bdaea3.zip and /dev/null differ diff --git a/.yarn/cache/type-detect-npm-4.0.8-8d8127b901-e01dc6ac90.zip b/.yarn/cache/type-detect-npm-4.0.8-8d8127b901-e01dc6ac90.zip new file mode 100644 index 00000000..be7a3e9b Binary files /dev/null and b/.yarn/cache/type-detect-npm-4.0.8-8d8127b901-e01dc6ac90.zip differ diff --git a/.yarn/cache/type-fest-npm-0.11.0-81410fe889-02e5cadf13.zip b/.yarn/cache/type-fest-npm-0.11.0-81410fe889-02e5cadf13.zip deleted file mode 100644 index 5d189788..00000000 Binary files a/.yarn/cache/type-fest-npm-0.11.0-81410fe889-02e5cadf13.zip and /dev/null differ diff --git a/.yarn/cache/typedarray-to-buffer-npm-3.1.5-aadc11995e-e6e0e6812a.zip b/.yarn/cache/typedarray-to-buffer-npm-3.1.5-aadc11995e-e6e0e6812a.zip new file mode 100644 index 00000000..6f84b7a3 Binary files /dev/null and b/.yarn/cache/typedarray-to-buffer-npm-3.1.5-aadc11995e-e6e0e6812a.zip differ diff --git a/.yarn/cache/uint8arrays-npm-3.0.0-8a9076adb2-75361a84ae.zip b/.yarn/cache/uint8arrays-npm-3.0.0-8a9076adb2-75361a84ae.zip new file mode 100644 index 00000000..87ee16ee Binary files /dev/null and b/.yarn/cache/uint8arrays-npm-3.0.0-8a9076adb2-75361a84ae.zip differ diff --git a/.yarn/cache/ursa-optional-npm-0.10.2-2e11b3a5da-f832f0ae69.zip b/.yarn/cache/ursa-optional-npm-0.10.2-2e11b3a5da-f832f0ae69.zip new file mode 100644 index 00000000..2f7051c0 Binary files /dev/null and b/.yarn/cache/ursa-optional-npm-0.10.2-2e11b3a5da-f832f0ae69.zip differ diff --git a/.yarn/cache/which-module-npm-2.0.0-daf3daa08d-3d2107ab18.zip b/.yarn/cache/which-module-npm-2.0.0-daf3daa08d-3d2107ab18.zip new file mode 100644 index 00000000..a7fd3d1f Binary files /dev/null and b/.yarn/cache/which-module-npm-2.0.0-daf3daa08d-3d2107ab18.zip differ diff --git a/.yarn/cache/workerpool-npm-6.1.5-61adb98c59-aaf220c463.zip b/.yarn/cache/workerpool-npm-6.1.5-61adb98c59-aaf220c463.zip new file mode 100644 index 00000000..b44f1bda Binary files /dev/null and b/.yarn/cache/workerpool-npm-6.1.5-61adb98c59-aaf220c463.zip differ diff --git a/.yarn/cache/write-file-atomic-npm-3.0.3-d948a237da-a26a8699c3.zip b/.yarn/cache/write-file-atomic-npm-3.0.3-d948a237da-a26a8699c3.zip new file mode 100644 index 00000000..9abf309d Binary files /dev/null and b/.yarn/cache/write-file-atomic-npm-3.0.3-d948a237da-a26a8699c3.zip differ diff --git a/.yarn/cache/y18n-npm-4.0.3-ced95acdbc-e6d08e9d14.zip b/.yarn/cache/y18n-npm-4.0.3-ced95acdbc-e6d08e9d14.zip new file mode 100644 index 00000000..6285de3e Binary files /dev/null and b/.yarn/cache/y18n-npm-4.0.3-ced95acdbc-e6d08e9d14.zip differ diff --git a/.yarn/cache/yargs-npm-15.4.1-ca1c444de1-dbf687d6b9.zip b/.yarn/cache/yargs-npm-15.4.1-ca1c444de1-dbf687d6b9.zip new file mode 100644 index 00000000..f8aa67c9 Binary files /dev/null and b/.yarn/cache/yargs-npm-15.4.1-ca1c444de1-dbf687d6b9.zip differ diff --git a/.yarn/cache/yargs-parser-npm-18.1.3-0ba9c4f088-3387172167.zip b/.yarn/cache/yargs-parser-npm-18.1.3-0ba9c4f088-3387172167.zip new file mode 100644 index 00000000..acb73c3c Binary files /dev/null and b/.yarn/cache/yargs-parser-npm-18.1.3-0ba9c4f088-3387172167.zip differ diff --git a/.yarn/cache/yargs-parser-npm-20.2.4-1de20916a6-00dd0f23b6.zip b/.yarn/cache/yargs-parser-npm-20.2.4-1de20916a6-00dd0f23b6.zip new file mode 100644 index 00000000..c772bcf8 Binary files /dev/null and b/.yarn/cache/yargs-parser-npm-20.2.4-1de20916a6-00dd0f23b6.zip differ diff --git a/.yarn/cache/yargs-unparser-npm-2.0.0-930f3ff3f6-afa83ec3fe.zip b/.yarn/cache/yargs-unparser-npm-2.0.0-930f3ff3f6-afa83ec3fe.zip new file mode 100644 index 00000000..2a29a84c Binary files /dev/null and b/.yarn/cache/yargs-unparser-npm-2.0.0-930f3ff3f6-afa83ec3fe.zip differ diff --git a/.yarn/cache/yocto-queue-npm-0.1.0-c6c9a7db29-096c3b40be.zip b/.yarn/cache/yocto-queue-npm-0.1.0-c6c9a7db29-096c3b40be.zip new file mode 100644 index 00000000..4d4a50b4 Binary files /dev/null and b/.yarn/cache/yocto-queue-npm-0.1.0-c6c9a7db29-096c3b40be.zip differ diff --git a/README.md b/README.md index 00401e6d..aab4ad88 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,172 @@ # ardrive-cli -The ArDrive Command Line Interface (CLI) Beta contains all of the needed public file synchronization and Arweave wallet capabilities via a Node.js application. This works in unison with the ArDrive Web App. +The _ArDrive Command Line Interface (CLI)_ is a Node.js application for terminal-based [ArDrive] workflows. It also offers utility operations for securely interacting with Arweave wallets and inspecting various [Arweave] blockchain conditions. -To use the ArDrive CLI, install it with your favorite package manager, and run "ardrive-cli". As of now, the local SQLite database will be created in the directory that you run the CLI in. +Create your first drive and permanently store your first file on the permaweb with a series of simple CLI commands like so: -If you are experiencing permissions issues (Mac OSX), you may also need to place your wallet file in the same directory you are running ardrive-cli +```shell +ardrive create-drive --wallet-file /path/to/my/wallet.json --drive-name "Teenage Love Poetry" +{ + "created": [ + { + "type": "drive", + "metadataTxId": "giv2R8Xj0bbe6l5taBTQJk_38zwIrMH_g1-knSCisjU", + "entityId": "898687ea-b678-4f86-b4e7-49560b190356" + }, + { + "type": "folder", + "metadataTxId": "VljnttwUxRStnVuPYakF9e2whjhYJVWB0nSxD5dVyJ8", + "entityId": "f0c58c11-430c-4383-8e54-4d864cc7e927" + } + ], + "tips": [], + "fees": { + "giv2R8Xj0bbe6l5taBTQJk_38zwIrMH_g1-knSCisjU": 1415103, + "VljnttwUxRStnVuPYakF9e2whjhYJVWB0nSxD5dVyJ8": 1391904 + } +} + +ardrive upload-file --wallet-file /path/to/my/wallet.json --parent-folder-id "f0c58c11-430c-4383-8e54-4d864cc7e927" --local-file-path ./helloworld.txt --dest-file-name "ode_to_ardrive.txt" +{ + "created": [ + { + "type": "file", + "metadataTxId": "EvE06MmE9IKeUzFMnxSgY1M5tJX4uHU64-n8Pf_lZfU", + "dataTxId": "tSMcfvAQu_tKLUkdvRRbqdX93oAf3h6c9eJsSj8mXL4", + "entityId": "bd2ce978-6ede-4b0d-8f79-2d7bc235a0e0" + } + ], + "tips": [ + { + "txId": "FidEhcZtmDtvQxWrnVJlKnj_ZkwxYXvn7wjbUpasRKo", + "recipient": { + "address": "i325n3L2UvgcavEM8UnFfY0OWBiyf2RrbNsLStPI73o" + }, + "winston": "10000000" + } + ], + "fees": { + "tSMcfvAQu_tKLUkdvRRbqdX93oAf3h6c9eJsSj8mXL4": 1384601, + "EvE06MmE9IKeUzFMnxSgY1M5tJX4uHU64-n8Pf_lZfU": 1447752, + "FidEhcZtmDtvQxWrnVJlKnj_ZkwxYXvn7wjbUpasRKo": 1379016 + } +} +``` + +**This project is in a state of active development. Use at your own risk!** + +# Table of Contents + +1. [ArDrive](#ardrive) + 1. [ArFS](#arfs) + 2. [Data Portability](#data-portability) + 3. [Intended Audience](#intended-audience) +2. [Getting Started](#getting-started) + 1. [Install Yarn 2 (CLI Users and Developers)](#yarn2) + 2. [Husky (Developers Only)](#husky) + 3. [NVM (Optional - Recommended)](#nvm) + 4. [Using a custom ArDrive-Core-JS (Optional - Developers)](#custom-ardrive-core-js) + 5. [Installing and Starting the CLI From NPM Package (CLI Users)](#install-from-npm) + 6. [Installing and Starting the CLI From Source (CLI Users and Developers)](#install-from-src) + 7. [Recommended Visual Studio Code extensions (Developers Only)](#vs-extensions) + 8. [Limitations](#limitations) +3. [Using the CLI](#using-the-cli) + 1. [CLI Help](#cli-help) + 2. [Wallet Operations](#wallet-operations) + 3. [Working With Entities](#working-with-entities) + 1. [Dry Run](#dry-run) + 4. [Working With Drives](#working-with-drives) + 1. [Understanding Drive Hierarchies](#understanding-drive-hierarchies) + 1. [Fetching Drive Info](#drive-info) + 2. [Understanding Drive and File Keys](#understanding-drive-and-file-keys) + 1. [Derive a Drive Key](#derive-drive-key) + 2. [Derive a File Key](#derive-file-key) + 3. [Managing Drive Passwords](#managing-drive-passwords) + 1. [Supplying Your Password: Environment Variable](#pw-environment-variable) + 2. [Supplying Your Password: STDIN](#pw-stdin) + 3. [Supplying Your Password: Prompt](#pw-prompt) + 4. [Creating Drives](#creating-drives) + 5. [Listing Drives for an Address](#listing-drives-for-an-address) + 6. [Listing Every Entity in a Drive](#list-drive) + 7. [List Drive Pipeline Examples](#list-drive-pipeline-examples) + 1. [Get Share Links for Files in the Drive](#get-share-links) + 2. [Get Total Size of Files in the Drive](#get-total-size) + 3. [Get Total Count of Files in the Drive](#get-file-count) + 5. [Working With Folders](#working-with-folders) + 1. [Creating Folders](#creating-folders) + 2. [Moving Folders](#moving-folders) + 3. [Viewing Folder Metadata](#viewing-folder-metadata) + 4. [Listing Contents of a Folder](#listing-contents-of-a-folder) + 6. [Working With Files](#working-with-files) + 1. [Uploading a Single File](#uploading-a-single-file) + 2. [Uploading a Folder with Files](#bulk-upload) + 3. [Fetching the Metadata of a File Entity](#fetching-the-metadata-of-a-file-entity) + 4. [Create New Drive and Upload Folder Pipeline Example](#create-upload-pipeline) + 7. [Other Utility Operations](#other-utility-operations) + 1. [Monitoring Transactions](#monitoring-transactions) + 2. [Dealing With Network Congestion](#dealing-with-network-congestion) + 3. [Check for network congestion before uploading](#check-congestion) + 4. [Front-run Congestion By Boosting Miner Rewards](#boost) +4. [All ArDrive CLI Commands](#all-ardrive-cli-commands) +5. [Getting Help](#getting-help) + +# ArDrive + +[ArDrive] is a permanent storage platform whose [applications and core libraries][ardrive-github] offer hierarchical organization, privacy via complete end-to-end encryption, flexibility, extensibility, and access control over your most valuable data, all made possible by its innovative core technology, the [Arweave File System (ArFS) Protocol][arfs]. + +## ArFS + +[ArFS] is a data modeling, storage, and retrieval protocol designed to emulate common file system operations and to provide aspects of mutability to your data hierarchy on [Arweave]'s otherwise permanent, immutable data storage blockweave. + +## Data Portability + +Data uploaded via the ArDrive CLI, once indexed by Arweave's Gateways and sufficiently seeded across enough nodes on the network, can be accessed via all other ArDrive applications including the [ArDrive Web application][ardrive-web-app] at https://app.ardrive.io. + +All transactions successfully executed by ArDrive can always be inspected in the [Viewblock blockchain explorer]. + +## Intended Audience + +This tool is intended for use by: + +
    +
  • ArDrive power users with advanced workflows and resource efficiency in mind: bulk uploaders, those with larger storage demand, game developers, nft creators, storage/db admins, etc.
  • +
  • Automation tools
  • +
  • Services
  • +
  • Terminal aficionados
  • +
  • Extant and aspiring cypherpunks
  • +
+ +For deeper integrations with the [ArDrive] platform, consider using the [ArDrive Core][ardrive-core] (Node) library's configurable and intuitive class interfaces directly within your application. -## Developer Setup +# Getting Started -Follow these steps to get the developer environment up and running: +CLI users and developers must both follow these steps to get the application and/or developer environment up and running: -### Install Yarn 2 +## Install Yarn 2 (CLI Users and Developers) -Both the ArDrive CLI and ArDrive Core JS use Yarn 2, so install the latest version with the [yarn installation instructions][yarn-install]. In most cases: +Both the ArDrive CLI and ArDrive Core JS use Yarn 2 to manage dependencies and initiate workflows, so follow the [yarn installation instructions][yarn-install] in order to get the latest version. In most cases: ```shell -# Brew: +# Brew (OSX): brew install yarn -# Or with NPM: +# Or with NPM (all supported platforms): npm install -g yarn ``` -We also use husky. To enable hooks locally, you will need to run: +## Husky (Developers Only) + +We use husky 6.x to manage the git commit hooks that help to improve the quality of our commits. Please run: ```shell yarn husky install ``` -### NVM +to enable git hooks for your local checkout. Without doing so, you risk committing non-compliant code to the repository. + +## NVM (Optional - Recommended) -This repository uses NVM and an `.nvmrc` file to lock the Node version to the current version used by `ardrive-core-js`. +This repository uses the Node Version Manager (NVM) and an `.nvmrc` file to lock the Node version to the current version used by `ardrive-core-js`. **Note for Windows: We recommend using WSL for setting up NVM on Windows using the [instructions described here][wsl-install]** @@ -39,9 +175,9 @@ Follow these steps to get NVM up and running on your system: 1. Install NVM using [these installation instructions][nvm-install]. 2. Navigate to this project's root directory 3. Ensure that the correct version of Node is installed by performing: `nvm install` -4. Use the correct version of Node, by using: `nvm use` +4. Use the correct version of Node, by performing: `nvm use` -### Using a custom ArDrive-Core-JS +## Using a custom ArDrive-Core-JS (Optional - Developers) To test a with a custom version of the `ardrive-core-js` library on your local system, change the `"ardrive-core-js"` line in `package.json` to the root of your local `ardrive-core-js` repo: @@ -50,21 +186,42 @@ To test a with a custom version of the `ardrive-core-js` library on your local s + "ardrive-core-js": "../ardrive-core-js/" ``` -### Installing and Starting the CLI +## Installing and Starting the CLI From NPM Package (CLI Users) -Now that everything is set up, to install the package simply run: +```shell +npm install -g ardrive-cli + +# then invoke the CLI from anywhere on your system: +ardrive +``` + +## Installing and Starting the CLI From Source (CLI Users and Developers) + +Now that your runtime and/or development environment is set up, to install the package simply run: ```shell -yarn +yarn && yarn build ``` -And then start the CLI: +And then start the CLI (always from the root of this repository): ```shell -yarn start +yarn ardrive ``` -### Recommended Visual Studio Code extensions +For convenience in the **non-developer case**, you can install the CLI globally on your system by performing the following step: + +```shell +yarn pack + +# then using the path generated by yarn from the step above: +npm install i -g /path/to/package.tgz + +# then invoke the CLI from anywhere on your system: +ardrive +``` + +## Recommended Visual Studio Code extensions (Developers Only) To ensure your environment is compatible, we also recommend the following VSCode extensions: @@ -73,6 +230,655 @@ To ensure your environment is compatible, we also recommend the following VSCode - [Prettier][prettier-vscode] - [ZipFS][zipfs-vscode] +## Limitations + +**Number of files in a bulk upload:** Theoretically unlimited
+**Max individual file size**: 2GB (Node.js limitation)
+**Max ANS-104 bundled transaction size:** Not yet implemented. 2GB per bundle. App will handle creating multiple bundles.
+**Max ANS-104 data item counts per bundled transaction:** Not yet implemented. Also not adequately specified, though a very large number of data items per bundle is associated with increased rates of GQL indexing failure. + +# Using the CLI + +## CLI Help + +Learn to use any command: + +```shell +ardrive --help +``` + +## Wallet Operations + +Browsing of ArDrive public data is possible without the need for an [Arweave wallet][kb-wallets]. However, for all write operations, or read operations without encryption/decryption keys, you'll need a wallet. + +As you utilize the CLI, you can use either your wallet file or your seed phrase interchangeably. Consider the security implications of each approach for your particular use case carefully. If at any time you'd like to generate a new wallet altogether, start by generating a new seed phase. And if you'd like to use that seed phrase in the form of a wallet file, or if you'd like to recover an existing wallet via its seed phrase, use either or both of the following commands: + +```shell +# Generate seed-phrase +ardrive generate-seedphrase +"this is an example twelve word seed phrase that you could use" + +# Generate/recover wallet file (with example output file path) +ardrive generate-wallet -s "this is an example twelve word seed phrase that you could use" > /path/to/wallet/file.json +``` + +Public attributes of Arweave wallets can be retrieved via their 43-character Arweave wallet address. You can retrieve the wallet address associated with [your wallet file or 12-word seed phrase][kb-wallets] (e.g. wallets generated by [ArConnect][arconnect]) like so: + +```shell +# Wallet file +ardrive get-address -w /path/to/wallet/file.json + +# Seed Phrase (with sample output) +ardrive get-address -s "this is an example twelve word seed phrase that you could use" +HTTn8F92tR32N8wuo-NIDkjmqPknrbl10JWo5MZ9x2k +``` + +You'll need AR in your wallet for any write operations you perform in ArDrive. You can always check your wallet balance (in both AR and Winston units) by performing: + +```shell +# Getting the balance for your own wallet +ardrive get-balance -w /path/to/wallet/file.json + +# Getting the balance for ANY wallet (with sample output) +ardrive get-balance -a "HTTn8F92tR32N8wuo-NIDkjmqPknrbl10JWo5MZ9x2k" +1500000000000 Winston +1.5 AR +``` + +If, at any time, you need to send AR out of your wallet to another wallet address, you may perform: + +```shell +# Using our previously generated wallet as the destination... +ardrive send-ar -w /path/to/wallet/file.json --dest-address "HTTn8F92tR32N8wuo-NIDkjmqPknrbl10JWo5MZ9x2k" --ar-amount 2.12345 +``` + +## Working With Entities + +[ArDrive]'s [ArFS] integration provides for hierarchical organization of your file and folder data on Arweave. + +The fundamental entity types specified by ArFS are: + +
    +
  • Drives
  • +
  • Folders
  • +
  • Files
  • +
+ +Each instance of these entities have a Version 4 UUID entity ID that is commonly referred to by its entity type, i.e. drive ID, folder ID, and file ID. + +When you execute write functions with the CLI, the JSON output will contain information about the Arweave Transaction IDs that were registered when writing your entities to the blockweave, any miner rewards or [ArDrive Community](https://ardrive.io/community/) tips that were disbursed from your wallet, and any new entity IDs and, when applicable, encryption keys that were generated in the process of creating the entities. Typically, you'll want to keep track of those and get proficient with retrieving them in order to build your drive hierarchy to your liking. See [Understanding Drive and File Keys](#understanding-drive-and-file-keys) for more info. + +### Dry Run + +An important feature of the ArDrive CLI is the `--dry-run` flag. On each command that would write an ArFS entity, there is the option to run it as a "dry run". This will run all of the steps and print the outputs of a regular ArFS write, but will skip sending the actual transaction: + +```shell +ardrive --dry-run +``` + +This can be very useful for gathering price estimations or to confirm that you've copy-pasted your entity IDs correctly before committing to an upload. + +## Working With Drives + +### Understanding Drive Hierarchies + +At the root of every data tree is a "Drive" entity. When a drive is created, a Root Folder is also created for it. The entity IDs for both are generated and returned when you create a new drive: + +```shell +# Use `tee` to keep a receipt of the full set of transactions info and `jq` to focus on the data of interest +ardrive create-drive --wallet-file /path/to/my/wallet.json --drive-name "Teenage Love Poetry" | +tee created_drive.json | +jq '[.created[] | del(.metadataTxId)]' +[ + { + "type": "drive", + "entityId": "6939b9e0-cc98-42cb-bae0-5888eca78885" + } + { + "type": "folder", + "entityId": "d1535126-fded-4990-809f-83a06f2a1118" + } +] +``` + +The relationship between the drive and its root folder is clearly visible when retrieving the drive's info: + +```shell +ardrive drive-info -d "6939b9e0-cc98-42cb-bae0-5888eca78885" +| jq '{driveId, rootFolderId}' +{ + "driveId": "6939b9e0-cc98-42cb-bae0-5888eca78885", + "rootFolderId": "d1535126-fded-4990-809f-83a06f2a1118" +} + +``` + +All file and folder entities in the drive will be anchored to it by a "Drive-ID" GQL Tag. And they'll each be anchored to a parent folder ID, tracked via the "Parent-Folder-ID" GQL tag, forming a tree structure whose base terminates at the Root Folder. + +### Understanding Drive and File Keys + +Private Drives achieve privacy via end-to-end encryption facilitated by hash-derived "Keys". Drive Keys encrypt/decrypt Drive and Folder data, and File Keys encrypt/decrypt File Data. + +The relationships among your data and their keys is as follows: + +
    +
  • Drive Key = functionOf(Wallet Signature, Randomly Generated Drive ID, User-specified Drive Password)
  • +
  • File Key = functionOf(Randomly Generated File ID, Drive Key)
  • +
+ +When you create private entities, the returned JSON data from the ArDrive CLI will contain the keys needed to decrypt the encrypted representation of your entity that is now securely and permanently stored on the blockweave. + +To derive the drive key again for a drive, perform the following: + +```shell +# Will throw an error if the wallet or password specified can't be used to decrypt the on-chain drive +ardrive get-drive-key -w /path/to/my/wallet.json -d "6939b9e0-cc98-42cb-bae0-5888eca78885" -P +``` + +To derive the file key again for a file, perform the following: + +```shell +# Will throw an error if the drive key or drive-key-derivation data specified can't be used to decrypt the on-chain file +ardrive get-file-key --file-id "bd2ce978-6ede-4b0d-8f79-2d7bc235a0e0" --drive-id "6939b9e0-cc98-42cb-bae0-5888eca78885" --drive-key "yHdCjpCK3EcuhQcKNx2d/NN5ReEjoKfZVqKunlCnPEo" +``` + +### Managing Drive Passwords + +The ArDrive CLI's private drive and folder functions all require either a drive password OR a drive key. Private file functions require either the drive password or the file key. **Keys and passwords are sensitive data, so manage the entry, display, storage, and transmission of them very carefully.** + +Drive passwords are the most portable, and fundamental, encryption facet, so a few options are available during private drive operations for supplying them: + +
    +
  • Environment Variable
  • +
  • STDIN
  • +
  • Secure Prompt
  • +
+ +#### Supplying Your Password: Environment Variable + +```shell +# Securely type your password into a read prompt, store it to TMP_ARDRIVE_PW, and export it for the shell session +read -rs TMP_ARDRIVE_PW +export ARDRIVE_DRIVE_PW=$(TMP_ARDRIVE_PW) +ardrive -w /path/to/wallet.json -P +``` + +#### Supplying Your Password: STDIN + +```shell +# Pipe your drive password to the ArDrive CLI +cat /path/to/my/drive/password.txt | ardrive -w /path/to/wallet.json -P + +# Redirect your drive password to the ArDrive CLI +ardrive -w /path/to/wallet.json -P < /path/to/my/drive/password.txt +``` + +#### Supplying Your Password: Prompt + +```shell +# When all other options fail, the CLI will prompt for your password (NOT COMPATIBLE WITH PIPES AND REDIRECTS!) +ardrive -w /path/to/wallet.json -P +? Enter drive password: › ******** +``` + +### Creating Drives + +```shell +# Public drive +ardrive create-drive --wallet-file /path/to/my/wallet.json --drive-name "My Public Archive" + +# Private drive +ardrive create-drive --wallet-file /path/to/my/wallet.json --drive-name "Teenage Love Poetry" -P +``` + +### Listing Drives for an Address + +You can list all the drives associated with any Arweave wallet address, though the details of private drives will be obfuscated from you unless you provide the necessary decryption data. + +```shell +# List all your own drives +ardrive list-all-drives -w /path/to/my/wallet.json -P + +# List any address's drives +ardrive list-all-drives --address "HTTn8F92tR32N8wuo-NIDkjmqPknrbl10JWo5MZ9x2k" +``` + +### Listing Every Entity in a Drive + +Useful notes on listing the contents of drives: + +
    +
  • Listing a drive is effectively the same as listing its root folder.
  • +
  • You can control the tree depth of the data returned.
  • +
  • path, txPath, and entityIdPath properties on entities can provide useful handholds for other forms of data navigation
  • + + +```shell +# List everything in a private drive +ardrive list-drive -d "c7f87712-b54e-4491-bc96-1c5fa7b1da50" -w /path/to/my/wallet.json -P + +# List the contents of a public drive up to and including those in the grandchild folders of the root folder +ardrive list-drive -d "c7f87712-b54e-4491-bc96-1c5fa7b1da50" --max-depth 2 +``` + +### List Drive Pipeline Examples + +You can utilize `jq` and the list commands to reshape the commands' output data into useful forms and stats for many use cases. Here are a few examples: + + + +```shell +# Get share links for a PUBLIC drive +ardrive list-drive -d a44482fd-592e-45fa-a08a-e526c31b87f1 | jq '.[] | select(.entityType == "file") | "https://app.ardrive.io/#/file/" + .entityId + "/view"' +``` + +Example output: + +```shell +"https://app.ardrive.io/#/file/1337babe-f000-dead-beef-ffffffffffff/view" +"https://app.ardrive.io/#/file/cdbc9ddd-1cab-41d9-acbd-fd4328929de3/view" +"https://app.ardrive.io/#/file/f19bc712-b57a-4e0d-8e5c-b7f1786b34a1/view" +"https://app.ardrive.io/#/file/4f8e081b-42f2-442d-be41-57f6f906e1c8/view" +"https://app.ardrive.io/#/file/0e02d254-c853-4ff0-9b6e-c4d23d2a95f5/view" +"https://app.ardrive.io/#/file/c098b869-29d1-4a86-960f-a9e10433f0b0/view" +"https://app.ardrive.io/#/file/4afc8cdf-4d27-408a-bfb9-0a2ec21eebf8/view" +"https://app.ardrive.io/#/file/85fe488d-fcf7-48ca-9df8-2b39958bbf15/view" +... +``` + + + +```shell +# Get total size of all files within drive +ardrive list-drive -d 13c3c232-6687-4d11-8ac1-35284102c7db | jq ' map(select(.entityType == "file") | .size) | add' +``` + + + +```shell +# Get total number of files within drive +ardrive list-drive -d 01ea6ba3-9e58-42e7-899d-622fd110211c | jq '[ .[] | select(.entityType == "file") ] | length' +``` + +## Working With Folders + +As discussed previously, all folders in a drive are linked by way of parent folder references back to the root folder of a drive. Folders can be moved into any folder in the hierarchy that's not in their own subtree. + +### Creating Folders + +Creating folders manually is straightforward: + +```shell +ardrive create-folder --parent-folder-id "63153bb3-2ca9-4d42-9106-0ce82e793321" --name "My Awesome Folder" -w /path/to/wallet.json +``` + +Example output: + +```shell +{ + "created": [ + { + "type": "folder", + "metadataTxId": "AYFMBVmwqhbg9y5Fbj3Iasy5oxUqhauOW7PcS1sl4Dk", + "entityId": "d1b7c514-fb12-4603-aad8-002cf63015d3", + "key": "yHdCjpCKD2cuhQcKNx2d/XF5ReEjoKfZVqKunlCnPEk" + } + ], + "tips": [], + "fees": { + "AYFMBVmwqhbg9y5Fbj3Iasy5oxUqhauOW7PcS1sl4Dk": 1378052 + } +} +``` + +Note: Folders can also be created when supplying a folder for the --local-file-path during an upload-file command, however, the folder hierarchy on the local disk will be reconstructed on chain during the course of the recursive bulk upload. + +### Moving Folders + +Moving a folder is as simple as supplying a new parent folder ID. Note that naming collisions among entities within a folder are not allowed. + +```shell +ardrive move-folder --folder-id "9af694f6-4cfc-4eee-88a8-1b02704760c0" --parent-folder-id "29850ab7-56d4-4e1f-a5be-cb86d5513921" -w /path/to/wallet.json +``` + +### Viewing Folder Metadata + +To view the metadata of a folder, users can use the `folder-info` command: + +```shell +ardrive folder-info --folder-id "9af694f6-4cfc-4eee-88a8-1b02704760c0" +``` + +### Listing Contents of a Folder + +Similar to drives, the `list-folder` command can be used to fetch the metadata of each entity within a folder. But by default, the command will fetch only the immediate children of that folder (`--maxdepth 0`): + +```shell +# List immediate children of folder "My Public Folder" +ardrive list-folder --parent-folder-id "29850ab7-56d4-4e1f-a5be-cb86d5513940" +``` + +Example output: + +```shell +[ + { + "appName": "ArDrive-CLI", + "appVersion": "2.0", + "arFS": "0.11", + "contentType": "application/json", + "driveId": "01ea6ba3-9e58-42e7-899d-622fd110211a", + "entityType": "folder", + "name": "mytestfolder", + "txId": "HYiKyfLwY7PT9NleTQoTiM_-qPVUwf4ClDhx1sjUAEU", + "unixTime": 1635102772, + "parentFolderId": "29850ab7-56d4-4e1f-a5be-cb86d5513940", + "entityId": "03df2929-1440-4ab4-bbf0-9dc776e1ed96", + "path": "/My Public Folder/mytestfolder", + "txIdPath": "/09_x0X2eZ3flXXLS72WdTDq6uaa5g2LjsT-QH1m0zhU/HYiKyfLwY7PT9NleTQoTiM_-qPVUwf4ClDhx1sjUAEU", + "entityIdPath": "/29850ab7-56d4-4e1f-a5be-cb86d5513940/03df2929-1440-4ab4-bbf0-9dc776e1ed96" + }, + { + "appName": "ArDrive-CLI", + "appVersion": "2.0", + "arFS": "0.11", + "contentType": "application/json", + "driveId": "01ea6ba3-9e58-42e7-899d-622fd110211a", + "entityType": "folder", + "name": "Super sonic public folder", + "txId": "VUk1B_vo1va2-EHLtqjsotzy0Rdn6lU4hQo3RD2xoTI", + "unixTime": 1631283259, + "parentFolderId": "29850ab7-56d4-4e1f-a5be-cb86d5513940", + "entityId": "452c6aec-43dc-4015-9abd-20083068d432", + "path": "/My Public Folder/Super sonic sub folder", + "txIdPath": "/09_x0X2eZ3flXXLS72WdTDq6uaa5g2LjsT-QH1m0zhU/VUk1B_vo1va2-EHLtqjsotzy0Rdn6lU4hQo3RD2xoTI", + "entityIdPath": "/29850ab7-56d4-4e1f-a5be-cb86d5513940/452c6aec-43dc-4015-9abd-20083068d432" + }, + { + "appName": "ArDrive-CLI", + "appVersion": "2.0", + "arFS": "0.11", + "contentType": "application/json", + "driveId": "01ea6ba3-9e58-42e7-899d-622fd110211a", + "entityType": "file", + "name": "test-number-twelve.txt", + "txId": "429zBqnd7ZBNzgukaix26RYz3g5SeXCCo_oIY6CPZLg", + "unixTime": 1631722234, + "size": 47, + "lastModifiedDate": 1631722217028, + "parentFolderId": "29850ab7-56d4-4e1f-a5be-cb86d5513940", + "entityId": "e5948327-d6de-4acf-a6fe-e091ecf78d71", + "path": "/My Public Folder/test-number-twelve.txt", + "txIdPath": "/09_x0X2eZ3flXXLS72WdTDq6uaa5g2LjsT-QH1m0zhU/429zBqnd7ZBNzgukaix26RYz3g5SeXCCo_oIY6CPZLg", + "entityIdPath": "/29850ab7-56d4-4e1f-a5be-cb86d5513940/e5948327-d6de-4acf-a6fe-e091ecf78d71" + }, + { + "appName": "ArDrive-CLI", + "appVersion": "2.0", + "arFS": "0.11", + "contentType": "application/json", + "driveId": "01ea6ba3-9e58-42e7-899d-622fd110211a", + "entityType": "file", + "name": "wonderful-test-file.txt", + "txId": "6CokwlzB81Fx7dq-lB654VM0XQykdU6eYohDmEJ2gk4", + "unixTime": 1631671275, + "size": 23, + "lastModifiedDate": 1631283389232, + "parentFolderId": "29850ab7-56d4-4e1f-a5be-cb86d5513940", + "entityId": "3274dae9-3487-41eb-94d5-8d5d3d8bc343", + "path": "/My Public Folder/wonderful-test-file.txt", + "txIdPath": "/09_x0X2eZ3flXXLS72WdTDq6uaa5g2LjsT-QH1m0zhU/6CokwlzB81Fx7dq-lB654VM0XQykdU6eYohDmEJ2gk4", + "entityIdPath": "/29850ab7-56d4-4e1f-a5be-cb86d5513940/3274dae9-3487-41eb-94d5-8d5d3d8bc343" + } +] +``` + +```shell +# List all contents of a folder +ardrive list-folder --parent-folder-id "9af694f6-4cfc-4eee-88a8-1b02704760c0" --all +``` + +## Working With Files + +Similar to folders, files are linked to a parent folder which ultimately chains the file back to the root folder of its parent drive. As such, a parent folder ID is required in order to upload files. Files can be freely moved to other folders within their original drive. + +The important difference for file entities is that they also hold a reference to their data transaction ID, which is the `dataTxId` as returned by the `file-info` command. This is where your uploaded data lives on the permaweb. + +**NOTE: The CLI currently (v1.0.0) has progress logging on uploads DISABLED for producing clean JSON outputs that can be piped in the terminal. On larger uploads, remember to be patient. You can check your system's `node` process to confirm the process is still uploading.** + +### Uploading a Single File + +To upload a file, you'll need a parent folder id, the file to upload's file path, and the path to your wallet: + +```shell +# Supply the parent folder ID to upload-file +ardrive upload-file --local-file-path /path/to/file.txt --parent-folder-id "9af694f6-4cfc-4eee-88a8-1b02704760c0" -w /path/to/wallet.json +``` + +Example output: + +```shell +{ + "created": [ + { + "type": "file", + "metadataTxId": "YfdDXUyerPCpBbGTm_gv_x5hR3tu5fnz8bM-jPL__JE", + "dataTxId": "l4iNWyBapfAIj7OU-nB8z9XrBhawyqzs5O9qhk-3EnI", + "entityId": "6613395a-cf19-4420-846a-f88b7b765c05" + } + ], + "tips": [ + { + "txId": "1zwdfZAIV8E26YjBs2ZQ4xjjP_1ewalvRgD_GyYw7f8", + "recipient": { + "address": "3mxGJ4xLcQQNv6_TiKx0F0d5XVE0mNvONQI5GZXJXkt" + }, + "winston": "10000000" + } + ], + "fees": { + "l4iNWyBapfAIj7OU-nB8z9XrBhawyqzs5O9qhk-3EnI": 1369131, + "YfdDXUyerPCpBbGTm_gv_x5hR3tu5fnz8bM-jPL__JE": 1432001, + "1zwdfZAIV8E26YjBs2ZQ4xjjP_1ewalvRgD_GyYw7f8": 1363608 + } +} +``` + +NOTE: To upload to the root of a drive, specify its root folder ID as the parent folder ID for the upload destination. You can retrieve it like so: + +```shell +ardrive drive-info -d "c7f87712-b54e-4491-bc96-1c5fa7b1da50" | jq -r '.rootFolderId' +``` + +### Uploading a Folder with Files (Bulk Upload) + +Users can perform a bulk upload by using the upload-file command on a target folder. The command will reconstruct the folder hierarchy on local disk as ArFS folders on the permaweb and upload each file into their corresponding folders: + +```shell +ardrive upload-file --local-file-path /path/to/folder --parent-folder-id "9af694f6-4cfc-4eee-88a8-1b02704760c0" -w /path/to/wallet.json +``` + +This method of upload can be used to upload a large number of files and folders within the folder tree. If existing entities are encountered in the destination folder tree that would cause naming conflicts, expect the following behaviors: + +- Folder names that conflict with a FILE name at the destination will cause an error to be thrown +- Folder names that conflict with a FOLDER name at the destination will use the existing folder ID (i.e. skip) rather than creating a new folder +- File names that conflict with a FOLDER name at the destination will cause an error to be thrown +- File names that conflict with a FILE name at the destination will be uploaded as a REVISION + +### Fetching the Metadata of a File Entity + +Simply perform the file-info command to retrieve the metadata of a file: + +```shell +ardrive file-info --file-id "e5ebc14c-5b2d-4462-8f59-7f4a62e7770f" +``` + +Example output: + +```shell +{ + "appName": "ArDrive-Web", + "appVersion": "0.1.0", + "arFS": "0.11", + "contentType": "application/json", + "driveId": "51062487-2e8b-4af7-bd81-4345dc28ea5d", + "entityType": "file", + "name": "2_depth.png", + "txId": "CZKdjqwnmxbWchGA1hjSO5ZH--4OYodIGWzI-FmX28U", + "unixTime": 1633625081, + "size": 41946, + "lastModifiedDate": 1605157729000, + "parentFolderId": "a2c8a0cb-0ca7-4dbb-8bf8-93f75f308e63", + "entityId": "e5ebc14c-5b2d-4462-8f59-7f4a62e7770f", + "fileId": "e5ebc14c-5b2d-4462-8f59-7f4a62e7770f", + "dataTxId": "Jz0WsWyAGVc0aE3UzACo-YJqG8OPrN3UucmDdt8Fbjc", + "dataContentType": "image/png" +} +``` + +### Create New Drive and Upload Folder Pipeline Example + +```shell +# Use `tee` to store command json outputs for later review/backup/automation/etc. +# Use `jq` to parse json output and retrieve the root folder ID for use in downstream command +ardrive create-drive -w /path/to/wallet.json -n "My Public Archive" | +tee create_drive_output.json | +jq -r '.created[] | select(.type == "folder") | .entityId' | +while read -r parentFolderId; do +ardrive upload-file -w /path/to/wallet.json --local-file-path ./myarchives -F "$parentFolderId"; +done | +tee upload_folder_output.json +``` + +## Other Utility Operations + +### Monitoring Transactions + +Block time on Arweave is typically between 2-3 minutes in duration, so transactions can be mined within that time frame when [network congestion](#dealing-with-network-congestion) is low. Transactions, in the general case, proceed through the following set of states: + +- Pending: the transaction is waiting the "mempool" to be mined +- Confirming: the transaction was mined on an Arweave Node, but has not yet been confirmed by at least 15 total nodes on the network +- Confirmed: the transaction was mined on an Arweave Node and confirmed by at least 15 total nodes on the network +- Not Found: the transaction is not available for any of the following reasons: + - Insufficient reward to join the mempool + - Insufficient reward to be mined within 50 blocks during a period of network congestion + - Transaction is transitioning between states + - Transaction ID is invalid + +Monitor any Arweave transaction's status via its transaction ID by performing: + +```shell +# Peek at the status: +yarn ardrive tx-status -t "ekSMckikdRJ8RGIkFa-X3xq3427tvM7J9adv8HP3Bzs" +``` + +Example output: + +```shell +ekSMckikdRJ8RGIkFa-X3xq3427tvM7J9adv8HP3Bzs: Mined at block height 775810 with 22439 confirmations +``` + +```shell +# Reprint the status every 10 seconds: +watch -n 10 yarn ardrive tx-status -t "ekSMckikdRJ8RGIkFa-X3xq3427tvM7J9adv8HP3Bzs" +``` + +### Dealing With Network Congestion + +Currently, Arweave blocks hold up to 1000 transactions per block. The "mempool", where pending transactions reside until they've been included into a block, will only hold a transaction for 50 blocks (~100-150 minutes) before it's discarded by the network resulting in no fees or data being transacted. During periods of network congestion (i.e. those where the mempool contains 1000 or more pending transactions), it may make sense to either: + +a) wait for congestion to dissipate before attempting your transactions. + +b) apply the fee boost multiplier to your transactions rewards with the --boost parameter during write operations in order to front-run some of the congestion. + +#### Check for network congestion before uploading + +```shell +# See all the transactions in the mempool +ardrive get-mempool + +# Return the count of the transactions in the mempool +ardrive get-mempool | jq 'length' +``` + +#### Front-run Congestion By Boosting Miner Rewards + +```shell +# Increase the miner reward on your transactions by 50% +ardrive upload-file --wallet-file /path/to/my/wallet.json --parent-folder-id "f0c58c11-430c-4383-8e54-4d864cc7e927" --local-file-path ./helloworld.txt --boost 1.5 +``` + +# All ArDrive CLI Commands + +```shell + █████╗ ██████╗ ██████╗ ██████╗ ██╗██╗ ██╗███████╗ + ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██║██║ ██║██╔════╝ + ███████║██████╔╝██║ ██║██████╔╝██║██║ ██║█████╗ + ██╔══██║██╔══██╗██║ ██║██╔══██╗██║╚██╗ ██╔╝██╔══╝ + ██║ ██║██║ ██║██████╔╝██║ ██║██║ ╚████╔╝ ███████╗ + ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚══════╝ + ██████╗██╗ ██╗ + ██╔════╝██║ ██║ + ██║ ██║ ██║ + ██║ ██║ ██║ + ╚██████╗███████╗██║ + ╚═════╝╚══════╝╚═╝ + + +Write ArFS +=========== +create-drive +create-folder +upload-file + +move-file +move-folder + + +Read ArFS +=========== +file-info +folder-info +drive-info + +list-folder +list-drive +list-all-drives + + +Wallet Ops +=========== +generate-seedphrase +generate-wallet + +get-address +get-balance +send-ar + +get-drive-key +get-file-key + + +Arweave Ops +=========== +tx-status +get-mempool + + +# Learn more about a command: +ardrive --help +``` + +# Getting Help + +[ArDrive Community Discord][ardrive-discord] + +[ardrive]: https://ardrive.io +[arweave]: https://ardrive.io/what-is-arweave/ +[ardrive-github]: https://github.com/ardriveapp/ +[arfs]: https://ardrive.atlassian.net/l/c/yDcGDbUm +[ardrive-web-app]: https://app.ardrive.io +[ardrive-core]: https://github.com/ardriveapp/ardrive-core-js [yarn-install]: https://yarnpkg.com/getting-started/install [nvm-install]: https://github.com/nvm-sh/nvm#installing-and-updating [wsl-install]: https://code.visualstudio.com/docs/remote/wsl @@ -80,3 +886,7 @@ To ensure your environment is compatible, we also recommend the following VSCode [prettier-vscode]: https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode [zipfs-vscode]: https://marketplace.visualstudio.com/items?itemName=arcanis.vscode-zipfs [eslint-vscode]: https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint +[viewblock blockchain explorer]: https://viewblock.io/arweave/ +[ardrive-discord]: https://discord.gg/w4vvrezD +[arconnect]: https://arconnect.io/ +[kb-wallets]: https://ardrive.atlassian.net/l/c/FpK8FuoQ diff --git a/nyc.config.js b/nyc.config.js new file mode 100644 index 00000000..3ac351c1 --- /dev/null +++ b/nyc.config.js @@ -0,0 +1,22 @@ +'use-strict'; + +// Istanbul `nyc` configuration file + +// Reference for config options: https://github.com/istanbuljs/nyc#common-configuration-options +// eslint-disable-next-line no-undef +module.exports = { + extends: '@istanbuljs/nyc-config-typescript', + extension: ['.ts'], + include: ['src/**/*.ts'], + exclude: ['**/*.d.ts', '**/*.test.ts'], + all: true, + // Reporter options: https://istanbul.js.org/docs/advanced/alternative-reporters/ + reporter: ['text-summary', 'html'] + + // Coverage options (once we have some coverage) + // 'check-coverage': true, + // branches: 80, + // lines: 80, + // functions: 80, + // statements: 80 +}; diff --git a/package.json b/package.json index 0d22677f..b9aea449 100644 --- a/package.json +++ b/package.json @@ -1,50 +1,69 @@ { "name": "ardrive-cli", - "version": "0.3.1", - "description": "The ArDrive Command Line Interface (CLI) contains all of the needed security, file synchronization and Arweave wallet capabilities via a node.js application.", + "version": "1.0.0", + "description": "The ArDrive Command Line Interface (CLI is a Node.js application for terminal-based ArDrive workflows. It also offers utility operations for securely interacting with Arweave wallets and inspecting various Arweave blockchain conditions.", "main": "./lib/index.js", - "bin": "./lib/index.js", + "bin": { + "ardrive": "./lib/index.js" + }, "types": "./lib/index.d.ts", "dependencies": { - "@types/node": "^14.14.32", - "@types/uuid": "^8.3.0", "ardrive-core-js": "0.5.1", - "arweave": "1.10.11", + "arweave": "^1.10.16", "arweave-bundles": "^1.0.3", - "community-js": "^1.1.36", - "progress": "^2.0.3", - "prompt-password": "^1.2.0", - "prompt-sync": "^4.2.0", + "arweave-mnemonic-keys": "^0.0.9", + "base64-js": "^1.5.1", + "commander": "^8.2.0", + "jwk-to-pem": "^2.0.4", + "lodash": "^4.17.21", + "node-fetch": "2.6.2", "prompts": "^2.4.0", + "regression": "^2.0.1", + "smartweave": "^0.4.45", "typescript": "^4.2.3", "uuid": "^8.3.2" }, "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@types/chai": "^4.2.21", "@types/jwk-to-pem": "^2.0.0", - "@types/prompt-sync": "^4.1.0", + "@types/lodash": "^4", + "@types/mocha": "^9.0.0", + "@types/node": "^14.14.32", + "@types/node-fetch": "2.5.3", "@types/prompts": "^2.0.9", + "@types/regression": "^2.0.2", + "@types/sinon": "^10.0.2", + "@types/source-map-support": "^0", + "@types/uuid": "^8.3.0", "@typescript-eslint/eslint-plugin": "^4.18.0", "@typescript-eslint/parser": "^4.18.0", + "chai": "^4.3.4", "eslint": "^7.23.0", "eslint-config-prettier": "^8.1.0", "eslint-plugin-prettier": "latest", "husky": "^=6", "lint-staged": "^11.0.0", + "mocha": "^9.1.1", + "nyc": "^15.1.0", "prettier": "^2.2.1", "rimraf": "^3.0.2", - "ts-node": "^9.1.1", - "tsc-files": "^1.1.2" + "sinon": "^11.1.2", + "source-map-support": "^0.5.20", + "ts-node": "^10.2.1", + "ts-sinon": "^2.0.1" }, "scripts": { - "clean": "rimraf lib", + "clean": "rimraf [ lib .nyc_output node_modules coverage ]", "format": "prettier --write \"src/**/*.ts\"", "lint": "eslint . --ext .ts", "lintfix": "eslint . --ext .ts --fix", + "test": "nyc mocha", + "coverage": "nyc --reporter text mocha", "typecheck": "tsc --noemit", - "build": "yarn clean && tsc", - "version": "yarn run format && git add -A src", - "postversion": "git push && git push --tags", - "start": "yarn run build && node ./lib/index.js" + "build": "yarn clean && tsc --project ./tsconfig.prod.json", + "ci": "yarn build && yarn test", + "dev": "yarn clean && tsc --project ./tsconfig.prod.json -w" }, "husky": { "hooks": { diff --git a/src/CLICommand/cli.ts b/src/CLICommand/cli.ts new file mode 100644 index 00000000..d4092004 --- /dev/null +++ b/src/CLICommand/cli.ts @@ -0,0 +1,23 @@ +export interface ParsedArguments { + // TODO: make parameterName to have type ParameterName + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [parameterName: string /** ParameterName */]: any; +} +/** + * @name CliApiObject + * abstract class intended to help encapsulate the cli api object + * also used for mocking in tests + */ +export abstract class CliApiObject { + abstract arguments(names: string): CliApiObject; + abstract action(action: (options: ParsedArguments) => Promise): CliApiObject; + abstract option(aliases: string, description: string, defaultValue?: string | boolean): CliApiObject; + abstract requiredOption(aliases: string, description: string, defaultValue?: string | boolean): CliApiObject; + abstract command(commandName: string): CliApiObject; + abstract parse(...args: [program: CliApiObject] | [argv: string[]]): void; + abstract addHelpCommand(addHelp: boolean): void; + abstract opts(): ParsedArguments; + abstract name(name: string): CliApiObject; + abstract usage(usage: string): CliApiObject; + abstract outputHelp(): void; +} diff --git a/src/CLICommand/cli_command.test.ts b/src/CLICommand/cli_command.test.ts new file mode 100644 index 00000000..448bc2c5 --- /dev/null +++ b/src/CLICommand/cli_command.test.ts @@ -0,0 +1,127 @@ +import { expect } from 'chai'; +import { Command } from 'commander'; +import { SinonStubbedInstance, stub } from 'sinon'; +import { assertConjunctionParameters, CLICommand, CommandDescriptor } from './cli_command'; + +import { + DriveNameParameter, + UnsafeDrivePasswordParameter, + SeedPhraseParameter, + WalletFileParameter +} from '../parameter_declarations'; +import { CliApiObject } from './cli'; +import { baseArgv } from './test_constants'; +import { Parameter } from './parameter'; + +const MY_DRIVE_NAME = 'My awesome drive!'; +const testingCommandName = 'drive-name-test'; +const driveNameCommandDescription: CommandDescriptor = { + name: testingCommandName, + parameters: [DriveNameParameter], + async action(option) { + /** This code here will run after argv is parsed */ + expect(option.driveNameTest).to.equal(MY_DRIVE_NAME); + } +}; +const driveNameArgv: string[] = [...baseArgv, testingCommandName, '--drive-name', MY_DRIVE_NAME]; +async function action() { + // eslint-disable-next-line no-console + console.log('DUMMY ACTION'); +} +const nonEmptyValue = 'non-empty value'; +const commandDescriptorRequiredWallet: CommandDescriptor = { + name: testingCommandName, + parameters: [ + WalletFileParameter, + { name: UnsafeDrivePasswordParameter, requiredConjunctionParameters: [WalletFileParameter] } + ], + action +}; +const parsedOptionsMissingWallet = { + [WalletFileParameter]: undefined, + [UnsafeDrivePasswordParameter]: nonEmptyValue +}; +const commandDescriptorForbiddenWalletFileAndSeedPhrase: CommandDescriptor = { + name: testingCommandName, + parameters: [WalletFileParameter, SeedPhraseParameter], + action +}; +const parsedCommandOptionsBothSpecified = { + [WalletFileParameter]: nonEmptyValue, + [SeedPhraseParameter]: nonEmptyValue +}; + +class TestCliApiObject { + constructor(private readonly program: CliApiObject = new Command() as CliApiObject) {} + arguments = stub(this.program, 'arguments').returnsThis(); + action = stub(this.program, 'action').returnsThis(); + option = stub(this.program, 'option').returnsThis(); + requiredOption = stub(this.program, 'requiredOption').returnsThis(); + command = stub(this.program, 'command').returnsThis(); + parse = stub(this.program, 'parse'); + addHelpCommand = stub(this.program, 'addHelpCommand').returnsThis(); + opts = stub(this.program, 'opts').returnsThis(); + + name = stub(this.program, 'name').returnsThis(); + usage = stub(this.program, 'usage').returnsThis(); + outputHelp = stub(this.program, 'outputHelp'); +} + +describe('CLICommand class', () => { + let stubbedProgram: SinonStubbedInstance; + const program: CliApiObject = new Command() as CliApiObject; + + before(() => { + stubbedProgram = new TestCliApiObject(program); + }); + + it('Calls the library API function once when a command is set', () => { + new CLICommand(driveNameCommandDescription, stubbedProgram); + expect(stubbedProgram.command.calledOnce).to.be.true; + expect(stubbedProgram.action.calledOnce).to.be.true; + }); + + it('The library parses the given argv', () => { + CLICommand.parse(stubbedProgram, driveNameArgv); + expect(stubbedProgram.parse.calledOnce).to.be.true; + }); + + it('Assert required in conjunction parameters', () => { + expect(function () { + assertConjunctionParameters(commandDescriptorRequiredWallet, parsedOptionsMissingWallet); + }).to.throw(); + }); + + it('Assert forbidden in conjunction parameters', () => { + expect(function () { + assertConjunctionParameters( + commandDescriptorForbiddenWalletFileAndSeedPhrase, + parsedCommandOptionsBothSpecified + ); + }).to.throw(); + }); + + it('No colliding parameters', () => { + const allCommandDescriptors = CLICommand._getAllCommandDescriptors(); + allCommandDescriptors.forEach((command) => { + const parameters = command.parameters.map((param) => new Parameter(param)); + parameters.forEach((parameter_1, index) => { + const allParametersExceptMe = parameters; + allParametersExceptMe.splice(index); + const collidingParameters = allParametersExceptMe.filter((parameter_2) => { + const areAllowedInConjunction = !parameter_2.forbiddenParametersInConjunction.includes( + parameter_1.name + ); + if (areAllowedInConjunction) { + return parameter_2.aliases.find((alias) => parameter_1.aliases.includes(alias)); + } + return false; + }); + // if (collidingParameters.length) { + // debugger; + // } + expect(collidingParameters).to.be.empty; + }); + }); + }); +}); diff --git a/src/CLICommand/cli_command.ts b/src/CLICommand/cli_command.ts new file mode 100644 index 00000000..a1f9eefa --- /dev/null +++ b/src/CLICommand/cli_command.ts @@ -0,0 +1,140 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import { Command } from 'commander'; +import { CliApiObject, ParsedArguments } from './cli'; +import { ERROR_EXIT_CODE } from './constants'; +import { Parameter, ParameterName, ParameterOverridenConfig } from './parameter'; + +export type CommandName = string; +export interface CommandDescriptor { + name: CommandName; + parameters: (ParameterName | ParameterOverridenConfig)[]; + action(options: ParsedArguments): Promise; +} + +const program: CliApiObject = new Command() as CliApiObject; +program.name('ardrive'); +program.addHelpCommand(true); +program.usage('[command] [command-specific options]'); + +/** + * @name setCommanderCommand + * @param {CommandDescriptor} commandDescriptor the description of the command to be set + * @param {CliApiObject} program the instance of the commander class + * This function is the responsible to tell the third party library to declare a command + */ +function setCommanderCommand(commandDescriptor: CommandDescriptor, program: CliApiObject): void { + let command: CliApiObject = program.command(commandDescriptor.name); + const parameters = commandDescriptor.parameters.map((param) => new Parameter(param)); + parameters.forEach((parameter) => { + const aliasesAsString = parameter.aliases.join(' '); + const paramTypeString = (function () { + if (parameter.type === 'array') { + return ` <${parameter.name}...>`; + } else if (parameter.type === 'boolean') { + return ''; + } + return ` <${parameter.name}>`; + })(); + const optionArguments = [ + `${aliasesAsString}${paramTypeString}`, + parameter.description, + parameter.default + ] as const; + if (parameter.required) { + command.requiredOption(...optionArguments); + } else { + command.option(...optionArguments); + } + }); + command = command.action(async (options) => { + await (async function () { + assertConjunctionParameters(commandDescriptor, options); + const exitCode = await commandDescriptor.action(options); + exitProgram(exitCode || 0); + })().catch((err) => { + console.log(err.message); + exitProgram(ERROR_EXIT_CODE); + }); + }); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function assertConjunctionParameters(commandDescriptor: CommandDescriptor, options: any): void { + const parameters = commandDescriptor.parameters; + parameters.forEach((param) => { + const parameter = new Parameter(param); + const parameterName = parameter.name; + const parameterValue = options[parameterName]; + if (parameterValue) { + assertRequired(parameter, options); + assertForbidden(parameter, options); + } + }); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function assertRequired(parameter: Parameter, options: any): void { + const parameterName = parameter.name; + const required = parameter.requiredParametersInConjunction; + required.forEach((requiredParameterName) => { + const requiredParameterValue = options[requiredParameterName]; + if (!requiredParameterValue) { + throw new Error(`Parameter ${parameterName} requires ${requiredParameterName} but it wasn't provided`); + } + }); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function assertForbidden(parameter: Parameter, options: any): void { + const parameterName = parameter.name; + const forbidden = parameter.forbiddenParametersInConjunction; + forbidden.forEach((forbiddenParameterName) => { + const forbiddenParameterValue = options[forbiddenParameterName]; + if (forbiddenParameterValue) { + throw new Error(`Parameter ${parameterName} cannot be used in conjunction with ${forbiddenParameterName}`); + } + }); +} + +export class CLICommand { + private static allCommandDescriptors: CommandDescriptor[] = []; + + /** + * @param {CommandDescriptor} commandDescription an immutable representation of a command + * @param {string[]} argv a custom argv for testing purposes + */ + constructor(private readonly commandDescription: CommandDescriptor, private readonly _program?: CliApiObject) { + CLICommand.allCommandDescriptors.push(commandDescription); + this.setCommand(); + } + + // A singleton instance of the commander's program object + public static get program(): CliApiObject { + // TODO: make me private when index.ts is fully de-coupled from commander library + return program; + } + + private get program(): CliApiObject { + return this._program || CLICommand.program; + } + + private setCommand(): void { + setCommanderCommand(this.commandDescription, this.program); + } + + public static parse(program: CliApiObject = this.program, argv: string[] = process.argv): void { + program.parse(argv); + } + + /** + * For test purposes only + * @returns {CommandDescriptor[]} all declared command descriptors + */ + public static _getAllCommandDescriptors(): CommandDescriptor[] { + return this.allCommandDescriptors; + } +} + +function exitProgram(exitCode: number): void { + process.exitCode = exitCode; +} diff --git a/src/CLICommand/constants.ts b/src/CLICommand/constants.ts new file mode 100644 index 00000000..f4dee84f --- /dev/null +++ b/src/CLICommand/constants.ts @@ -0,0 +1,2 @@ +export const SUCCESS_EXIT_CODE = 0; +export const ERROR_EXIT_CODE = 1; diff --git a/src/CLICommand/index.ts b/src/CLICommand/index.ts new file mode 100644 index 00000000..0c64769d --- /dev/null +++ b/src/CLICommand/index.ts @@ -0,0 +1,2 @@ +export * from './cli_command'; +export { ParametersHelper } from './parameters_helper'; diff --git a/src/CLICommand/parameter.test.ts b/src/CLICommand/parameter.test.ts new file mode 100644 index 00000000..7d7656a4 --- /dev/null +++ b/src/CLICommand/parameter.test.ts @@ -0,0 +1,54 @@ +import { expect } from 'chai'; +import { Parameter, ParameterConfig, ParameterOverridenConfig } from './parameter'; +import { defaultParameter, defaultParameterName } from './test_constants'; + +const parameterName = 'myCustomParam'; +const parameterAliases = ['-c', '--my-custom-param']; +const parameterDescription = 'This is my custom parameter'; +const overridenParameterDescription = 'OVERRIDEN!'; +const initialParameterConfig: ParameterConfig = { + name: parameterName, + aliases: parameterAliases, + description: parameterDescription +}; +const overridenParameterConfig: ParameterOverridenConfig = { + name: parameterName, + description: overridenParameterDescription +}; + +describe('Parameter class', () => { + let parameter: Parameter; + + before(() => { + Parameter.declare(initialParameterConfig); + }); + + it('A previously defined parameter can be read', () => { + parameter = new Parameter(parameterName); + expect(parameter).instanceOf(Parameter); + }); + + it('Throws an error if reading a undefined parameter', () => { + expect(() => new Parameter('undefinedParameter')).to.throw(); + }); + + it('Default type parameter is "single-value"', () => { + Parameter.declare(defaultParameter); + const defaultParam = new Parameter(defaultParameterName); + expect(defaultParam.type).to.equal('single-value'); + }); + + it('The getters holds the right values', () => { + parameter = new Parameter(parameterName); + expect(parameter.name).to.equal(parameterName); + expect(parameter.aliases).deep.equal(parameterAliases); + expect(parameter.description).to.equal(parameterDescription); + expect(parameter.default).to.be.undefined; + expect(parameter.type).to.equal('single-value'); + }); + + it('Passing an object as argument would overwrite the default configuration', () => { + parameter = new Parameter(overridenParameterConfig); + expect(parameter.description).to.equal(overridenParameterDescription); + }); +}); diff --git a/src/CLICommand/parameter.ts b/src/CLICommand/parameter.ts new file mode 100644 index 00000000..8cfa944d --- /dev/null +++ b/src/CLICommand/parameter.ts @@ -0,0 +1,88 @@ +export type ParameterName = string; +export type ParameterType = 'single-value' | 'boolean' | 'array'; +export type ParameterOverridenConfig = Partial & Pick; + +export interface ParameterConfig { + name: ParameterName; + aliases: ParameterName[]; + description: string; + type?: ParameterType; + default?: string; + required?: boolean; + forbiddenConjunctionParameters?: ParameterName[]; + requiredConjunctionParameters?: ParameterName[]; +} + +export class Parameter implements ParameterConfig { + public readonly name; + private parameterData: ParameterConfig; + private static parameters: ParameterConfig[] = []; + + constructor(arg: ParameterName | ParameterOverridenConfig) { + const argAsParameterName = arg as ParameterName; + const argAsOverridenConfig = arg as ParameterOverridenConfig; + const overridenConfig = (function () { + if (typeof arg === 'string') { + return undefined; + } else { + return argAsOverridenConfig; + } + })(); + this.name = overridenConfig?.name || argAsParameterName; + this.parameterData = Object.assign(Parameter.get(this.name), overridenConfig); + } + + public get aliases(): ParameterName[] { + return this.parameterData.aliases; + } + + public get description(): string { + return this.parameterData.description; + } + + public get default(): string | undefined { + return this.parameterData.default; + } + + public get type(): ParameterType { + return this.parameterData.type || 'single-value'; + } + + public get required(): boolean { + return !!this.parameterData.required; + } + + public get forbiddenParametersInConjunction(): ParameterName[] { + return Array.isArray(this.parameterData.forbiddenConjunctionParameters) + ? this.parameterData.forbiddenConjunctionParameters.slice() + : []; + } + + public get requiredParametersInConjunction(): ParameterName[] { + return Array.isArray(this.parameterData.requiredConjunctionParameters) + ? this.parameterData.requiredConjunctionParameters.slice() + : []; + } + + public static declare(parameter: ParameterConfig): void { + Parameter.parameters.push(parameter); + } + + /** + * @name reset + * For testing purposes only. It will just remove all parameters declaration + * @returns {ParameterConfig[]} the removed parameters + */ + public static reset(): ParameterConfig[] { + return this.parameters.splice(0, this.parameters.length); + } + + public static get(parameterName: ParameterName): ParameterConfig { + const param = Parameter.parameters.find((p) => p.name === parameterName); + if (!param) { + throw new Error(`No such parameter ${parameterName}`); + } + // It uses Object.assign in order to return a copy of the instance instead of the original one, so modifying it won't affect the original + return Object.assign({}, param); + } +} diff --git a/src/CLICommand/parameters_helper.test.ts b/src/CLICommand/parameters_helper.test.ts new file mode 100644 index 00000000..c0727637 --- /dev/null +++ b/src/CLICommand/parameters_helper.test.ts @@ -0,0 +1,385 @@ +import { expect } from 'chai'; +import { Command } from 'commander'; +import { CliApiObject, ParsedArguments } from './cli'; +import { CLICommand, CommandDescriptor } from './cli_command'; +import { Parameter, ParameterName } from './parameter'; +import { + arrayParameter, + arrayParameterName, + baseArgv, + booleanParameter, + booleanParameterName, + requiredParameter, + requiredParameterName, + singleValueParameter, + singleValueParameterName, + testCommandName +} from './test_constants'; +import { ParametersHelper } from './parameters_helper'; +import { + AddressParameter, + DriveKeyParameter, + UnsafeDrivePasswordParameter, + SeedPhraseParameter, + WalletFileParameter +} from '../parameter_declarations'; +import '../parameter_declarations'; +import { urlEncodeHashKey } from '../utils'; +import { stubArweaveAddress } from '../utils/stubs'; + +const expectedArweaveAddress = stubArweaveAddress('P8aFJizMVBl7HeoRAz2i1dNYkG_KoN7oB9tZpIw6lo4'); + +function declareCommandWithParams( + program: CliApiObject, + parameters: ParameterName[], + action: (options: ParsedArguments) => Promise +): void { + const command: CommandDescriptor = { + name: testCommandName, + parameters, + action + }; + new CLICommand(command, program); +} + +describe('ParametersHelper class', () => { + let program: CliApiObject; + + beforeEach(() => { + program = new Command() as CliApiObject; + }); + + it('Actually reads the value from argv', () => { + Parameter.declare(singleValueParameter); + declareCommandWithParams(program, [singleValueParameterName], async (options) => { + const parameters = new ParametersHelper(options); + expect(parameters.getParameterValue(singleValueParameterName)).to.not.be.undefined; + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '--single-value-parameter', '1234567890']); + }); + + it('Boolean parameter false', () => { + Parameter.declare(booleanParameter); + declareCommandWithParams(program, [booleanParameterName], async (options) => { + const parameters = new ParametersHelper(options); + expect(!!parameters.getParameterValue(booleanParameterName)).to.be.false; + }); + CLICommand.parse(program, [...baseArgv, testCommandName]); + }); + + it('Boolean parameter true', () => { + Parameter.declare(booleanParameter); + declareCommandWithParams(program, [booleanParameterName], async (options) => { + const parameters = new ParametersHelper(options); + expect(parameters.getParameterValue(booleanParameterName)).to.be.true; + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '--boolean-parameter']); + }); + + it('Array parameter', () => { + const colorsArray = ['red', 'green', 'blue']; + Parameter.declare(arrayParameter); + declareCommandWithParams(program, [arrayParameterName], async (options) => { + const parameters = new ParametersHelper(options); + expect(parameters.getParameterValue(arrayParameterName)).to.deep.equal(colorsArray); + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '--array-parameter', ...colorsArray]); + }); + + it('Required parameter throws if missing', () => { + CLICommand.parse(program, [...baseArgv, requiredParameterName]); + Parameter.declare(requiredParameter); + }); + + describe('getIsPrivate method', () => { + it('returns false when none of --unsafe-drive-password, --drive-key, -p, or -k are provided', () => { + declareCommandWithParams(program, [], async (options) => { + const parameters = new ParametersHelper(options); + expect(await parameters.getIsPrivate()).to.be.false; + }); + CLICommand.parse(program, [...baseArgv, testCommandName]); + }); + + it('returns true when --unsafe-drive-password is provided', () => { + declareCommandWithParams(program, [UnsafeDrivePasswordParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(await parameters.getIsPrivate()).to.be.true; + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '--unsafe-drive-password', 'pw']); + }); + + it('returns true when -p is provided', () => { + declareCommandWithParams(program, [UnsafeDrivePasswordParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(await parameters.getIsPrivate()).to.be.true; + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '-p', 'pw']); + }); + + it('returns true when --drive-key is provided', () => { + declareCommandWithParams(program, [DriveKeyParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(await parameters.getIsPrivate()).to.be.true; + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '--drive-key', 'key']); + }); + + it('returns true when -k is provided', () => { + declareCommandWithParams(program, [DriveKeyParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(await parameters.getIsPrivate()).to.be.true; + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '-k', 'key']); + }); + }); + + describe('getRequiredWallet method', () => { + it('returns a wallet when a valid --wallet-file is provided', () => { + declareCommandWithParams(program, [WalletFileParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(await parameters.getRequiredWallet()).to.not.be.null; + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '--wallet-file', './test_wallet.json']); + }); + + it('returns a wallet when a valid --w file is provided', () => { + declareCommandWithParams(program, [WalletFileParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(await parameters.getRequiredWallet()).to.not.be.null; + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '-w', './test_wallet.json']); + }); + + it('returns a wallet when a valid --seed-phrase option is provided', () => { + declareCommandWithParams(program, [SeedPhraseParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(await parameters.getRequiredWallet()).to.not.be.null; + }); + CLICommand.parse(program, [ + ...baseArgv, + testCommandName, + '--seed-phrase', + 'alcohol wisdom allow used april recycle exhibit parent music field cabbage treat' + ]); + }); + + // Note: Redundant prolonged seed-phrase tests are commented out to save testing time + + // it('returns a wallet when a valid -s option is provided', () => { + // declareCommandWithParams(program, [SeedPhraseParameter], async (options) => { + // const parameters = new ParametersHelper(options); + // expect(await parameters.getRequiredWallet()).to.not.be.null; + // }); + // CLICommand.parse(program, [ + // ...baseArgv, + // testCommandName, + // '-s', + // 'alcohol wisdom allow used april recycle exhibit parent music field cabbage treat' + // ]); + // }); + + it('throws when none of --wallet-file, -w, --seed-phrase, or -s option are provided', (done) => { + declareCommandWithParams(program, [], async (options) => { + const parameters = new ParametersHelper(options); + await parameters + .getRequiredWallet() + .then((wallet) => { + done(`It shouldn't have returned a wallet: ${wallet}`); + }) + .catch(() => { + done(); + }); + }); + CLICommand.parse(program, [...baseArgv, testCommandName]); + }); + }); + + describe('getOptionalWallet method', () => { + it('returns a wallet when a valid --wallet-file is provided', () => { + declareCommandWithParams(program, [WalletFileParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(await parameters.getOptionalWallet()).to.not.be.null; + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '--wallet-file', './test_wallet.json']); + }); + + it('returns a wallet when a valid --w file is provided', () => { + declareCommandWithParams(program, [WalletFileParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(await parameters.getOptionalWallet()).to.not.be.null; + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '-w', './test_wallet.json']); + }); + + // Note: Redundant prolonged seed-phrase tests are commented out to save testing time + + // it('returns a wallet when a valid --seed-phrase option is provided', () => { + // declareCommandWithParams(program, [SeedPhraseParameter], async (options) => { + // const parameters = new ParametersHelper(options); + // expect(await parameters.getOptionalWallet()).to.not.be.null; + // }); + // CLICommand.parse(program, [ + // ...baseArgv, + // testCommandName, + // '--seed-phrase', + // 'alcohol wisdom allow used april recycle exhibit parent music field cabbage treat' + // ]); + // }); + + // it('returns a wallet when a valid -s option is provided', () => { + // declareCommandWithParams(program, [SeedPhraseParameter], async (options) => { + // const parameters = new ParametersHelper(options); + // expect(await parameters.getOptionalWallet()).to.not.be.null; + // }); + // CLICommand.parse(program, [ + // ...baseArgv, + // testCommandName, + // '-s', + // 'alcohol wisdom allow used april recycle exhibit parent music field cabbage treat' + // ]); + // }); + + it('returns null when none of --wallet-file, -w, --seed-phrase, or -s option are provided', () => { + declareCommandWithParams(program, [], async (options) => { + const parameters = new ParametersHelper(options); + const wallet = await parameters.getOptionalWallet().catch(() => null); + expect(wallet).to.be.null; + }); + CLICommand.parse(program, [...baseArgv, testCommandName]); + }); + }); + + describe('getWalletAddress method', () => { + it('returns the address of the wallet when a valid --wallet-file is provided', () => { + declareCommandWithParams(program, [WalletFileParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(`${await parameters.getWalletAddress()}`).to.equal(`${expectedArweaveAddress}`); + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '--wallet-file', './test_wallet.json']); + }); + + it('returns the address of the wallet when a valid --w file is provided', () => { + declareCommandWithParams(program, [WalletFileParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(`${await parameters.getWalletAddress()}`).to.equal(`${expectedArweaveAddress}`); + }); + CLICommand.parse(program, [...baseArgv, testCommandName, '-w', './test_wallet.json']); + }); + + // Note: Redundant prolonged seed-phrase tests are commented out to save testing time + + // it('returns the address of the wallet when a valid --seed-phrase option is provided', () => { + // declareCommandWithParams(program, [SeedPhraseParameter], async (options) => { + // const parameters = new ParametersHelper(options); + // expect((`${await parameters.getWalletAddress()}`)).to.equal(expectedArweaveAddress); + // }); + // CLICommand.parse(program, [ + // ...baseArgv, + // testCommandName, + // '--seed-phrase', + // 'alcohol wisdom allow used april recycle exhibit parent music field cabbage treat' + // ]); + // }); + + // it('returns the address of the wallet when a valid -s option is provided', () => { + // declareCommandWithParams(program, [SeedPhraseParameter], async (options) => { + // const parameters = new ParametersHelper(options); + // expect((`${await parameters.getWalletAddress()}`)).to.equal(expectedArweaveAddress); + // }); + // CLICommand.parse(program, [ + // ...baseArgv, + // testCommandName, + // '-s', + // 'alcohol wisdom allow used april recycle exhibit parent music field cabbage treat' + // ]); + // }); + + it('returns the address provided by the --address option value', () => { + declareCommandWithParams(program, [AddressParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(`${await parameters.getWalletAddress()}`).to.equal(`${expectedArweaveAddress}`); + }); + CLICommand.parse(program, [ + ...baseArgv, + testCommandName, + '--address', + 'P8aFJizMVBl7HeoRAz2i1dNYkG_KoN7oB9tZpIw6lo4' + ]); + }); + + it('returns the address provided by the -a option value', () => { + declareCommandWithParams(program, [AddressParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect(`${await parameters.getWalletAddress()}`).to.equal(`${expectedArweaveAddress}`); + }); + CLICommand.parse(program, [ + ...baseArgv, + testCommandName, + '-a', + 'P8aFJizMVBl7HeoRAz2i1dNYkG_KoN7oB9tZpIw6lo4' + ]); + }); + + it('throws when none of --wallet-file, -w, --seed-phrase, -s, --address, or -a option are provided', () => { + declareCommandWithParams(program, [], async (options) => { + const parameters = new ParametersHelper(options); + const wallet = await parameters.getWalletAddress().catch(() => null); + expect(wallet).to.be.null; + }); + CLICommand.parse(program, [...baseArgv, testCommandName]); + }); + }); + + describe('getDriveKey method', () => { + it('returns the correct drive key given a valid --wallet-file and --unsafe-drive-password', () => { + declareCommandWithParams(program, [WalletFileParameter, UnsafeDrivePasswordParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect( + urlEncodeHashKey(await parameters.getDriveKey({ driveId: '00000000-0000-0000-0000-000000000000' })) + ).to.equal('Fqjb/eoHUHkoPwyTe52VUJkUkOtLg0eoWdV1u03DDzg'); + }); + CLICommand.parse(program, [ + ...baseArgv, + testCommandName, + '--wallet-file', + './test_wallet.json', + '--unsafe-drive-password', + 'password' + ]); + }); + + it('returns the drive key provided by the --drive-key option', () => { + declareCommandWithParams(program, [DriveKeyParameter], async (options) => { + const parameters = new ParametersHelper(options); + expect( + urlEncodeHashKey(await parameters.getDriveKey({ driveId: '00000000-0000-0000-0000-000000000000' })) + ).to.equal('Fqjb/eoHUHkoPwyTe52VUJkUkOtLg0eoWdV1u03DDzg'); + }); + CLICommand.parse(program, [ + ...baseArgv, + testCommandName, + '--drive-key', + 'Fqjb/eoHUHkoPwyTe52VUJkUkOtLg0eoWdV1u03DDzg' + ]); + }); + + it('throws when none of --wallet-file, -w, --seed-phrase, -s, --drive-key, or -k option are provided', () => { + declareCommandWithParams(program, [], async (options) => { + const parameters = new ParametersHelper(options); + const driveKey = await parameters + .getDriveKey({ driveId: '00000000-0000-0000-0000-000000000000' }) + .catch(() => null); + expect(driveKey).to.be.null; + }); + CLICommand.parse(program, [...baseArgv, testCommandName]); + }); + }); + + describe('getMaxDepth method', () => { + it(`Defaults to zero`); + it(`Does not accept a decimal`); + it(`Does not accept a negative integer`); + it(`Max depth is infinity when --all is specified`); + it(`Custom positive value is providen`); + }); +}); diff --git a/src/CLICommand/parameters_helper.ts b/src/CLICommand/parameters_helper.ts new file mode 100644 index 00000000..e65a4506 --- /dev/null +++ b/src/CLICommand/parameters_helper.ts @@ -0,0 +1,229 @@ +import { JWKWallet, Wallet, WalletDAO } from '../wallet'; +import { ParameterName } from './parameter'; +import * as fs from 'fs'; +import { deriveDriveKey, JWKInterface } from 'ardrive-core-js'; +import { + AddressParameter, + AllParameter, + DriveKeyParameter, + UnsafeDrivePasswordParameter, + MaxDepthParameter, + SeedPhraseParameter, + WalletFileParameter, + PrivateParameter +} from '../parameter_declarations'; +import { cliWalletDao } from '..'; +import { DriveID, DriveKey } from '../types'; +import passwordPrompt from 'prompts'; +import { PrivateKeyData } from '../private_key_data'; +import { ArweaveAddress } from '../arweave_address'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type ParameterOptions = any; + +interface GetDriveKeyParams { + driveId: DriveID; + drivePassword?: string; + useCache?: boolean; +} + +/** + * @type {ParametersHelper} + * A class that assists with handling Commander options during common ArDrive CLI workflows + */ +export class ParametersHelper { + private static readonly driveKeyCache: { [key: string]: DriveKey } = {}; + + /** + * @returns {ParametersHelper} + * @param {any} options The object containing the parameterName: value mapping + * An immutable instance of ParametersHelper holding the parsed values of the parameters + */ + constructor(private readonly options: ParameterOptions, private readonly walletDao: WalletDAO = cliWalletDao) {} + + /** + * @returns {Promise} + * Returns true when a drive password or drive key is provided + */ + public async getIsPrivate(): Promise { + return ( + this.getParameterValue(PrivateParameter) !== undefined || + this.getParameterValue(UnsafeDrivePasswordParameter) !== undefined || + this.getParameterValue(DriveKeyParameter) !== undefined + ); + } + + /** + * @returns {Promise} + * Will return a wallet instance created from the seed phrase or the walletFile. + * Throws an error if a wallet can't be created. + */ + public async getRequiredWallet(): Promise { + const walletFile = this.getParameterValue(WalletFileParameter); + const seedPhrase = this.getParameterValue(SeedPhraseParameter); + if (walletFile) { + const walletFileData = fs.readFileSync(walletFile, { encoding: 'utf8', flag: 'r' }); + const walletJSON = JSON.parse(walletFileData); + const walletJWK: JWKInterface = walletJSON as JWKInterface; + return new JWKWallet(walletJWK); + } else if (seedPhrase) { + return await this.walletDao.generateJWKWallet(seedPhrase); + } + throw new Error('Neither a wallet file nor seed phrase was provided!'); + } + + public async getOptionalWallet(): Promise { + return this.getRequiredWallet().catch(() => null); + } + + public async getWalletAddress(): Promise { + const address = this.getParameterValue(AddressParameter); + if (address) { + return new ArweaveAddress(address); + } + + return this.getRequiredWallet().then((wallet) => wallet.getAddress()); + } + + public async getPrivateKeyData(): Promise { + // Gather optional private parameters + const driveKey = this.getParameterValue(DriveKeyParameter); + const wallet = await this.getOptionalWallet(); + const password = await ((): Promise => { + if ( + // If private param specified or an unsafe password param is provided + this.getParameterValue(PrivateParameter) !== undefined || + this.getParameterValue(UnsafeDrivePasswordParameter) !== undefined + ) { + return this.getDrivePassword(); + } + return Promise.resolve(undefined); + })(); + + return new PrivateKeyData({ + password, + driveKeys: driveKey ? [Buffer.from(driveKey, 'base64')] : undefined, + wallet: (wallet as JWKWallet) ?? undefined + }); + } + + public async getDriveKey({ driveId, drivePassword, useCache = false }: GetDriveKeyParams): Promise { + // Obtain drive key from one of: + // • --drive-key param + // • (--wallet-file or --seed-phrase) + (--unsafe-drive-password or --private password) + + if (useCache) { + const cachedDriveKey = ParametersHelper.driveKeyCache[driveId]; + if (cachedDriveKey) { + return cachedDriveKey; + } + } + + const driveKey = this.getParameterValue(DriveKeyParameter); + if (driveKey) { + const paramDriveKey = Buffer.from(driveKey, 'base64'); + ParametersHelper.driveKeyCache[driveId] = paramDriveKey; + return paramDriveKey; + } + + drivePassword = drivePassword ?? (await this.getDrivePassword()); + if (drivePassword) { + const wallet: JWKWallet = (await this.getRequiredWallet()) as JWKWallet; + const derivedDriveKey: DriveKey = await deriveDriveKey( + drivePassword, + driveId, + JSON.stringify(wallet.getPrivateKey()) + ); + ParametersHelper.driveKeyCache[driveId] = derivedDriveKey; + return derivedDriveKey; + } + throw new Error(`No drive key or password provided for drive ID ${driveId}!`); + } + + public async getDrivePassword(isForNewDrive = false): Promise { + if (this.getParameterValue(PrivateParameter)) { + // Try to get password from STDIN, then ENV.ARDRIVE_DRIVE_PW, then interactive secure prompt + try { + const stdInPassword = fs.readFileSync(process.stdin.fd).toString().replace(/\n*$/, ''); + if (stdInPassword) { + return stdInPassword; + } + } catch (_err) { + // Do nothing + } + + const envPassword = process.env['ARDRIVE_DRIVE_PW']; + if (envPassword) { + return envPassword; + } + + const promptedPassword = await passwordPrompt({ + type: 'text', + name: 'password', + style: 'password', + message: isForNewDrive ? 'Enter new drive password:' : 'Enter drive password:' + }); + if (isForNewDrive) { + const confirmedPassword = await passwordPrompt({ + type: 'text', + name: 'password', + style: 'password', + message: 'Re-enter new drive password: ' + }); + if (confirmedPassword.password !== promptedPassword.password) { + throw new Error('Drive passwords do not match!'); + } + } + if (!promptedPassword.password.length) { + throw new Error('New drive password must not be empty when --private is specified!'); + } + + return promptedPassword.password; + } + + const unsafePassword = this.getParameterValue(UnsafeDrivePasswordParameter); + if (!unsafePassword) { + throw new Error( + 'Password not detected for private drive operation! Please provide a password via the --private option (recommended) or the --unsafe-drive-password option (not recommended).' + ); + } + return unsafePassword; + } + + public async getMaxDepth(defaultDepth: number): Promise { + if (this.getParameterValue(AllParameter)) { + return Number.MAX_SAFE_INTEGER; + } + + const maxDepthValue = Number(this.getParameterValue(MaxDepthParameter) ?? defaultDepth); + + if (!Number.isInteger(maxDepthValue) || maxDepthValue < 0) { + throw new Error('maxDepth should be a non-negative integer!'); + } + + return maxDepthValue; + } + + /** + * @param {ParameterName} parameterName + * @returns {string | undefined} + * Returns the string value for the specific parameter; returns undefined if not set + */ + public getParameterValue(parameterName: ParameterName): string | undefined { + const value = this.options[parameterName]; + return value; + } + + /** + * @param {ParameterName} parameterName + * @returns {string | undefined} + * Returns the string value for the specific parameter; throws an error if not set + */ + public getRequiredParameterValue(parameterName: ParameterName): string { + const value = this.options[parameterName]; + if (!value) { + throw new Error(`Required parameter ${parameterName} wasn't provided!`); + } + return value; + } +} diff --git a/src/CLICommand/test_constants.ts b/src/CLICommand/test_constants.ts new file mode 100644 index 00000000..533afdb9 --- /dev/null +++ b/src/CLICommand/test_constants.ts @@ -0,0 +1,38 @@ +import { ParameterConfig } from './parameter'; + +export const booleanParameterName = 'booleanParameter'; +export const arrayParameterName = 'arrayParameter'; +export const singleValueParameterName = 'singleValueParameter'; +export const defaultParameterName = 'defaultTypeParameter'; +export const requiredParameterName = 'requiredTypeParameter'; +export const booleanParameter: ParameterConfig = { + name: booleanParameterName, + aliases: ['-b', '--boolean-parameter'], + description: 'A boolean flag', + type: 'boolean' +}; +export const arrayParameter: ParameterConfig = { + name: arrayParameterName, + aliases: ['-c', '--array-parameter'], + description: 'A parameter with multiple values', + type: 'array' +}; +export const singleValueParameter: ParameterConfig = { + name: singleValueParameterName, + aliases: ['-u', '--single-value-parameter'], + description: 'A parameter with a single string value', + type: 'single-value' +}; +export const defaultParameter: ParameterConfig = { + name: defaultParameterName, + aliases: ['-d', '--default-type-parameter'], + description: 'The default type is also a single string value' +}; +export const requiredParameter: ParameterConfig = { + name: requiredParameterName, + aliases: ['-p', '--required-type-parameter'], + description: 'Required parameter', + required: true +}; +export const testCommandName = 'test-command'; +export const baseArgv = ['ardrive', testCommandName]; diff --git a/src/ardrive.ts b/src/ardrive.ts new file mode 100644 index 00000000..876dea3e --- /dev/null +++ b/src/ardrive.ts @@ -0,0 +1,1569 @@ +import { ArFSDAO, PrivateDriveKeyData } from './arfsdao'; +import { CommunityOracle } from './community/community_oracle'; +import { ArFSDriveEntity, deriveDriveKey, DrivePrivacy, GQLTagInterface, winstonToAr } from 'ardrive-core-js'; +import { + TransactionID, + Winston, + DriveID, + FolderID, + TipType, + FeeMultiple, + DriveKey, + EntityID, + FileID, + ByteCount, + MakeOptional +} from './types'; +import { WalletDAO, Wallet, JWKWallet } from './wallet'; +import { ARDataPriceRegressionEstimator } from './utils/ar_data_price_regression_estimator'; +import { ArFSFolderToUpload, ArFSFileToUpload } from './arfs_file_wrapper'; +import { ARDataPriceEstimator } from './utils/ar_data_price_estimator'; +import { + ArFSDriveTransactionData, + ArFSFileMetadataTransactionData, + ArFSFolderTransactionData, + ArFSObjectTransactionData, + ArFSPrivateDriveTransactionData, + ArFSPrivateFileMetadataTransactionData, + ArFSPrivateFolderTransactionData, + ArFSPublicDriveTransactionData, + ArFSPublicFileMetadataTransactionData, + ArFSPublicFolderTransactionData +} from './arfs_trx_data_types'; +import { urlEncodeHashKey } from './utils'; +import { ArFSDAOAnonymous, ArFSDAOType, ArFSListPublicFolderParams } from './arfsdao_anonymous'; +import { + ArFSPrivateDrive, + ArFSPrivateFile, + ArFSPrivateFileOrFolderWithPaths, + ArFSPrivateFolder, + ArFSPublicDrive, + ArFSPublicFile, + ArFSPublicFileOrFolderWithPaths, + ArFSPublicFolder +} from './arfs_entities'; +import { stubEntityID, stubTransactionID } from './utils/stubs'; +import { errorMessage } from './error_message'; +import { PrivateKeyData } from './private_key_data'; +import { EntityNamesAndIds } from './utils/mapper_functions'; +import { ArweaveAddress } from './arweave_address'; +import { WithDriveKey } from './arfs_entity_result_factory'; + +export type ArFSEntityDataType = 'drive' | 'folder' | 'file'; + +export interface ArFSEntityData { + type: ArFSEntityDataType; + metadataTxId: TransactionID; + dataTxId?: TransactionID; + entityId: EntityID; + key?: string; +} + +export type ListPublicFolderParams = MakeOptional; +export type ListPrivateFolderParams = ListPublicFolderParams & WithDriveKey; + +export interface TipData { + txId: TransactionID; + recipient: ArweaveAddress; + winston: Winston; +} + +export interface TipResult { + tipData: TipData; + reward: Winston; +} + +export type ArFSFees = { [key: string]: number }; + +export interface ArFSResult { + created: ArFSEntityData[]; + tips: TipData[]; + fees: ArFSFees; +} + +export interface MetaDataBaseCosts { + metaDataBaseReward: Winston; +} + +export interface BulkFileBaseCosts extends MetaDataBaseCosts { + fileDataBaseReward: Winston; +} +export interface FileUploadBaseCosts extends BulkFileBaseCosts { + communityWinstonTip: Winston; +} + +export interface DriveUploadBaseCosts { + driveMetaDataBaseReward: Winston; + rootFolderMetaDataBaseReward: Winston; +} + +interface RecursivePublicBulkUploadParams { + parentFolderId: FolderID; + wrappedFolder: ArFSFolderToUpload; + driveId: DriveID; + owner: ArweaveAddress; +} +type RecursivePrivateBulkUploadParams = RecursivePublicBulkUploadParams & WithDriveKey; + +interface CreatePublicFolderParams { + folderName: string; + driveId: DriveID; + parentFolderId: FolderID; +} +type CreatePrivateFolderParams = CreatePublicFolderParams & WithDriveKey; + +interface MovePublicFolderParams { + folderId: FolderID; + newParentFolderId: FolderID; +} +type MovePrivateFolderParams = MovePublicFolderParams & WithDriveKey; + +export abstract class ArDriveType { + protected abstract readonly arFsDao: ArFSDAOType; +} + +export class ArDriveAnonymous extends ArDriveType { + constructor(protected readonly arFsDao: ArFSDAOAnonymous) { + super(); + } + + async getOwnerForDriveId(driveId: DriveID): Promise { + return this.arFsDao.getOwnerForDriveId(driveId); + } + + async getPublicDrive(driveId: DriveID, owner?: ArweaveAddress): Promise { + if (!owner) { + owner = await this.getOwnerForDriveId(driveId); + } + + return this.arFsDao.getPublicDrive(driveId, owner); + } + + async getPublicFolder(folderId: FolderID, owner?: ArweaveAddress): Promise { + if (!owner) { + owner = await this.arFsDao.getDriveOwnerForFolderId(folderId); + } + + return this.arFsDao.getPublicFolder(folderId, owner); + } + + async getPublicFile(fileId: FileID, owner?: ArweaveAddress): Promise { + if (!owner) { + owner = await this.arFsDao.getDriveOwnerForFileId(fileId); + } + + return this.arFsDao.getPublicFile(fileId, owner); + } + + async getAllDrivesForAddress(address: ArweaveAddress, privateKeyData: PrivateKeyData): Promise { + return this.arFsDao.getAllDrivesForAddress(address, privateKeyData); + } + + /** + * Lists the children of certain public folder + * @param {FolderID} folderId the folder ID to list children of + * @returns {ArFSPublicFileOrFolderWithPaths[]} an array representation of the children and parent folder + */ + async listPublicFolder({ + folderId, + maxDepth = 0, + includeRoot = false, + owner + }: ListPublicFolderParams): Promise { + if (!owner) { + owner = await this.arFsDao.getDriveOwnerForFolderId(folderId); + } + + const children = await this.arFsDao.listPublicFolder({ folderId, maxDepth, includeRoot, owner }); + return children; + } +} + +export class ArDrive extends ArDriveAnonymous { + constructor( + private readonly wallet: Wallet, + private readonly walletDao: WalletDAO, + protected readonly arFsDao: ArFSDAO, + private readonly communityOracle: CommunityOracle, + private readonly appName: string, + private readonly appVersion: string, + private readonly priceEstimator: ARDataPriceEstimator = new ARDataPriceRegressionEstimator(true), + private readonly feeMultiple: FeeMultiple = 1.0, + private readonly dryRun: boolean = false + ) { + super(arFsDao); + } + + // NOTE: Presumes that there's a sufficient wallet balance + async sendCommunityTip(communityWinstonTip: Winston, assertBalance = false): Promise { + const tokenHolder: ArweaveAddress = await this.communityOracle.selectTokenHolder(); + const arTransferBaseFee = await this.priceEstimator.getBaseWinstonPriceForByteCount(0); + + const transferResult = await this.walletDao.sendARToAddress( + winstonToAr(+communityWinstonTip), + this.wallet, + tokenHolder, + { reward: arTransferBaseFee.toString(), feeMultiple: this.feeMultiple }, + this.dryRun, + this.getTipTags(), + assertBalance + ); + + return { + tipData: { txId: transferResult.trxID, recipient: tokenHolder, winston: communityWinstonTip }, + reward: transferResult.reward + }; + } + + getTipTags(tipType: TipType = 'data upload'): GQLTagInterface[] { + return [ + { name: 'App-Name', value: this.appName }, + { name: 'App-Version', value: this.appVersion }, + { name: 'Tip-Type', value: tipType } + ]; + } + + async movePublicFile(fileId: FileID, newParentFolderId: FolderID): Promise { + const destFolderDriveId = await this.arFsDao.getDriveIdForFolderId(newParentFolderId); + + const owner = await this.getOwnerForDriveId(destFolderDriveId); + await this.assertOwnerAddress(owner); + + const originalFileMetaData = await this.getPublicFile(fileId); + + if (destFolderDriveId !== originalFileMetaData.driveId) { + throw new Error(errorMessage.cannotMoveToDifferentDrive); + } + + if (originalFileMetaData.parentFolderId === newParentFolderId) { + throw new Error(errorMessage.cannotMoveIntoSamePlace('File', newParentFolderId)); + } + + // Assert that there are no duplicate names in the destination folder + const entityNamesInParentFolder = await this.arFsDao.getPublicEntityNamesInFolder(newParentFolderId); + if (entityNamesInParentFolder.includes(originalFileMetaData.name)) { + // TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599 + throw new Error(errorMessage.entityNameExists); + } + + const fileTransactionData = new ArFSPublicFileMetadataTransactionData( + originalFileMetaData.name, + originalFileMetaData.size, + originalFileMetaData.lastModifiedDate, + originalFileMetaData.dataTxId, + originalFileMetaData.dataContentType + ); + + const moveFileBaseCosts = await this.estimateAndAssertCostOfMoveFile(fileTransactionData); + const fileMetaDataBaseReward = { reward: moveFileBaseCosts.metaDataBaseReward, feeMultiple: this.feeMultiple }; + + // Move file will create a new meta data tx with identical meta data except for a new parentFolderId + const moveFileResult = await this.arFsDao.movePublicFile({ + originalMetaData: originalFileMetaData, + transactionData: fileTransactionData, + newParentFolderId, + metaDataBaseReward: fileMetaDataBaseReward + }); + + return Promise.resolve({ + created: [ + { + type: 'file', + metadataTxId: moveFileResult.metaDataTrxId, + dataTxId: moveFileResult.dataTrxId, + entityId: fileId + } + ], + tips: [], + fees: { + [moveFileResult.metaDataTrxId]: +moveFileResult.metaDataTrxReward + } + }); + } + + async movePrivateFile(fileId: FileID, newParentFolderId: FolderID, driveKey: DriveKey): Promise { + const destFolderDriveId = await this.arFsDao.getDriveIdForFolderId(newParentFolderId); + + const owner = await this.getOwnerForDriveId(destFolderDriveId); + await this.assertOwnerAddress(owner); + + const originalFileMetaData = await this.getPrivateFile(fileId, driveKey); + + if (destFolderDriveId !== originalFileMetaData.driveId) { + throw new Error(errorMessage.cannotMoveToDifferentDrive); + } + + if (originalFileMetaData.parentFolderId === newParentFolderId) { + throw new Error(errorMessage.cannotMoveIntoSamePlace('File', newParentFolderId)); + } + + // Assert that there are no duplicate names in the destination folder + const entityNamesInParentFolder = await this.arFsDao.getPrivateEntityNamesInFolder(newParentFolderId, driveKey); + if (entityNamesInParentFolder.includes(originalFileMetaData.name)) { + // TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599 + throw new Error(errorMessage.entityNameExists); + } + + const fileTransactionData = await ArFSPrivateFileMetadataTransactionData.from( + originalFileMetaData.name, + originalFileMetaData.size, + originalFileMetaData.lastModifiedDate, + originalFileMetaData.dataTxId, + originalFileMetaData.dataContentType, + fileId, + driveKey + ); + + const moveFileBaseCosts = await this.estimateAndAssertCostOfMoveFile(fileTransactionData); + const fileMetaDataBaseReward = { reward: moveFileBaseCosts.metaDataBaseReward, feeMultiple: this.feeMultiple }; + + // Move file will create a new meta data tx with identical meta data except for a new parentFolderId + const moveFileResult = await this.arFsDao.movePrivateFile({ + originalMetaData: originalFileMetaData, + transactionData: fileTransactionData, + newParentFolderId, + metaDataBaseReward: fileMetaDataBaseReward + }); + + return Promise.resolve({ + created: [ + { + type: 'file', + metadataTxId: moveFileResult.metaDataTrxId, + dataTxId: moveFileResult.dataTrxId, + entityId: fileId, + key: urlEncodeHashKey(moveFileResult.fileKey) + } + ], + tips: [], + fees: { + [moveFileResult.metaDataTrxId]: +moveFileResult.metaDataTrxReward + } + }); + } + + async movePublicFolder({ folderId, newParentFolderId }: MovePublicFolderParams): Promise { + if (folderId === newParentFolderId) { + throw new Error(errorMessage.folderCannotMoveIntoItself); + } + + const destFolderDriveId = await this.arFsDao.getDriveIdForFolderId(newParentFolderId); + + const owner = await this.getOwnerForDriveId(destFolderDriveId); + await this.assertOwnerAddress(owner); + + const originalFolderMetaData = await this.getPublicFolder(folderId); + + if (destFolderDriveId !== originalFolderMetaData.driveId) { + throw new Error(errorMessage.cannotMoveToDifferentDrive); + } + + if (originalFolderMetaData.parentFolderId === newParentFolderId) { + throw new Error(errorMessage.cannotMoveIntoSamePlace('Folder', newParentFolderId)); + } + + // Assert that there are no duplicate names in the destination folder + const entityNamesInParentFolder = await this.arFsDao.getPublicEntityNamesInFolder(newParentFolderId); + if (entityNamesInParentFolder.includes(originalFolderMetaData.name)) { + // TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599 + throw new Error(errorMessage.entityNameExists); + } + + const childrenFolderIds = await this.arFsDao.getPublicChildrenFolderIds({ + folderId, + driveId: destFolderDriveId + }); + + if (childrenFolderIds.includes(newParentFolderId)) { + throw new Error(errorMessage.cannotMoveParentIntoChildFolder); + } + + const folderTransactionData = new ArFSPublicFolderTransactionData(originalFolderMetaData.name); + const { metaDataBaseReward: baseReward } = await this.estimateAndAssertCostOfFolderUpload( + folderTransactionData + ); + + const folderMetaDataBaseReward = { reward: baseReward, feeMultiple: this.feeMultiple }; + + // Move folder will create a new meta data tx with identical meta data except for a new parentFolderId + const moveFolderResult = await this.arFsDao.movePublicFolder({ + originalMetaData: originalFolderMetaData, + transactionData: folderTransactionData, + newParentFolderId, + metaDataBaseReward: folderMetaDataBaseReward + }); + + return Promise.resolve({ + created: [ + { + type: 'folder', + metadataTxId: moveFolderResult.metaDataTrxId, + entityId: folderId + } + ], + tips: [], + fees: { + [moveFolderResult.metaDataTrxId]: +moveFolderResult.metaDataTrxReward + } + }); + } + + async movePrivateFolder({ folderId, newParentFolderId, driveKey }: MovePrivateFolderParams): Promise { + if (folderId === newParentFolderId) { + throw new Error(errorMessage.folderCannotMoveIntoItself); + } + + const destFolderDriveId = await this.arFsDao.getDriveIdForFolderId(newParentFolderId); + + const owner = await this.getOwnerForDriveId(destFolderDriveId); + await this.assertOwnerAddress(owner); + + const originalFolderMetaData = await this.getPrivateFolder(folderId, driveKey); + + if (destFolderDriveId !== originalFolderMetaData.driveId) { + throw new Error(errorMessage.cannotMoveToDifferentDrive); + } + + if (originalFolderMetaData.parentFolderId === newParentFolderId) { + throw new Error(errorMessage.cannotMoveIntoSamePlace('Folder', newParentFolderId)); + } + + // Assert that there are no duplicate names in the destination folder + const entityNamesInParentFolder = await this.arFsDao.getPrivateEntityNamesInFolder(newParentFolderId, driveKey); + if (entityNamesInParentFolder.includes(originalFolderMetaData.name)) { + // TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599 + throw new Error(errorMessage.entityNameExists); + } + + const childrenFolderIds = await this.arFsDao.getPrivateChildrenFolderIds({ + folderId, + driveId: destFolderDriveId, + driveKey + }); + + if (childrenFolderIds.includes(newParentFolderId)) { + throw new Error(errorMessage.cannotMoveParentIntoChildFolder); + } + + const folderTransactionData = await ArFSPrivateFolderTransactionData.from( + originalFolderMetaData.name, + driveKey + ); + const { metaDataBaseReward: baseReward } = await this.estimateAndAssertCostOfFolderUpload( + folderTransactionData + ); + + const folderMetaDataBaseReward = { reward: baseReward, feeMultiple: this.feeMultiple }; + + // Move folder will create a new meta data tx with identical meta data except for a new parentFolderId + const moveFolderResult = await this.arFsDao.movePrivateFolder({ + originalMetaData: originalFolderMetaData, + transactionData: folderTransactionData, + newParentFolderId, + metaDataBaseReward: folderMetaDataBaseReward + }); + + return Promise.resolve({ + created: [ + { + type: 'folder', + metadataTxId: moveFolderResult.metaDataTrxId, + entityId: folderId, + key: urlEncodeHashKey(moveFolderResult.driveKey) + } + ], + tips: [], + fees: { + [moveFolderResult.metaDataTrxId]: +moveFolderResult.metaDataTrxReward + } + }); + } + + async uploadPublicFile( + parentFolderId: FolderID, + wrappedFile: ArFSFileToUpload, + destinationFileName?: string + ): Promise { + const driveId = await this.arFsDao.getDriveIdForFolderId(parentFolderId); + + const owner = await this.getOwnerForDriveId(driveId); + await this.assertOwnerAddress(owner); + + // Derive destination name and names already within provided destination folder + const destFileName = destinationFileName ?? wrappedFile.getBaseFileName(); + const filesAndFolderNames = await this.arFsDao.getPublicEntityNamesAndIdsInFolder(parentFolderId); + + // Files cannot overwrite folder names + if (filesAndFolderNames.folders.find((f) => f.folderName === destFileName)) { + // TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599 + throw new Error(errorMessage.entityNameExists); + } + + // File is a new revision if destination name conflicts + // with an existing file in the destination folder + const existingFileId = filesAndFolderNames.files.find((f) => f.fileName === destFileName)?.fileId; + + const uploadBaseCosts = await this.estimateAndAssertCostOfFileUpload( + wrappedFile.fileStats.size, + this.stubPublicFileMetadata(wrappedFile, destinationFileName), + 'public' + ); + const fileDataRewardSettings = { reward: uploadBaseCosts.fileDataBaseReward, feeMultiple: this.feeMultiple }; + const metadataRewardSettings = { reward: uploadBaseCosts.metaDataBaseReward, feeMultiple: this.feeMultiple }; + + const uploadFileResult = await this.arFsDao.uploadPublicFile({ + parentFolderId, + wrappedFile, + driveId, + fileDataRewardSettings, + metadataRewardSettings, + destFileName: destinationFileName, + existingFileId + }); + + const { tipData, reward: communityTipTrxReward } = await this.sendCommunityTip( + uploadBaseCosts.communityWinstonTip + ); + + return Promise.resolve({ + created: [ + { + type: 'file', + metadataTxId: uploadFileResult.metaDataTrxId, + dataTxId: uploadFileResult.dataTrxId, + entityId: uploadFileResult.fileId + } + ], + tips: [tipData], + fees: { + [uploadFileResult.dataTrxId]: +uploadFileResult.dataTrxReward, + [uploadFileResult.metaDataTrxId]: +uploadFileResult.metaDataTrxReward, + [tipData.txId]: +communityTipTrxReward + } + }); + } + + public async createPublicFolderAndUploadChildren( + parentFolderId: FolderID, + wrappedFolder: ArFSFolderToUpload, + parentFolderName?: string + ): Promise { + const driveId = await this.arFsDao.getDriveIdForFolderId(parentFolderId); + + const owner = await this.getOwnerForDriveId(driveId); + await this.assertOwnerAddress(owner); + + // Derive destination name and names already within provided destination folder + const destFolderName = parentFolderName ?? wrappedFolder.getBaseFileName(); + const filesAndFolderNames = await this.arFsDao.getPublicEntityNamesAndIdsInFolder(parentFolderId); + + // Folders cannot overwrite file names + if (filesAndFolderNames.files.find((f) => f.fileName === destFolderName)) { + // TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599 + throw new Error(errorMessage.entityNameExists); + } + + // Use existing folder id if the intended destination name + // conflicts with an existing folder in the destination folder + wrappedFolder.existingId = filesAndFolderNames.folders.find((f) => f.folderName === destFolderName)?.folderId; + wrappedFolder.destinationName = parentFolderName; + + // Check for conflicting names and assign existing IDs for later use + await this.checkAndAssignExistingPublicNames(wrappedFolder); + + // Estimate and assert the cost of the entire bulk upload + // This will assign the calculated base costs to each wrapped file and folder + const bulkEstimation = await this.estimateAndAssertCostOfBulkUpload(wrappedFolder); + + // TODO: Add interactive confirmation of price estimation before uploading + + const results = await this.recursivelyCreatePublicFolderAndUploadChildren({ + parentFolderId, + wrappedFolder, + driveId, + owner: await this.wallet.getAddress() + }); + + if (+bulkEstimation.communityWinstonTip > 0) { + // Send community tip only if communityWinstonTip has a value + // This can be zero when a user uses this method to upload empty folders + + const { tipData, reward: communityTipTrxReward } = await this.sendCommunityTip( + bulkEstimation.communityWinstonTip + ); + + return Promise.resolve({ + created: results.entityResults, + tips: [tipData], + fees: { ...results.feeResults, [tipData.txId]: +communityTipTrxReward } + }); + } + + return Promise.resolve({ + created: results.entityResults, + tips: [], + fees: results.feeResults + }); + } + + protected async recursivelyCreatePublicFolderAndUploadChildren({ + parentFolderId, + wrappedFolder, + driveId, + owner + }: RecursivePublicBulkUploadParams): Promise<{ + entityResults: ArFSEntityData[]; + feeResults: ArFSFees; + }> { + let uploadEntityFees: ArFSFees = {}; + let uploadEntityResults: ArFSEntityData[] = []; + let folderId: FolderID; + + if (wrappedFolder.existingId) { + // Use existing parent folder ID for bulk upload + folderId = wrappedFolder.existingId; + } else { + // Create the parent folder + const folderData = new ArFSPublicFolderTransactionData( + wrappedFolder.destinationName ?? wrappedFolder.getBaseFileName() + ); + + const createFolderResult = await this.arFsDao.createPublicFolder({ + folderData: folderData, + driveId, + rewardSettings: { + reward: wrappedFolder.getBaseCosts().metaDataBaseReward, + feeMultiple: this.feeMultiple + }, + parentFolderId, + syncParentFolderId: false, + owner + }); + + const { metaDataTrxId, folderId: newFolderId, metaDataTrxReward } = createFolderResult; + + // Capture parent folder results + uploadEntityFees = { [metaDataTrxId]: +metaDataTrxReward }; + uploadEntityResults = [ + { + type: 'folder', + metadataTxId: metaDataTrxId, + entityId: newFolderId + } + ]; + + folderId = newFolderId; + } + + // Upload all files in the folder + for await (const wrappedFile of wrappedFolder.files) { + const fileDataRewardSettings = { + reward: wrappedFile.getBaseCosts().fileDataBaseReward, + feeMultiple: this.feeMultiple + }; + + const metadataRewardSettings = { + reward: wrappedFile.getBaseCosts().metaDataBaseReward, + feeMultiple: this.feeMultiple + }; + + const uploadFileResult = await this.arFsDao.uploadPublicFile({ + parentFolderId: folderId, + wrappedFile, + driveId, + fileDataRewardSettings, + metadataRewardSettings, + existingFileId: wrappedFile.existingId + }); + + // Capture all file results + uploadEntityFees = { + ...uploadEntityFees, + [uploadFileResult.dataTrxId]: +uploadFileResult.dataTrxReward, + [uploadFileResult.metaDataTrxId]: +uploadFileResult.metaDataTrxReward + }; + uploadEntityResults = [ + ...uploadEntityResults, + { + type: 'file', + metadataTxId: uploadFileResult.metaDataTrxId, + dataTxId: uploadFileResult.dataTrxId, + entityId: uploadFileResult.fileId + } + ]; + } + + // Upload folders, and children of those folders + for await (const childFolder of wrappedFolder.folders) { + // Recursion alert, will keep creating folders of all nested folders + const results = await this.recursivelyCreatePublicFolderAndUploadChildren({ + parentFolderId: folderId, + wrappedFolder: childFolder, + driveId, + owner + }); + + // Capture all folder results + uploadEntityFees = { + ...uploadEntityFees, + ...results.feeResults + }; + uploadEntityResults = [...uploadEntityResults, ...results.entityResults]; + } + + return { + entityResults: uploadEntityResults, + feeResults: uploadEntityFees + }; + } + + /** Computes the size of a private file encrypted with AES256-GCM */ + encryptedDataSize(dataSize: ByteCount): ByteCount { + if (dataSize < 0 || !Number.isInteger(dataSize)) { + throw new Error(`dataSize must be non-negative, integer value! ${dataSize} is invalid!`); + } + if (dataSize > Number.MAX_SAFE_INTEGER - 16) { + throw new Error(`Max un-encrypted dataSize allowed is ${Number.MAX_SAFE_INTEGER - 16}!`); + } + const modulo16 = dataSize % 16; + return dataSize - modulo16 + 16; + } + + async uploadPrivateFile( + parentFolderId: FolderID, + wrappedFile: ArFSFileToUpload, + driveKey: DriveKey, + destinationFileName?: string + ): Promise { + const driveId = await this.arFsDao.getDriveIdForFolderId(parentFolderId); + + const owner = await this.getOwnerForDriveId(driveId); + await this.assertOwnerAddress(owner); + + // Derive destination name and names already within provided destination folder + const destFileName = destinationFileName ?? wrappedFile.getBaseFileName(); + const filesAndFolderNames = await this.arFsDao.getPrivateEntityNamesAndIdsInFolder(parentFolderId, driveKey); + + // Files cannot overwrite folder names + if (filesAndFolderNames.folders.find((f) => f.folderName === destFileName)) { + // TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599 + throw new Error(errorMessage.entityNameExists); + } + + // File is a new revision if destination name conflicts + // with an existing file in the destination folder + const existingFileId = filesAndFolderNames.files.find((f) => f.fileName === destFileName)?.fileId; + + const uploadBaseCosts = await this.estimateAndAssertCostOfFileUpload( + wrappedFile.fileStats.size, + await this.stubPrivateFileMetadata(wrappedFile, destinationFileName), + 'private' + ); + + const fileDataRewardSettings = { + reward: uploadBaseCosts.fileDataBaseReward, + feeMultiple: this.feeMultiple + }; + const metadataRewardSettings = { + reward: uploadBaseCosts.metaDataBaseReward, + feeMultiple: this.feeMultiple + }; + + // TODO: Add interactive confirmation of AR price estimation + + const uploadFileResult = await this.arFsDao.uploadPrivateFile({ + parentFolderId, + wrappedFile, + driveId, + driveKey, + fileDataRewardSettings, + metadataRewardSettings, + destFileName: destinationFileName, + existingFileId + }); + + const { tipData, reward: communityTipTrxReward } = await this.sendCommunityTip( + uploadBaseCosts.communityWinstonTip + ); + + return Promise.resolve({ + created: [ + { + type: 'file', + metadataTxId: uploadFileResult.metaDataTrxId, + dataTxId: uploadFileResult.dataTrxId, + entityId: uploadFileResult.fileId, + key: urlEncodeHashKey(uploadFileResult.fileKey) + } + ], + tips: [tipData], + fees: { + [uploadFileResult.dataTrxId]: +uploadFileResult.dataTrxReward, + [uploadFileResult.metaDataTrxId]: +uploadFileResult.metaDataTrxReward, + [tipData.txId]: +communityTipTrxReward + } + }); + } + + public async createPrivateFolderAndUploadChildren( + parentFolderId: FolderID, + wrappedFolder: ArFSFolderToUpload, + driveKey: DriveKey, + parentFolderName?: string + ): Promise { + // Retrieve drive ID from folder ID + const driveId = await this.arFsDao.getDriveIdForFolderId(parentFolderId); + + // Get owner of drive, will error if no drives are found + const owner = await this.getOwnerForDriveId(driveId); + + // Assert that the provided wallet is the owner of the drive + await this.assertOwnerAddress(owner); + + // Derive destination name and names already within provided destination folder + const destFolderName = parentFolderName ?? wrappedFolder.getBaseFileName(); + const filesAndFolderNames = await this.arFsDao.getPrivateEntityNamesAndIdsInFolder(parentFolderId, driveKey); + + // Folders cannot overwrite file names + if (filesAndFolderNames.files.find((f) => f.fileName === destFolderName)) { + // TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599 + throw new Error(errorMessage.entityNameExists); + } + + // Use existing folder id if the intended destination name + // conflicts with an existing folder in the destination folder + wrappedFolder.existingId = filesAndFolderNames.folders.find((f) => f.folderName === destFolderName)?.folderId; + wrappedFolder.destinationName = parentFolderName; + + // Check for conflicting names and assign existing IDs for later use + await this.checkAndAssignExistingPrivateNames(wrappedFolder, driveKey); + + // Estimate and assert the cost of the entire bulk upload + // This will assign the calculated base costs to each wrapped file and folder + const bulkEstimation = await this.estimateAndAssertCostOfBulkUpload(wrappedFolder, driveKey); + + // TODO: Add interactive confirmation of price estimation before uploading + + const results = await this.recursivelyCreatePrivateFolderAndUploadChildren({ + parentFolderId, + wrappedFolder, + driveKey, + driveId, + owner + }); + + if (+bulkEstimation.communityWinstonTip > 0) { + // Send community tip only if communityWinstonTip has a value + // This can be zero when a user uses this method to upload empty folders + + const { tipData, reward: communityTipTrxReward } = await this.sendCommunityTip( + bulkEstimation.communityWinstonTip + ); + + return Promise.resolve({ + created: results.entityResults, + tips: [tipData], + fees: { ...results.feeResults, [tipData.txId]: +communityTipTrxReward } + }); + } + + return Promise.resolve({ + created: results.entityResults, + tips: [], + fees: results.feeResults + }); + } + + protected async checkAndAssignExistingNames( + wrappedFolder: ArFSFolderToUpload, + getExistingNamesFn: (parentFolderId: FolderID) => Promise + ): Promise { + if (!wrappedFolder.existingId) { + // Folder has no existing ID to check + return; + } + + const existingEntityNamesAndIds = await getExistingNamesFn(wrappedFolder.existingId); + + for await (const file of wrappedFolder.files) { + const baseFileName = file.getBaseFileName(); + + const folderNameConflict = existingEntityNamesAndIds.folders.find( + ({ folderName }) => folderName === baseFileName + ); + + // File name cannot conflict with a folder name + if (folderNameConflict) { + throw new Error(errorMessage.entityNameExists); + } + + const fileNameConflict = existingEntityNamesAndIds.files.find(({ fileName }) => fileName === baseFileName); + + // Conflicting file name creates a REVISION by default + if (fileNameConflict) { + // Assigns existing id for later use + file.existingId = fileNameConflict.fileId; + } + } + + for await (const folder of wrappedFolder.folders) { + const baseFolderName = folder.getBaseFileName(); + + const fileNameConflict = existingEntityNamesAndIds.files.find( + ({ fileName }) => fileName === baseFolderName + ); + + // Folder name cannot conflict with a file name + if (fileNameConflict) { + throw new Error(errorMessage.entityNameExists); + } + + const folderNameConflict = existingEntityNamesAndIds.folders.find( + ({ folderName }) => folderName === baseFolderName + ); + + // Conflicting folder name uses EXISTING folder by default + if (folderNameConflict) { + // Assigns existing id for later use + folder.existingId = folderNameConflict.folderId; + + // Recurse into existing folder on folder name conflict + await this.checkAndAssignExistingNames(folder, getExistingNamesFn); + } + } + } + + protected async checkAndAssignExistingPublicNames(wrappedFolder: ArFSFolderToUpload): Promise { + await this.checkAndAssignExistingNames(wrappedFolder, (parentFolderId) => + this.arFsDao.getPublicEntityNamesAndIdsInFolder(parentFolderId) + ); + } + + protected async checkAndAssignExistingPrivateNames( + wrappedFolder: ArFSFolderToUpload, + driveKey: DriveKey + ): Promise { + await this.checkAndAssignExistingNames(wrappedFolder, (parentFolderId) => + this.arFsDao.getPrivateEntityNamesAndIdsInFolder(parentFolderId, driveKey) + ); + } + + protected async recursivelyCreatePrivateFolderAndUploadChildren({ + wrappedFolder, + driveId, + parentFolderId, + driveKey, + owner + }: RecursivePrivateBulkUploadParams): Promise<{ + entityResults: ArFSEntityData[]; + feeResults: ArFSFees; + }> { + let uploadEntityFees: ArFSFees = {}; + let uploadEntityResults: ArFSEntityData[] = []; + let folderId: FolderID; + + if (wrappedFolder.existingId) { + // Use existing parent folder ID for bulk upload. + // This happens when the parent folder's name conflicts + folderId = wrappedFolder.existingId; + } else { + // Create parent folder + const folderData = await ArFSPrivateFolderTransactionData.from( + wrappedFolder.destinationName ?? wrappedFolder.getBaseFileName(), + driveKey + ); + const createFolderResult = await this.arFsDao.createPrivateFolder({ + folderData: folderData, + driveId, + rewardSettings: { + reward: wrappedFolder.getBaseCosts().metaDataBaseReward, + feeMultiple: this.feeMultiple + }, + parentFolderId, + driveKey, + syncParentFolderId: false, + owner + }); + + const { metaDataTrxId, folderId: newFolderId, metaDataTrxReward } = createFolderResult; + + // Capture parent folder results + uploadEntityFees = { [metaDataTrxId]: +metaDataTrxReward }; + uploadEntityResults = [ + { + type: 'folder', + metadataTxId: metaDataTrxId, + entityId: newFolderId, + key: urlEncodeHashKey(driveKey) + } + ]; + + folderId = newFolderId; + } + + // Upload all files in the folder + for await (const wrappedFile of wrappedFolder.files) { + const fileDataRewardSettings = { + reward: wrappedFile.getBaseCosts().fileDataBaseReward, + feeMultiple: this.feeMultiple + }; + const metadataRewardSettings = { + reward: wrappedFile.getBaseCosts().metaDataBaseReward, + feeMultiple: this.feeMultiple + }; + + const uploadFileResult = await this.arFsDao.uploadPrivateFile({ + parentFolderId: folderId, + wrappedFile, + driveId, + driveKey, + fileDataRewardSettings, + metadataRewardSettings, + existingFileId: wrappedFile.existingId + }); + + // Capture all file results + uploadEntityFees = { + ...uploadEntityFees, + [uploadFileResult.dataTrxId]: +uploadFileResult.dataTrxReward, + [uploadFileResult.metaDataTrxId]: +uploadFileResult.metaDataTrxReward + }; + uploadEntityResults = [ + ...uploadEntityResults, + { + type: 'file', + metadataTxId: uploadFileResult.metaDataTrxId, + dataTxId: uploadFileResult.dataTrxId, + entityId: uploadFileResult.fileId, + key: urlEncodeHashKey(uploadFileResult.fileKey) + } + ]; + } + + // Upload folders, and children of those folders + for await (const childFolder of wrappedFolder.folders) { + // Recursion alert, will keep creating folders of all nested folders + const results = await this.recursivelyCreatePrivateFolderAndUploadChildren({ + parentFolderId: folderId, + wrappedFolder: childFolder, + driveId, + driveKey, + owner + }); + + // Capture all folder results + uploadEntityFees = { + ...uploadEntityFees, + ...results.feeResults + }; + uploadEntityResults = [...uploadEntityResults, ...results.entityResults]; + } + + return { + entityResults: uploadEntityResults, + feeResults: uploadEntityFees + }; + } + + async createPublicFolder({ folderName, driveId, parentFolderId }: CreatePublicFolderParams): Promise { + const owner = await this.getOwnerForDriveId(driveId); + await this.assertOwnerAddress(owner); + + // Assert that there are no duplicate names in the destination folder + const entityNamesInParentFolder = await this.arFsDao.getPublicEntityNamesInFolder(parentFolderId); + if (entityNamesInParentFolder.includes(folderName)) { + // TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599 + throw new Error(errorMessage.entityNameExists); + } + + // Assert that there's enough AR available in the wallet + const folderData = new ArFSPublicFolderTransactionData(folderName); + const { metaDataBaseReward } = await this.estimateAndAssertCostOfFolderUpload(folderData); + + // Create the folder and retrieve its folder ID + const { metaDataTrxId, metaDataTrxReward, folderId } = await this.arFsDao.createPublicFolder({ + folderData, + driveId, + rewardSettings: { reward: metaDataBaseReward, feeMultiple: this.feeMultiple }, + parentFolderId, + owner + }); + + // IN THE FUTURE WE MIGHT SEND A COMMUNITY TIP HERE + return Promise.resolve({ + created: [ + { + type: 'folder', + metadataTxId: metaDataTrxId, + entityId: folderId + } + ], + tips: [], + fees: { + [metaDataTrxId]: +metaDataTrxReward + } + }); + } + + async createPrivateFolder({ + folderName, + driveId, + driveKey, + parentFolderId + }: CreatePrivateFolderParams): Promise { + const owner = await this.getOwnerForDriveId(driveId); + await this.assertOwnerAddress(owner); + + // Assert that there are no duplicate names in the destination folder + const entityNamesInParentFolder = await this.arFsDao.getPrivateEntityNamesInFolder(parentFolderId, driveKey); + if (entityNamesInParentFolder.includes(folderName)) { + // TODO: Add optional interactive prompt to resolve name conflicts in ticket PE-599 + throw new Error(errorMessage.entityNameExists); + } + + // Assert that there's enough AR available in the wallet + const folderData = await ArFSPrivateFolderTransactionData.from(folderName, driveKey); + const { metaDataBaseReward } = await this.estimateAndAssertCostOfFolderUpload(folderData); + + // Create the folder and retrieve its folder ID + const { metaDataTrxId, metaDataTrxReward, folderId } = await this.arFsDao.createPrivateFolder({ + folderData, + driveId, + rewardSettings: { reward: metaDataBaseReward, feeMultiple: this.feeMultiple }, + driveKey, + parentFolderId, + owner + }); + + // IN THE FUTURE WE MIGHT SEND A COMMUNITY TIP HERE + return Promise.resolve({ + created: [ + { + type: 'folder', + metadataTxId: metaDataTrxId, + entityId: folderId, + key: urlEncodeHashKey(driveKey) + } + ], + tips: [], + fees: { + [metaDataTrxId]: +metaDataTrxReward + } + }); + } + + async createPublicDrive(driveName: string): Promise { + // Assert that there's enough AR available in the wallet + // Use stub data to estimate costs since actual data requires entity IDs generated by ArFSDao + const stubRootFolderData = new ArFSPublicFolderTransactionData(driveName); + const stubDriveData = new ArFSPublicDriveTransactionData(driveName, stubEntityID); + const driveUploadCosts = await this.estimateAndAssertCostOfDriveCreation(stubDriveData, stubRootFolderData); + const driveRewardSettings = { + reward: driveUploadCosts.driveMetaDataBaseReward, + feeMultiple: this.feeMultiple + }; + const rootFolderRewardSettings = { + reward: driveUploadCosts.rootFolderMetaDataBaseReward, + feeMultiple: this.feeMultiple + }; + const createDriveResult = await this.arFsDao.createPublicDrive( + driveName, + driveRewardSettings, + rootFolderRewardSettings, + // There is no need to assert ownership during drive creation + await this.wallet.getAddress() + ); + return Promise.resolve({ + created: [ + { + type: 'drive', + metadataTxId: createDriveResult.metaDataTrxId, + entityId: createDriveResult.driveId + }, + { + type: 'folder', + metadataTxId: createDriveResult.rootFolderTrxId, + entityId: createDriveResult.rootFolderId + } + ], + tips: [], + fees: { + [createDriveResult.metaDataTrxId]: +createDriveResult.metaDataTrxReward, + [createDriveResult.rootFolderTrxId]: +createDriveResult.rootFolderTrxReward + } + }); + } + + async createPrivateDrive(driveName: string, newPrivateDriveData: PrivateDriveKeyData): Promise { + // Assert that there's enough AR available in the wallet + const stubRootFolderData = await ArFSPrivateFolderTransactionData.from(driveName, newPrivateDriveData.driveKey); + const stubDriveData = await ArFSPrivateDriveTransactionData.from( + driveName, + stubEntityID, + newPrivateDriveData.driveKey + ); + const driveCreationCosts = await this.estimateAndAssertCostOfDriveCreation(stubDriveData, stubRootFolderData); + const driveRewardSettings = { + reward: driveCreationCosts.driveMetaDataBaseReward, + feeMultiple: this.feeMultiple + }; + const rootFolderRewardSettings = { + reward: driveCreationCosts.rootFolderMetaDataBaseReward, + feeMultiple: this.feeMultiple + }; + const createDriveResult = await this.arFsDao.createPrivateDrive( + driveName, + newPrivateDriveData, + driveRewardSettings, + rootFolderRewardSettings, + // Ownership of drive has been verified by assertValidPassword successfully decrypting + await this.wallet.getAddress() + ); + + // IN THE FUTURE WE MIGHT SEND A COMMUNITY TIP HERE + return Promise.resolve({ + created: [ + { + type: 'drive', + metadataTxId: createDriveResult.metaDataTrxId, + entityId: createDriveResult.driveId, + key: urlEncodeHashKey(createDriveResult.driveKey) + }, + { + type: 'folder', + metadataTxId: createDriveResult.rootFolderTrxId, + entityId: createDriveResult.rootFolderId, + key: urlEncodeHashKey(createDriveResult.driveKey) + } + ], + tips: [], + fees: { + [createDriveResult.metaDataTrxId]: +createDriveResult.metaDataTrxReward, + [createDriveResult.rootFolderTrxId]: +createDriveResult.rootFolderTrxReward + } + }); + } + + /** + * Utility function to estimate and assert the cost of a bulk upload + * + * @remarks This function will recurse into the folder contents of the provided folderToUpload + * + * @throws when the wallet does not contain enough AR for the bulk upload + * + * @param folderToUpload The wrapped folder to estimate the cost of + * @param driveKey Optional parameter to determine whether to estimate the cost of a private or public upload + * @param isParentFolder Boolean to determine whether to Assert the total cost. This parameter + * is only to be handled as false internally within the recursive function. Always use default + * of TRUE when calling this method + * */ + async estimateAndAssertCostOfBulkUpload( + folderToUpload: ArFSFolderToUpload, + driveKey?: DriveKey, + isParentFolder = true + ): Promise<{ totalPrice: Winston; totalFilePrice: Winston; communityWinstonTip: Winston }> { + let totalPrice = 0; + let totalFilePrice = 0; + + if (!folderToUpload.existingId) { + // Don't estimate cost of folder metadata if using existing folder + const folderMetadataTrxData = await (async () => { + const folderName = folderToUpload.destinationName ?? folderToUpload.getBaseFileName(); + + if (driveKey) { + return ArFSPrivateFolderTransactionData.from(folderName, driveKey); + } + return new ArFSPublicFolderTransactionData(folderName); + })(); + const metaDataBaseReward = await this.priceEstimator.getBaseWinstonPriceForByteCount( + folderMetadataTrxData.sizeOf() + ); + const parentFolderWinstonPrice = metaDataBaseReward.toString(); + + // Assign base costs to folder + folderToUpload.baseCosts = { metaDataBaseReward: parentFolderWinstonPrice }; + + totalPrice += +parentFolderWinstonPrice; + } + + for await (const file of folderToUpload.files) { + const fileSize = driveKey ? file.encryptedDataSize() : file.fileStats.size; + + const fileDataBaseReward = await this.priceEstimator.getBaseWinstonPriceForByteCount(fileSize); + + const stubFileMetaData = driveKey + ? await this.stubPrivateFileMetadata(file, file.getBaseFileName()) + : this.stubPublicFileMetadata(file, file.getBaseFileName()); + const metaDataBaseReward = await this.priceEstimator.getBaseWinstonPriceForByteCount( + stubFileMetaData.sizeOf() + ); + + totalPrice += fileDataBaseReward; + totalPrice += metaDataBaseReward; + + totalFilePrice += fileDataBaseReward; + + // Assign base costs to the file + file.baseCosts = { + fileDataBaseReward: fileDataBaseReward.toString(), + metaDataBaseReward: metaDataBaseReward.toString() + }; + } + + for await (const folder of folderToUpload.folders) { + const childFolderResults = await this.estimateAndAssertCostOfBulkUpload(folder, driveKey, false); + + totalPrice += +childFolderResults.totalPrice; + totalFilePrice += +childFolderResults.totalFilePrice; + } + + const totalWinstonPrice = totalPrice.toString(); + let communityWinstonTip = '0'; + + if (isParentFolder) { + if (totalFilePrice > 0) { + communityWinstonTip = await this.communityOracle.getCommunityWinstonTip(String(totalFilePrice)); + } + + // Check and assert balance of the total bulk upload if this folder is the parent folder + const walletHasBalance = await this.walletDao.walletHasBalance( + this.wallet, + String(+communityWinstonTip + +totalWinstonPrice) + ); + + if (!walletHasBalance) { + const walletBalance = await this.walletDao.getWalletWinstonBalance(this.wallet); + + throw new Error( + `Wallet balance of ${walletBalance} Winston is not enough (${totalWinstonPrice}) for data upload of size ${folderToUpload.getTotalByteCount( + driveKey !== undefined + )} bytes!` + ); + } + } + + return { totalPrice: String(totalPrice), totalFilePrice: String(totalFilePrice), communityWinstonTip }; + } + + async assertOwnerAddress(owner: ArweaveAddress): Promise { + if (!owner.equalsAddress(await this.wallet.getAddress())) { + throw new Error('Supplied wallet is not the owner of this drive!'); + } + } + + async getPrivateDrive(driveId: DriveID, driveKey: DriveKey, owner?: ArweaveAddress): Promise { + if (!owner) { + owner = await this.getOwnerForDriveId(driveId); + } + await this.assertOwnerAddress(owner); + + return this.arFsDao.getPrivateDrive(driveId, driveKey, owner); + } + + async getPrivateFolder(folderId: FolderID, driveKey: DriveKey, owner?: ArweaveAddress): Promise { + if (!owner) { + owner = await this.arFsDao.getDriveOwnerForFolderId(folderId); + } + await this.assertOwnerAddress(owner); + + return this.arFsDao.getPrivateFolder(folderId, driveKey, owner); + } + + async getPrivateFile(fileId: FileID, driveKey: DriveKey, owner?: ArweaveAddress): Promise { + if (!owner) { + owner = await this.arFsDao.getDriveOwnerForFileId(fileId); + } + await this.assertOwnerAddress(owner); + + return this.arFsDao.getPrivateFile(fileId, driveKey, owner); + } + + /** + * Lists the children of certain private folder + * @param {FolderID} folderId the folder ID to list children of + * @returns {ArFSPrivateFileOrFolderWithPaths[]} an array representation of the children and parent folder + */ + async listPrivateFolder({ + folderId, + driveKey, + maxDepth = 0, + includeRoot = false, + owner + }: ListPrivateFolderParams): Promise { + if (!owner) { + owner = await this.arFsDao.getDriveOwnerForFolderId(folderId); + } + await this.assertOwnerAddress(owner); + + const children = this.arFsDao.listPrivateFolder({ folderId, driveKey, maxDepth, includeRoot, owner }); + return children; + } + + async estimateAndAssertCostOfMoveFile( + fileTransactionData: ArFSFileMetadataTransactionData + ): Promise { + const fileMetaTransactionDataReward = String( + await this.priceEstimator.getBaseWinstonPriceForByteCount(fileTransactionData.sizeOf()) + ); + + const walletHasBalance = await this.walletDao.walletHasBalance(this.wallet, fileMetaTransactionDataReward); + + if (!walletHasBalance) { + const walletBalance = await this.walletDao.getWalletWinstonBalance(this.wallet); + + throw new Error( + `Wallet balance of ${walletBalance} Winston is not enough (${fileMetaTransactionDataReward}) for moving file!` + ); + } + + return { metaDataBaseReward: fileMetaTransactionDataReward }; + } + + async estimateAndAssertCostOfFileUpload( + decryptedFileSize: ByteCount, + metaData: ArFSObjectTransactionData, + drivePrivacy: DrivePrivacy + ): Promise { + if (decryptedFileSize < 0 || !Number.isInteger(decryptedFileSize)) { + throw new Error('File size should be non-negative integer number!'); + } + + let fileSize = decryptedFileSize; + if (drivePrivacy === 'private') { + fileSize = this.encryptedDataSize(fileSize); + } + + let totalPrice = 0; + let fileDataBaseReward = 0; + let communityWinstonTip = '0'; + if (fileSize) { + fileDataBaseReward = await this.priceEstimator.getBaseWinstonPriceForByteCount(fileSize); + communityWinstonTip = await this.communityOracle.getCommunityWinstonTip(fileDataBaseReward.toString()); + const tipReward = await this.priceEstimator.getBaseWinstonPriceForByteCount(0); + totalPrice += fileDataBaseReward; + totalPrice += +communityWinstonTip; + totalPrice += tipReward; + } + const metaDataBaseReward = await this.priceEstimator.getBaseWinstonPriceForByteCount(metaData.sizeOf()); + totalPrice += metaDataBaseReward; + + const totalWinstonPrice = totalPrice.toString(); + + const walletHasBalance = await this.walletDao.walletHasBalance(this.wallet, totalWinstonPrice); + + if (!walletHasBalance) { + const walletBalance = await this.walletDao.getWalletWinstonBalance(this.wallet); + + throw new Error( + `Wallet balance of ${walletBalance} Winston is not enough (${totalWinstonPrice}) for data upload of size ${fileSize} bytes!` + ); + } + + return { + fileDataBaseReward: fileDataBaseReward.toString(), + metaDataBaseReward: metaDataBaseReward.toString(), + communityWinstonTip + }; + } + + async estimateAndAssertCostOfFolderUpload(metaData: ArFSObjectTransactionData): Promise { + const metaDataBaseReward = await this.priceEstimator.getBaseWinstonPriceForByteCount(metaData.sizeOf()); + const totalWinstonPrice = metaDataBaseReward.toString(); + + const walletHasBalance = await this.walletDao.walletHasBalance(this.wallet, totalWinstonPrice); + + if (!walletHasBalance) { + const walletBalance = await this.walletDao.getWalletWinstonBalance(this.wallet); + + throw new Error( + `Wallet balance of ${walletBalance} Winston is not enough (${totalWinstonPrice}) for folder creation!` + ); + } + + return { + metaDataBaseReward: totalWinstonPrice + }; + } + + async estimateAndAssertCostOfDriveCreation( + driveMetaData: ArFSDriveTransactionData, + rootFolderMetaData: ArFSFolderTransactionData + ): Promise { + let totalPrice = 0; + const driveMetaDataBaseReward = await this.priceEstimator.getBaseWinstonPriceForByteCount( + driveMetaData.sizeOf() + ); + totalPrice += driveMetaDataBaseReward; + const rootFolderMetaDataBaseReward = await this.priceEstimator.getBaseWinstonPriceForByteCount( + rootFolderMetaData.sizeOf() + ); + totalPrice += rootFolderMetaDataBaseReward; + + const totalWinstonPrice = totalPrice.toString(); + + const walletHasBalance = await this.walletDao.walletHasBalance(this.wallet, totalWinstonPrice); + + if (!walletHasBalance) { + const walletBalance = await this.walletDao.getWalletWinstonBalance(this.wallet); + + throw new Error( + `Wallet balance of ${walletBalance} Winston is not enough (${totalPrice}) for drive creation!` + ); + } + + return { + driveMetaDataBaseReward: driveMetaDataBaseReward.toString(), + rootFolderMetaDataBaseReward: rootFolderMetaDataBaseReward.toString() + }; + } + + async getDriveIdForFileId(fileId: FileID): Promise { + return this.arFsDao.getDriveIdForFileId(fileId); + } + + async getDriveIdForFolderId(folderId: FolderID): Promise { + return this.arFsDao.getDriveIdForFolderId(folderId); + } + + // Provides for stubbing metadata during cost estimations since the data trx ID won't yet be known + private stubPublicFileMetadata( + wrappedFile: ArFSFileToUpload, + destinationFileName?: string + ): ArFSPublicFileMetadataTransactionData { + const { fileSize, dataContentType, lastModifiedDateMS } = wrappedFile.gatherFileInfo(); + + return new ArFSPublicFileMetadataTransactionData( + destinationFileName ?? wrappedFile.getBaseFileName(), + fileSize, + lastModifiedDateMS, + stubTransactionID, + dataContentType + ); + } + + // Provides for stubbing metadata during cost estimations since the data trx and File IDs won't yet be known + private async stubPrivateFileMetadata( + wrappedFile: ArFSFileToUpload, + destinationFileName?: string + ): Promise { + const { fileSize, dataContentType, lastModifiedDateMS } = wrappedFile.gatherFileInfo(); + + return await ArFSPrivateFileMetadataTransactionData.from( + destinationFileName ?? wrappedFile.getBaseFileName(), + fileSize, + lastModifiedDateMS, + stubTransactionID, + dataContentType, + stubEntityID, + await deriveDriveKey( + 'stubPassword', + stubEntityID, + JSON.stringify((this.wallet as JWKWallet).getPrivateKey()) + ) + ); + } + + async assertValidPassword(password: string): Promise { + await this.arFsDao.assertValidPassword(password); + } +} diff --git a/src/arfs_entities.ts b/src/arfs_entities.ts new file mode 100644 index 00000000..2368397b --- /dev/null +++ b/src/arfs_entities.ts @@ -0,0 +1,285 @@ +import { + ArFSDriveEntity, + ArFSEntity, + ArFSFileFolderEntity, + ContentType, + DriveAuthMode, + DrivePrivacy, + EntityType +} from 'ardrive-core-js'; +import { FolderHierarchy } from './folderHierarchy'; +import { + ByteCount, + CipherIV, + DataContentType, + DriveID, + EntityID, + FileID, + FolderID, + TransactionID, + UnixTime +} from './types'; + +export class ArFSPublicDrive extends ArFSEntity implements ArFSDriveEntity { + constructor( + readonly appName: string, + readonly appVersion: string, + readonly arFS: string, + readonly contentType: ContentType, + readonly driveId: DriveID, + readonly entityType: EntityType, + readonly name: string, + readonly txId: TransactionID, + readonly unixTime: UnixTime, + readonly drivePrivacy: DrivePrivacy, + readonly rootFolderId: FolderID + ) { + super(appName, appVersion, arFS, contentType, driveId, entityType, name, 0, txId, unixTime); + } +} + +export class ArFSPrivateDrive extends ArFSEntity implements ArFSDriveEntity { + constructor( + readonly appName: string, + readonly appVersion: string, + readonly arFS: string, + readonly contentType: ContentType, + readonly driveId: DriveID, + readonly entityType: EntityType, + readonly name: string, + readonly txId: TransactionID, + readonly unixTime: UnixTime, + readonly drivePrivacy: DrivePrivacy, + readonly rootFolderId: FolderID, + readonly driveAuthMode: DriveAuthMode, + readonly cipher: string, + readonly cipherIV: CipherIV + ) { + super(appName, appVersion, arFS, contentType, driveId, entityType, name, 0, txId, unixTime); + } +} + +export class ArFSFileOrFolderEntity extends ArFSEntity implements ArFSFileFolderEntity { + folderId?: FolderID; + + constructor( + appName: string, + appVersion: string, + arFS: string, + contentType: ContentType, + driveId: DriveID, + entityType: EntityType, + name: string, + public size: ByteCount, + txId: TransactionID, + unixTime: UnixTime, + public lastModifiedDate: UnixTime, + readonly parentFolderId: FolderID, + readonly entityId: EntityID + ) { + super(appName, appVersion, arFS, contentType, driveId, entityType, name, lastModifiedDate, txId, unixTime); + } +} + +export interface ArFSWithPath { + readonly path: string; + readonly txIdPath: string; + readonly entityIdPath: string; +} + +export class ArFSPublicFileOrFolderWithPaths extends ArFSFileOrFolderEntity implements ArFSWithPath { + readonly path: string; + readonly txIdPath: string; + readonly entityIdPath: string; + + constructor(entity: ArFSPublicFile | ArFSPublicFolder, hierarchy: FolderHierarchy) { + super( + entity.appName, + entity.appVersion, + entity.arFS, + entity.contentType, + entity.driveId, + entity.entityType, + entity.name, + entity.size, + entity.txId, + entity.unixTime, + entity.lastModifiedDate, + entity.parentFolderId, + entity.entityId + ); + this.path = `${hierarchy.pathToFolderId(entity.parentFolderId)}${entity.name}`; + this.txIdPath = `${hierarchy.txPathToFolderId(entity.parentFolderId)}${entity.txId}`; + this.entityIdPath = `${hierarchy.entityPathToFolderId(entity.parentFolderId)}${entity.entityId}`; + } +} + +export class ArFSPrivateFileOrFolderWithPaths extends ArFSFileOrFolderEntity implements ArFSWithPath { + readonly cipher: string; + readonly cipherIV: CipherIV; + readonly path: string; + readonly txIdPath: string; + readonly entityIdPath: string; + + constructor(entity: ArFSPrivateFile | ArFSPrivateFolder, hierarchy: FolderHierarchy) { + super( + entity.appName, + entity.appVersion, + entity.arFS, + entity.contentType, + entity.driveId, + entity.entityType, + entity.name, + entity.size, + entity.txId, + entity.unixTime, + entity.lastModifiedDate, + entity.parentFolderId, + entity.entityId + ); + this.cipher = entity.cipher; + this.cipherIV = entity.cipherIV; + this.path = `${hierarchy.pathToFolderId(entity.parentFolderId)}${entity.name}`; + this.txIdPath = `${hierarchy.txPathToFolderId(entity.parentFolderId)}${entity.txId}`; + this.entityIdPath = `${hierarchy.entityPathToFolderId(entity.parentFolderId)}${entity.entityId}`; + } +} + +export class ArFSPublicFile extends ArFSFileOrFolderEntity { + constructor( + readonly appName: string, + readonly appVersion: string, + readonly arFS: string, + readonly contentType: ContentType, + readonly driveId: DriveID, + readonly entityType: EntityType, + readonly name: string, + readonly txId: TransactionID, + readonly unixTime: UnixTime, + readonly parentFolderId: FolderID, + readonly fileId: FileID, + readonly size: ByteCount, + readonly lastModifiedDate: UnixTime, + readonly dataTxId: TransactionID, + readonly dataContentType: DataContentType + ) { + super( + appName, + appVersion, + arFS, + contentType, + driveId, + entityType, + name, + size, + txId, + unixTime, + lastModifiedDate, + parentFolderId, + fileId + ); + } +} + +export class ArFSPrivateFile extends ArFSFileOrFolderEntity { + constructor( + readonly appName: string, + readonly appVersion: string, + readonly arFS: string, + readonly contentType: ContentType, + readonly driveId: DriveID, + readonly entityType: EntityType, + readonly name: string, + readonly txId: TransactionID, + readonly unixTime: UnixTime, + readonly parentFolderId: FolderID, + readonly fileId: FileID, + readonly size: ByteCount, + readonly lastModifiedDate: UnixTime, + readonly dataTxId: TransactionID, + readonly dataContentType: DataContentType, + readonly cipher: string, + readonly cipherIV: CipherIV + ) { + super( + appName, + appVersion, + arFS, + contentType, + driveId, + entityType, + name, + size, + txId, + unixTime, + lastModifiedDate, + parentFolderId, + fileId + ); + } +} + +export class ArFSPublicFolder extends ArFSFileOrFolderEntity { + constructor( + readonly appName: string, + readonly appVersion: string, + readonly arFS: string, + readonly contentType: ContentType, + readonly driveId: DriveID, + readonly entityType: EntityType, + readonly name: string, + readonly txId: TransactionID, + readonly unixTime: UnixTime, + readonly parentFolderId: FolderID, + readonly entityId: FolderID + ) { + super( + appName, + appVersion, + arFS, + contentType, + driveId, + entityType, + name, + 0, + txId, + unixTime, + 0, + parentFolderId, + entityId + ); + } +} +export class ArFSPrivateFolder extends ArFSFileOrFolderEntity { + constructor( + readonly appName: string, + readonly appVersion: string, + readonly arFS: string, + readonly contentType: ContentType, + readonly driveId: DriveID, + readonly entityType: EntityType, + readonly name: string, + readonly txId: TransactionID, + readonly unixTime: UnixTime, + readonly parentFolderId: FolderID, + readonly entityId: FolderID, + readonly cipher: string, + readonly cipherIV: CipherIV + ) { + super( + appName, + appVersion, + arFS, + contentType, + driveId, + entityType, + name, + 0, + txId, + unixTime, + 0, + parentFolderId, + entityId + ); + } +} diff --git a/src/arfs_entity_result_factory.ts b/src/arfs_entity_result_factory.ts new file mode 100644 index 00000000..6bcb169c --- /dev/null +++ b/src/arfs_entity_result_factory.ts @@ -0,0 +1,57 @@ +import { ArFSFileMetadataTransactionData } from './arfs_trx_data_types'; +import { TransactionID, Winston, DriveID, FolderID, FileID, FileKey, DriveKey } from './types'; + +export interface ArFSWriteResult { + metaDataTrxId: TransactionID; + metaDataTrxReward: Winston; +} + +export interface ArFSCreateDriveResult extends ArFSWriteResult { + rootFolderTrxId: TransactionID; + rootFolderTrxReward: Winston; + driveId: DriveID; + rootFolderId: FolderID; +} + +export interface ArFSCreateFolderResult extends ArFSWriteResult { + folderId: FolderID; +} + +export interface ArFSUploadFileResult extends ArFSWriteResult { + dataTrxId: TransactionID; + dataTrxReward: Winston; + fileId: FileID; +} + +export type ArFSMoveEntityResult = ArFSWriteResult; + +export interface ArFSMoveFileResult extends ArFSMoveEntityResult { + dataTrxId: TransactionID; +} + +export type WithDriveKey = { driveKey: DriveKey }; +type WithFileKey = { fileKey: FileKey }; + +export type ArFSCreatePublicDriveResult = ArFSCreateDriveResult; +export type ArFSCreatePrivateDriveResult = ArFSCreateDriveResult & WithDriveKey; + +export type ArFSCreatePublicFolderResult = ArFSCreateFolderResult; +export type ArFSCreatePrivateFolderResult = ArFSCreateFolderResult & WithDriveKey; + +export type ArFSUploadPublicFileResult = ArFSUploadFileResult; +export type ArFSUploadPrivateFileResult = ArFSUploadFileResult & WithFileKey; + +export type ArFSMovePublicFolderResult = ArFSMoveEntityResult; +export type ArFSMovePrivateFolderResult = ArFSMoveEntityResult & WithDriveKey; + +export type ArFSMovePublicFileResult = ArFSMoveFileResult; +export type ArFSMovePrivateFileResult = ArFSMoveFileResult & WithFileKey; + +// Result factory function types +export type ArFSMoveEntityResultFactory = (result: ArFSMoveEntityResult) => R; +export type ArFSCreateDriveResultFactory = (result: ArFSCreateDriveResult) => R; +export type ArFSCreateFolderResultFactory = (result: ArFSCreateFolderResult) => R; +export type ArFSUploadFileResultFactory = ( + result: ArFSUploadFileResult, + trxData: D +) => R; diff --git a/src/arfs_file_wrapper.ts b/src/arfs_file_wrapper.ts new file mode 100644 index 00000000..893a406d --- /dev/null +++ b/src/arfs_file_wrapper.ts @@ -0,0 +1,148 @@ +import * as fs from 'fs'; +import { extToMime } from 'ardrive-core-js'; +import { basename, join } from 'path'; +import { ByteCount, DataContentType, FileID, FolderID, UnixTime } from './types'; +import { BulkFileBaseCosts, MetaDataBaseCosts } from './ardrive'; + +type BaseFileName = string; +type FilePath = string; + +/** + * Fs + Node implementation file size limitations -- tested on MacOS Sep 27, 2021 + * + * Public : 2147483647 bytes + * Private: 2147483646 bytes + */ +const maxFileSize: ByteCount = 2147483646; + +export interface FileInfo { + dataContentType: DataContentType; + lastModifiedDateMS: UnixTime; + fileSize: ByteCount; +} + +/** + * Reads stats of a file or folder and constructs a File or Folder wrapper class + * + * @remarks import and use `isFolder` type-guard to later determine whether a folder or file + * + * @example + * + * const fileOrFolder = wrapFileOrFolder(myFilePath); + * + * if (isFolder(fileOrFolder)) { + * // Type is: Folder + * } else { + * // Type is: File + * } + * + */ +export function wrapFileOrFolder(fileOrFolderPath: FilePath): ArFSFileToUpload | ArFSFolderToUpload { + const entityStats = fs.statSync(fileOrFolderPath); + + if (entityStats.isDirectory()) { + return new ArFSFolderToUpload(fileOrFolderPath, entityStats); + } + + return new ArFSFileToUpload(fileOrFolderPath, entityStats); +} + +/** Type-guard function to determine if returned class is a File or Folder */ +export function isFolder(fileOrFolder: ArFSFileToUpload | ArFSFolderToUpload): fileOrFolder is ArFSFolderToUpload { + return fileOrFolder instanceof ArFSFolderToUpload; +} + +export class ArFSFileToUpload { + constructor(public readonly filePath: FilePath, public readonly fileStats: fs.Stats) { + if (this.fileStats.size >= maxFileSize) { + throw new Error(`Files greater than "${maxFileSize}" bytes are not yet supported!`); + } + } + + baseCosts?: BulkFileBaseCosts; + existingId?: FileID; + + public gatherFileInfo(): FileInfo { + const dataContentType = this.getContentType(); + const lastModifiedDateMS = Math.floor(this.fileStats.mtimeMs); + const fileSize = this.fileStats.size; + + return { dataContentType, lastModifiedDateMS, fileSize }; + } + + public getBaseCosts(): BulkFileBaseCosts { + if (!this.baseCosts) { + throw new Error('Base costs on file were never set!'); + } + return this.baseCosts; + } + + public getFileDataBuffer(): Buffer { + return fs.readFileSync(this.filePath); + } + + public getContentType(): DataContentType { + return extToMime(this.filePath); + } + + public getBaseFileName(): BaseFileName { + return basename(this.filePath); + } + + /** Computes the size of a private file encrypted with AES256-GCM */ + public encryptedDataSize(): ByteCount { + return (this.fileStats.size / 16 + 1) * 16; + } +} + +export class ArFSFolderToUpload { + files: ArFSFileToUpload[] = []; + folders: ArFSFolderToUpload[] = []; + + baseCosts?: MetaDataBaseCosts; + existingId?: FolderID; + destinationName?: string; + + constructor(public readonly filePath: FilePath, public readonly fileStats: fs.Stats) { + const entitiesInFolder = fs.readdirSync(this.filePath); + + for (const entityPath of entitiesInFolder) { + const absoluteEntityPath = join(this.filePath, entityPath); + const entityStats = fs.statSync(absoluteEntityPath); + + if (entityStats.isDirectory()) { + // Child is a folder, build a new folder which will construct it's own children + const childFolder = new ArFSFolderToUpload(absoluteEntityPath, entityStats); + this.folders.push(childFolder); + } else { + // Child is a file, build a new file + const childFile = new ArFSFileToUpload(absoluteEntityPath, entityStats); + this.files.push(childFile); + } + } + } + + public getBaseCosts(): MetaDataBaseCosts { + if (!this.baseCosts) { + throw new Error('Base costs on folder were never set!'); + } + return this.baseCosts; + } + + public getBaseFileName(): BaseFileName { + return basename(this.filePath); + } + + getTotalByteCount(encrypted = false): ByteCount { + let totalByteCount = 0; + + for (const file of this.files) { + totalByteCount += encrypted ? file.encryptedDataSize() : file.fileStats.size; + } + for (const folder of this.folders) { + totalByteCount += folder.getTotalByteCount(encrypted); + } + + return totalByteCount; + } +} diff --git a/src/arfs_meta_data_factory.ts b/src/arfs_meta_data_factory.ts new file mode 100644 index 00000000..cc634099 --- /dev/null +++ b/src/arfs_meta_data_factory.ts @@ -0,0 +1,39 @@ +import { + ArFSDriveMetaDataPrototype, + ArFSEntityMetaDataPrototype, + ArFSFileDataPrototype, + ArFSFileMetaDataPrototype, + ArFSFolderMetaDataPrototype +} from './arfs_prototypes'; +import { ArFSFileMetadataTransactionData } from './arfs_trx_data_types'; + +import { ByteCount, DataContentType, DriveID, FileID, FolderID, TransactionID, UnixTime } from './types'; + +export type MoveEntityMetaDataFactory = () => ArFSEntityMetaDataPrototype; + +export type FolderMetaDataFactory = (folderId: FolderID, parentFolderId?: FolderID) => ArFSFolderMetaDataPrototype; + +export type FileDataPrototypeFactory = ( + desfileData: Buffer, + dataContentType: DataContentType, + fileId: FileID +) => Promise; + +export type FileMetadataTrxDataFactory = ( + destinationFileName: string, + fileSize: ByteCount, + lastModifiedDateMS: UnixTime, + dataTrxId: TransactionID, + dataContentType: DataContentType, + fileId: FileID +) => Promise; + +export type FileMetaDataFactory = ( + metadataTrxData: D, + fileId: FileID +) => ArFSFileMetaDataPrototype; + +export type CreateDriveMetaDataFactory = ( + driveID: DriveID, + rootFolderId: FolderID +) => Promise; diff --git a/src/arfs_prototypes.ts b/src/arfs_prototypes.ts new file mode 100644 index 00000000..98020d38 --- /dev/null +++ b/src/arfs_prototypes.ts @@ -0,0 +1,245 @@ +import { + ArFSDriveTransactionData, + ArFSFileDataTransactionData, + ArFSFileMetadataTransactionData, + ArFSFolderTransactionData, + ArFSObjectTransactionData, + ArFSPrivateDriveTransactionData, + ArFSPrivateFileDataTransactionData, + ArFSPrivateFileMetadataTransactionData, + ArFSPrivateFolderTransactionData, + ArFSPublicDriveTransactionData, + ArFSPublicFileDataTransactionData, + ArFSPublicFileMetadataTransactionData, + ArFSPublicFolderTransactionData +} from './arfs_trx_data_types'; +import Transaction from 'arweave/node/lib/transaction'; +import { ContentType, DrivePrivacy, GQLTagInterface } from 'ardrive-core-js'; +import { DataContentType, DriveID, FileID, FolderID, UnixTime } from './types'; + +export abstract class ArFSObjectMetadataPrototype { + abstract protectedTags: string[]; + abstract objectData: ArFSObjectTransactionData; + abstract addTagsToTransaction(transaction: Transaction): void; + + // Implementation should throw if any protected tags are identified + assertProtectedTags(tags: GQLTagInterface[]): void { + tags.forEach((tag) => { + if (this.protectedTags.includes(tag.name)) { + throw new Error(`Tag ${tag.name} is protected and cannot be used in this context!`); + } + }); + } +} + +export abstract class ArFSEntityMetaDataPrototype extends ArFSObjectMetadataPrototype { + readonly unixTime: UnixTime; + + constructor() { + super(); + + // Get the current time so the app can display the "created" data later on + this.unixTime = Math.round(Date.now() / 1000); + } +} + +export abstract class ArFSDriveMetaDataPrototype extends ArFSEntityMetaDataPrototype { + abstract driveId: DriveID; + abstract objectData: ArFSDriveTransactionData; + abstract readonly privacy: DrivePrivacy; + abstract readonly contentType: ContentType; + + get protectedTags(): string[] { + return ['Content-Type', 'Entity-Type', 'Unix-Time', 'Drive-Id', 'Drive-Privacy']; + } + + addTagsToTransaction(transaction: Transaction): void { + transaction.addTag('Content-Type', this.contentType); + transaction.addTag('Entity-Type', 'drive'); + transaction.addTag('Unix-Time', this.unixTime.toString()); + transaction.addTag('Drive-Id', this.driveId); + transaction.addTag('Drive-Privacy', this.privacy); + } +} + +export class ArFSPublicDriveMetaDataPrototype extends ArFSDriveMetaDataPrototype { + readonly privacy: DrivePrivacy = 'public'; + readonly contentType: ContentType = 'application/json'; + + constructor(readonly objectData: ArFSPublicDriveTransactionData, readonly driveId: DriveID) { + super(); + } +} + +export class ArFSPrivateDriveMetaDataPrototype extends ArFSDriveMetaDataPrototype { + readonly privacy: DrivePrivacy = 'private'; + readonly contentType: ContentType = 'application/octet-stream'; + + constructor(readonly driveId: DriveID, readonly objectData: ArFSPrivateDriveTransactionData) { + super(); + } + + get protectedTags(): string[] { + return ['Cipher', 'Cipher-IV', 'Drive-Auth-Mode', ...super.protectedTags]; + } + + addTagsToTransaction(transaction: Transaction): void { + super.addTagsToTransaction(transaction); + transaction.addTag('Cipher', this.objectData.cipher); + transaction.addTag('Cipher-IV', this.objectData.cipherIV); + transaction.addTag('Drive-Auth-Mode', this.objectData.driveAuthMode); + } +} + +export abstract class ArFSFolderMetaDataPrototype extends ArFSEntityMetaDataPrototype { + abstract driveId: DriveID; + abstract folderId: FolderID; + abstract objectData: ArFSFolderTransactionData; + abstract parentFolderId?: FolderID; + abstract readonly contentType: ContentType; + + get protectedTags(): string[] { + return ['Content-Type', 'Entity-Type', 'Unix-Time', 'Drive-Id', 'Folder-Id', 'Parent-Folder-Id']; + } + + addTagsToTransaction(transaction: Transaction): void { + transaction.addTag('Content-Type', this.contentType); + transaction.addTag('Entity-Type', 'folder'); + transaction.addTag('Unix-Time', this.unixTime.toString()); + transaction.addTag('Drive-Id', this.driveId); + transaction.addTag('Folder-Id', this.folderId); + if (this.parentFolderId) { + // Root folder transactions do not have Parent-Folder-Id + transaction.addTag('Parent-Folder-Id', this.parentFolderId); + } + } +} + +export class ArFSPublicFolderMetaDataPrototype extends ArFSFolderMetaDataPrototype { + readonly contentType: ContentType = 'application/json'; + + constructor( + readonly objectData: ArFSPublicFolderTransactionData, + readonly driveId: DriveID, + readonly folderId: FolderID, + readonly parentFolderId?: FolderID + ) { + super(); + } +} + +export class ArFSPrivateFolderMetaDataPrototype extends ArFSFolderMetaDataPrototype { + readonly privacy: DrivePrivacy = 'private'; + readonly contentType: ContentType = 'application/octet-stream'; + + constructor( + readonly driveId: DriveID, + readonly folderId: FolderID, + readonly objectData: ArFSPrivateFolderTransactionData, + readonly parentFolderId?: FolderID + ) { + super(); + } + + get protectedTags(): string[] { + return ['Cipher', 'Cipher-IV', ...super.protectedTags]; + } + + addTagsToTransaction(transaction: Transaction): void { + super.addTagsToTransaction(transaction); + transaction.addTag('Cipher', this.objectData.cipher); + transaction.addTag('Cipher-IV', this.objectData.cipherIV); + } +} + +export abstract class ArFSFileMetaDataPrototype extends ArFSEntityMetaDataPrototype { + abstract driveId: DriveID; + abstract fileId: FileID; + abstract objectData: ArFSFileMetadataTransactionData; + abstract parentFolderId: FolderID; + abstract contentType: ContentType; + + get protectedTags(): string[] { + return ['Content-Type', 'Entity-Type', 'Unix-Time', 'Drive-Id', 'File-Id', 'Parent-Folder-Id']; + } + + addTagsToTransaction(transaction: Transaction): void { + transaction.addTag('Content-Type', this.contentType); + transaction.addTag('Entity-Type', 'file'); + transaction.addTag('Unix-Time', this.unixTime.toString()); + transaction.addTag('Drive-Id', this.driveId); + transaction.addTag('File-Id', this.fileId); + transaction.addTag('Parent-Folder-Id', this.parentFolderId); + } +} +export class ArFSPublicFileMetaDataPrototype extends ArFSFileMetaDataPrototype { + readonly contentType: ContentType = 'application/json'; + + constructor( + readonly objectData: ArFSPublicFileMetadataTransactionData, + readonly driveId: DriveID, + readonly fileId: FileID, + readonly parentFolderId: FolderID + ) { + super(); + } +} + +export class ArFSPrivateFileMetaDataPrototype extends ArFSFileMetaDataPrototype { + readonly contentType: ContentType = 'application/octet-stream'; + + constructor( + readonly objectData: ArFSPrivateFileMetadataTransactionData, + readonly driveId: DriveID, + readonly fileId: FileID, + readonly parentFolderId: FolderID + ) { + super(); + } + + get protectedTags(): string[] { + return ['Cipher', 'Cipher-IV', ...super.protectedTags]; + } + + addTagsToTransaction(transaction: Transaction): void { + super.addTagsToTransaction(transaction); + transaction.addTag('Cipher', this.objectData.cipher); + transaction.addTag('Cipher-IV', this.objectData.cipherIV); + } +} + +export abstract class ArFSFileDataPrototype extends ArFSObjectMetadataPrototype { + abstract readonly objectData: ArFSFileDataTransactionData; + abstract readonly contentType: DataContentType | 'application/octet-stream'; + + get protectedTags(): string[] { + return ['Content-Type']; + } + + addTagsToTransaction(transaction: Transaction): void { + transaction.addTag('Content-Type', this.contentType); + } +} + +export class ArFSPublicFileDataPrototype extends ArFSFileDataPrototype { + constructor(readonly objectData: ArFSPublicFileDataTransactionData, readonly contentType: DataContentType) { + super(); + } +} + +export class ArFSPrivateFileDataPrototype extends ArFSFileDataPrototype { + readonly contentType = 'application/octet-stream'; + constructor(readonly objectData: ArFSPrivateFileDataTransactionData) { + super(); + } + + get protectedTags(): string[] { + return ['Cipher', 'Cipher-IV', ...super.protectedTags]; + } + + addTagsToTransaction(transaction: Transaction): void { + super.addTagsToTransaction(transaction); + transaction.addTag('Cipher', this.objectData.cipher); + transaction.addTag('Cipher-IV', this.objectData.cipherIV); + } +} diff --git a/src/arfs_trx_data_types.ts b/src/arfs_trx_data_types.ts new file mode 100644 index 00000000..562324fd --- /dev/null +++ b/src/arfs_trx_data_types.ts @@ -0,0 +1,234 @@ +import { + ArFSEncryptedData, + CipherType, + deriveFileKey, + DriveAuthMode, + driveEncrypt, + fileEncrypt +} from 'ardrive-core-js'; +import { + ByteCount, + CipherIV, + DataContentType, + DriveKey, + FileID, + FileKey, + FolderID, + TransactionID, + UnixTime +} from './types'; + +export interface ArFSObjectTransactionData { + asTransactionData(): string | Buffer; + sizeOf(): number; +} + +export abstract class ArFSDriveTransactionData implements ArFSObjectTransactionData { + abstract asTransactionData(): string | Buffer; + // TODO: Share repeated sizeOf() function to all classes + sizeOf(): number { + return this.asTransactionData().length; + } +} + +export class ArFSPublicDriveTransactionData extends ArFSDriveTransactionData { + constructor(private readonly name: string, private readonly rootFolderId: FolderID) { + super(); + } + asTransactionData(): string { + return JSON.stringify({ + name: this.name, + rootFolderId: this.rootFolderId + }); + } +} + +export class ArFSPrivateDriveTransactionData extends ArFSDriveTransactionData { + private constructor( + readonly cipher: CipherType, + readonly cipherIV: CipherIV, + readonly encryptedDriveData: Buffer, + readonly driveKey: DriveKey, + readonly driveAuthMode: DriveAuthMode = 'password' + ) { + super(); + } + + static async from( + name: string, + rootFolderId: FolderID, + driveKey: DriveKey + ): Promise { + const { cipher, cipherIV, data } = await driveEncrypt( + driveKey, + Buffer.from( + JSON.stringify({ + name: name, + rootFolderId: rootFolderId + }) + ) + ); + return new ArFSPrivateDriveTransactionData(cipher, cipherIV, data, driveKey); + } + + asTransactionData(): Buffer { + return this.encryptedDriveData; + } +} + +export abstract class ArFSFolderTransactionData implements ArFSObjectTransactionData { + abstract asTransactionData(): string | Buffer; + sizeOf(): number { + return this.asTransactionData().length; + } +} + +export class ArFSPublicFolderTransactionData extends ArFSFolderTransactionData { + constructor(private readonly name: string) { + super(); + } + asTransactionData(): string { + return JSON.stringify({ + name: this.name + }); + } +} + +export class ArFSPrivateFolderTransactionData extends ArFSFolderTransactionData { + private constructor( + readonly name: string, + readonly cipher: CipherType, + readonly cipherIV: CipherIV, + readonly encryptedFolderData: Buffer, + readonly driveKey: DriveKey + ) { + super(); + } + + static async from(name: string, driveKey: DriveKey): Promise { + const { cipher, cipherIV, data }: ArFSEncryptedData = await fileEncrypt( + driveKey, + Buffer.from( + JSON.stringify({ + name: name + }) + ) + ); + return new ArFSPrivateFolderTransactionData(name, cipher, cipherIV, data, driveKey); + } + + asTransactionData(): Buffer { + return this.encryptedFolderData; + } +} + +export abstract class ArFSFileMetadataTransactionData implements ArFSObjectTransactionData { + abstract asTransactionData(): string | Buffer; + sizeOf(): number { + return this.asTransactionData().length; + } +} + +export class ArFSPublicFileMetadataTransactionData extends ArFSFileMetadataTransactionData { + constructor( + private readonly name: string, + private readonly size: ByteCount, + private readonly lastModifiedDate: UnixTime, + private readonly dataTxId: TransactionID, + private readonly dataContentType: DataContentType + ) { + super(); + } + + asTransactionData(): string { + return JSON.stringify({ + name: this.name, + size: this.size, + lastModifiedDate: this.lastModifiedDate, + dataTxId: this.dataTxId, + dataContentType: this.dataContentType + }); + } +} + +export class ArFSPrivateFileMetadataTransactionData extends ArFSFileMetadataTransactionData { + private constructor( + readonly cipher: CipherType, + readonly cipherIV: CipherIV, + readonly encryptedFileMetadata: Buffer, + readonly fileKey: FileKey, + readonly driveAuthMode: DriveAuthMode = 'password' + ) { + super(); + } + + static async from( + name: string, + size: ByteCount, + lastModifiedDate: UnixTime, + dataTxId: TransactionID, + dataContentType: DataContentType, + fileId: FileID, + driveKey: DriveKey + ): Promise { + const fileKey: FileKey = await deriveFileKey(fileId, driveKey); + const { cipher, cipherIV, data }: ArFSEncryptedData = await fileEncrypt( + fileKey, + Buffer.from( + JSON.stringify({ + name: name, + size: size, + lastModifiedDate: lastModifiedDate, + dataTxId: dataTxId, + dataContentType: dataContentType + }) + ) + ); + return new ArFSPrivateFileMetadataTransactionData(cipher, cipherIV, data, fileKey); + } + + asTransactionData(): Buffer { + return this.encryptedFileMetadata; + } +} + +export abstract class ArFSFileDataTransactionData implements ArFSObjectTransactionData { + abstract asTransactionData(): string | Buffer; + sizeOf(): number { + return this.asTransactionData().length; + } +} +export class ArFSPublicFileDataTransactionData extends ArFSFileDataTransactionData { + constructor(private readonly fileData: Buffer) { + super(); + } + + asTransactionData(): Buffer { + return this.fileData; + } +} + +export class ArFSPrivateFileDataTransactionData extends ArFSFileDataTransactionData { + private constructor( + readonly cipher: CipherType, + readonly cipherIV: CipherIV, + readonly encryptedFileData: Buffer, + readonly driveAuthMode: DriveAuthMode = 'password' + ) { + super(); + } + + static async from( + fileData: Buffer, + fileId: FileID, + driveKey: DriveKey + ): Promise { + const fileKey: FileKey = await deriveFileKey(fileId, driveKey); + const { cipher, cipherIV, data }: ArFSEncryptedData = await fileEncrypt(fileKey, fileData); + return new ArFSPrivateFileDataTransactionData(cipher, cipherIV, data); + } + + asTransactionData(): string | Buffer { + return this.encryptedFileData; + } +} diff --git a/src/arfsdao.ts b/src/arfsdao.ts new file mode 100644 index 00000000..ba5eaad4 --- /dev/null +++ b/src/arfsdao.ts @@ -0,0 +1,964 @@ +/* eslint-disable no-console */ +import type { JWKWallet, Wallet } from './wallet'; +import Arweave from 'arweave'; +import { v4 as uuidv4 } from 'uuid'; +import Transaction from 'arweave/node/lib/transaction'; +import { deriveDriveKey, GQLEdgeInterface, GQLNodeInterface, GQLTagInterface, JWKInterface } from 'ardrive-core-js'; +import { + ArFSPublicFileDataPrototype, + ArFSObjectMetadataPrototype, + ArFSPrivateDriveMetaDataPrototype, + ArFSPrivateFolderMetaDataPrototype, + ArFSPublicDriveMetaDataPrototype, + ArFSPublicFileMetaDataPrototype, + ArFSPublicFolderMetaDataPrototype, + ArFSPrivateFileDataPrototype, + ArFSPrivateFileMetaDataPrototype +} from './arfs_prototypes'; +import { + ArFSFileMetadataTransactionData, + ArFSObjectTransactionData, + ArFSPrivateDriveTransactionData, + ArFSPrivateFileDataTransactionData, + ArFSPrivateFileMetadataTransactionData, + ArFSPrivateFolderTransactionData, + ArFSPublicDriveTransactionData, + ArFSPublicFileDataTransactionData, + ArFSPublicFileMetadataTransactionData, + ArFSPublicFolderTransactionData +} from './arfs_trx_data_types'; +import { ASCENDING_ORDER, buildQuery } from './query'; +import { ArFSFileToUpload } from './arfs_file_wrapper'; +import { + DriveID, + FolderID, + FileID, + DriveKey, + DEFAULT_APP_NAME, + DEFAULT_APP_VERSION, + CURRENT_ARFS_VERSION, + RewardSettings +} from './types'; +import { CreateTransactionInterface } from 'arweave/node/common'; +import { ArFSPrivateFileBuilder, ArFSPublicFileBuilder } from './utils/arfs_builders/arfs_file_builders'; +import { ArFSPrivateFolderBuilder, ArFSPublicFolderBuilder } from './utils/arfs_builders/arfs_folder_builders'; +import { latestRevisionFilter, fileFilter, folderFilter } from './utils/filter_methods'; +import { + ArFSPrivateDriveBuilder, + ENCRYPTED_DATA_PLACEHOLDER, + SafeArFSDriveBuilder +} from './utils/arfs_builders/arfs_drive_builders'; +import { FolderHierarchy } from './folderHierarchy'; +import { + CreateDriveMetaDataFactory, + FileDataPrototypeFactory, + FileMetaDataFactory, + FileMetadataTrxDataFactory, + FolderMetaDataFactory, + MoveEntityMetaDataFactory +} from './arfs_meta_data_factory'; +import { + ArFSCreateDriveResult, + ArFSCreateFolderResult, + ArFSCreatePrivateDriveResult, + ArFSMoveEntityResult, + ArFSMovePrivateFileResult, + ArFSMovePrivateFolderResult, + ArFSMovePublicFileResult, + ArFSMovePublicFolderResult, + ArFSUploadFileResult, + ArFSUploadPrivateFileResult, + ArFSCreateDriveResultFactory, + ArFSMoveEntityResultFactory, + ArFSUploadFileResultFactory +} from './arfs_entity_result_factory'; +import { + ArFSFileOrFolderEntity, + ArFSPrivateDrive, + ArFSPrivateFile, + ArFSPrivateFileOrFolderWithPaths, + ArFSPrivateFolder, + ArFSPublicDrive, + ArFSPublicFile, + ArFSPublicFolder +} from './arfs_entities'; +import { ArFSDAOAnonymous } from './arfsdao_anonymous'; +import { ArFSFileOrFolderBuilder } from './utils/arfs_builders/arfs_builders'; +import { PrivateKeyData } from './private_key_data'; +import { ArweaveAddress } from './arweave_address'; +import { EntityNamesAndIds, entityToNameMap, fileToNameAndIdMap, folderToNameAndIdMap } from './utils/mapper_functions'; +import { ListPrivateFolderParams } from './ardrive'; + +export const graphQLURL = 'https://arweave.net/graphql'; + +export class PrivateDriveKeyData { + private constructor(readonly driveId: DriveID, readonly driveKey: DriveKey) {} + + static async from(drivePassword: string, privateKey: JWKInterface): Promise { + const driveId = uuidv4(); + const driveKey = await deriveDriveKey(drivePassword, driveId, JSON.stringify(privateKey)); + return new PrivateDriveKeyData(driveId, driveKey); + } +} + +export interface ArFSMoveParams { + originalMetaData: O; + newParentFolderId: FolderID; + metaDataBaseReward: RewardSettings; + transactionData: T; +} + +export type GetDriveFunction = () => Promise; +export type CreateFolderFunction = (driveId: DriveID) => Promise; +export type GenerateDriveIdFn = () => DriveID; + +export type ArFSListPrivateFolderParams = Required; + +export interface UploadPublicFileParams { + parentFolderId: FolderID; + wrappedFile: ArFSFileToUpload; + driveId: DriveID; + fileDataRewardSettings: RewardSettings; + metadataRewardSettings: RewardSettings; + destFileName?: string; + existingFileId?: FileID; +} + +export interface UploadPrivateFileParams extends UploadPublicFileParams { + driveKey: DriveKey; +} +export interface CreateFolderSettings { + driveId: DriveID; + rewardSettings: RewardSettings; + parentFolderId?: FolderID; + syncParentFolderId?: boolean; + owner: ArweaveAddress; +} + +export interface CreatePublicFolderSettings extends CreateFolderSettings { + folderData: ArFSPublicFolderTransactionData; +} + +export interface CreatePrivateFolderSettings extends CreateFolderSettings { + folderData: ArFSPrivateFolderTransactionData; + driveKey: DriveKey; +} + +interface getPublicChildrenFolderIdsParams { + folderId: FolderID; + driveId: DriveID; +} +interface getPrivateChildrenFolderIdsParams extends getPublicChildrenFolderIdsParams { + driveKey: DriveKey; +} + +export class ArFSDAO extends ArFSDAOAnonymous { + // TODO: Can we abstract Arweave type(s)? + constructor( + private readonly wallet: Wallet, + arweave: Arweave, + private readonly dryRun = false, + protected appName = DEFAULT_APP_NAME, + protected appVersion = DEFAULT_APP_VERSION + ) { + super(arweave, appName, appVersion); + } + + // For generic use with public and private drives. Generic types should all be harmonious. + async createFolder( + { driveId, rewardSettings, parentFolderId, syncParentFolderId = true }: CreateFolderSettings, + getDriveFn: GetDriveFunction, + folderPrototypeFactory: FolderMetaDataFactory + ): Promise { + if (parentFolderId && syncParentFolderId) { + // Assert that drive ID is consistent with parent folder ID + const actualDriveId = await this.getDriveIdForFolderId(parentFolderId); + + if (actualDriveId !== driveId) { + throw new Error( + `Drive id: ${driveId} does not match actual drive id: ${actualDriveId} for parent folder id` + ); + } + } else if (syncParentFolderId) { + // If drive contains a root folder ID, treat this as a subfolder to the root folder + const drive = await getDriveFn(); + + if (!drive) { + throw new Error(`Drive with Drive ID ${driveId} not found!`); + } + + if (drive.rootFolderId) { + parentFolderId = drive.rootFolderId; + } + } + + // Generate a new folder ID + const folderId = uuidv4(); + + // Create a root folder metadata transaction + const folderMetadata = folderPrototypeFactory(folderId, parentFolderId); + const folderTrx = await this.prepareArFSObjectTransaction(folderMetadata, rewardSettings); + + // Execute the upload + if (!this.dryRun) { + const folderUploader = await this.arweave.transactions.getUploader(folderTrx); + while (!folderUploader.isComplete) { + await folderUploader.uploadChunk(); + } + } + + return { metaDataTrxId: folderTrx.id, metaDataTrxReward: folderTrx.reward, folderId }; + } + + // Convenience wrapper for folder creation in a known-public use case + async createPublicFolder({ + folderData, + driveId, + rewardSettings, + parentFolderId, + syncParentFolderId = true, + owner + }: CreatePublicFolderSettings): Promise { + return this.createFolder( + { driveId, rewardSettings, parentFolderId, syncParentFolderId, owner }, + () => this.getPublicDrive(driveId, owner), + (folderId, parentFolderId) => + new ArFSPublicFolderMetaDataPrototype(folderData, driveId, folderId, parentFolderId) + ); + } + + // Convenience wrapper for folder creation in a known-private use case + async createPrivateFolder({ + folderData, + driveId, + driveKey, + parentFolderId, + rewardSettings, + syncParentFolderId = true, + owner + }: CreatePrivateFolderSettings): Promise { + return this.createFolder( + { driveId, rewardSettings, parentFolderId, syncParentFolderId, owner }, + () => this.getPrivateDrive(driveId, driveKey, owner), + (folderId, parentFolderId) => + new ArFSPrivateFolderMetaDataPrototype(driveId, folderId, folderData, parentFolderId) + ); + } + + async createDrive( + driveRewardSettings: RewardSettings, + generateDriveIdFn: GenerateDriveIdFn, + createFolderFn: CreateFolderFunction, + createMetadataFn: CreateDriveMetaDataFactory, + resultFactory: ArFSCreateDriveResultFactory + ): Promise { + // Generate a new drive ID for the new drive + const driveId = generateDriveIdFn(); + + // Create root folder + const { + metaDataTrxId: rootFolderTrxId, + metaDataTrxReward: rootFolderTrxReward, + folderId: rootFolderId + } = await createFolderFn(driveId); + + // Create a drive metadata transaction + const driveMetaData = await createMetadataFn(driveId, rootFolderId); + const driveTrx = await this.prepareArFSObjectTransaction(driveMetaData, driveRewardSettings); + + // Execute the upload + if (!this.dryRun) { + const driveUploader = await this.arweave.transactions.getUploader(driveTrx); + while (!driveUploader.isComplete) { + await driveUploader.uploadChunk(); + } + } + + return resultFactory({ + metaDataTrxId: driveTrx.id, + metaDataTrxReward: driveTrx.reward, + rootFolderTrxId: rootFolderTrxId, + rootFolderTrxReward: rootFolderTrxReward, + driveId: driveId, + rootFolderId: rootFolderId + }); + } + + async createPublicDrive( + driveName: string, + driveRewardSettings: RewardSettings, + rootFolderRewardSettings: RewardSettings, + owner: ArweaveAddress + ): Promise { + return this.createDrive( + driveRewardSettings, + () => uuidv4(), + async (driveId) => { + const folderData = new ArFSPublicFolderTransactionData(driveName); + return this.createPublicFolder({ + folderData, + driveId, + rewardSettings: rootFolderRewardSettings, + syncParentFolderId: false, + owner + }); + }, + (driveId, rootFolderId) => { + return Promise.resolve( + new ArFSPublicDriveMetaDataPrototype( + new ArFSPublicDriveTransactionData(driveName, rootFolderId), + driveId + ) + ); + }, + (result) => result // No change + ); + } + + async createPrivateDrive( + driveName: string, + newDriveData: PrivateDriveKeyData, + driveRewardSettings: RewardSettings, + rootFolderRewardSettings: RewardSettings, + owner: ArweaveAddress + ): Promise { + return this.createDrive( + driveRewardSettings, + () => newDriveData.driveId, + async (driveId) => { + const folderData = await ArFSPrivateFolderTransactionData.from(driveName, newDriveData.driveKey); + return this.createPrivateFolder({ + folderData, + driveId, + rewardSettings: rootFolderRewardSettings, + syncParentFolderId: false, + driveKey: newDriveData.driveKey, + owner + }); + }, + async (driveId, rootFolderId) => { + return Promise.resolve( + new ArFSPrivateDriveMetaDataPrototype( + driveId, + await ArFSPrivateDriveTransactionData.from(driveName, rootFolderId, newDriveData.driveKey) + ) + ); + }, + (result) => { + return { ...result, driveKey: newDriveData.driveKey }; // Add drive key for private return type + } + ); + } + + async moveEntity( + metaDataBaseReward: RewardSettings, + metaDataFactory: MoveEntityMetaDataFactory, + resultFactory: ArFSMoveEntityResultFactory + ): Promise { + const metadataPrototype = metaDataFactory(); + + // Prepare meta data transaction + const metaDataTrx = await this.prepareArFSObjectTransaction(metadataPrototype, metaDataBaseReward); + + // Upload meta data + if (!this.dryRun) { + const metaDataUploader = await this.arweave.transactions.getUploader(metaDataTrx); + while (!metaDataUploader.isComplete) { + await metaDataUploader.uploadChunk(); + } + } + + return resultFactory({ metaDataTrxId: metaDataTrx.id, metaDataTrxReward: metaDataTrx.reward }); + } + + async movePublicFile({ + metaDataBaseReward, + originalMetaData, + transactionData, + newParentFolderId + }: ArFSMoveParams): Promise { + return this.moveEntity( + metaDataBaseReward, + () => { + return new ArFSPublicFileMetaDataPrototype( + transactionData, + originalMetaData.driveId, + originalMetaData.fileId, + newParentFolderId + ); + }, + (results) => { + return { ...results, dataTrxId: originalMetaData.dataTxId }; + } + ); + } + + async movePrivateFile({ + metaDataBaseReward, + originalMetaData, + transactionData, + newParentFolderId + }: ArFSMoveParams): Promise { + return this.moveEntity( + metaDataBaseReward, + () => { + return new ArFSPrivateFileMetaDataPrototype( + transactionData, + originalMetaData.driveId, + originalMetaData.fileId, + newParentFolderId + ); + }, + (results) => { + return { ...results, dataTrxId: originalMetaData.dataTxId, fileKey: transactionData.fileKey }; + } + ); + } + + async movePublicFolder({ + metaDataBaseReward, + originalMetaData, + transactionData, + newParentFolderId + }: ArFSMoveParams): Promise { + return this.moveEntity( + metaDataBaseReward, + () => { + return new ArFSPublicFolderMetaDataPrototype( + transactionData, + originalMetaData.driveId, + originalMetaData.entityId, + newParentFolderId + ); + }, + (results) => results + ); + } + + async movePrivateFolder({ + metaDataBaseReward, + originalMetaData, + transactionData, + newParentFolderId + }: ArFSMoveParams): Promise { + return this.moveEntity( + metaDataBaseReward, + () => { + return new ArFSPrivateFolderMetaDataPrototype( + originalMetaData.driveId, + originalMetaData.entityId, + transactionData, + newParentFolderId + ); + }, + (results) => { + return { ...results, driveKey: transactionData.driveKey }; + } + ); + } + + async uploadFile( + wrappedFile: ArFSFileToUpload, + fileDataRewardSettings: RewardSettings, + metadataRewardSettings: RewardSettings, + dataPrototypeFactoryFn: FileDataPrototypeFactory, + metadataTrxDataFactoryFn: FileMetadataTrxDataFactory, + metadataFactoryFn: FileMetaDataFactory, + resultFactoryFn: ArFSUploadFileResultFactory, + destFileName?: string, + existingFileId?: FileID + ): Promise { + // Establish destination file name + const destinationFileName = destFileName ?? wrappedFile.getBaseFileName(); + + // Use existing file ID (create a revision) or generate new file ID + const fileId = existingFileId ?? uuidv4(); + + // Gather file information + const { fileSize, dataContentType, lastModifiedDateMS } = wrappedFile.gatherFileInfo(); + + // Read file data into memory + const fileData = wrappedFile.getFileDataBuffer(); + + // Build file data transaction + const fileDataPrototype = await dataPrototypeFactoryFn(fileData, dataContentType, fileId); + const dataTrx = await this.prepareArFSObjectTransaction(fileDataPrototype, fileDataRewardSettings); + + // Upload file data + if (!this.dryRun) { + const dataUploader = await this.arweave.transactions.getUploader(dataTrx); + while (!dataUploader.isComplete) { + await dataUploader.uploadChunk(); + } + } + + // Prepare meta data transaction + const metadataTrxData = await metadataTrxDataFactoryFn( + destinationFileName, + fileSize, + lastModifiedDateMS, + dataTrx.id, + dataContentType, + fileId + ); + const fileMetadata = metadataFactoryFn(metadataTrxData, fileId); + const metaDataTrx = await this.prepareArFSObjectTransaction(fileMetadata, metadataRewardSettings); + + // Upload meta data + if (!this.dryRun) { + const metaDataUploader = await this.arweave.transactions.getUploader(metaDataTrx); + while (!metaDataUploader.isComplete) { + await metaDataUploader.uploadChunk(); + } + } + + return resultFactoryFn( + { + dataTrxId: dataTrx.id, + dataTrxReward: dataTrx.reward, + metaDataTrxId: metaDataTrx.id, + metaDataTrxReward: metaDataTrx.reward, + fileId + }, + metadataTrxData + ); + } + + async uploadPublicFile({ + parentFolderId, + wrappedFile, + driveId, + fileDataRewardSettings, + metadataRewardSettings, + destFileName, + existingFileId + }: UploadPublicFileParams): Promise { + return this.uploadFile( + wrappedFile, + fileDataRewardSettings, + metadataRewardSettings, + async (fileData, dataContentType) => { + return new ArFSPublicFileDataPrototype( + new ArFSPublicFileDataTransactionData(fileData), + dataContentType + ); + }, + async (destinationFileName, fileSize, lastModifiedDateMS, dataTrxId, dataContentType) => { + return new ArFSPublicFileMetadataTransactionData( + destinationFileName, + fileSize, + lastModifiedDateMS, + dataTrxId, + dataContentType + ); + }, + (metadataTrxData, fileId) => { + return new ArFSPublicFileMetaDataPrototype(metadataTrxData, driveId, fileId, parentFolderId); + }, + (result) => result, // no change + destFileName, + existingFileId + ); + } + + async uploadPrivateFile({ + parentFolderId, + wrappedFile, + driveId, + driveKey, + fileDataRewardSettings, + metadataRewardSettings, + destFileName, + existingFileId + }: UploadPrivateFileParams): Promise { + return this.uploadFile( + wrappedFile, + fileDataRewardSettings, + metadataRewardSettings, + async (fileData, _dataContentType, fileId) => { + const trxData = await ArFSPrivateFileDataTransactionData.from(fileData, fileId, driveKey); + return new ArFSPrivateFileDataPrototype(trxData); + }, + async (destinationFileName, fileSize, lastModifiedDateMS, dataTrxId, dataContentType, fileId) => { + return await ArFSPrivateFileMetadataTransactionData.from( + destinationFileName, + fileSize, + lastModifiedDateMS, + dataTrxId, + dataContentType, + fileId, + driveKey + ); + }, + (metadataTrxData, fileId) => { + return new ArFSPrivateFileMetaDataPrototype(metadataTrxData, driveId, fileId, parentFolderId); + }, + (result, trxData) => { + return { ...result, fileKey: trxData.fileKey }; // add the file key to the result data + }, + destFileName, + existingFileId + ); + } + + // /** + // * Uploads a v2 transaction in chunks with progress logging + // * + // * @example await this.sendChunkedUpload(myTransaction); + // */ + // async sendChunkedUploadWithProgress(trx: Transaction): Promise { + // const dataUploader = await this.arweave.transactions.getUploader(trx); + + // while (!dataUploader.isComplete) { + // const nextChunk = await uploadDataChunk(dataUploader); + // if (nextChunk === null) { + // break; + // } else { + // // TODO: Add custom logger function that produces various levels of detail + // console.log( + // `${dataUploader.pctComplete}% complete, ${dataUploader.uploadedChunks}/${dataUploader.totalChunks}` + // ); + // } + // } + // } + + async prepareArFSObjectTransaction( + objectMetaData: ArFSObjectMetadataPrototype, + rewardSettings: RewardSettings = {}, + otherTags: GQLTagInterface[] = [] + ): Promise { + const wallet = this.wallet as JWKWallet; + + // Create transaction + const trxAttributes: Partial = { + data: objectMetaData.objectData.asTransactionData() + }; + + // If we provided our own reward setting, use it now + if (rewardSettings.reward) { + trxAttributes.reward = rewardSettings.reward; + } + + // TODO: Use a mock arweave server instead + if (process.env.NODE_ENV === 'test') { + trxAttributes.last_tx = 'STUB'; + } + + const transaction = await this.arweave.createTransaction(trxAttributes, wallet.getPrivateKey()); + + // If we've opted to boost the transaction, do so now + if (rewardSettings.feeMultiple && rewardSettings.feeMultiple > 1.0) { + // Round up with ceil because fractional Winston will cause an Arweave API failure + transaction.reward = Math.ceil(+transaction.reward * rewardSettings.feeMultiple).toString(); + } + + // Add baseline ArFS Tags + transaction.addTag('App-Name', this.appName); + transaction.addTag('App-Version', this.appVersion); + transaction.addTag('ArFS', CURRENT_ARFS_VERSION); + if (rewardSettings.feeMultiple && rewardSettings.feeMultiple > 1.0) { + transaction.addTag('Boost', rewardSettings.feeMultiple.toString()); + } + + // Add object-specific tags + objectMetaData.addTagsToTransaction(transaction); + + // Enforce that other tags are not protected + objectMetaData.assertProtectedTags(otherTags); + otherTags.forEach((tag) => { + transaction.addTag(tag.name, tag.value); + }); + + // Sign the transaction + await this.arweave.transactions.sign(transaction, wallet.getPrivateKey()); + return transaction; + } + + // Convenience function for known-private use cases + async getPrivateDrive(driveId: DriveID, driveKey: DriveKey, owner: ArweaveAddress): Promise { + return new ArFSPrivateDriveBuilder({ entityId: driveId, arweave: this.arweave, key: driveKey, owner }).build(); + } + + // Convenience function for known-private use cases + async getPrivateFolder(folderId: FolderID, driveKey: DriveKey, owner: ArweaveAddress): Promise { + return new ArFSPrivateFolderBuilder(folderId, this.arweave, driveKey, owner).build(); + } + + async getPrivateFile(fileId: FileID, driveKey: DriveKey, owner: ArweaveAddress): Promise { + return new ArFSPrivateFileBuilder(fileId, this.arweave, driveKey, owner).build(); + } + + async getAllFoldersOfPrivateDrive( + driveId: DriveID, + driveKey: DriveKey, + latestRevisionsOnly = false + ): Promise { + let cursor = ''; + let hasNextPage = true; + const allFolders: ArFSPrivateFolder[] = []; + + while (hasNextPage) { + const gqlQuery = buildQuery({ + tags: [ + { name: 'Drive-Id', value: driveId }, + { name: 'Entity-Type', value: 'folder' } + ], + cursor + }); + + const response = await this.arweave.api.post(graphQLURL, gqlQuery); + const { data } = response.data; + const { transactions } = data; + const { edges } = transactions; + hasNextPage = transactions.pageInfo.hasNextPage; + + const folders: Promise[] = edges.map(async (edge: GQLEdgeInterface) => { + cursor = edge.cursor; + const { node } = edge; + const folderBuilder = await ArFSPrivateFolderBuilder.fromArweaveNode(node, this.arweave, driveKey); + return await folderBuilder.build(node); + }); + allFolders.push(...(await Promise.all(folders))); + } + return latestRevisionsOnly ? allFolders.filter(latestRevisionFilter) : allFolders; + } + + async getPrivateFilesWithParentFolderIds( + folderIDs: FolderID[], + driveKey: DriveKey, + latestRevisionsOnly = false + ): Promise { + let cursor = ''; + let hasNextPage = true; + const allFiles: ArFSPrivateFile[] = []; + + while (hasNextPage) { + const gqlQuery = buildQuery({ + tags: [ + { name: 'Parent-Folder-Id', value: folderIDs }, + { name: 'Entity-Type', value: 'file' } + ], + cursor + }); + + const response = await this.arweave.api.post(graphQLURL, gqlQuery); + const { data } = response.data; + const { transactions } = data; + const { edges } = transactions; + hasNextPage = transactions.pageInfo.hasNextPage; + const files: Promise[] = edges.map(async (edge: GQLEdgeInterface) => { + const { node } = edge; + cursor = edge.cursor; + const fileBuilder = await ArFSPrivateFileBuilder.fromArweaveNode(node, this.arweave, driveKey); + return await fileBuilder.build(node); + }); + allFiles.push(...(await Promise.all(files))); + } + return latestRevisionsOnly ? allFiles.filter(latestRevisionFilter) : allFiles; + } + + async getEntitiesInFolder( + parentFolderId: FolderID, + builder: ( + node: GQLNodeInterface, + entityType: 'file' | 'folder' + ) => ArFSFileOrFolderBuilder, + latestRevisionsOnly = true, + filterOnOwner = true + ): Promise { + let cursor = ''; + let hasNextPage = true; + const allEntities: ArFSFileOrFolderEntity[] = []; + + // TODO: Derive the owner of a wallet from earliest transaction of a drive by default + const owner = await this.wallet.getAddress(); + + while (hasNextPage) { + const gqlQuery = buildQuery({ + tags: [ + { name: 'Parent-Folder-Id', value: parentFolderId }, + { name: 'Entity-Type', value: ['file', 'folder'] } + ], + cursor, + owner: filterOnOwner ? owner : undefined + }); + + const response = await this.arweave.api.post(graphQLURL, gqlQuery); + const { data } = response.data; + const { transactions } = data; + const { edges } = transactions; + hasNextPage = transactions.pageInfo.hasNextPage; + + const folders: Promise[] = edges.map(async (edge: GQLEdgeInterface) => { + const { node } = edge; + cursor = edge.cursor; + const { tags } = node; + + // Check entityType to determine which builder to use + const entityType = tags.find((t) => t.name === 'Entity-Type')?.value; + if (!entityType || (entityType !== 'file' && entityType !== 'folder')) { + throw new Error('Entity-Type tag is missing or invalid!'); + } + + return builder(node, entityType).build(node); + }); + + allEntities.push(...(await Promise.all(folders))); + } + return latestRevisionsOnly ? allEntities.filter(latestRevisionFilter) : allEntities; + } + + async getPrivateEntitiesInFolder( + parentFolderId: FolderID, + driveKey: DriveKey, + latestRevisionsOnly = true + ): Promise { + return this.getEntitiesInFolder( + parentFolderId, + (node, entityType) => + entityType === 'folder' + ? ArFSPrivateFolderBuilder.fromArweaveNode(node, this.arweave, driveKey) + : ArFSPrivateFileBuilder.fromArweaveNode(node, this.arweave, driveKey), + latestRevisionsOnly + ); + } + + async getPublicEntitiesInFolder( + parentFolderId: FolderID, + latestRevisionsOnly = true + ): Promise { + return this.getEntitiesInFolder( + parentFolderId, + (node, entityType) => + entityType === 'folder' + ? ArFSPublicFolderBuilder.fromArweaveNode(node, this.arweave) + : ArFSPublicFileBuilder.fromArweaveNode(node, this.arweave), + latestRevisionsOnly + ); + } + + async getChildrenFolderIds( + folderId: FolderID, + allFolderEntitiesOfDrive: ArFSFileOrFolderEntity[] + ): Promise { + const hierarchy = FolderHierarchy.newFromEntities(allFolderEntitiesOfDrive); + return hierarchy.folderIdSubtreeFromFolderId(folderId, Number.MAX_SAFE_INTEGER); + } + + async getPrivateEntityNamesInFolder(folderId: FolderID, driveKey: DriveKey): Promise { + const childrenOfFolder = await this.getPrivateEntitiesInFolder(folderId, driveKey, true); + return childrenOfFolder.map(entityToNameMap); + } + + async getPublicEntityNamesInFolder(folderId: FolderID): Promise { + const childrenOfFolder = await this.getPublicEntitiesInFolder(folderId, true); + return childrenOfFolder.map(entityToNameMap); + } + + async getPublicEntityNamesAndIdsInFolder(folderId: FolderID): Promise { + const childrenOfFolder = await this.getPublicEntitiesInFolder(folderId, true); + return { + files: childrenOfFolder.filter(fileFilter).map(fileToNameAndIdMap), + folders: childrenOfFolder.filter(folderFilter).map(folderToNameAndIdMap) + }; + } + + async getPrivateEntityNamesAndIdsInFolder(folderId: FolderID, driveKey: DriveKey): Promise { + const childrenOfFolder = await this.getPrivateEntitiesInFolder(folderId, driveKey, true); + return { + files: childrenOfFolder.filter(fileFilter).map(fileToNameAndIdMap), + folders: childrenOfFolder.filter(folderFilter).map(folderToNameAndIdMap) + }; + } + + async getPrivateChildrenFolderIds({ + folderId, + driveId, + driveKey + }: getPrivateChildrenFolderIdsParams): Promise { + return this.getChildrenFolderIds(folderId, await this.getAllFoldersOfPrivateDrive(driveId, driveKey, true)); + } + async getPublicChildrenFolderIds({ folderId, driveId }: getPublicChildrenFolderIdsParams): Promise { + return this.getChildrenFolderIds(folderId, await this.getAllFoldersOfPublicDrive(driveId, true)); + } + + /** + * Lists the children of certain private folder + * @param {FolderID} folderId the folder ID to list children of + * @param {DriveKey} driveKey the drive key used for drive and folder data decryption and file key derivation + * @param {number} maxDepth a non-negative integer value indicating the depth of the folder tree to list where 0 = this folder's contents only + * @param {boolean} includeRoot whether or not folderId's folder data should be included in the listing + * @returns {ArFSPrivateFileOrFolderWithPaths[]} an array representation of the children and parent folder + */ + async listPrivateFolder({ + folderId, + driveKey, + maxDepth, + includeRoot, + owner + }: ArFSListPrivateFolderParams): Promise { + if (!Number.isInteger(maxDepth) || maxDepth < 0) { + throw new Error('maxDepth should be a non-negative integer!'); + } + + const folder = await this.getPrivateFolder(folderId, driveKey, owner); + + // Fetch all of the folder entities within the drive + const driveIdOfFolder = folder.driveId; + const allFolderEntitiesOfDrive = await this.getAllFoldersOfPrivateDrive(driveIdOfFolder, driveKey, true); + + const hierarchy = FolderHierarchy.newFromEntities(allFolderEntitiesOfDrive); + const searchFolderIDs = hierarchy.folderIdSubtreeFromFolderId(folderId, maxDepth - 1); + const [, ...subFolderIDs]: FolderID[] = hierarchy.folderIdSubtreeFromFolderId(folderId, maxDepth); + + const childrenFolderEntities = allFolderEntitiesOfDrive.filter((folder) => + subFolderIDs.includes(folder.entityId) + ); + + if (includeRoot) { + childrenFolderEntities.unshift(folder); + } + + // Fetch all file entities within all Folders of the drive + const childrenFileEntities = await this.getPrivateFilesWithParentFolderIds(searchFolderIDs, driveKey, true); + + const children = [...childrenFolderEntities, ...childrenFileEntities]; + + const entitiesWithPath = children.map((entity) => new ArFSPrivateFileOrFolderWithPaths(entity, hierarchy)); + return entitiesWithPath; + } + + async assertValidPassword(password: string): Promise { + const wallet = this.wallet; + const walletAddress = await wallet.getAddress(); + const query = buildQuery({ + tags: [ + { name: 'Entity-Type', value: 'drive' }, + { name: 'Drive-Privacy', value: 'private' } + ], + owner: walletAddress, + sort: ASCENDING_ORDER + }); + const response = await this.arweave.api.post(graphQLURL, query); + const { data } = response.data; + const { transactions } = data; + const { edges } = transactions; + if (!edges.length) { + // No drive has been created for this wallet + return; + } + const { node }: { node: GQLNodeInterface } = edges[0]; + const safeDriveBuilder = SafeArFSDriveBuilder.fromArweaveNode( + node, + this.arweave, + new PrivateKeyData({ password, wallet: this.wallet as JWKWallet }) + ); + const safelyBuiltDrive = await safeDriveBuilder.build(); + if ( + safelyBuiltDrive.name === ENCRYPTED_DATA_PLACEHOLDER || + safelyBuiltDrive.rootFolderId === ENCRYPTED_DATA_PLACEHOLDER + ) { + throw new Error(`Invalid password! Please type the same as your other private drives!`); + } + } +} diff --git a/src/arfsdao_anonymous.ts b/src/arfsdao_anonymous.ts new file mode 100644 index 00000000..169a14a6 --- /dev/null +++ b/src/arfsdao_anonymous.ts @@ -0,0 +1,248 @@ +/* eslint-disable no-console */ +import Arweave from 'arweave'; +import { ArFSDriveEntity, GQLEdgeInterface } from 'ardrive-core-js'; +import { ASCENDING_ORDER, buildQuery } from './query'; +import { DriveID, FolderID, FileID, DEFAULT_APP_NAME, DEFAULT_APP_VERSION, EntityID } from './types'; +import { latestRevisionFilter, latestRevisionFilterForDrives } from './utils/filter_methods'; +import { FolderHierarchy } from './folderHierarchy'; +import { ArFSPublicDriveBuilder, SafeArFSDriveBuilder } from './utils/arfs_builders/arfs_drive_builders'; +import { ArFSPublicFolderBuilder } from './utils/arfs_builders/arfs_folder_builders'; +import { ArFSPublicFileBuilder } from './utils/arfs_builders/arfs_file_builders'; +import { ArFSPublicDrive, ArFSPublicFile, ArFSPublicFileOrFolderWithPaths, ArFSPublicFolder } from './arfs_entities'; +import { PrivateKeyData } from './private_key_data'; +import { ArweaveAddress } from './arweave_address'; + +export const graphQLURL = 'https://arweave.net/graphql'; + +export interface ArFSListPublicFolderParams { + folderId: FolderID; + maxDepth: number; + includeRoot: boolean; + owner: ArweaveAddress; +} + +export abstract class ArFSDAOType { + protected abstract readonly arweave: Arweave; + protected abstract readonly appName: string; + protected abstract readonly appVersion: string; +} + +/** + * Performs all ArFS spec operations that do NOT require a wallet for signing or decryption + */ +export class ArFSDAOAnonymous extends ArFSDAOType { + constructor( + protected readonly arweave: Arweave, + protected appName = DEFAULT_APP_NAME, + protected appVersion = DEFAULT_APP_VERSION + ) { + super(); + } + + public async getOwnerForDriveId(driveId: DriveID): Promise { + const gqlQuery = buildQuery({ tags: [{ name: 'Drive-Id', value: driveId }], sort: ASCENDING_ORDER }); + const response = await this.arweave.api.post(graphQLURL, gqlQuery); + const edges: GQLEdgeInterface[] = response.data.data.transactions.edges; + + if (!edges.length) { + throw new Error(`Could not find a transaction with "Drive-Id": ${driveId}`); + } + + const edgeOfFirstDrive = edges[0]; + const driveOwnerAddress = edgeOfFirstDrive.node.owner.address; + + return new ArweaveAddress(driveOwnerAddress); + } + + async getDriveIDForEntityId(entityId: EntityID, gqlTypeTag: 'File-Id' | 'Folder-Id'): Promise { + const gqlQuery = buildQuery({ tags: [{ name: gqlTypeTag, value: entityId }] }); + + const response = await this.arweave.api.post(graphQLURL, gqlQuery); + const { data } = response.data; + const { transactions } = data; + + const edges: GQLEdgeInterface[] = transactions.edges; + + if (!edges.length) { + throw new Error(`Entity with ${gqlTypeTag} ${entityId} not found!`); + } + + const driveIdTag = edges[0].node.tags.find((t) => t.name === 'Drive-Id'); + if (driveIdTag) { + return driveIdTag.value; + } + + throw new Error(`No Drive-Id tag found for meta data transaction of ${gqlTypeTag}: ${entityId}`); + } + + async getDriveOwnerForFolderId(folderId: FolderID): Promise { + return this.getOwnerForDriveId(await this.getDriveIdForFolderId(folderId)); + } + + async getDriveOwnerForFileId(fileId: FileID): Promise { + return this.getOwnerForDriveId(await this.getDriveIdForFileId(fileId)); + } + + async getDriveIdForFileId(fileId: FileID): Promise { + return this.getDriveIDForEntityId(fileId, 'File-Id'); + } + + async getDriveIdForFolderId(folderId: FolderID): Promise { + return this.getDriveIDForEntityId(folderId, 'Folder-Id'); + } + + // Convenience function for known-public use cases + async getPublicDrive(driveId: DriveID, owner: ArweaveAddress): Promise { + return new ArFSPublicDriveBuilder({ entityId: driveId, arweave: this.arweave, owner }).build(); + } + + // Convenience function for known-private use cases + async getPublicFolder(folderId: FolderID, owner: ArweaveAddress): Promise { + return new ArFSPublicFolderBuilder({ entityId: folderId, arweave: this.arweave, owner }).build(); + } + + async getPublicFile(fileId: FileID, owner: ArweaveAddress): Promise { + return new ArFSPublicFileBuilder({ entityId: fileId, arweave: this.arweave, owner }).build(); + } + + async getAllDrivesForAddress( + address: ArweaveAddress, + privateKeyData: PrivateKeyData, + latestRevisionsOnly = true + ): Promise { + let cursor = ''; + let hasNextPage = true; + const allDrives: ArFSDriveEntity[] = []; + + while (hasNextPage) { + const gqlQuery = buildQuery({ tags: [{ name: 'Entity-Type', value: 'drive' }], cursor, owner: address }); + + const response = await this.arweave.api.post(graphQLURL, gqlQuery); + const { data } = response.data; + const { transactions } = data; + const { edges } = transactions; + hasNextPage = transactions.pageInfo.hasNextPage; + + const drives: Promise[] = edges.map(async (edge: GQLEdgeInterface) => { + const { node } = edge; + cursor = edge.cursor; + + const driveBuilder = SafeArFSDriveBuilder.fromArweaveNode(node, this.arweave, privateKeyData); + + return driveBuilder.build(node); + }); + + allDrives.push(...(await Promise.all(drives))); + } + + return latestRevisionsOnly ? allDrives.filter(latestRevisionFilterForDrives) : allDrives; + } + + async getPublicFilesWithParentFolderIds( + folderIDs: FolderID[], + latestRevisionsOnly = false + ): Promise { + let cursor = ''; + let hasNextPage = true; + const allFiles: ArFSPublicFile[] = []; + + while (hasNextPage) { + const gqlQuery = buildQuery({ + tags: [ + { name: 'Parent-Folder-Id', value: folderIDs }, + { name: 'Entity-Type', value: 'file' } + ], + cursor + }); + + const response = await this.arweave.api.post(graphQLURL, gqlQuery); + const { data } = response.data; + const { transactions } = data; + const { edges } = transactions; + hasNextPage = transactions.pageInfo.hasNextPage; + const files: Promise[] = edges.map(async (edge: GQLEdgeInterface) => { + const { node } = edge; + cursor = edge.cursor; + const fileBuilder = ArFSPublicFileBuilder.fromArweaveNode(node, this.arweave); + return fileBuilder.build(node); + }); + allFiles.push(...(await Promise.all(files))); + } + return latestRevisionsOnly ? allFiles.filter(latestRevisionFilter) : allFiles; + } + + async getAllFoldersOfPublicDrive(driveId: DriveID, latestRevisionsOnly = false): Promise { + let cursor = ''; + let hasNextPage = true; + const allFolders: ArFSPublicFolder[] = []; + + while (hasNextPage) { + const gqlQuery = buildQuery({ + tags: [ + { name: 'Drive-Id', value: driveId }, + { name: 'Entity-Type', value: 'folder' } + ], + cursor + }); + + const response = await this.arweave.api.post(graphQLURL, gqlQuery); + const { data } = response.data; + const { transactions } = data; + const { edges } = transactions; + hasNextPage = transactions.pageInfo.hasNextPage; + const folders: Promise[] = edges.map(async (edge: GQLEdgeInterface) => { + const { node } = edge; + cursor = edge.cursor; + const folderBuilder = ArFSPublicFolderBuilder.fromArweaveNode(node, this.arweave); + return await folderBuilder.build(node); + }); + allFolders.push(...(await Promise.all(folders))); + } + return latestRevisionsOnly ? allFolders.filter(latestRevisionFilter) : allFolders; + } + + /** + * Lists the children of certain public folder + * @param {FolderID} folderId the folder ID to list children of + * @param {number} maxDepth a non-negative integer value indicating the depth of the folder tree to list where 0 = this folder's contents only + * @param {boolean} includeRoot whether or not folderId's folder data should be included in the listing + * @returns {ArFSPublicFileOrFolderWithPaths[]} an array representation of the children and parent folder + */ + async listPublicFolder({ + folderId, + maxDepth, + includeRoot, + owner + }: ArFSListPublicFolderParams): Promise { + if (!Number.isInteger(maxDepth) || maxDepth < 0) { + throw new Error('maxDepth should be a non-negative integer!'); + } + + const folder = await this.getPublicFolder(folderId, owner); + + // Fetch all of the folder entities within the drive + const driveIdOfFolder = folder.driveId; + const allFolderEntitiesOfDrive = await this.getAllFoldersOfPublicDrive(driveIdOfFolder, true); + + // Feed entities to FolderHierarchy + const hierarchy = FolderHierarchy.newFromEntities(allFolderEntitiesOfDrive); + const searchFolderIDs = hierarchy.folderIdSubtreeFromFolderId(folderId, maxDepth - 1); + const [, ...subFolderIDs]: FolderID[] = hierarchy.folderIdSubtreeFromFolderId(folderId, maxDepth); + + const childrenFolderEntities = allFolderEntitiesOfDrive.filter((folder) => + subFolderIDs.includes(folder.entityId) + ); + + if (includeRoot) { + childrenFolderEntities.unshift(folder); + } + + // Fetch all file entities within all Folders of the drive + const childrenFileEntities = await this.getPublicFilesWithParentFolderIds(searchFolderIDs, true); + + const children = [...childrenFolderEntities, ...childrenFileEntities]; + + const entitiesWithPath = children.map((entity) => new ArFSPublicFileOrFolderWithPaths(entity, hierarchy)); + return entitiesWithPath; + } +} diff --git a/src/arweave_address.test.ts b/src/arweave_address.test.ts new file mode 100644 index 00000000..7ca718dd --- /dev/null +++ b/src/arweave_address.test.ts @@ -0,0 +1,42 @@ +import { expect } from 'chai'; +import { ArweaveAddress } from './arweave_address'; + +describe('The ArweaveAddress class', () => { + describe('constructor', () => { + it('creates a new address when given a valid address string', () => { + const validAddresses = [ + '-------------------------------------------', + '___________________________________________', + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + 'ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ', + '0000000000000000000000000000000000000000000', + '0123456789012345678901234567890123456789012', + 'g1hzNXVbh2M6LMQSUYp7HgkgxdadYqYEfw-HAajlms0' + ]; + validAddresses.forEach((address) => { + expect(() => new ArweaveAddress(address)).to.not.throw(); + }); + }); + + it('throws an error for input addresses that are not 43 characters in length', () => { + const invalidAddresses = ['', '-', 'g1hzNXVbh2M6LMQSUYp7HgkgxdadYqYEfw-HAajlms01']; + invalidAddresses.forEach((badAddress) => { + expect(() => new ArweaveAddress(badAddress)).to.throw(Error); + }); + }); + + it('throws an error for input addresses with invalid characters', () => { + const invalidAddresses = '!@#$%^&*()+=~`{[}]\\|;:\'"<,>.?/'.split('').map((char) => char.repeat(43)); + invalidAddresses.forEach((badAddress) => { + expect(() => new ArweaveAddress(badAddress)).to.throw(Error); + }); + }); + }); + + describe('interpolated toString function', () => { + it('returns the underlying address string', () => { + const address = new ArweaveAddress('g1hzNXVbh2M6LMQSUYp7HgkgxdadYqYEfw-HAajlms0'); + expect(`${address}`).to.equal('g1hzNXVbh2M6LMQSUYp7HgkgxdadYqYEfw-HAajlms0'); + }); + }); +}); diff --git a/src/arweave_address.ts b/src/arweave_address.ts new file mode 100644 index 00000000..eac227ab --- /dev/null +++ b/src/arweave_address.ts @@ -0,0 +1,21 @@ +export class ArweaveAddress { + constructor(private readonly address: string) { + if (!address.match(new RegExp('^[a-zA-Z0-9_-]{43}$'))) { + throw new Error( + 'Arweave addresses must be 43 characters in length with characters in the following set: [a-zA-Z0-9_-]' + ); + } + } + + equalsAddress(other: ArweaveAddress): boolean { + return this.address === other.address; + } + + toString(): string { + return this.address; + } + + valueOf(): string { + return this.address; + } +} diff --git a/src/commands/create_drive.ts b/src/commands/create_drive.ts new file mode 100644 index 00000000..5f060a23 --- /dev/null +++ b/src/commands/create_drive.ts @@ -0,0 +1,41 @@ +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { + BoostParameter, + DriveCreationPrivacyParameters, + DriveNameParameter, + DryRunParameter +} from '../parameter_declarations'; +import { arDriveFactory } from '..'; +import { JWKWallet, Wallet } from '../wallet'; +import { FeeMultiple } from '../types'; +import { PrivateDriveKeyData } from '../arfsdao'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; + +new CLICommand({ + name: 'create-drive', + parameters: [...DriveCreationPrivacyParameters, DriveNameParameter, BoostParameter, DryRunParameter], + async action(options) { + const parameters = new ParametersHelper(options); + const wallet: Wallet = await parameters.getRequiredWallet(); + + const ardrive = arDriveFactory({ + wallet: wallet, + feeMultiple: options.boost as FeeMultiple, + dryRun: options.dryRun + }); + const createDriveResult = await (async function () { + if (await parameters.getIsPrivate()) { + const drivePassword = await parameters.getDrivePassword(true); + const walletPrivateKey = (wallet as JWKWallet).getPrivateKey(); + const newDriveData = await PrivateDriveKeyData.from(drivePassword, walletPrivateKey); + await ardrive.assertValidPassword(drivePassword); + return ardrive.createPrivateDrive(options.driveName, newDriveData); + } else { + return ardrive.createPublicDrive(options.driveName); + } + })(); + console.log(JSON.stringify(createDriveResult, null, 4)); + + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/create_folder.ts b/src/commands/create_folder.ts new file mode 100644 index 00000000..f6ac3758 --- /dev/null +++ b/src/commands/create_folder.ts @@ -0,0 +1,54 @@ +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { + BoostParameter, + FolderNameParameter, + DryRunParameter, + ParentFolderIdParameter, + DrivePrivacyParameters +} from '../parameter_declarations'; +import { arDriveFactory } from '..'; +import { Wallet } from '../wallet'; +import { FeeMultiple } from '../types'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; + +new CLICommand({ + name: 'create-folder', + parameters: [ + ParentFolderIdParameter, + FolderNameParameter, + BoostParameter, + DryRunParameter, + ...DrivePrivacyParameters + ], + async action(options) { + const parameters = new ParametersHelper(options); + const wallet: Wallet = await parameters.getRequiredWallet(); + + const ardrive = arDriveFactory({ + wallet: wallet, + feeMultiple: options.boost as FeeMultiple, + dryRun: options.dryRun + }); + + const parentFolderId = parameters.getRequiredParameterValue(ParentFolderIdParameter); + const driveId = await ardrive.getDriveIdForFolderId(options.parentFolderId); + const folderName = options.folderName; + + const createFolderResult = await (async function () { + if (await parameters.getIsPrivate()) { + const driveKey = await parameters.getDriveKey({ driveId }); + return ardrive.createPrivateFolder({ + folderName, + driveId, + driveKey, + parentFolderId + }); + } else { + return ardrive.createPublicFolder({ folderName, driveId, parentFolderId }); + } + })(); + console.log(JSON.stringify(createFolderResult, null, 4)); + + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/drive_info.ts b/src/commands/drive_info.ts new file mode 100644 index 00000000..90c3548a --- /dev/null +++ b/src/commands/drive_info.ts @@ -0,0 +1,36 @@ +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { DriveID } from '../types'; +import { DriveIdParameter, GetAllRevisionsParameter, DrivePrivacyParameters } from '../parameter_declarations'; +import { arDriveAnonymousFactory, arDriveFactory } from '..'; +import { ArFSPrivateDrive, ArFSPublicDrive } from '../arfs_entities'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; + +new CLICommand({ + name: 'drive-info', + parameters: [DriveIdParameter, GetAllRevisionsParameter, ...DrivePrivacyParameters], + async action(options) { + const parameters = new ParametersHelper(options); + + const driveId: DriveID = options.driveId; + // const shouldGetAllRevisions: boolean = options.getAllRevisions; + + const result: Partial = await (async function () { + if (await parameters.getIsPrivate()) { + const wallet = await parameters.getRequiredWallet(); + const arDrive = arDriveFactory({ wallet: wallet }); + const driveKey = await parameters.getDriveKey({ driveId }); + + return arDrive.getPrivateDrive(driveId, driveKey /*, shouldGetAllRevisions*/); + } else { + const arDrive = arDriveAnonymousFactory(); + return arDrive.getPublicDrive(driveId /*, shouldGetAllRevisions*/); + } + })(); + + // TODO: Fix base types so deleting un-used values is not necessary; Tickets: PE-525 + PE-556 + delete result.syncStatus; + + console.log(JSON.stringify(result, null, 4)); + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/file_info.ts b/src/commands/file_info.ts new file mode 100644 index 00000000..9e04d851 --- /dev/null +++ b/src/commands/file_info.ts @@ -0,0 +1,40 @@ +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { GetAllRevisionsParameter, FileIdParameter, DrivePrivacyParameters } from '../parameter_declarations'; +import { arDriveAnonymousFactory, arDriveFactory, cliWalletDao } from '..'; +import { FileID } from '../types'; +import { ArFSPrivateFile, ArFSPublicFile } from '../arfs_entities'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; + +new CLICommand({ + name: 'file-info', + parameters: [FileIdParameter, GetAllRevisionsParameter, ...DrivePrivacyParameters], + async action(options) { + const parameters = new ParametersHelper(options, cliWalletDao); + const fileId: FileID = parameters.getRequiredParameterValue(FileIdParameter); + // const shouldGetAllRevisions: boolean = options.getAllRevisions; + + const result: Partial = await (async function () { + if (await parameters.getIsPrivate()) { + const wallet = await parameters.getRequiredWallet(); + const arDrive = arDriveFactory({ wallet: wallet }); + const driveId = await arDrive.getDriveIdForFileId(fileId); + + const driveKey = await parameters.getDriveKey({ driveId }); + + // We have the drive id from deriving a key, we can derive the owner + const driveOwner = await arDrive.getOwnerForDriveId(driveId); + + return arDrive.getPrivateFile(fileId, driveKey, driveOwner /*, shouldGetAllRevisions*/); + } else { + const arDrive = arDriveAnonymousFactory(); + return arDrive.getPublicFile(fileId /*, shouldGetAllRevisions*/); + } + })(); + + // TODO: Fix base types so deleting un-used values is not necessary; Tickets: PE-525 + PE-556 + delete result.syncStatus; + + console.log(JSON.stringify(result, null, 4)); + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/folder_info.ts b/src/commands/folder_info.ts new file mode 100644 index 00000000..4b76a25f --- /dev/null +++ b/src/commands/folder_info.ts @@ -0,0 +1,43 @@ +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { FolderID } from '../types'; +import { GetAllRevisionsParameter, FolderIdParameter, DrivePrivacyParameters } from '../parameter_declarations'; +import { arDriveAnonymousFactory, arDriveFactory } from '..'; +import { ArFSPrivateFolder, ArFSPublicFolder } from '../arfs_entities'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; + +new CLICommand({ + name: 'folder-info', + parameters: [FolderIdParameter, GetAllRevisionsParameter, ...DrivePrivacyParameters], + async action(options) { + const parameters = new ParametersHelper(options); + // const shouldGetAllRevisions: boolean = options.getAllRevisions; + + const result: Partial = await (async function () { + const folderId: FolderID = options.folderId; + + if (await parameters.getIsPrivate()) { + const wallet = await parameters.getRequiredWallet(); + const arDrive = arDriveFactory({ wallet: wallet }); + + const driveId = await arDrive.getDriveIdForFolderId(folderId); + const driveKey = await parameters.getDriveKey({ driveId }); + + // We have the drive id from deriving a key, we can derive the owner + const driveOwner = await arDrive.getOwnerForDriveId(driveId); + + return arDrive.getPrivateFolder(folderId, driveKey, driveOwner /*, shouldGetAllRevisions*/); + } else { + const arDrive = arDriveAnonymousFactory(); + const folderId: string = options.folderId; + return arDrive.getPublicFolder(folderId /*, shouldGetAllRevisions*/); + } + })(); + + // TODO: Fix base types so deleting un-used values is not necessary; Tickets: PE-525 + PE-556 + delete result.lastModifiedDate; + delete result.syncStatus; + + console.log(JSON.stringify(result, null, 4)); + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/generate_seedphrase.ts b/src/commands/generate_seedphrase.ts new file mode 100644 index 00000000..89907451 --- /dev/null +++ b/src/commands/generate_seedphrase.ts @@ -0,0 +1,13 @@ +import { cliWalletDao } from '..'; +import { CLICommand } from '../CLICommand'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; + +new CLICommand({ + name: 'generate-seedphrase', + parameters: [], + async action() { + const seedPhrase = await cliWalletDao.generateSeedPhrase(); + console.log(JSON.stringify(seedPhrase)); + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/generate_wallet.ts b/src/commands/generate_wallet.ts new file mode 100644 index 00000000..47d6b245 --- /dev/null +++ b/src/commands/generate_wallet.ts @@ -0,0 +1,16 @@ +import { cliWalletDao } from '..'; +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; +import { SeedPhraseParameter } from '../parameter_declarations'; + +new CLICommand({ + name: 'generate-wallet', + parameters: [SeedPhraseParameter], + async action(options) { + const parameters = new ParametersHelper(options); + const seedPhrase = await parameters.getRequiredParameterValue(SeedPhraseParameter); + const wallet = await cliWalletDao.generateJWKWallet(seedPhrase); + console.log(JSON.stringify(wallet)); + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/get_address.ts b/src/commands/get_address.ts new file mode 100644 index 00000000..f769070b --- /dev/null +++ b/src/commands/get_address.ts @@ -0,0 +1,14 @@ +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; +import { SeedPhraseParameter, WalletFileParameter } from '../parameter_declarations'; + +new CLICommand({ + name: 'get-address', + parameters: [WalletFileParameter, SeedPhraseParameter], + async action(options) { + const parameters = new ParametersHelper(options); + const address = await parameters.getWalletAddress(); + console.log(`${address}`); + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/get_balance.ts b/src/commands/get_balance.ts new file mode 100644 index 00000000..f9ff7bef --- /dev/null +++ b/src/commands/get_balance.ts @@ -0,0 +1,19 @@ +import { winstonToAr } from 'ardrive-core-js'; +import { cliWalletDao } from '..'; +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; +import { AddressParameter, SeedPhraseParameter, WalletFileParameter } from '../parameter_declarations'; + +new CLICommand({ + name: 'get-balance', + parameters: [WalletFileParameter, SeedPhraseParameter, AddressParameter], + async action(options) { + const parameters = new ParametersHelper(options); + const address = await parameters.getWalletAddress(); + const balanceInWinston = await cliWalletDao.getAddressWinstonBalance(address); + const balanceInAR = winstonToAr(balanceInWinston); + console.log(`${balanceInWinston}\tWinston`); + console.log(`${balanceInAR}\tAR`); + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/get_drive_key.ts b/src/commands/get_drive_key.ts new file mode 100644 index 00000000..cd2c2e11 --- /dev/null +++ b/src/commands/get_drive_key.ts @@ -0,0 +1,19 @@ +import { arDriveFactory } from '..'; +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { DriveCreationPrivacyParameters, DriveIdParameter, NoVerifyParameter } from '../parameter_declarations'; +import { urlEncodeHashKey } from '../utils'; + +new CLICommand({ + name: 'get-drive-key', + parameters: [...DriveCreationPrivacyParameters, DriveIdParameter, NoVerifyParameter], + async action(options) { + const parameters = new ParametersHelper(options); + const driveId = parameters.getRequiredParameterValue(DriveIdParameter); + const driveKey = await parameters.getDriveKey({ driveId }); + if (options.verify) { + const arDrive = arDriveFactory({ wallet: await parameters.getRequiredWallet() }); + await arDrive.getPrivateDrive(driveId, driveKey); + } + console.log(urlEncodeHashKey(driveKey)); + } +}); diff --git a/src/commands/get_file_key.ts b/src/commands/get_file_key.ts new file mode 100644 index 00000000..d5cc0567 --- /dev/null +++ b/src/commands/get_file_key.ts @@ -0,0 +1,48 @@ +import { deriveFileKey } from 'ardrive-core-js'; +import { cliArweave } from '..'; +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { + DriveCreationPrivacyParameters, + DriveIdParameter, + DriveKeyParameter, + FileIdParameter, + NoVerifyParameter +} from '../parameter_declarations'; +import { DriveID } from '../types'; +import { urlEncodeHashKey } from '../utils'; +import { ArFSPrivateFileBuilder } from '../utils/arfs_builders/arfs_file_builders'; + +new CLICommand({ + name: 'get-file-key', + parameters: [ + ...DriveCreationPrivacyParameters, + DriveIdParameter, + DriveKeyParameter, + FileIdParameter, + NoVerifyParameter + ], + async action(options) { + const parameters = new ParametersHelper(options); + const fileId = parameters.getRequiredParameterValue(FileIdParameter); + + // Obviate the need for a drive ID when a drive key is specified + const driveKey = await (async () => { + const driveKeyParam = parameters.getParameterValue(DriveKeyParameter); + if (driveKeyParam) { + return Buffer.from(driveKeyParam, 'base64'); + } + + // Lean on getDriveKey with a specified driveID + // TODO: In the future, loosen driveID requirement and fetch from fileID + const driveId: DriveID = parameters.getRequiredParameterValue(DriveIdParameter); + return await parameters.getDriveKey({ driveId }); + })(); + + const fileKey = await deriveFileKey(fileId, driveKey); + if (options.verify) { + await new ArFSPrivateFileBuilder(fileId, cliArweave, driveKey, undefined, fileKey).build(); + } + + console.log(urlEncodeHashKey(fileKey)); + } +}); diff --git a/src/commands/get_mempool.ts b/src/commands/get_mempool.ts new file mode 100644 index 00000000..ff453e1d --- /dev/null +++ b/src/commands/get_mempool.ts @@ -0,0 +1,13 @@ +import { CLICommand } from '../CLICommand'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; +import { fetchMempool } from '../utils'; + +new CLICommand({ + name: 'get-mempool', + parameters: [], + async action() { + const transactionsInMempool = await fetchMempool(); + console.log(JSON.stringify(transactionsInMempool, null, 4)); + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/index.ts b/src/commands/index.ts new file mode 100644 index 00000000..cb7cf7c0 --- /dev/null +++ b/src/commands/index.ts @@ -0,0 +1,24 @@ +import { CLICommand } from '../CLICommand'; +import '../parameter_declarations'; +import './create_drive'; +import './drive_info'; +import './upload_file'; +import './tx_status'; +import './get_mempool'; +import './send_ar'; +import './get_balance'; +import './get_address'; +import './generate_seedphrase'; +import './generate_wallet'; +import './list_folder'; +import './list_drive'; +import './list_all_drives'; +import './folder_info'; +import './create_folder'; +import './file_info'; +import './move_file'; +import './move_folder'; +import './get_drive_key'; +import './get_file_key'; + +CLICommand.parse(); diff --git a/src/commands/list_all_drives.ts b/src/commands/list_all_drives.ts new file mode 100644 index 00000000..a2e7c2a2 --- /dev/null +++ b/src/commands/list_all_drives.ts @@ -0,0 +1,29 @@ +import { ArFSDriveEntity } from 'ardrive-core-js'; +import { arDriveAnonymousFactory, cliWalletDao } from '..'; +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; +import { AddressParameter, DrivePrivacyParameters } from '../parameter_declarations'; + +new CLICommand({ + name: 'list-all-drives', + parameters: [AddressParameter, ...DrivePrivacyParameters], + async action(options) { + const parameters = new ParametersHelper(options, cliWalletDao); + const ardrive = arDriveAnonymousFactory(); + + const address = await parameters.getWalletAddress(); + const privateKeyData = await parameters.getPrivateKeyData(); + + const drives: Partial[] = await ardrive.getAllDrivesForAddress(address, privateKeyData); + + // TODO: Fix base types so deleting un-used values is not necessary; Tickets: PE-525 + PE-556 + for (const drive of drives) { + delete drive.syncStatus; + } + + // Display data + console.log(JSON.stringify(drives, null, 4)); + + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/list_drive.ts b/src/commands/list_drive.ts new file mode 100644 index 00000000..0358698a --- /dev/null +++ b/src/commands/list_drive.ts @@ -0,0 +1,61 @@ +import { arDriveFactory, cliArweave, cliWalletDao } from '..'; +import { ArDriveAnonymous } from '../ardrive'; +import { ArFSDAOAnonymous } from '../arfsdao_anonymous'; +import { ArFSPrivateFileOrFolderWithPaths, ArFSPublicFileOrFolderWithPaths } from '../arfs_entities'; +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; +import { DriveIdParameter, DrivePrivacyParameters, TreeDepthParams } from '../parameter_declarations'; +import { alphabeticalOrder } from '../utils/sort_functions'; + +new CLICommand({ + name: 'list-drive', + parameters: [DriveIdParameter, ...TreeDepthParams, ...DrivePrivacyParameters], + async action(options) { + const parameters = new ParametersHelper(options, cliWalletDao); + const driveId = parameters.getRequiredParameterValue(DriveIdParameter); + let children: (ArFSPrivateFileOrFolderWithPaths | ArFSPublicFileOrFolderWithPaths)[]; + const maxDepth = await parameters.getMaxDepth(Number.MAX_SAFE_INTEGER); + + if (await parameters.getIsPrivate()) { + const wallet = await parameters.getRequiredWallet(); + const arDrive = arDriveFactory({ wallet }); + const driveKey = await parameters.getDriveKey({ driveId }); + const drive = await arDrive.getPrivateDrive(driveId, driveKey); + const rootFolderId = drive.rootFolderId; + + // We have the drive id from deriving a key, we can derive the owner + const driveOwner = await arDrive.getOwnerForDriveId(driveId); + + children = await arDrive.listPrivateFolder({ + folderId: rootFolderId, + driveKey, + maxDepth, + includeRoot: true, + owner: driveOwner + }); + } else { + const arDrive = new ArDriveAnonymous(new ArFSDAOAnonymous(cliArweave)); + const drive = await arDrive.getPublicDrive(driveId); + const rootFolderId = drive.rootFolderId; + children = await arDrive.listPublicFolder({ folderId: rootFolderId, maxDepth, includeRoot: true }); + } + + const sortedChildren = children.sort((a, b) => alphabeticalOrder(a.path, b.path)) as ( + | Partial + | Partial + )[]; + + // TODO: Fix base types so deleting un-used values is not necessary; Tickets: PE-525 + PE-556 + sortedChildren.map((fileOrFolderMetaData) => { + if (fileOrFolderMetaData.entityType === 'folder') { + delete fileOrFolderMetaData.lastModifiedDate; + delete fileOrFolderMetaData.size; + } + delete fileOrFolderMetaData.syncStatus; + }); + + // Display data + console.log(JSON.stringify(sortedChildren, null, 4)); + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/list_folder.ts b/src/commands/list_folder.ts new file mode 100644 index 00000000..0625650b --- /dev/null +++ b/src/commands/list_folder.ts @@ -0,0 +1,51 @@ +import { arDriveAnonymousFactory, arDriveFactory } from '..'; +import { ArFSPrivateFileOrFolderWithPaths, ArFSPublicFileOrFolderWithPaths } from '../arfs_entities'; +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; +import { DrivePrivacyParameters, ParentFolderIdParameter, TreeDepthParams } from '../parameter_declarations'; +import { alphabeticalOrder } from '../utils/sort_functions'; + +new CLICommand({ + name: 'list-folder', + parameters: [ParentFolderIdParameter, ...TreeDepthParams, ...DrivePrivacyParameters], + async action(options) { + const parameters = new ParametersHelper(options); + const folderId = parameters.getRequiredParameterValue(ParentFolderIdParameter); + let children: (ArFSPrivateFileOrFolderWithPaths | ArFSPublicFileOrFolderWithPaths)[]; + const maxDepth = await parameters.getMaxDepth(0); + + if (await parameters.getIsPrivate()) { + const wallet = await parameters.getRequiredWallet(); + const arDrive = arDriveFactory({ wallet }); + + const driveId = await arDrive.getDriveIdForFolderId(folderId); + const driveKey = await parameters.getDriveKey({ driveId }); + + // We have the drive id from deriving a key, we can derive the owner + const driveOwner = await arDrive.getOwnerForDriveId(driveId); + + children = await arDrive.listPrivateFolder({ folderId, driveKey, maxDepth, owner: driveOwner }); + } else { + const arDrive = arDriveAnonymousFactory(); + children = await arDrive.listPublicFolder({ folderId, maxDepth }); + } + + const sortedChildren = children.sort((a, b) => alphabeticalOrder(a.path, b.path)) as ( + | Partial + | Partial + )[]; + + // TODO: Fix base types so deleting un-used values is not necessary; Tickets: PE-525 + PE-556 + sortedChildren.map((fileOrFolderMetaData) => { + if (fileOrFolderMetaData.entityType === 'folder') { + delete fileOrFolderMetaData.lastModifiedDate; + delete fileOrFolderMetaData.size; + } + delete fileOrFolderMetaData.syncStatus; + }); + + // Display data + console.log(JSON.stringify(sortedChildren, null, 4)); + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/move_file.ts b/src/commands/move_file.ts new file mode 100644 index 00000000..bd8050af --- /dev/null +++ b/src/commands/move_file.ts @@ -0,0 +1,43 @@ +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { + BoostParameter, + DryRunParameter, + FileIdParameter, + ParentFolderIdParameter, + DrivePrivacyParameters +} from '../parameter_declarations'; +import { Wallet } from '../wallet'; +import { arDriveFactory } from '..'; +import { FeeMultiple } from '../types'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; + +new CLICommand({ + name: 'move-file', + parameters: [FileIdParameter, ParentFolderIdParameter, BoostParameter, DryRunParameter, ...DrivePrivacyParameters], + async action(options) { + const parameters = new ParametersHelper(options); + + const { fileId, parentFolderId, boost, dryRun } = options; + + const wallet: Wallet = await parameters.getRequiredWallet(); + const ardrive = arDriveFactory({ + wallet: wallet, + feeMultiple: boost as FeeMultiple, + dryRun: dryRun + }); + + const createDriveResult = await (async function () { + if (await parameters.getIsPrivate()) { + const driveId = await ardrive.getDriveIdForFolderId(parentFolderId); + const driveKey = await parameters.getDriveKey({ driveId }); + + return ardrive.movePrivateFile(fileId, parentFolderId, driveKey); + } else { + return ardrive.movePublicFile(fileId, parentFolderId); + } + })(); + console.log(JSON.stringify(createDriveResult, null, 4)); + + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/move_folder.ts b/src/commands/move_folder.ts new file mode 100644 index 00000000..e7190834 --- /dev/null +++ b/src/commands/move_folder.ts @@ -0,0 +1,50 @@ +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { + BoostParameter, + DryRunParameter, + FolderIdParameter, + ParentFolderIdParameter, + DrivePrivacyParameters +} from '../parameter_declarations'; +import { Wallet } from '../wallet'; +import { arDriveFactory } from '..'; +import { FeeMultiple } from '../types'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; + +new CLICommand({ + name: 'move-folder', + parameters: [ + FolderIdParameter, + ParentFolderIdParameter, + BoostParameter, + DryRunParameter, + ...DrivePrivacyParameters + ], + async action(options) { + const parameters = new ParametersHelper(options); + + const { folderId, parentFolderId: newParentFolderId, boost, dryRun } = options; + + const wallet: Wallet = await parameters.getRequiredWallet(); + const ardrive = arDriveFactory({ + wallet: wallet, + feeMultiple: boost as FeeMultiple, + dryRun: dryRun + }); + + const moveFolderResult = await (async function () { + if (await parameters.getIsPrivate()) { + const driveId = await ardrive.getDriveIdForFolderId(folderId); + const driveKey = await parameters.getDriveKey({ driveId }); + + return ardrive.movePrivateFolder({ folderId, newParentFolderId, driveKey }); + } else { + return ardrive.movePublicFolder({ folderId, newParentFolderId }); + } + })(); + + console.log(JSON.stringify(moveFolderResult, null, 4)); + + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/send_ar.ts b/src/commands/send_ar.ts new file mode 100644 index 00000000..dbd120ea --- /dev/null +++ b/src/commands/send_ar.ts @@ -0,0 +1,47 @@ +import { cliWalletDao } from '..'; +import { ArweaveAddress } from '../arweave_address'; +import { CLICommand } from '../CLICommand'; +import { ParametersHelper } from '../CLICommand'; +import { SUCCESS_EXIT_CODE } from '../CLICommand/constants'; +import { + ArAmountParameter, + BoostParameter, + DestinationAddressParameter, + DryRunParameter, + WalletFileParameter +} from '../parameter_declarations'; +import { assertARPrecision } from '../utils/ar_unit'; + +new CLICommand({ + name: 'send-ar', + parameters: [ArAmountParameter, DestinationAddressParameter, WalletFileParameter, BoostParameter, DryRunParameter], + async action(options) { + assertARPrecision(options.arAmount); + const parameters = new ParametersHelper(options); + const arAmount: number = +options.arAmount; + const destAddress = new ArweaveAddress(options.destAddress); + const wallet = await parameters.getRequiredWallet(); + const walletAddress = await wallet.getAddress(); + console.log(`Source address: ${walletAddress}`); + console.log(`AR amount sent: ${arAmount.toFixed(12)}`); + console.log(`Destination address: ${destAddress}`); + const rewardSetting = options.boost ? { feeMultiple: +options.boost } : undefined; + + const arTransferResult = await cliWalletDao.sendARToAddress( + arAmount, + wallet, + destAddress, + rewardSetting, + options.dryRun, + [ + { name: 'App-Name', value: 'ArDrive-CLI' }, + { name: 'App-Version', value: '2.0' }, + { name: 'Type', value: 'transfer' } + ], + true + ); + + console.log(JSON.stringify(arTransferResult, null, 4)); + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/tx_status.ts b/src/commands/tx_status.ts new file mode 100644 index 00000000..5cc54a56 --- /dev/null +++ b/src/commands/tx_status.ts @@ -0,0 +1,40 @@ +import { cliArweave } from '..'; +import { CLICommand } from '../CLICommand'; +import { ERROR_EXIT_CODE, SUCCESS_EXIT_CODE } from '../CLICommand/constants'; +import { ConfirmationsParameter, TransactionIdParameter } from '../parameter_declarations'; +import { fetchMempool } from '../utils'; + +new CLICommand({ + name: 'tx-status', + parameters: [TransactionIdParameter, ConfirmationsParameter], + async action(options) { + const { txId, confirmations } = options; + const transactionsInMempool = await fetchMempool(); + const pending = transactionsInMempool.includes(txId); + const confirmationAmount = confirmations ?? 15; + + if (pending) { + console.log(`${txId}: Pending`); + return SUCCESS_EXIT_CODE; + } + + const confStatus = (await cliArweave.transactions.getStatus(txId)).confirmed; + + if (!confStatus?.block_height) { + console.log(`${txId}: Not found`); + return ERROR_EXIT_CODE; + } + + if (confStatus?.number_of_confirmations >= confirmationAmount) { + console.log( + `${txId}: Mined at block height ${confStatus.block_height} with ${confStatus.number_of_confirmations} confirmations` + ); + } else { + console.log( + `${txId}: Confirming at block height ${confStatus.block_height} with ${confStatus.number_of_confirmations} confirmations` + ); + } + + return SUCCESS_EXIT_CODE; + } +}); diff --git a/src/commands/upload_file.ts b/src/commands/upload_file.ts new file mode 100644 index 00000000..9d9ad263 --- /dev/null +++ b/src/commands/upload_file.ts @@ -0,0 +1,135 @@ +import { ArFSFileToUpload, ArFSFolderToUpload, isFolder, wrapFileOrFolder } from '../arfs_file_wrapper'; +import { arDriveFactory } from '..'; +import { CLICommand, ParametersHelper } from '../CLICommand'; +import { + BoostParameter, + DestinationFileNameParameter, + DrivePrivacyParameters, + DryRunParameter, + LocalFilePathParameter, + LocalFilesParameter, + ParentFolderIdParameter +} from '../parameter_declarations'; +import { DriveKey, FeeMultiple, FolderID } from '../types'; +import { readJWKFile } from '../utils'; +import { ERROR_EXIT_CODE, SUCCESS_EXIT_CODE } from '../CLICommand/constants'; + +interface UploadFileParameter { + parentFolderId: FolderID; + wrappedEntity: ArFSFileToUpload | ArFSFolderToUpload; + destinationFileName?: string; + drivePassword?: string; + driveKey?: DriveKey; +} + +new CLICommand({ + name: 'upload-file', + parameters: [ + ParentFolderIdParameter, + LocalFilePathParameter, + DestinationFileNameParameter, + LocalFilesParameter, + BoostParameter, + DryRunParameter, + ...DrivePrivacyParameters + ], + async action(options) { + const filesToUpload: UploadFileParameter[] = await (async function (): Promise { + if (options.localFiles) { + const COLUMN_SEPARATOR = ','; + const ROW_SEPARATOR = '.'; + const csvRows = options.localFiles.split(ROW_SEPARATOR); + const fileParameters: UploadFileParameter[] = csvRows.map((row: string) => { + const csvFields = row.split(COLUMN_SEPARATOR).map((f: string) => f.trim()); + const [parentFolderId, localFilePath, destinationFileName, drivePassword, driveKey] = csvFields; + + // TODO: Make CSV uploads more bulk performant + const wrappedEntity = wrapFileOrFolder(localFilePath); + + return { + parentFolderId, + wrappedEntity, + destinationFileName, + drivePassword, + driveKey + }; + }); + return fileParameters; + } + + if (!options.localFilePath) { + throw new Error('Must provide a local file path!'); + } + + const singleParameter = { + parentFolderId: options.parentFolderId, + wrappedEntity: wrapFileOrFolder(options.localFilePath), + destinationFileName: options.destFileName + }; + + return [singleParameter]; + })(); + if (filesToUpload.length) { + const parameters = new ParametersHelper(options); + + const wallet = readJWKFile(options.walletFile); + + const arDrive = arDriveFactory({ + wallet: wallet, + feeMultiple: options.boost as FeeMultiple, + dryRun: options.dryRun + }); + + await Promise.all( + filesToUpload.map(async (fileToUpload) => { + const { + parentFolderId, + wrappedEntity, + destinationFileName, + drivePassword, + driveKey: fileDriveKey + } = fileToUpload; + + const result = await (async () => { + if (await parameters.getIsPrivate()) { + const driveId = await arDrive.getDriveIdForFolderId(parentFolderId); + const driveKey = + fileDriveKey ?? + (await parameters.getDriveKey({ driveId, drivePassword, useCache: true })); + + if (isFolder(wrappedEntity)) { + return arDrive.createPrivateFolderAndUploadChildren( + parentFolderId, + wrappedEntity, + driveKey, + destinationFileName + ); + } else { + return arDrive.uploadPrivateFile( + parentFolderId, + wrappedEntity, + driveKey, + destinationFileName + ); + } + } else { + if (isFolder(wrappedEntity)) { + return arDrive.createPublicFolderAndUploadChildren( + parentFolderId, + wrappedEntity, + destinationFileName + ); + } else { + return arDrive.uploadPublicFile(parentFolderId, wrappedEntity, destinationFileName); + } + } + })(); + console.log(JSON.stringify(result, null, 4)); + }) + ); + return SUCCESS_EXIT_CODE; + } + console.log(`No files to upload`); + return ERROR_EXIT_CODE; + } +}); diff --git a/src/community/ardrive_community_oracle.ts b/src/community/ardrive_community_oracle.ts new file mode 100644 index 00000000..fb32c7d3 --- /dev/null +++ b/src/community/ardrive_community_oracle.ts @@ -0,0 +1,97 @@ +import { weightedRandom } from 'ardrive-core-js'; +import { ContractOracle, ContractReader } from './contract_oracle'; +import { CommunityOracle } from './community_oracle'; +import { Winston } from '../types'; +import { ArDriveContractOracle } from './ardrive_contract_oracle'; +import Arweave from 'arweave'; +import { SmartweaveContractReader } from './smartweave_contract_oracle'; +import { VertoContractReader } from './verto_contract_oracle'; +import { ArweaveAddress } from '../arweave_address'; + +/** + * Minimum ArDrive community tip from the Community Improvement Proposal Doc: + * https://arweave.net/Yop13NrLwqlm36P_FDCdMaTBwSlj0sdNGAC4FqfRUgo + */ +export const minArDriveCommunityWinstonTip = 10_000_000; + +/** + * Oracle class responsible for determining the community tip + * and selecting the PST token holder for tip distribution + * + * TODO: Unit testing for important functions + */ +export class ArDriveCommunityOracle implements CommunityOracle { + constructor(readonly arweave: Arweave, contractReaders?: ContractReader[]) { + this.contractOracle = new ArDriveContractOracle( + contractReaders ? contractReaders : this.defaultContractReaders + ); + } + + private readonly contractOracle: ContractOracle; + + private defaultContractReaders: ContractReader[] = [ + new VertoContractReader(), + new SmartweaveContractReader(this.arweave) + ]; + + /** + * Given a Winston data cost, returns a calculated ArDrive community tip amount in Winston + * + * TODO: Use big int library on Winston types + */ + async getCommunityWinstonTip(winstonCost: Winston): Promise { + const communityTipPercentage = await this.contractOracle.getTipPercentageFromContract(); + const arDriveCommunityTip = +winstonCost * communityTipPercentage; + return Math.round(Math.max(arDriveCommunityTip, minArDriveCommunityWinstonTip)).toString(); + } + + /** + * Gets a random ArDrive token holder based off their weight (amount of tokens they hold) + * + * TODO: This is mostly copy-paste from core -- refactor into a more testable state + */ + async selectTokenHolder(): Promise { + // Read the ArDrive Smart Contract to get the latest state + const contract = await this.contractOracle.getCommunityContract(); + + const balances = contract.balances; + const vault = contract.vault; + + // Get the total number of token holders + let total = 0; + for (const addr of Object.keys(balances)) { + total += balances[addr]; + } + + // Check for how many tokens the user has staked/vaulted + for (const addr of Object.keys(vault)) { + if (!vault[addr].length) continue; + + const vaultBalance = vault[addr] + .map((a: { balance: number; start: number; end: number }) => a.balance) + .reduce((a: number, b: number) => a + b, 0); + + total += vaultBalance; + + if (addr in balances) { + balances[addr] += vaultBalance; + } else { + balances[addr] = vaultBalance; + } + } + + // Create a weighted list of token holders + const weighted: { [addr: string]: number } = {}; + for (const addr of Object.keys(balances)) { + weighted[addr] = balances[addr] / total; + } + // Get a random holder based off of the weighted list of holders + const randomHolder = weightedRandom(weighted); + + if (randomHolder === undefined) { + throw new Error('Token holder target could not be determined for community tip distribution..'); + } + + return new ArweaveAddress(randomHolder); + } +} diff --git a/src/community/ardrive_contract_oracle.ts b/src/community/ardrive_contract_oracle.ts new file mode 100644 index 00000000..4f4df75d --- /dev/null +++ b/src/community/ardrive_contract_oracle.ts @@ -0,0 +1,131 @@ +import { CommunityContractData, CommunityTipPercentage } from './contract_types'; +import { ContractOracle, ContractReader } from './contract_oracle'; +import { TransactionID } from '../types'; + +// ArDrive Profit Sharing Community Smart Contract +export const communityTxId = '-8A6RexFkpfWwuyVO98wzSFZh0d6VJuI-buTJvlwOJQ'; + +const maxReadContractAttempts = 3; +const initialContractReader = 0; +const initialContractAttempts = 0; + +/** + * Oracle class responsible for retrieving the correct data fields from + * the ArDrive Community Contract. This class can utilize several different + * contract readers and will fallback to other readers if one fails + * + * @remarks Will begin fetching data from default contract reader on construction + */ +export class ArDriveContractOracle implements ContractOracle { + constructor( + /** + * Array of contract readers to use as fall back if one fails + * Uses contract reader at index 0 first then descends down the list + */ + private readonly contractReaders: ContractReader[], + skipSetup = true + ) { + if (!skipSetup) { + // Get contract data upon construction + this.getCommunityContract(); + } + } + + private communityContract?: CommunityContractData; + private contractPromise?: Promise; + + /** + * Reads a smart contract with the current contract reader + * + * @remarks Will fallback to other contract readers when one fails + */ + async readContract(txId: TransactionID): Promise { + let contract: unknown; + let currentContractReader = initialContractReader; + let readContractAttempts = initialContractAttempts; + + while (!contract) { + try { + // Get contract with current contract reader's readContract implementation + contract = await this.contractReaders[currentContractReader].readContract(txId); + } catch (error) { + console.error(`Contract could not be fetched: ${error}`); + readContractAttempts++; + + if (readContractAttempts >= maxReadContractAttempts) { + // Max attempts for contract reader has been reached + if (currentContractReader === this.contractReaders.length - 1) { + // Current contract reader is the last fallback, throw an error + throw new Error( + `Max contract read attempts has been reached on the last fallback contract reader..` + ); + } + + // Else fallback to next reader + const nextContractReaderIndex = currentContractReader + 1; + readContractAttempts = initialContractAttempts; + currentContractReader = nextContractReaderIndex; + + console.log('Falling back to next contract reader..'); + } else { + console.log('Retrying with current contract reader..'); + } + } + } + return contract; + } + + /** + * @returns the ArDrive Community Smartweave Contract + * @throws when the Community Contract cannot be fetched or is returned as falsy + * @throws when the Community Contract is returned in an unexpected shape + */ + public async getCommunityContract(): Promise { + // Contract data already cached, return contract data + if (this.communityContract) { + return this.communityContract; + } + + // Contract promise still resolving, return promise with contract data + if (this.contractPromise) { + return this.contractPromise; + } + + // Begin new contract read; cast result to known ArDrive Community Contract type + this.contractPromise = this.readContract(communityTxId) as Promise; + + this.communityContract = await this.contractPromise; + + delete this.contractPromise; + + return this.communityContract; + } + + /** + * Grabs fee directly from the settings at the bottom of the community contract + * + * @throws When community fee cannot be read from the contract, is negative, or is the wrong type + */ + + async getTipPercentageFromContract(): Promise { + const contract = await this.getCommunityContract(); + + const arDriveCommTipFromSettings: [string, unknown] | undefined = contract.settings.find( + (setting) => setting[0] === 'fee' + ); + + if (!arDriveCommTipFromSettings) { + throw new Error('Fee does not exist on smart contract settings'); + } + + if (typeof arDriveCommTipFromSettings[1] !== 'number') { + throw new Error('Fee on smart contract settings is not a number'); + } + + if (arDriveCommTipFromSettings[1] < 0) { + throw new Error('Fee on smart contract community settings is set to a negative number'); + } + + return arDriveCommTipFromSettings[1] / 100; + } +} diff --git a/src/community/community_oracle.ts b/src/community/community_oracle.ts new file mode 100644 index 00000000..59ef6c70 --- /dev/null +++ b/src/community/community_oracle.ts @@ -0,0 +1,7 @@ +import { ArweaveAddress } from '../arweave_address'; +import { Winston } from '../types'; + +export interface CommunityOracle { + getCommunityWinstonTip(winstonCost: Winston): Promise; + selectTokenHolder(): Promise; +} diff --git a/src/community/contract_oracle.ts b/src/community/contract_oracle.ts new file mode 100644 index 00000000..89851220 --- /dev/null +++ b/src/community/contract_oracle.ts @@ -0,0 +1,12 @@ +import { TransactionID } from '../types'; +import { CommunityContractData, CommunityTipPercentage } from './contract_types'; + +/** An oracle interface responsible for reading contracts and retrieving the ArDrive Community Contract */ +export interface ContractOracle extends ContractReader { + getCommunityContract(): Promise; + getTipPercentageFromContract(): Promise; +} + +export interface ContractReader { + readContract(txId: TransactionID): Promise; +} diff --git a/src/community/contract_types.ts b/src/community/contract_types.ts new file mode 100644 index 00000000..23852b20 --- /dev/null +++ b/src/community/contract_types.ts @@ -0,0 +1,40 @@ +import { ArweaveAddress } from '../arweave_address'; + +export type CommunityTipPercentage = number; + +/** Shape of the ArDrive Community Smart Contract */ +export interface CommunityContractData { + name: 'ArDrive'; + ticker: 'ARDRIVE'; + votes: communityContractVotes[]; + settings: CommunityContractSettings; + balances: { [tokenHolderAddress: string]: number }; + vault: { [tokenHolderAddress: string]: { balance: number; start: number; end: number }[] }; +} + +interface communityContractVotes { + status: 'passed' | 'quorumFailed'; + type: 'burnVault' | 'mintLocked' | 'mint' | 'set'; + note: string; + yays: number; + nays: number; + voted: ArweaveAddress[]; + start: number; + totalWeight: number; + recipient?: ArweaveAddress; + qty?: number; + lockLength?: number; +} + +type CommunityContractSettings = [ + ['quorum', number], + ['support', number], + ['voteLength', number], + ['lockMinLength', number], + ['lockMaxLength', number], + ['communityAppUrl', string], + ['communityDiscussionLinks', string[]], + ['communityDescription', string], + ['communityLogo', ArweaveAddress], + ['fee', number] +]; diff --git a/src/community/smartweave_contract_oracle.ts b/src/community/smartweave_contract_oracle.ts new file mode 100644 index 00000000..7639604a --- /dev/null +++ b/src/community/smartweave_contract_oracle.ts @@ -0,0 +1,17 @@ +import Arweave from 'arweave'; +import { readContract } from 'smartweave'; +import { TransactionID } from '../types'; +import { ContractReader } from './contract_oracle'; + +/** + * Oracle class responsible for retrieving and reading + * Smartweave Contracts from Arweave with the `smartweave` package + */ +export class SmartweaveContractReader implements ContractReader { + constructor(private readonly arweave: Arweave) {} + + /** Fetches smartweave contracts from Arweave with smartweave-js */ + async readContract(txId: TransactionID): Promise { + return readContract(this.arweave, txId); + } +} diff --git a/src/community/verto_contract_oracle.ts b/src/community/verto_contract_oracle.ts new file mode 100644 index 00000000..3051470a --- /dev/null +++ b/src/community/verto_contract_oracle.ts @@ -0,0 +1,16 @@ +import fetch from 'node-fetch'; +import { TransactionID } from '../types'; +import { ContractReader } from './contract_oracle'; + +/** + * Oracle class responsible for retrieving and + * reading Smartweave Contracts from the Verto cache + */ +export class VertoContractReader implements ContractReader { + /** Fetches smartweave contracts from the Verto cache */ + public async readContract(txId: TransactionID): Promise { + const response = await fetch(`https://v2.cache.verto.exchange/${txId}`); + const contract = await response.json(); + return contract.state; + } +} diff --git a/src/error_message.ts b/src/error_message.ts new file mode 100644 index 00000000..19009dc1 --- /dev/null +++ b/src/error_message.ts @@ -0,0 +1,10 @@ +import { FolderID } from './types'; + +export const errorMessage = { + entityNameExists: 'Entity name already exists in destination folder!', + cannotMoveToDifferentDrive: 'Entity must stay in the same drive!', + cannotMoveParentIntoChildFolder: 'Parent folder cannot be moved inside any of its children folders!', + folderCannotMoveIntoItself: 'Folders cannot be moved into themselves!', + cannotMoveIntoSamePlace: (type: 'File' | 'Folder', parentFolderId: FolderID): string => + `${type} already has parent folder with ID: ${parentFolderId}` +}; diff --git a/src/example.test.ts b/src/example.test.ts new file mode 100644 index 00000000..7c52e10b --- /dev/null +++ b/src/example.test.ts @@ -0,0 +1,130 @@ +/** + * This is an example unit test used to showcase the testing + * libraries and to ensure each the following are functional: + * + * Chai + * Asynchronous Mocha testing + * Sinon -- spies / stubs / mocks + * Power-assert + * + * To run this example on it's own, use: yarn test -g 'basicInputOutputExample' + * + * For an integration test example, visit `/tests/example.test.ts` + */ + +import { sleep } from 'ardrive-core-js'; + +// Common imports with chai/sinon +import { expect } from 'chai'; +import { mock, spy, stub } from 'sinon'; + +// Power-assert must be imported with require to work +import assert = require('assert'); + +/** + * Normally we would not define our test functions here. Unit + * tests belong adjacent to the file they're testing. In this case + * they would preferably be defined in `src/example.ts` + */ + +// Synchronous input/output example function +function basicInputOutputExample(input: number) { + return input * input + Math.round(input - input * 5); +} + +// Async input/output example function +async function asyncInputOutputExample(input: number) { + // Waits 1ms to simulate async call + await sleep(1); + return basicInputOutputExample(input); +} + +// Describe the function or behavior to test +describe('The basicInputOutputExample function', () => { + // Define input + const input = 42; + + // Define expectedOutput + const expectedOutput = 1596; + + // Basic Mocha/Chai unit test example + it('returns the expected output', () => { + const actual = basicInputOutputExample(input); + expect(actual).to.equal(expectedOutput); + }); + + // Asynchronous mocha/chai example + it('asynchronously returns the expected output', async () => { + const actual = await asyncInputOutputExample(input); + + // Returning anything to `it()` will conclude an async test + return expect(actual).to.equal(expectedOutput); + }); + + // To more easily be used with Sinon, use test function inside of an object + const objectWithExampleFunctions = { basicInputOutputExample }; + + // Sinon spy example + it('returns correct output when checked by Sinon spy', () => { + // Setup spy + const sinonSpy = spy(objectWithExampleFunctions, 'basicInputOutputExample'); + + // Run test as normal + const actual = objectWithExampleFunctions.basicInputOutputExample(input); + expect(actual).to.equal(expectedOutput); + + // Verify spy calls with Chai + expect(sinonSpy.calledWith(input)).to.be.ok; + expect(sinonSpy.calledOnce).to.be.ok; + }); + + // Sinon stub example + it('can be stubbed by a Sinon stub', () => { + // Stub in a fake function + stub(objectWithExampleFunctions, 'basicInputOutputExample').callsFake(() => 1337); + + const actual = objectWithExampleFunctions.basicInputOutputExample(input); + + // Verify stubbed output + expect(actual).to.equal(1337); + }); + + // Sinon mock example + it('can be used in a Sinon mock', () => { + // Create mock + const sinonMock = mock(objectWithExampleFunctions); + + // Setup mock expectations + sinonMock.expects('basicInputOutputExample').once().returns(10101); + + const actual = objectWithExampleFunctions.basicInputOutputExample(input); + + // Confirm output with Chai + expect(actual).to.equal(10101); + + // Verify mock expectations + sinonMock.verify(); + }); + + // Power-assert debugging example + it('can provide detailed error output when used with the power-assert library', () => { + // Comment out the regular Chai test + // const output = basicInputOutputExample(input); + // expect(output).to.equal(expectedOutput); + + // Put everything relevant inside a power-assert assertion + // More info inside the assertion results in a more detailed output + assert(basicInputOutputExample(input) === expectedOutput); + + /** + * This test has been left in a passing state because all tests must pass + * To view the detailed error output example, change above assertion to fail in some way + * + * For instance: + * assert(basicInputOutputExample(56) === expectedOutput); + * assert(basicInputOutputExample(input) !== expectedOutput); + * + * And then use: yarn power-assert -g 'power-assert' + */ + }); +}); diff --git a/src/folderHierarchy.ts b/src/folderHierarchy.ts new file mode 100644 index 00000000..8fa46dbc --- /dev/null +++ b/src/folderHierarchy.ts @@ -0,0 +1,188 @@ +import { ArFSFileOrFolderEntity } from './arfs_entities'; +import { FolderID } from './types'; + +export class FolderTreeNode { + constructor( + public readonly folderId: FolderID, + public readonly parent?: FolderTreeNode, + public children: FolderTreeNode[] = [] + ) {} + + public static fromEntity(folderEntity: ArFSFileOrFolderEntity): FolderTreeNode { + const node = new FolderTreeNode(folderEntity.entityId); + return node; + } +} + +export class FolderHierarchy { + private _rootNode?: FolderTreeNode; + + constructor( + private readonly folderIdToEntityMap: { [k: string]: ArFSFileOrFolderEntity }, + private readonly folderIdToNodeMap: { [k: string]: FolderTreeNode } + ) {} + + static newFromEntities(entities: ArFSFileOrFolderEntity[]): FolderHierarchy { + const folderIdToEntityMap = entities.reduce((accumulator, entity) => { + return Object.assign(accumulator, { [entity.entityId]: entity }); + }, {}); + const folderIdToNodeMap: { [k: string]: FolderTreeNode } = {}; + + for (const entity of entities) { + this.setupNodesWithEntity(entity, folderIdToEntityMap, folderIdToNodeMap); + } + + return new FolderHierarchy(folderIdToEntityMap, folderIdToNodeMap); + } + + private static setupNodesWithEntity( + entity: ArFSFileOrFolderEntity, + folderIdToEntityMap: { [k: string]: ArFSFileOrFolderEntity }, + folderIdToNodeMap: { [k: string]: FolderTreeNode } + ): void { + const folderIdKeyIsPresent = Object.keys(folderIdToNodeMap).includes(entity.entityId); + const parentFolderIdKeyIsPresent = Object.keys(folderIdToNodeMap).includes(entity.parentFolderId); + if (!folderIdKeyIsPresent) { + if (!parentFolderIdKeyIsPresent) { + const parentFolderEntity = folderIdToEntityMap[entity.parentFolderId]; + if (parentFolderEntity) { + this.setupNodesWithEntity(parentFolderEntity, folderIdToEntityMap, folderIdToNodeMap); + } + } + const parent = folderIdToNodeMap[entity.parentFolderId]; + if (parent) { + const node = new FolderTreeNode(entity.entityId, parent); + parent.children.push(node); + folderIdToNodeMap[entity.entityId] = node; + } else { + // this one is supposed to be the new root + const rootNode = new FolderTreeNode(entity.entityId); + folderIdToNodeMap[entity.entityId] = rootNode; + } + } + } + + public get rootNode(): FolderTreeNode { + if (this._rootNode) { + return this._rootNode; + } + + const someFolderId = Object.keys(this.folderIdToEntityMap)[0]; + let tmpNode = this.folderIdToNodeMap[someFolderId]; + while (tmpNode.parent && this.folderIdToNodeMap[tmpNode.parent.folderId]) { + tmpNode = tmpNode.parent; + } + this._rootNode = tmpNode; + return tmpNode; + } + + public subTreeOf(folderId: FolderID, maxDepth = Number.MAX_SAFE_INTEGER): FolderHierarchy { + const newRootNode = this.folderIdToNodeMap[folderId]; + + const subTreeNodes = this.nodeAndChildrenOf(newRootNode, maxDepth); + + const entitiesMapping = subTreeNodes.reduce((accumulator, node) => { + return Object.assign(accumulator, { [node.folderId]: this.folderIdToEntityMap[node.folderId] }); + }, {}); + const nodesMapping = subTreeNodes.reduce((accumulator, node) => { + return Object.assign(accumulator, { [node.folderId]: node }); + }, {}); + + return new FolderHierarchy(entitiesMapping, nodesMapping); + } + + public allFolderIDs(): FolderID[] { + return Object.keys(this.folderIdToEntityMap); + } + + public nodeAndChildrenOf(node: FolderTreeNode, maxDepth: number): FolderTreeNode[] { + const subTreeEntities: FolderTreeNode[] = [node]; + if (maxDepth > 0) { + node.children.forEach((child) => { + subTreeEntities.push(...this.nodeAndChildrenOf(child, maxDepth - 1)); + }); + } + return subTreeEntities; + } + + public folderIdSubtreeFromFolderId(folderId: FolderID, maxDepth: number): FolderID[] { + const rootNode = this.folderIdToNodeMap[folderId]; + const subTree: FolderID[] = [rootNode.folderId]; + switch (maxDepth) { + case -1: + // Recursion stopping condition - hit the max allowable depth + break; + default: { + // Recursion stopping condition - no further child nodes to recurse to + rootNode.children + .map((node) => node.folderId) + .forEach((childFolderID) => { + subTree.push(...this.folderIdSubtreeFromFolderId(childFolderID, maxDepth - 1)); + }); + break; + } + } + return subTree; + } + + public pathToFolderId(folderId: FolderID): string { + if (this.rootNode.parent) { + throw new Error(`Can't compute paths from sub-tree`); + } + if (folderId === 'root folder') { + return '/'; + } + let folderNode = this.folderIdToNodeMap[folderId]; + const nodesInPathToFolder = [folderNode]; + while (folderNode.parent && folderNode.folderId !== this.rootNode.folderId) { + folderNode = folderNode.parent; + nodesInPathToFolder.push(folderNode); + } + const olderFirstNodesInPathToFolder = nodesInPathToFolder.reverse(); + const olderFirstNamesOfNodesInPath = olderFirstNodesInPathToFolder.map( + (n) => this.folderIdToEntityMap[n.folderId].name + ); + const stringPath = olderFirstNamesOfNodesInPath.join('/'); + return `/${stringPath}/`; + } + + public entityPathToFolderId(folderId: FolderID): string { + if (this.rootNode.parent) { + throw new Error(`Can't compute paths from sub-tree`); + } + if (folderId === 'root folder') { + return '/'; + } + let folderNode = this.folderIdToNodeMap[folderId]; + const nodesInPathToFolder = [folderNode]; + while (folderNode.parent && folderNode.folderId !== this.rootNode.folderId) { + folderNode = folderNode.parent; + nodesInPathToFolder.push(folderNode); + } + const olderFirstNodesInPathToFolder = nodesInPathToFolder.reverse(); + const olderFirstFolderIDsOfNodesInPath = olderFirstNodesInPathToFolder.map((n) => n.folderId); + const stringPath = olderFirstFolderIDsOfNodesInPath.join('/'); + return `/${stringPath}/`; + } + + public txPathToFolderId(folderId: FolderID): string { + if (this.rootNode.parent) { + throw new Error(`Can't compute paths from sub-tree`); + } + if (folderId === 'root folder') { + return '/'; + } + let folderNode = this.folderIdToNodeMap[folderId]; + const nodesInPathToFolder = [folderNode]; + while (folderNode.parent && folderNode.folderId !== this.rootNode.folderId) { + folderNode = folderNode.parent; + nodesInPathToFolder.push(folderNode); + } + const olderFirstNodesInPathToFolder = nodesInPathToFolder.reverse(); + const olderFirstTxTDsOfNodesInPath = olderFirstNodesInPathToFolder.map( + (n) => this.folderIdToEntityMap[n.folderId].txId + ); + const stringPath = olderFirstTxTDsOfNodesInPath.join('/'); + return `/${stringPath}/`; + } +} diff --git a/src/index.ts b/src/index.ts old mode 100644 new mode 100755 index b5fe9580..ea5b3d13 --- a/src/index.ts +++ b/src/index.ts @@ -1,175 +1,81 @@ #!/usr/bin/env node -/* eslint-disable no-await-in-loop */ -// index.ts -import * as ardrive from 'ardrive-core-js'; -import { arDriveCommunityOracle } from 'ardrive-core-js'; -import * as cli from './prompts'; -async function main() { - // Setup database if it doesnt exist - try { - await ardrive.setupDatabase('./.ardrive-cli.db'); - } catch (err) { - console.error(err); - return; - } - let user: ardrive.ArDriveUser = { - login: '', - dataProtectionKey: '', - walletPrivateKey: '', - walletPublicKey: '', - syncFolderPath: '', - autoSyncApproval: 0 - }; - let fileDownloadConflicts: ardrive.ArFSFileMetaData[] = []; - - // Start background task to fetch ArDrive community tip setting - arDriveCommunityOracle.setExactTipSettingInBackground(); - - // Ask the user for their login name - const login = await cli.promptForLogin(); - - // Check to see if it exists - user = await ardrive.getUserFromProfile(login); - - // If no user is found, prompt the user to create a new one - if (user === undefined) { - // Welcome message and info - console.log("We have not detected a profile for your login! Let's get one set up."); - user = await cli.promptForNewUserInfo(login); - const loginPassword = user.dataProtectionKey; - await ardrive.addNewUser(user.dataProtectionKey, user); - user = await ardrive.getUser(loginPassword, login); - } else { - // Allow the user to login - console.log('You already have an existing ArDrive', login); - const loginPassword = await cli.promptForLoginPassword(); - const passwordResult: boolean = await ardrive.passwordCheck(loginPassword, login); - if (passwordResult) { - user = await ardrive.getUser(loginPassword, login); - console.log('Before we get syncing...'); - - // Allow the user to add other drives - await cli.promptToAddOrCreatePersonalPrivateDrive(user); - await cli.promptToAddOrCreatePersonalPublicDrive(user); - await cli.promptToAddSharedPublicDrive(user); - - // Allow the user to change sync location - const newSyncFolderPath: string = await cli.promptToChangeSyncFolderPath(user.syncFolderPath); - if (newSyncFolderPath != 'Skipped') { - console.log('Updating to new sync folder path ', newSyncFolderPath); - const result = await ardrive.updateUserSyncFolderPath(user.login, newSyncFolderPath); - if (result === 'Success') { - console.log('Successfully moved Sync folder path to %s', newSyncFolderPath); - - // Update current user object - user.syncFolderPath = newSyncFolderPath; - } else { - console.log('Error moving Sync folder path. Continuing to use %s', user.syncFolderPath); - } - } - - // Allow the user to remove a shared, public or private drive - await cli.promptToRemoveDrive(user.login); - - // Allow the user to change the auto approve setting - user.autoSyncApproval = await cli.promptForAutoSyncApproval(); - await ardrive.setProfileAutoSyncApproval(user.autoSyncApproval, user.login); - } else { - console.log('You have entered a bad password for this ArDrive... Goodbye'); - return 0; - } - } - - // Initialize Drives - await ardrive.setupDrives(user.login, user.syncFolderPath); - - // Get all of the public and private files for the user and store in the local database before starting folder watcher - await ardrive.getMyArDriveFilesFromPermaWeb(user); - - // Download any files from Arweave that need to be synchronized locally - await ardrive.downloadMyArDriveFiles(user); - - // Get latest wallet balance - const balance = await ardrive.getWalletBalance(user.walletPublicKey); - await ardrive.setProfileWalletBalance(+balance, login); - - // Initialize Chokidar Folder Watcher by providing the Sync Folder Path, Private and Public ArDrive IDs - ardrive.startWatchingFolders(user); - - // Continually check for things to process and actions to notify the user - let loop = true; - while (loop === true) { - try { - // Get all of the latest personal public and private drives for the user, and store in the local database - await ardrive.getAllMyPersonalDrives(user); - - // Get all of the public and private files for the user and store in the local database - await ardrive.getMyArDriveFilesFromPermaWeb(user); - - // Download any files from Arweave that need to be synchronized locally - await ardrive.downloadMyArDriveFiles(user); +import { Wallet, WalletDAO } from './wallet'; +import Arweave from 'arweave'; +import { ArDriveCommunityOracle } from './community/ardrive_community_oracle'; +import { ArDrive, ArDriveAnonymous } from './ardrive'; +import { ArFSDAO } from './arfsdao'; +import { ARDataPriceEstimator } from './utils/ar_data_price_estimator'; +import { ARDataPriceRegressionEstimator } from './utils/ar_data_price_regression_estimator'; +import { FeeMultiple } from './types'; +import { CommunityOracle } from './community/community_oracle'; +import { ArFSDAOAnonymous } from './arfsdao_anonymous'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { version: CLI_APP_VERSION } = require('../package.json'); + +if (require.main === module) { + // declare all parameters + import('./parameter_declarations').then(() => { + // declares the commands + import('./commands'); + }); +} - // Check the status of any files that may have been already been uploaded - await ardrive.checkUploadStatus(user.login); +export const CLI_APP_NAME = 'ArDrive-CLI'; +export { CLI_APP_VERSION }; - // Figure out the cost of the next batch of uploads, and ask the user if they want to approve - // If the size is -1, then the user does not have enough funds and the upload is skipped - const uploadBatch: ardrive.UploadBatch = await ardrive.getPriceOfNextUploadBatch(user.login); - if (uploadBatch.totalArDrivePrice > 0) { - if (await cli.promptForArDriveUpload(login, uploadBatch, user.autoSyncApproval)) { - await ardrive.uploadArDriveFiles(user); - } - } +// TODO: Make configurable from CLI +export const cliArweave = Arweave.init({ + host: 'arweave.net', // Arweave Gateway + //host: 'arweave.dev', // Arweave Dev Gateway + port: 443, + protocol: 'https', + timeout: 600000 +}); - // Resolve and download conflicts, and process on the next batch - fileDownloadConflicts = await ardrive.getMyFileDownloadConflicts(user.login); - if (fileDownloadConflicts) { - fileDownloadConflicts.forEach(async (fileDownloadConflict: ardrive.ArFSFileMetaData) => { - const response = await cli.promptForFileOverwrite(fileDownloadConflict.filePath); - await ardrive.resolveFileDownloadConflict( - response, - fileDownloadConflict.fileName, - fileDownloadConflict.filePath, - fileDownloadConflict.id.toString() - ); - }); - } +export const cliWalletDao = new WalletDAO(cliArweave, CLI_APP_NAME, CLI_APP_VERSION); - // Update date - const today = new Date(); - const date = `${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`; - const time = `${today.getHours()}:${today.getMinutes()}:${today.getSeconds()}`; - const dateTime = `${date} ${time}`; +export interface ArDriveSettingsAnonymous { + arweave?: Arweave; +} +export interface ArDriveSettings extends ArDriveSettingsAnonymous { + wallet: Wallet; + walletDao?: WalletDAO; + priceEstimator?: ARDataPriceEstimator; + communityOracle?: CommunityOracle; + feeMultiple?: FeeMultiple; + dryRun?: boolean; + arfsDao?: ArFSDAO; +} - // Get the latest balance of the loaded wallet. - const balance = await ardrive.getWalletBalance(user.walletPublicKey); - await ardrive.setProfileWalletBalance(+balance, login); - console.log('%s Syncronization completed. Current AR Balance: %s', dateTime, balance); - await ardrive.sleep(30000); - } catch (err) { - console.log(err); - loop = false; - } - } - return 0; +export function arDriveFactory({ + arweave = cliArweave, + priceEstimator = new ARDataPriceRegressionEstimator(), + communityOracle = new ArDriveCommunityOracle(arweave), + wallet, + walletDao = cliWalletDao, + dryRun, + feeMultiple, + arfsDao = new ArFSDAO(wallet, arweave, dryRun, CLI_APP_NAME, CLI_APP_VERSION) +}: ArDriveSettings): ArDrive { + return new ArDrive( + wallet, + walletDao, + arfsDao, + communityOracle, + CLI_APP_NAME, + CLI_APP_VERSION, + priceEstimator, + feeMultiple, + dryRun + ); } -function displayBanner() { - console.log(' █████╗ ██████╗ ██████╗ ██████╗ ██╗██╗ ██╗███████╗'); - console.log(' ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██║██║ ██║██╔════╝'); - console.log(' ███████║██████╔╝██║ ██║██████╔╝██║██║ ██║█████╗ '); - console.log(' ██╔══██║██╔══██╗██║ ██║██╔══██╗██║╚██╗ ██╔╝██╔══╝ '); - console.log(' ██║ ██║██║ ██║██████╔╝██║ ██║██║ ╚████╔╝ ███████╗'); - console.log(' ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚══════╝'); - console.log(' '); - console.log(' ██████╗ ███████╗████████╗ █████╗ '); - console.log(' ██╔══██╗██╔════╝╚══██╔══╝██╔══██╗ '); - console.log(' ██████╔╝█████╗ ██║ ███████║ '); - console.log(' ██╔══██╗██╔══╝ ██║ ██╔══██║ '); - console.log(' ██████╔╝███████╗ ██║ ██║ ██║ '); - console.log(' ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ '); - console.log(''); +export function arDriveAnonymousFactory( + settings: ArDriveSettingsAnonymous = { + arweave: cliArweave + } +): ArDriveAnonymous { + return new ArDriveAnonymous(new ArFSDAOAnonymous(settings.arweave ?? cliArweave, CLI_APP_NAME, CLI_APP_VERSION)); } -displayBanner(); -main(); diff --git a/src/parameter_declarations.ts b/src/parameter_declarations.ts new file mode 100644 index 00000000..971ca69b --- /dev/null +++ b/src/parameter_declarations.ts @@ -0,0 +1,238 @@ +import { Parameter } from './CLICommand/parameter'; + +export const WalletFileParameter = 'walletFile'; +export const SeedPhraseParameter = 'seedPhrase'; +export const PrivateParameter = 'private'; +export const UnsafeDrivePasswordParameter = 'unsafeDrivePassword'; +export const DriveNameParameter = 'driveName'; +export const FolderNameParameter = 'folderName'; +export const DriveKeyParameter = 'driveKey'; +export const AddressParameter = 'address'; +export const DriveIdParameter = 'driveId'; +export const ArAmountParameter = 'arAmount'; +export const DestinationAddressParameter = 'destAddress'; +export const TransactionIdParameter = 'txId'; +export const ConfirmationsParameter = 'confirmations'; +export const FolderIdParameter = 'folderId'; +export const FileIdParameter = 'fileId'; +export const ParentFolderIdParameter = 'parentFolderId'; +export const LocalFilePathParameter = 'localFilePath'; +export const DestinationFileNameParameter = 'destFileName'; +export const LocalFilesParameter = 'localFiles'; +export const GetAllRevisionsParameter = 'getAllRevisions'; +export const AllParameter = 'all'; +export const MaxDepthParameter = 'maxDepth'; +export const BoostParameter = 'boost'; +export const DryRunParameter = 'dryRun'; +export const NoVerifyParameter = 'verify'; // commander maps --no-x style params to options.x and always includes in options + +// Aggregates for convenience +export const DriveCreationPrivacyParameters = [ + PrivateParameter, + UnsafeDrivePasswordParameter, + WalletFileParameter, + SeedPhraseParameter +]; +export const DrivePrivacyParameters = [DriveKeyParameter, ...DriveCreationPrivacyParameters]; +export const TreeDepthParams = [AllParameter, MaxDepthParameter]; + +/** + * Note: importing this file will declare all the above parameters + */ + +Parameter.declare({ + name: WalletFileParameter, + aliases: ['-w', '--wallet-file'], + description: `the path to a JWK file on the file system representing your Arweave wallet +\t\t\t\t\t\t\t• Can't be used with --seed-phrase`, + forbiddenConjunctionParameters: [SeedPhraseParameter] +}); + +Parameter.declare({ + name: SeedPhraseParameter, + aliases: ['-s', '--seed-phrase'], + description: `a 12-word seed phrase representing a JWK +\t\t\t\t\t\t\t• Can't be used with --wallet-file`, + forbiddenConjunctionParameters: [WalletFileParameter] +}); + +Parameter.declare({ + name: UnsafeDrivePasswordParameter, + aliases: ['-p', '--unsafe-drive-password'], + description: `(OPTIONAL - NOT RECOMMENDED) the encryption password for the private drive +\t\t\t\t\t\t\t• When provided, creates/accesses a private drive. Public drive otherwise. +\t\t\t\t\t\t\t• Can NOT be used in conjunction with --private +\t\t\t\t\t\t\t• Can NOT be used in conjunction with --drive-key`, + forbiddenConjunctionParameters: [DriveKeyParameter, PrivateParameter] +}); + +Parameter.declare({ + name: PrivateParameter, + aliases: ['-P', '--private'], + description: `(OPTIONAL - RECOMMENDED OVER --unsafe-drive-password) specify to create/interact with private entities, i.e. drives, folders, and files +\t\t\t\t\t\t\t• obtains the drive password from the following locations, in precedence order: +\t\t\t\t\t\t\t\t- STDIN +\t\t\t\t\t\t\t\t- Environment variable ARDRIVE_DRIVE_PW +\t\t\t\t\t\t\t\t- Interactive, secure prompt +\t\t\t\t\t\t\t• Can NOT be used in conjunction with --unsafe-drive-password +\t\t\t\t\t\t\t• Can NOT be used in conjunction with --drive-key`, + forbiddenConjunctionParameters: [DriveKeyParameter, UnsafeDrivePasswordParameter], + type: 'boolean' +}); + +Parameter.declare({ + name: DriveKeyParameter, + aliases: ['-k', '--drive-key'], + description: `the base64 encoded symmetric encryption key (with '=' characters excised) for the drive (or parent drive in the case of folder operations) +\t\t\t\t\t\t\t• Required only for operations involving private drives or entities within them +\t\t\t\t\t\t\t• Can NOT be used in conjunction with --unsafe-drive-password +\t\t\t\t\t\t\t• Can NOT be used in conjunction with --private`, + forbiddenConjunctionParameters: [UnsafeDrivePasswordParameter, PrivateParameter] +}); + +Parameter.declare({ + name: DriveNameParameter, + aliases: ['-n', '--drive-name'], + description: `the name for the new drive`, + required: true +}); + +Parameter.declare({ + name: FolderNameParameter, + aliases: ['-n', '--folder-name'], + description: `the name for the new folder`, + required: true +}); + +Parameter.declare({ + name: AddressParameter, + aliases: ['-a', '--address'], + description: 'the 43-character Arweave wallet address to use for lookups' +}); + +Parameter.declare({ + name: DriveIdParameter, + aliases: ['-d', '--drive-id'], + description: 'the ArFS entity ID associated with the target drive', + required: true +}); + +Parameter.declare({ + name: ArAmountParameter, + aliases: ['-a', '--ar-amount'], + description: `amount of AR to send to the --dest-address +\t\t\t\t\t\t\t• does NOT include transaction mining base rewards`, + required: true +}); + +Parameter.declare({ + name: DestinationAddressParameter, + aliases: ['-d', '--dest-address'], + description: 'the 43-character Arweave wallet address to which AR should be sent', + required: true +}); + +Parameter.declare({ + name: TransactionIdParameter, + aliases: ['-t', '--tx-id'], + description: 'The transaction id to check the status of in the mempool', + required: true +}); + +Parameter.declare({ + name: ConfirmationsParameter, + aliases: ['-c', '--confirmations'], + description: 'Number of confirmations to determine if a transaction is mined' +}); + +Parameter.declare({ + name: ParentFolderIdParameter, + aliases: ['-F', '--parent-folder-id'], + description: `the ArFS folder ID for the folder in which this file will reside (i.e. its parent folder) +\t\t\t\t\t\t\t• To upload the file to the root of a drive, use the root folder ID of the drive`, + required: true +}); + +Parameter.declare({ + name: FolderIdParameter, + aliases: ['-f', '--folder-id'], + description: `the ArFS folder ID for the folder to query`, + required: true +}); + +Parameter.declare({ + name: FileIdParameter, + aliases: ['-f', '--file-id'], + description: `the ArFS file ID for the file to query`, + required: true +}); + +Parameter.declare({ + name: LocalFilePathParameter, + aliases: ['-l', '--local-file-path'], + description: `the path on the local filesystem for the file that will be uploaded` +}); + +Parameter.declare({ + name: DestinationFileNameParameter, + aliases: ['-d', '--dest-file-name'], + description: `(OPTIONAL) a destination file name to use when uploaded to ArDrive` +}); + +Parameter.declare({ + name: LocalFilesParameter, + aliases: ['--local-files'], + description: `(BETA) a path to a csv (tab delimited) file containing rows of data for the following columns: +\t\t\t\t\t\t\t• CSV Columns: +\t\t\t\t\t\t\t\t• local file path +\t\t\t\t\t\t\t\t• destination file name (optional) +\t\t\t\t\t\t\t\t• parent folder ID (optional) +\t\t\t\t\t\t\t\t\t• --parent-folder-id used, otherwise +\t\t\t\t\t\t\t• all parent folder IDs should reside in the same drive +\t\t\t\t\t\t\t• Can NOT be used in conjunction with --local-file-path`, + forbiddenConjunctionParameters: [LocalFilePathParameter] +}); + +Parameter.declare({ + name: GetAllRevisionsParameter, + aliases: ['--get-all-revisions'], + description: '(OPTIONAL) gets every revision of the entity', + type: 'boolean' +}); + +Parameter.declare({ + name: BoostParameter, + aliases: ['--boost'], + description: + '(OPTIONAL) a multiple of the base transaction mining reward that can be used to accelerate transaction mining. A multiple of 2.5 would boost a 100 Winston transaction reward to 250 Winston.' +}); + +Parameter.declare({ + name: DryRunParameter, + aliases: ['--dry-run'], + description: + '(OPTIONAL) Print the results of the transactions that would occur, and their potential tips and mining rewards, without sending the transactions.', + type: 'boolean' +}); + +Parameter.declare({ + name: AllParameter, + aliases: ['--all'], + description: `(OPTIONAL) gets all contents within this folder, including child files/folders`, + type: 'boolean', + forbiddenConjunctionParameters: [MaxDepthParameter] +}); + +Parameter.declare({ + name: MaxDepthParameter, + aliases: ['--max-depth'], + description: `(OPTIONAL) a non-negative integer value indicating the depth of the folder tree to list. 0 = specified folder's contents OR root folder for drives` +}); + +Parameter.declare({ + name: NoVerifyParameter, + aliases: ['--no-verify'], + description: + '(OPTIONAL) Derives a drive key for the given drive ID without verifying its correctness against the drive on chain.', + type: 'boolean' +}); diff --git a/src/private_key_data.ts b/src/private_key_data.ts new file mode 100644 index 00000000..bfd079d2 --- /dev/null +++ b/src/private_key_data.ts @@ -0,0 +1,118 @@ +import { deriveDriveKey, driveDecrypt, Utf8ArrayToStr } from 'ardrive-core-js'; +import { CipherIV, DriveID, DriveKey } from './types'; +import { JWKWallet } from './wallet'; + +type DriveIdKeyPair = { [key: string /* DriveID */]: DriveKey }; + +export type EntityMetaDataTransactionData = { [key: string]: string | number }; + +// Users may optionally supply any drive keys, a password, or a wallet +interface PrivateKeyDataParams { + readonly driveKeys?: DriveKey[]; + readonly password?: string; + readonly wallet?: JWKWallet; +} + +/** + * A utility class that uses optional private key data to safely decrypt metadata + * transaction data (the data JSON). Upon a successful decryption, the class + * will cache the verified driveId and driveKey as a pair for future use. + */ +export class PrivateKeyData { + private readonly password?: string; + private readonly wallet?: JWKWallet; + + // Drive IDs are paired with their Drive Keys upon successful decryption + // TODO: Migrate this to ArFS Cache so it can persist between commands + private readonly driveKeyCache: DriveIdKeyPair = {}; + + // Drive keys provided by the user are initially unverified + // until we successfully decrypt a drive with them + private unverifiedDriveKeys: DriveKey[]; + + constructor({ password, driveKeys, wallet }: PrivateKeyDataParams) { + if (password && !wallet) { + throw new Error('Password supplied without a wallet. Did you forget to include your wallet?'); + } + + if (password && driveKeys) { + throw new Error("Password and drive keys can't be used together. Please provide one or the other."); + } + + this.unverifiedDriveKeys = driveKeys ?? []; + this.password = password; + this.wallet = wallet; + } + + /** Safely decrypts a private data buffer into a decrypted transaction data */ + public async safelyDecryptToJson( + cipherIV: CipherIV, + driveId: DriveID, + dataBuffer: Buffer, + placeholder: T + ): Promise { + // Check for a cached key that is matching provided driveId first + const cachedDriveKey = this.driveKeyForDriveId(driveId); + if (cachedDriveKey) { + return this.decryptToJson(cipherIV, dataBuffer, cachedDriveKey); + } + + // Next, try any unverified drive keys provided by the user + for await (const driveKey of this.unverifiedDriveKeys) { + try { + const decryptedDriveJSON = await this.decryptToJson(cipherIV, dataBuffer, driveKey); + + // Correct key, add this pair to the cache + this.driveKeyCache[driveId] = driveKey; + this.unverifiedDriveKeys = this.unverifiedDriveKeys.filter((k) => k !== driveKey); + + return decryptedDriveJSON; + } catch { + // Wrong key, continue + } + } + + // Finally, if we have a password and a wallet, we can derive a drive key and try it + if (this.password && this.wallet) { + const derivedDriveKey: DriveKey = await deriveDriveKey( + this.password, + driveId, + JSON.stringify(this.wallet.getPrivateKey()) + ); + + try { + const decryptedDriveJSON = await this.decryptToJson(cipherIV, dataBuffer, derivedDriveKey); + + // Correct key, add this pair to the cache + this.driveKeyCache[driveId] = derivedDriveKey; + + return decryptedDriveJSON; + } catch (error) { + // Wrong key, continue + } + } + + // Decryption is not possible, return placeholder data + return placeholder; + } + + /** + * Decrypts a private data buffer into a decrypted transaction data + * + * @throws when the provided driveKey or cipher fails to decrypt the transaction data + */ + public async decryptToJson( + cipherIV: CipherIV, + encryptedDataBuffer: Buffer, + driveKey: DriveKey + ): Promise { + const decryptedDriveBuffer: Buffer = await driveDecrypt(cipherIV, driveKey, encryptedDataBuffer); + const decryptedDriveString: string = await Utf8ArrayToStr(decryptedDriveBuffer); + return JSON.parse(decryptedDriveString); + } + + /** Synchronously returns a driveKey from the cache by its driveId */ + public driveKeyForDriveId(driveId: DriveID): DriveKey | false { + return this.driveKeyCache[driveId] ?? false; + } +} diff --git a/src/prompts.ts b/src/prompts.ts deleted file mode 100644 index 343facf2..00000000 --- a/src/prompts.ts +++ /dev/null @@ -1,519 +0,0 @@ -import { - getCachedWallet, - createArDriveWallet, - checkOrCreateFolder, - backupWallet, - addDriveToDriveTable, - createNewPublicDrive, - createNewPrivateDrive, - addSharedPublicDrive, - deleteDrive, - sanitizePath, - getAllDrivesByLoginFromDriveTable, - getAllUnSyncedPersonalDrivesByLoginFromDriveTable, - getProfileWalletBalance, - setDriveToSync, - ArDriveUser, - ArFSDriveMetaData, - UploadBatch, - getAllMyPersonalDrives, - checkFileExistsSync -} from 'ardrive-core-js'; - -import promptSync from 'prompt-sync'; -const prompt = promptSync({ sigint: true }); - -import passwordPrompt from 'prompts'; - -// Get the users wallet or create a new one -const promptForWallet = (): string => { - // Create new or import Arweave wallet - const existingWallet: string = prompt( - ' Do you have an existing Arweave Wallet .json file? (default is Yes) Y/N ' - ); - return existingWallet; -}; - -// Get path to local wallet and return that wallet public and private key -const promptForLocalWalletPath = (): string => { - console.log( - 'Please enter the path of your existing Arweave Wallet JSON file eg. C:\\Source\\ardrive_test_key.json' - ); - const existingWalletPath: string = prompt(' Wallet Path: '); - - // This should be updated to check if valid .json file - const validPath = checkFileExistsSync(existingWalletPath); - if (validPath) { - return existingWalletPath; - } else { - console.log('File path is invalid!'); - return promptForLocalWalletPath(); - } -}; - -// Get the location to backup the new wallet -const promptForBackupWalletPath = (): string => { - console.log('Please enter the path to backup your new ArDrive Wallet e.g C:\\My_Safe_Location'); - const backupFolderPath: string = prompt(' ArDrive Wallet Backup Folder Path (hit enter for current directory): '); - if (backupFolderPath === '') { - return backupFolderPath; - } else { - const validPath: string = checkOrCreateFolder(backupFolderPath); - if (validPath === '0') { - return promptForBackupWalletPath(); - } - return backupFolderPath; - } -}; - -// Get the ArDrive owner nickname -export const promptForLogin = async (): Promise => { - console.log( - 'Please enter your ArDrive Login Name. If no name exists, we will begin to setup a ArDrive Profile for you.' - ); - const login = prompt(' Login Name: '); - if (login === '') { - console.log(' Invalid entry!'); - return await promptForLogin(); - } - return login; -}; - -export const promptForAutoSyncApproval = async (): Promise => { - const autoSyncApproval: string = prompt( - ' Would you like to automatically approve the fees for all uploads to Arweave? (Default is No) Y/N ' - ); - if (autoSyncApproval.toUpperCase() === 'Y') { - console.log(' Data will be uploaded without fee approval.'); - return 1; // enable autoSyncApproval - } else { - return 0; // disable autoSyncApproval - } -}; - -// Prompts the user to change their sync folder path and move all drives, folders and files. -export const promptToChangeSyncFolderPath = async (currentSyncFolderPath: string): Promise => { - const changeSyncFolderPathApproval: string = prompt( - ' Would you like to change your local sync folder and move all files? (Default is No) Y/N ' - ); - if (changeSyncFolderPathApproval.toUpperCase() === 'Y') { - const newSyncFolderPath: string = prompt( - ' Enter your new ArDrive Sync Folder Path (hit enter for current directory): ' - ); - if (newSyncFolderPath === currentSyncFolderPath) { - console.log(' Please enter in a new path.'); - return promptToChangeSyncFolderPath(currentSyncFolderPath); - } else { - return newSyncFolderPath; - } - } - return 'Skipped'; -}; - -// Asks the user to delete a Drive. If the drive ID is invalid the user will get prompted again -export const promptToRemoveDrive = async (login: string): Promise => { - const driveRemoval: string = prompt( - ' Would you like to remove a locally synced Public, Private or Shared Drive? (Default is No) Y/N ' - ); - if (driveRemoval.toUpperCase() === 'Y') { - console.log(' Please select the local drive you would like to stop synchronizing and remove.'); - let i = 0; - const drives: ArFSDriveMetaData[] = await getAllDrivesByLoginFromDriveTable(login); - drives.forEach((drive: ArFSDriveMetaData) => { - const createdOn = new Date(+drive.unixTime * 1000); - console.log('%s: %s', i, drive.driveName); - console.log( - ' %s | %s | Created On: %s | Drive Id: %s', - drive.driveSharing, - drive.drivePrivacy, - createdOn, - drive.driveId - ); - i += 1; - }); - const choice: string = prompt(' Please select which number: '); - if (+choice <= i) { - console.log('Deleting drive %s', drives[+choice].driveName); - await deleteDrive(drives[+choice].driveId); - return 'Deleted'; - } else { - console.log('Invalid Drive selection'); - return 'Invalid'; - } - } - return 'Skipped'; -}; - -// Asks the user to add a Public Drive ID. If the drive ID is invalid, the user will get prompted again -export const promptToAddSharedPublicDrive = async (user: ArDriveUser): Promise => { - const newDrive: string = prompt(' Would you like to add a Public Shared Drive? (default is No) Y/N '); - if (newDrive.toUpperCase() === 'Y') { - console.log(' Please enter the Drive Id you would like to add eg. jfj70d03-2353-4bb4-8606-d9db1c40772z'); - const driveId: string = prompt(' Drive Id: '); - const result: string = await addSharedPublicDrive(user, driveId); - // If it is an invalid drive id, we will reprompt - if (result === 'Invalid') { - console.log(' The Drive Id, %s, cannot be found', driveId); - return await promptToAddSharedPublicDrive(user); - } else { - console.log('Added Drive %s!', result); - return 'Added'; - } - } else { - return 'Skipped'; - } -}; - -// Asks the user if they want to add or create a new personal private drive -export const promptToAddOrCreatePersonalPrivateDrive = async (user: ArDriveUser): Promise => { - const addDrive: string = prompt(' Would you like to add a Private Personal Drive? (default is No) Y/N '); - if (addDrive.toUpperCase() === 'Y') { - const privateDrives = await getAllUnSyncedPersonalDrivesByLoginFromDriveTable(user.login, 'private'); - if (privateDrives.length > 0) { - const privateDrive: ArFSDriveMetaData = await promptForArDriveId(user.login, privateDrives, 'private'); - await addDriveToDriveTable(privateDrive); - await setDriveToSync(privateDrive.driveId); - return 'Added Drive'; - } else { - let driveName: string = prompt(' Please enter a name for your new private drive: '); - driveName = await sanitizePath(driveName); - if (driveName !== '') { - const privateDrive = await createNewPrivateDrive(user.login, driveName); - await addDriveToDriveTable(privateDrive); - return 'Created Drive'; - } else { - console.log(' Invalid drive name!'); - return await promptToAddOrCreatePersonalPrivateDrive(user); - } - } - } - return 'None Added'; -}; - -// Asks the user if they want to add or create a new personal private drive -export const promptToAddOrCreatePersonalPublicDrive = async (user: ArDriveUser): Promise => { - const addDrive: string = prompt(' Would you like to add a Public Personal Drive? (default is No) Y/N '); - if (addDrive.toUpperCase() === 'Y') { - // Load an existing default Public ArDrive - const publicDrives = await getAllUnSyncedPersonalDrivesByLoginFromDriveTable(user.login, 'public'); - if (publicDrives.length > 0) { - const publicDrive: ArFSDriveMetaData = await promptForArDriveId(user.login, publicDrives, 'public'); - await addDriveToDriveTable(publicDrive); - await setDriveToSync(publicDrive.driveId); - return 'Added Drive'; - } else { - let driveName: string = prompt(' Please enter a name for your new public drive: '); - driveName = await sanitizePath(driveName); - if (driveName !== '') { - const publicDrive = await createNewPublicDrive(user.login, driveName); - await addDriveToDriveTable(publicDrive); - return 'Created Drive'; - } else { - console.log(' Invalid drive name!'); - return await promptToAddOrCreatePersonalPublicDrive(user); - } - } - } - return 'None Added'; -}; - -// Ask the user which public or private drive they should use. -const promptForArDriveId = async ( - login: string, - drives: ArFSDriveMetaData[], - drivePrivacy: string -): Promise => { - console.log('Existing %s Drive IDs have been found for your Arweave wallet.', drivePrivacy); - console.log('Either pick an existing %s Drive or create a new one', drivePrivacy); - let i = 0; - drives.forEach((drive: ArFSDriveMetaData) => { - const createdOn = new Date(+drive.unixTime * 1000); - console.log('%s: %s', i, drive.driveName); - console.log(' Created On: %s | Drive Id: %s', createdOn, drive.driveId); - i += 1; - }); - console.log('%s: Create a new %s Drive', i, drivePrivacy); - const choice: string = prompt(' Please select which number: '); - if (+choice === i) { - let driveName: string = prompt(' Please enter in a new name for your new drive: '); - driveName = await sanitizePath(driveName); - console.log('Drive name is %s', driveName); - if (driveName === '') { - console.log(' Invalid drive name!'); - return await promptForArDriveId(login, drives, drivePrivacy); - } - if (drivePrivacy === 'public') { - const newDrive = await createNewPublicDrive(login, driveName); - drives.push(newDrive); - } else if (drivePrivacy === 'private') { - const newDrive = await createNewPrivateDrive(login, driveName); - drives.push(newDrive); - } - drives[+choice].login = login; - return drives[+choice]; - } else if (+choice < i && +choice >= 0 && choice !== null) { - try { - drives[+choice].login = login; - return drives[+choice]; - } catch (err) { - console.log(' Invalid selection!'); - return await promptForArDriveId(login, drives, drivePrivacy); - } - } else { - console.log(' Invalid selection!'); - return await promptForArDriveId(login, drives, drivePrivacy); - } -}; - -// Get the ArDrive Sync Folder path -// Will handle error checking and ensuring it is a valid path -const promptForSyncFolderPath = (): string => { - // Setup ArDrive Sync Folder - console.log('Please enter the path of your local root ArDrive folder e.g D:\\ArDriveSync.'); - let syncFolderPath: string = prompt(' ArDrive Sync Folder Path (hit enter for current directory): '); - if (syncFolderPath === '') { - syncFolderPath = process.cwd(); - return syncFolderPath; - } else { - const validPath = checkOrCreateFolder(syncFolderPath); - if (validPath === '0') { - return promptForSyncFolderPath(); - } - return syncFolderPath; - } -}; - -// Setup ArDrive Login Password -// Modify to check for password strength -const promptForNewLoginPassword = async (): Promise => { - let password = ''; - console.log( - 'Strong passwords should have at least 15 characters, with upper/lower case letters, numbers and symbols.' - ); - console.log('This password can NOT be changed, so choose it wisely and store it carefully.'); - const newLoginPasswordResponse = await passwordPrompt({ - type: 'text', - name: 'password', - style: 'password', - message: ' Please enter your strong ArDrive Login password:' - }); - const checkLoginPasswordResponse = await passwordPrompt({ - type: 'text', - name: 'password', - style: 'password', - message: ' Please re-enter your strong ArDrive Login password: ' - }); - - // Lets check to ensure the passwords match - if (newLoginPasswordResponse.password !== checkLoginPasswordResponse.password) { - console.log('The passwords you have entered do not match!'); - password = await promptForNewLoginPassword(); - } else { - password = newLoginPasswordResponse.password; - } - return password; -}; - -// Prompts the user to enter a login password -const promptForLoginPassword = async (): Promise => { - const loginPasswordResponse = await passwordPrompt({ - type: 'text', - name: 'password', - style: 'password', - message: ' Please enter your ArDrive Login password: ' - }); - if (loginPasswordResponse.password === '') { - console.log(' Invalid password entered!'); - return await promptForLoginPassword(); - } - return loginPasswordResponse.password; -}; - -// Collects all of the information needed to create a new user -// This includes Wallet, Existing ArDrives and Sync Folder Path -const promptForNewUserInfo = async (login: string): Promise => { - let wallet; - const user: ArDriveUser = { - login, - dataProtectionKey: '', - walletPrivateKey: '', - walletPublicKey: '', - syncFolderPath: '', - autoSyncApproval: 0 - }; - console.log(''); - console.log( - ' Welcome to ArDrive ' - ); - console.log( - '--------------------------------------------------------------------------------------------------------' - ); - console.log( - 'Your secure and private... ' - ); - console.log( - ' censorship-resistant... ' - ); - console.log( - ' pay-as-you-go... ' - ); - console.log( - ' decentralized... ' - ); - console.log( - ' and PERMANENT hard drive! ' - ); - console.log( - '--------------------------------------------------------------------------------------------------------' - ); - console.log(''); - console.log( - 'ArDrive is a simple, yet robust app that protects and syncs your data to and from the decentralized cloud.' - ); - console.log(''); - console.log('- No subscription needed! Pay once to store your personal files, photos, videos and apps.'); - console.log(' PERMANENTLY!'); - console.log( - '- Your Private Drives are encrypted, so noone including the ArDrive community will ever be able to read your content.' - ); - console.log(' ONLY YOU!'); - console.log('- Your Public Drives are open for anyone on the internet to view or download, forever.'); - console.log(' POST CAREFULLY!'); - console.log( - '- Any data you upload is stored and secured on an immutable and decentralized blockchain network, powered by Arweave.' - ); - console.log(' DELETING IS NOT AN OPTION!'); - console.log(''); - - try { - // Get a strong password for login - // TO DO - make password strong!! - console.log('Your Arweave Wallet and ArDrive Login password will be combined to encrypt all your private data'); - console.log(' Do NOT share them!'); - console.log(' Do NOT lose them!'); - console.log(' Do NOT save them in public places!'); - console.log(' So please KEEP THEM SECRET and KEEP THEM SAFE!!!'); - console.log(''); - - // Get the user's wallet information - console.log('Your Arweave wallet is used to pay for all data you upload through ArDrive.'); - console.log('Want to learn more? Head to https://arweave.org'); - const existingWallet = promptForWallet(); - if (existingWallet.toLowerCase() === 'n') { - wallet = await createArDriveWallet(); - const backupWalletPath: string = promptForBackupWalletPath(); - await backupWallet(backupWalletPath, wallet, login); - } else { - const existingWalletPath: string = promptForLocalWalletPath(); - wallet = await getCachedWallet(existingWalletPath); - } - // Set the wallet in the users profile - user.walletPrivateKey = JSON.stringify(wallet.walletPrivateKey); - user.walletPublicKey = wallet.walletPublicKey; - console.log(''); - - // Get strong login password - console.log( - 'Already set up some Private Drives with this wallet?? Please reuse that password here to unlock them.' - ); - const loginPassword: string = await promptForNewLoginPassword(); - console.log(''); - - // Get the local root folder that will contain all of the user's drives - console.log('Your ArDrive Sync Folder is the root directory for any of your Public, Private or Shared Drives.'); - user.syncFolderPath = promptForSyncFolderPath(); - console.log('Using %s', user.syncFolderPath); - - // Load an existing default Private ArDrive - console.log(''); - console.log("Let's get you ready to start syncing by setting up some Drives."); - console.log('Each Drive will be created under your ArDrive Sync Folder using its specified name.'); - console.log("A new folder will be created if it doesn't exist e.g D:\\ArDriveSync\\MyDrive_Name"); - - // Set the data protection key used for all data encryption. - // The key is based on the uesr's login - user.dataProtectionKey = loginPassword; - user.walletPrivateKey = JSON.stringify(wallet.walletPrivateKey); - - // Sync all of the Drives that a user has created - await getAllMyPersonalDrives(user); - - console.log(''); - console.log( - 'Private Drives encrypt and protect all of your personal data, ensuring only you can read and write to it.' - ); - await promptToAddOrCreatePersonalPrivateDrive(user); - - console.log(''); - console.log( - 'Public Drives are open and read-only to the entire internet. Anything uploaded here is accessable forever on the PermaWeb!' - ); - await promptToAddOrCreatePersonalPublicDrive(user); - - console.log(''); - console.log( - "Shared Public Drives can also be added, allowing you to download (but not upload) someone else's Public Drive" - ); - await promptToAddSharedPublicDrive(user); - - console.log(''); - console.log('All data uploads are paid with the native Arweave token (AR)'); - user.autoSyncApproval = await promptForAutoSyncApproval(); - - return user; - } catch (err) { - console.log(err); - return user; - } -}; - -// Asks the user to approve an upload to Arweave -const promptForArDriveUpload = async ( - login: string, - uploadBatch: UploadBatch, - autoSyncApproval: number -): Promise => { - console.log( - 'Uploading %s files, %s folders and %s changes (%s) to the Permaweb with estimated cost: %s AR / %s USD', - uploadBatch.totalNumberOfFileUploads, - uploadBatch.totalNumberOfFolderUploads, - uploadBatch.totalNumberOfMetaDataUploads, - uploadBatch.totalSize, - uploadBatch.totalArDrivePrice, - uploadBatch.totalUSDPrice - ); - // Ensure the user has enough AR to pay for this upload. If not, do not proceed. - const profile = await getProfileWalletBalance(login); - if (profile.walletBalance >= uploadBatch.totalArDrivePrice) { - if (autoSyncApproval) { - return true; - } - const readyToUpload = prompt('Upload all unsynced files? Y/N '); - if (readyToUpload.toUpperCase() === 'Y') { - return true; - } else { - // User selects NO so we do not upload - return false; - } - } else { - // Not enough AR to upload - console.log('Oops! You do not have enough AR to upload! Your balance is %s AR.', profile.walletBalance); - console.log('Please remove your recent files, or add more AR to your wallet and try again.'); - return false; - } -}; - -// Prompt the user if they want to rename, overwrite or ignore file conflict -const promptForFileOverwrite = async (filePath: string): Promise => { - console.log('A file has been found on the Permaweb with a different hash but the same file name %s', filePath); - const conflict = prompt('Would you like to Overwrite (O) Rename (R) or Ignore (I): '); - return conflict; -}; - -export { - promptForArDriveUpload, - promptForFileOverwrite, - promptForNewUserInfo, - promptForNewLoginPassword, - promptForLoginPassword -}; diff --git a/src/query.ts b/src/query.ts new file mode 100644 index 00000000..ded9e394 --- /dev/null +++ b/src/query.ts @@ -0,0 +1,83 @@ +import { ArweaveAddress } from './arweave_address'; + +const ownerFragment = ` + owner { + address + } +`; + +const nodeFragment = ` + node { + id + tags { + name + value + } + ${ownerFragment} + } +`; + +const edgesFragment = (singleResult: boolean) => ` + edges { + ${singleResult ? '' : 'cursor'} + ${nodeFragment} + } +`; + +const pageInfoFragment = ` + pageInfo { + hasNextPage + } +`; + +export type GQLQuery = { query: string }; + +export const ASCENDING_ORDER = 'HEIGHT_ASC'; +export const DESCENDING_ORDER = 'HEIGHT_DESC'; +const latestResult = 1; +const pageLimit = 100; + +type Sort = typeof ASCENDING_ORDER | typeof DESCENDING_ORDER; + +export interface BuildGQLQueryParams { + tags: { name: string; value: string | string[] }[]; + cursor?: string; + owner?: ArweaveAddress; + sort?: Sort; +} + +/** + * Builds a GraphQL query which will only return the latest result + * + * TODO: Add parameters and support for all possible upcoming GQL queries + * + * @example + * const query = buildQuery([{ name: 'Folder-Id', value: folderId }]); + */ +export function buildQuery({ tags = [], cursor, owner, sort = DESCENDING_ORDER }: BuildGQLQueryParams): GQLQuery { + let queryTags = ``; + + tags.forEach((t) => { + queryTags = `${queryTags} + { name: "${t.name}", values: ${Array.isArray(t.value) ? JSON.stringify(t.value) : `"${t.value}"`} }`; + }); + + const singleResult = cursor === undefined; + + return { + query: `query { + transactions( + first: ${singleResult ? latestResult : pageLimit} + sort: ${sort} + ${singleResult ? '' : `after: "${cursor}"`} + ${owner === undefined ? '' : `owners: ["${owner}"]`} + tags: [ + ${queryTags} + ] + ) { + ${singleResult ? '' : pageInfoFragment} + ${edgesFragment(singleResult)} + } + }` + }; +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 00000000..9a89f01e --- /dev/null +++ b/src/types.ts @@ -0,0 +1,45 @@ +export const ArFS_O_11 = '0.11'; +export const CURRENT_ARFS_VERSION = ArFS_O_11; +export const DEFAULT_APP_NAME = 'ArDrive-Core'; +export const DEFAULT_APP_VERSION = '1.0.0'; + +export type PublicKey = string; +export type SeedPhrase = string; + +/** TODO: Use big int library on Winston types */ +export type Winston = string; // TODO: make a type that checks validity +export type NetworkReward = Winston; + +export type FolderID = string; +export type FileID = string; +export type DriveID = string; +export type EntityID = DriveID | FolderID | FileID; + +export type CipherIV = string; +export type EntityKey = Buffer; +export type DriveKey = EntityKey; +export type FileKey = EntityKey; + +export type UnixTime = number; +export type ByteCount = number; +export type DataContentType = string; + +export type TransactionID = string; // TODO: make a type that checks lengths + +export interface ArDriveCommunityTip { + tipPercentage: number; + minWinstonFee: number; // TODO: Align with Winston type? +} + +export type TipType = 'data upload'; + +export type GQLCursor = string; +export type FeeMultiple = number; // TODO: assert always >= 1.0 + +export type RewardSettings = { + reward?: Winston; + feeMultiple?: FeeMultiple; +}; + +type Omit = Pick>; +export type MakeOptional = Omit & Partial; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 00000000..e0f420d5 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,21 @@ +import { JWKWallet, Wallet } from './wallet'; +import * as fs from 'fs'; +import fetch from 'node-fetch'; +import { JWKInterface } from 'ardrive-core-js'; + +export function readJWKFile(path: string): Wallet { + const walletFileData = fs.readFileSync(path, { encoding: 'utf8', flag: 'r' }); + const walletJSON = JSON.parse(walletFileData); + const walletJWK = walletJSON as JWKInterface; + const wallet = new JWKWallet(walletJWK); + return wallet; +} + +export async function fetchMempool(): Promise { + const response = await fetch('https://arweave.net/tx/pending'); + return response.json(); +} + +export function urlEncodeHashKey(keyBuffer: Buffer): string { + return keyBuffer.toString('base64').replace('=', ''); +} diff --git a/src/utils/ar_data_price.test.ts b/src/utils/ar_data_price.test.ts new file mode 100644 index 00000000..b6bca565 --- /dev/null +++ b/src/utils/ar_data_price.test.ts @@ -0,0 +1,50 @@ +import { expect } from 'chai'; +import { ARDataPrice } from './ar_data_price'; + +describe('ARDataPrice class', () => { + it('constructor throws an exception when a negative data volume is provided', () => { + expect(() => new ARDataPrice(-1, -1)).to.throw(Error); + expect(() => new ARDataPrice(-1, 0)).to.throw(Error); + expect(() => new ARDataPrice(-1, 0.5)).to.throw(Error); + expect(() => new ARDataPrice(-1, 1)).to.throw(Error); + }); + + it('constructor throws an exception when a non-integer data volume is provided', () => { + expect(() => new ARDataPrice(0.5, -1)).to.throw(Error); + expect(() => new ARDataPrice(0.5, 0)).to.throw(Error); + expect(() => new ARDataPrice(0.5, 0.5)).to.throw(Error); + expect(() => new ARDataPrice(0.5, 1)).to.throw(Error); + }); + + it('constructor throws an exception when a negative Winston value is provided', () => { + expect(() => new ARDataPrice(-1, -1)).to.throw(Error); + expect(() => new ARDataPrice(0, -1)).to.throw(Error); + expect(() => new ARDataPrice(0.5, -1)).to.throw(Error); + expect(() => new ARDataPrice(1, -1)).to.throw(Error); + }); + + it('constructor throws an exception when a non-integer Winston value is provided', () => { + expect(() => new ARDataPrice(-1, 0.5)).to.throw(Error); + expect(() => new ARDataPrice(0, 0.5)).to.throw(Error); + expect(() => new ARDataPrice(0.5, 0.5)).to.throw(Error); + expect(() => new ARDataPrice(1, 0.5)).to.throw(Error); + }); + + it('constructs a valid object when zeros are provided', () => { + const actual = new ARDataPrice(0, 0); + expect(actual.numBytes).to.equal(0); + expect(actual.winstonPrice).to.equal(0); + }); + + it('constructs a valid object when non-zero values are provided', () => { + const actual = new ARDataPrice(1, 1); + expect(actual.numBytes).to.equal(1); + expect(actual.winstonPrice).to.equal(1); + }); + + it('constructs a valid object when max Int values are provided', () => { + const actual = new ARDataPrice(Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER); + expect(actual.numBytes).to.equal(Number.MAX_SAFE_INTEGER); + expect(actual.winstonPrice).to.equal(Number.MAX_SAFE_INTEGER); + }); +}); diff --git a/src/utils/ar_data_price.ts b/src/utils/ar_data_price.ts new file mode 100644 index 00000000..c30dfefb --- /dev/null +++ b/src/utils/ar_data_price.ts @@ -0,0 +1,22 @@ +import { ByteCount } from '../types'; + +/** + * Immutable data container class representing a market price in Winston for a particular volume + * of data that enforces valid number ranges for byte counts and Winston price values. + */ +export class ARDataPrice { + /** + * @returns an ARDataPrice instance with the given byte count and Winston amount + * @throws {@link Error} if negative or non-integer values are provided for either value + */ + constructor(readonly numBytes: ByteCount, readonly winstonPrice: number) { + if (numBytes < 0 || !Number.isInteger(numBytes) || winstonPrice < 0 || !Number.isInteger(winstonPrice)) { + throw new Error( + `numBytes (${numBytes}) and winstonPrice (${winstonPrice}) should be non-negative integer values.` + ); + } + + this.numBytes = numBytes; + this.winstonPrice = winstonPrice; + } +} diff --git a/src/utils/ar_data_price_estimator.ts b/src/utils/ar_data_price_estimator.ts new file mode 100644 index 00000000..580551d1 --- /dev/null +++ b/src/utils/ar_data_price_estimator.ts @@ -0,0 +1,61 @@ +import type { ArDriveCommunityTip, ByteCount } from '../types'; + +export const arPerWinston = 0.000_000_000_001; + +export interface ARDataPriceEstimator { + getBaseWinstonPriceForByteCount(byteCount: ByteCount): Promise; + getARPriceForByteCount: (byteCount: ByteCount, arDriveCommunityTip: ArDriveCommunityTip) => Promise; +} + +export abstract class AbstractARDataPriceEstimator implements ARDataPriceEstimator { + abstract getBaseWinstonPriceForByteCount(byteCount: ByteCount): Promise; + + /** + * Estimates the price in AR for a given byte count, including the ArDrive community tip + */ + async getARPriceForByteCount( + byteCount: ByteCount, + { minWinstonFee, tipPercentage }: ArDriveCommunityTip + ): Promise { + const winstonPrice = await this.getBaseWinstonPriceForByteCount(byteCount); + const communityWinstonFee = Math.max(winstonPrice * tipPercentage, minWinstonFee); + + const totalWinstonPrice = winstonPrice + communityWinstonFee; + + return totalWinstonPrice * arPerWinston; + } +} + +export interface ARDataCapacityEstimator { + getByteCountForWinston: (winston: number) => Promise; + getByteCountForAR: (arPrice: number, arDriveCommunityTip: ArDriveCommunityTip) => Promise; +} + +// prettier-ignore +export abstract class AbstractARDataPriceAndCapacityEstimator extends AbstractARDataPriceEstimator implements ARDataCapacityEstimator +{ + abstract getByteCountForWinston(winston: number): Promise; + + /** + * Estimates the number of bytes that can be stored for a given amount of AR + * + * @remarks Returns 0 bytes when the price does not cover minimum ArDrive community fee + */ + public async getByteCountForAR( + arPrice: number, + { minWinstonFee, tipPercentage }: ArDriveCommunityTip + ): Promise { + const winstonPrice = arPrice / arPerWinston; + + const communityWinstonFee = Math.max(winstonPrice - winstonPrice / (1 + tipPercentage), minWinstonFee); + + const winstonPriceWithoutFee = Math.round(winstonPrice - communityWinstonFee); + + if (winstonPriceWithoutFee > 0) { + return this.getByteCountForWinston(winstonPriceWithoutFee); + } + + // Specified `arPrice` does not cover provided `minimumWinstonFee` + return 0; + } +} diff --git a/src/utils/ar_data_price_oracle_estimator.ts b/src/utils/ar_data_price_oracle_estimator.ts new file mode 100644 index 00000000..c99b8721 --- /dev/null +++ b/src/utils/ar_data_price_oracle_estimator.ts @@ -0,0 +1,27 @@ +import { GatewayOracle } from './gateway_oracle'; +import type { ArweaveOracle } from './arweave_oracle'; +import { AbstractARDataPriceEstimator } from './ar_data_price_estimator'; +import { ByteCount } from '../types'; + +export const arPerWinston = 0.000_000_000_001; + +/** + * A utility class for Arweave data pricing estimation. + * Fetches Arweave data prices from an ArweaveOracle each time it's requested + */ +export class ARDataPriceOracleEstimator extends AbstractARDataPriceEstimator { + constructor(private readonly oracle: ArweaveOracle = new GatewayOracle()) { + super(); + } + + /** + * Generates a price estimate, in Winston, for an upload of size `byteCount`. + * + * @param byteCount the number of bytes for which a price estimate should be generated + * + * @returns Promise for the price of an upload of size `byteCount` in Winston + */ + public async getBaseWinstonPriceForByteCount(byteCount: ByteCount): Promise { + return this.oracle.getWinstonPriceForByteCount(byteCount); + } +} diff --git a/src/utils/ar_data_price_regression_estimator.test.ts b/src/utils/ar_data_price_regression_estimator.test.ts new file mode 100644 index 00000000..56cbc463 --- /dev/null +++ b/src/utils/ar_data_price_regression_estimator.test.ts @@ -0,0 +1,134 @@ +import { GatewayOracle } from './gateway_oracle'; +import type { ArweaveOracle } from './arweave_oracle'; +import { expect } from 'chai'; +import { SinonStubbedInstance, stub } from 'sinon'; +import { ARDataPriceRegressionEstimator } from './ar_data_price_regression_estimator'; +import { expectAsyncErrorThrow } from './test_helpers'; +import type { ArDriveCommunityTip } from '../types'; + +describe('ARDataPriceEstimator class', () => { + let spyedOracle: SinonStubbedInstance; + let calculator: ARDataPriceRegressionEstimator; + + const arDriveCommunityTip: ArDriveCommunityTip = { minWinstonFee: 10, tipPercentage: 0.15 }; + + beforeEach(() => { + // Set pricing algo up as x = y (bytes = Winston) + // TODO: Get ts-sinon working with snowpack so we don't have to use a concrete type here + spyedOracle = stub(new GatewayOracle()); + spyedOracle.getWinstonPriceForByteCount.callsFake((input) => Promise.resolve(input)); + calculator = new ARDataPriceRegressionEstimator(true, spyedOracle); + }); + + it('can be instantiated without making oracle calls', async () => { + const gatewayOracleStub = stub(new GatewayOracle()); + gatewayOracleStub.getWinstonPriceForByteCount.callsFake(() => Promise.resolve(123)); + new ARDataPriceRegressionEstimator(true, gatewayOracleStub); + expect(gatewayOracleStub.getWinstonPriceForByteCount.notCalled).to.be.true; + }); + + it('makes 3 oracle calls during routine instantiation', async () => { + const gatewayOracleStub = stub(new GatewayOracle()); + gatewayOracleStub.getWinstonPriceForByteCount.callsFake(() => Promise.resolve(123)); + new ARDataPriceRegressionEstimator(false, gatewayOracleStub); + expect(gatewayOracleStub.getWinstonPriceForByteCount.calledThrice).to.be.true; + }); + + it('makes three oracle calls after the first price estimation request', async () => { + await calculator.getBaseWinstonPriceForByteCount(0); + expect(spyedOracle.getWinstonPriceForByteCount.calledThrice).to.be.true; + }); + + it('throws an error when constructed with a byte volume array that has only one number', () => { + expect(() => new ARDataPriceRegressionEstimator(true, spyedOracle, [1])).to.throw(Error); + }); + + it('throws an error when constructed with a byte volume array that has negative integers', () => { + expect(() => new ARDataPriceRegressionEstimator(true, spyedOracle, [-1, -2])).to.throw(Error); + }); + + it('throws an error when constructed with a byte volume array that has non-integer decimal values', () => { + expect(() => new ARDataPriceRegressionEstimator(true, spyedOracle, [0.1, 5.5])).to.throw(Error); + }); + + it('uses byte volumes from provided byte volume array', () => { + const byteVolumes = [1, 5, 10]; + new ARDataPriceRegressionEstimator(false, spyedOracle, byteVolumes); + + expect(spyedOracle.getWinstonPriceForByteCount.firstCall.args[0]).to.equal(byteVolumes[0]); + expect(spyedOracle.getWinstonPriceForByteCount.secondCall.args[0]).to.equal(byteVolumes[1]); + expect(spyedOracle.getWinstonPriceForByteCount.thirdCall.args[0]).to.equal(byteVolumes[2]); + }); + + it('getWinstonPriceForByteCount function returns the expected value', async () => { + const actualWinstonPriceEstimation = await calculator.getBaseWinstonPriceForByteCount(100); + expect(actualWinstonPriceEstimation).to.equal(100); + }); + + describe('getByteCountForWinston function', () => { + it('returns the expected value', async () => { + const actualByteCountEstimation = await calculator.getByteCountForWinston(100); + expect(actualByteCountEstimation).to.equal(100); + }); + + it('throws an error when provided winston value is a negative integer', async () => { + await expectAsyncErrorThrow({ promiseToError: calculator.getByteCountForWinston(-1) }); + }); + + it('throws an error when provided winston value is represented as a decimal', async () => { + await expectAsyncErrorThrow({ promiseToError: calculator.getByteCountForWinston(0.1) }); + }); + + it('makes three oracle calls after the first price estimation request', async () => { + await calculator.getByteCountForWinston(0); + expect(spyedOracle.getWinstonPriceForByteCount.calledThrice).to.be.true; + }); + + it('returns 0 if provided winston value does not cover baseWinstonPrice', async () => { + const stubRegressionByteVolumes = [0, 1]; + + const priceEstimator = new ARDataPriceRegressionEstimator(true, spyedOracle, stubRegressionByteVolumes); + + // Stub out the returned prices for each byte value to arrive at base price 5 and marginal price 1 + spyedOracle.getWinstonPriceForByteCount.onFirstCall().callsFake(() => Promise.resolve(5)); + spyedOracle.getWinstonPriceForByteCount.onSecondCall().callsFake(() => Promise.resolve(6)); + + // Expect 4 to be reduced to 0 because it does not cover baseWinstonPrice of 5 + expect(await priceEstimator.getByteCountForWinston(4)).to.equal(0); + }); + }); + + describe('getByteCountForAR function', () => { + it('returns the expected value', async () => { + const actualByteCountEstimation = await calculator.getByteCountForAR( + 0.000_000_000_100, + arDriveCommunityTip + ); + expect(actualByteCountEstimation).to.equal(87); + }); + + it('returns 0 if estimation does not cover the minimum winston fee', async () => { + const actualByteCountEstimation = await calculator.getByteCountForAR( + 0.000_000_000_010, + arDriveCommunityTip + ); + expect(actualByteCountEstimation).to.equal(0); + }); + }); + + it('getARPriceForByteCount function returns the expected value', async () => { + const actualARPriceEstimation = await calculator.getARPriceForByteCount(100, arDriveCommunityTip); + + expect(actualARPriceEstimation).to.equal(0.000_000_000_115); + }); + + describe('refreshPriceData function', () => { + it('avoids duplicate oracle calls', async () => { + const expected = await calculator.refreshPriceData(); + const actual = await calculator.refreshPriceData(); + + expect(actual).to.equal(expected); + expect(spyedOracle.getWinstonPriceForByteCount.calledThrice).to.be.true; + }); + }); +}); diff --git a/src/utils/ar_data_price_regression_estimator.ts b/src/utils/ar_data_price_regression_estimator.ts new file mode 100644 index 00000000..23bb7fb7 --- /dev/null +++ b/src/utils/ar_data_price_regression_estimator.ts @@ -0,0 +1,145 @@ +import { GatewayOracle } from './gateway_oracle'; +import type { ArweaveOracle } from './arweave_oracle'; +import { ARDataPriceRegression } from './data_price_regression'; +import { ARDataPrice } from './ar_data_price'; +import { AbstractARDataPriceAndCapacityEstimator } from './ar_data_price_estimator'; +import type { ArDriveCommunityTip, ByteCount } from '../types'; + +/** + * A utility class for Arweave data pricing estimation. + * Fetches Arweave data prices to build a linear regression model to use for estimations. + */ +export class ARDataPriceRegressionEstimator extends AbstractARDataPriceAndCapacityEstimator { + public static readonly sampleByteVolumes = [ + Math.pow(2, 10) * 100, // 100 KiB + Math.pow(2, 20) * 100, // 100 MiB + Math.pow(2, 30) * 10 // 10 GiB + ]; + private predictor?: ARDataPriceRegression; + private setupPromise?: Promise; + + /** + * Creates a new estimator. Fetches pricing data proactively unless `skipSetup` is true. + * + * @param skipSetup allows for instantiation without prefetching pricing data from the oracle + * @param oracle a datasource for Arweave data pricing + * @param byteVolumes an array of non-negative byte integers to fetch for pricing data + * + * @throws when byteVolumes array has less than 2 values + * @throws when volumes on byteVolumes array are negative or non-integer decimal values + * + * @returns an ARDataPriceEstimator + */ + constructor( + skipSetup = false, + private readonly oracle: ArweaveOracle = new GatewayOracle(), + private readonly byteVolumes: ByteCount[] = ARDataPriceRegressionEstimator.sampleByteVolumes + ) { + super(); + if (byteVolumes.length < 2) { + throw new Error('Byte volume array must contain at least 2 values to calculate regression'); + } + + for (const volume of byteVolumes) { + if (!Number.isInteger(volume) || volume < 0) { + throw new Error(`Byte volume (${volume}) on byte volume array should be a positive integer!`); + } + } + + if (!skipSetup) { + this.refreshPriceData(); + } + } + + /** + * Updates the regression model with fresh data from the pricing oracle + * + * @returns Promise for an {@link ARDataPriceRegression} + */ + public async refreshPriceData(): Promise { + // Don't kick off another refresh while refresh is in progress + if (this.setupPromise) { + return this.setupPromise; + } + + // Fetch the price for all values in byteVolume array and feed them into a linear regression + this.setupPromise = Promise.all( + // TODO: What to do if one fails? + this.byteVolumes.map( + async (sampleByteCount) => + new ARDataPrice(sampleByteCount, await this.oracle.getWinstonPriceForByteCount(sampleByteCount)) + ) + ).then((pricingData) => new ARDataPriceRegression(pricingData)); + + this.predictor = await this.setupPromise; + return this.predictor; + } + + /** + * Generates a price estimate, in Winston, for an upload of size `byteCount`. + * + * @param byteCount the number of bytes for which a price estimate should be generated + * + * @returns Promise for the price of an upload of size `byteCount` in Winston + * + * @remarks Will fetch pricing data for regression modeling if a regression has not yet been run. + */ + public async getBaseWinstonPriceForByteCount(byteCount: ByteCount): Promise { + // Lazily generate the price predictor + if (!this.predictor) { + await this.refreshPriceData(); + if (!this.predictor) { + throw Error('Failed to generate pricing model!'); + } + } + + const predictedPrice = this.predictor.predictedPriceForByteCount(byteCount); + return predictedPrice.winstonPrice; + } + + /** + * Estimates the number of bytes that can be stored for a given amount of Winston + * + * @throws On invalid winston values and on any issues generating pricing models + * + * @remarks Will fetch pricing data for regression modeling if a regression has not yet been run. + * @remarks The ArDrive community fee is not considered in this estimation + */ + public async getByteCountForWinston(winston: number): Promise { + if (winston < 0 || !Number.isInteger(winston)) { + throw new Error('winston value should be a non-negative integer!'); + } + + // Lazily generate the price predictor + if (!this.predictor) { + await this.refreshPriceData(); + if (!this.predictor) { + throw Error('Failed to generate pricing model!'); + } + } + + // Return 0 if winston price given does not cover the base winston price for a transaction + return Math.max(0, (winston - this.predictor.baseWinstonPrice()) / this.predictor.marginalWinstonPrice()); + } + + /** + * Estimates the number of bytes that can be stored for a given amount of AR + * + * @remarks Will fetch pricing data for regression modeling if a regression has not yet been run. + * @remarks Returns 0 bytes when the price does not cover minimum ArDrive community fee + */ + public async getByteCountForAR( + arPrice: number, + { minWinstonFee, tipPercentage }: ArDriveCommunityTip + ): Promise { + // Lazily generate the price predictor + if (!this.predictor) { + await this.refreshPriceData(); + if (!this.predictor) { + throw Error('Failed to generate pricing model!'); + } + } + + return super.getByteCountForAR(arPrice, { minWinstonFee, tipPercentage }); + } +} diff --git a/src/utils/ar_unit.test.ts b/src/utils/ar_unit.test.ts new file mode 100644 index 00000000..9a7eb374 --- /dev/null +++ b/src/utils/ar_unit.test.ts @@ -0,0 +1,42 @@ +import { expect } from 'chai'; +import { assertARPrecision } from './ar_unit'; + +describe('The assertARPrecision method', () => { + const VALID_INTEGER = '1'; + const VALID_FLOATING_POINT = '0.0'; + const VALID_FLOATING_POINT_NO_LEADING_DIGITS = '.0123'; + const VALID_FLOATING_POINT_WITH_TRAILING_ZEROS = '.00000000000100000000000000000'; + const EXCESSIVE_NONZERO_DIGITS = '.000000000001010'; + const A_HALF_A_WINSTON = '.0000000000005'; + const NOT_A_NUMBER = 'not a number >:b .00000'; + const NEGATIVE_VALUE = '-10'; + + it('Passes when asserting an integer', () => { + expect(assertARPrecision.bind(this, VALID_INTEGER)).to.not.throw(); + }); + + it('Passes when asserting a floating point', () => { + expect(assertARPrecision.bind(this, VALID_FLOATING_POINT)).to.not.throw(); + }); + + it('Passes when asserting a floating point with no leading digits', () => { + expect(assertARPrecision.bind(this, VALID_FLOATING_POINT_NO_LEADING_DIGITS)).to.not.throw(); + }); + + it('Passes when asserting a valid floating point with trailing zeros at the end', () => { + expect(assertARPrecision.bind(this, VALID_FLOATING_POINT_WITH_TRAILING_ZEROS)).to.not.throw(); + }); + + it('Throws when asserting a fraction of a Winston', () => { + expect(assertARPrecision.bind(this, A_HALF_A_WINSTON)).to.throw(); + expect(assertARPrecision.bind(this, EXCESSIVE_NONZERO_DIGITS)).to.throw(); + }); + + it('Throws when asserting an NaN', () => { + expect(assertARPrecision.bind(this, NOT_A_NUMBER)).to.throw(); + }); + + it('Throws when asserting a negative value', () => { + expect(assertARPrecision.bind(this, NEGATIVE_VALUE)).to.throw(); + }); +}); diff --git a/src/utils/ar_unit.ts b/src/utils/ar_unit.ts new file mode 100644 index 00000000..99eb61ae --- /dev/null +++ b/src/utils/ar_unit.ts @@ -0,0 +1,21 @@ +/** + * Capture group 1: decimal precision beyond 12 digits + */ +export const FLOATING_POINT_REGEXP = /^\d*\.\d{12}(\d+)$/; + +export function assertARPrecision(arAmount: string): void { + if (Number.isNaN(+arAmount)) { + throw new Error(`The AR amount must be a number. Got: ${arAmount}`); + } + + if (+arAmount < 0.0) { + throw new Error(`The AR amount must be a positive number. Got: ${arAmount}`); + } + + const excessiveDigits = arAmount.match(FLOATING_POINT_REGEXP)?.[1] || ''; + if (+excessiveDigits !== 0.0) { + throw new Error( + `The AR amount must have a maximum of 12 digits of precision, but got ${12 + excessiveDigits.length}` + ); + } +} diff --git a/src/utils/ardrive.test.ts b/src/utils/ardrive.test.ts new file mode 100644 index 00000000..7a18af7a --- /dev/null +++ b/src/utils/ardrive.test.ts @@ -0,0 +1,236 @@ +import Arweave from 'arweave'; +import { expect } from 'chai'; +import { SinonStubbedInstance, stub } from 'sinon'; +import { ArDrive } from '../../src/ardrive'; +import { + ArFSPublicDriveTransactionData, + ArFSPublicFileMetadataTransactionData, + ArFSPublicFolderTransactionData +} from '../../src/arfs_trx_data_types'; +import { TipType } from '../../src/types'; +import { readJWKFile } from '../../src/utils'; +import { ArweaveOracle } from '../../src/utils/arweave_oracle'; +import { ARDataPriceRegressionEstimator } from '../../src/utils/ar_data_price_regression_estimator'; +import { GatewayOracle } from '../../src/utils/gateway_oracle'; +import { WalletDAO } from '../wallet'; +import { expectAsyncErrorThrow } from '../../src/utils/test_helpers'; +import { ArDriveCommunityOracle } from '../../src/community/ardrive_community_oracle'; +import { CommunityOracle } from '../../src/community/community_oracle'; +import { ArFSDAO } from '../arfsdao'; +import { stubEntityID, stubTransactionID } from './stubs'; + +describe('ArDrive class', () => { + let arDrive: ArDrive; + let arweaveOracleStub: SinonStubbedInstance; + let communityOracleStub: SinonStubbedInstance; + let priceEstimator: ARDataPriceRegressionEstimator; + let walletDao: WalletDAO; + const fakeArweave = Arweave.init({ + host: 'localhost', + port: 443, + protocol: 'https', + timeout: 600000 + }); + const wallet = readJWKFile('./test_wallet.json'); + const stubPublicFileTransactionData = new ArFSPublicFileMetadataTransactionData( + 'stubName', + 12345, + 0, + stubTransactionID, + 'application/json' + ); + const stubPublicFolderTransactionData = new ArFSPublicFolderTransactionData('stubName'); + const stubPublicDriveMetadataTransactionData = new ArFSPublicDriveTransactionData('stubName', stubEntityID); + + beforeEach(async () => { + // Set pricing algo up as x = y (bytes = Winston) + arweaveOracleStub = stub(new GatewayOracle()); + arweaveOracleStub.getWinstonPriceForByteCount.callsFake((input) => Promise.resolve(input)); + communityOracleStub = stub(new ArDriveCommunityOracle(fakeArweave)); + priceEstimator = new ARDataPriceRegressionEstimator(true, arweaveOracleStub); + walletDao = new WalletDAO(fakeArweave, 'Unit Test', '1.0'); + arDrive = new ArDrive( + wallet, + walletDao, + new ArFSDAO(wallet, fakeArweave, true, 'Unit Test', '1.0'), + communityOracleStub, + 'Unit Test', + '1.0', + priceEstimator, + 1.0, + true + ); + }); + + describe('encryptedDataSize function', () => { + it('throws an error when passed a negative value', () => { + expect(() => arDrive.encryptedDataSize(-1)).to.throw(Error); + }); + + it('throws an error when passed a non-integer value', () => { + expect(() => arDrive.encryptedDataSize(0.5)).to.throw(Error); + }); + + it('throws an error when passed a value too large for computation', () => { + expect(() => arDrive.encryptedDataSize(Number.MAX_SAFE_INTEGER - 15)).to.throw(Error); + }); + + it('returns the expected values for valid inputs', () => { + const inputsAndExpectedOutputs = [ + [0, 16], + [1, 16], + [15, 16], + [16, 32], + [17, 32], + [Number.MAX_SAFE_INTEGER - 16, Number.MAX_SAFE_INTEGER - 15] + ]; + inputsAndExpectedOutputs.forEach(([input, expectedOutput]) => { + expect(arDrive.encryptedDataSize(input)).to.equal(expectedOutput); + }); + }); + }); + + describe('getTipTags function', () => { + it('returns the expected tags', () => { + const baseTags = [ + { name: 'App-Name', value: 'Unit Test' }, + { name: 'App-Version', value: '1.0' } + ]; + const inputsAndExpectedOutputs = [ + [undefined, [...baseTags, { name: 'Tip-Type', value: 'data upload' }]], + ['data upload', [...baseTags, { name: 'Tip-Type', value: 'data upload' }]] + ]; + inputsAndExpectedOutputs.forEach(([input, expectedOutput]) => { + expect(arDrive.getTipTags(input as TipType)).to.deep.equal(expectedOutput); + }); + }); + }); + + describe('estimateAndAssertCostOfFileUpload function', () => { + it('throws an error when decryptedFileSize is negative', async () => { + await expectAsyncErrorThrow({ + promiseToError: arDrive.estimateAndAssertCostOfFileUpload(-1, stubPublicFileTransactionData, 'private') + }); + }); + + it('throws an error when decryptedFileSize is not an integer', async () => { + await expectAsyncErrorThrow({ + promiseToError: arDrive.estimateAndAssertCostOfFileUpload(0.1, stubPublicFileTransactionData, 'private') + }); + }); + + it('throws an error when there is an insufficient wallet balance', async () => { + stub(walletDao, 'walletHasBalance').callsFake(() => { + return Promise.resolve(false); + }); + stub(walletDao, 'getWalletWinstonBalance').callsFake(() => { + return Promise.resolve(0); + }); + await expectAsyncErrorThrow({ + promiseToError: arDrive.estimateAndAssertCostOfFileUpload(1, stubPublicFileTransactionData, 'private') + }); + }); + + it('returns the correct reward and tip data', async () => { + stub(walletDao, 'walletHasBalance').callsFake(() => { + return Promise.resolve(true); + }); + communityOracleStub.getCommunityWinstonTip.callsFake(() => { + return Promise.resolve('9876543210'); + }); + + const actual = await arDrive.estimateAndAssertCostOfFileUpload( + 1234567, + stubPublicFileTransactionData, + 'private' + ); + expect(actual).to.deep.equal({ + metaDataBaseReward: '147', + fileDataBaseReward: '1234576', + communityWinstonTip: '9876543210' + }); + }); + }); + + describe('estimateAndAssertCostOfFolderUpload function', () => { + it('throws an error when there is an insufficient wallet balance', async () => { + stub(walletDao, 'walletHasBalance').callsFake(() => { + return Promise.resolve(false); + }); + stub(walletDao, 'getWalletWinstonBalance').callsFake(() => { + return Promise.resolve(0); + }); + await expectAsyncErrorThrow({ + promiseToError: arDrive.estimateAndAssertCostOfFolderUpload(stubPublicFolderTransactionData) + }); + }); + + it('returns the correct reward data', async () => { + stub(walletDao, 'walletHasBalance').callsFake(() => { + return Promise.resolve(true); + }); + + const actual = await arDrive.estimateAndAssertCostOfFolderUpload(stubPublicFileTransactionData); + expect(actual).to.deep.equal({ + metaDataBaseReward: '147' + }); + }); + }); + + describe('estimateAndAssertCostOfDriveCreation function', () => { + it('throws an error when there is an insufficient wallet balance', async () => { + stub(walletDao, 'walletHasBalance').callsFake(() => { + return Promise.resolve(false); + }); + stub(walletDao, 'getWalletWinstonBalance').callsFake(() => { + return Promise.resolve(0); + }); + await expectAsyncErrorThrow({ + promiseToError: arDrive.estimateAndAssertCostOfDriveCreation( + stubPublicDriveMetadataTransactionData, + stubPublicFolderTransactionData + ) + }); + }); + + it('returns the correct reward data', async () => { + stub(walletDao, 'walletHasBalance').callsFake(() => { + return Promise.resolve(true); + }); + + const actual = await arDrive.estimateAndAssertCostOfDriveCreation( + stubPublicDriveMetadataTransactionData, + stubPublicFolderTransactionData + ); + expect(actual).to.deep.equal({ + driveMetaDataBaseReward: '73', + rootFolderMetaDataBaseReward: '19' + }); + }); + }); + + describe('estimateAndAssertCostOfMoveFile function', () => { + it('throws an error when there is an insufficient wallet balance', async () => { + stub(walletDao, 'walletHasBalance').callsFake(() => { + return Promise.resolve(false); + }); + stub(walletDao, 'getWalletWinstonBalance').callsFake(() => { + return Promise.resolve(0); + }); + await expectAsyncErrorThrow({ + promiseToError: arDrive.estimateAndAssertCostOfMoveFile(stubPublicFileTransactionData) + }); + }); + + it('returns the correct reward data', async () => { + stub(walletDao, 'walletHasBalance').callsFake(() => { + return Promise.resolve(true); + }); + + const actual = await arDrive.estimateAndAssertCostOfMoveFile(stubPublicFileTransactionData); + expect(actual).to.deep.equal({ + metaDataBaseReward: '147' + }); + }); + }); +}); diff --git a/src/utils/arfs_builders/arfs_builders.ts b/src/utils/arfs_builders/arfs_builders.ts new file mode 100644 index 00000000..894c5580 --- /dev/null +++ b/src/utils/arfs_builders/arfs_builders.ts @@ -0,0 +1,144 @@ +import { + ArFSEntity, + ContentType, + EntityType, + GQLNodeInterface, + GQLTagInterface, + GQLTransactionsResultInterface +} from 'ardrive-core-js'; +import Arweave from 'arweave'; +import { graphQLURL } from '../../arfsdao'; +import { ArFSFileOrFolderEntity } from '../../arfs_entities'; +import { ArweaveAddress } from '../../arweave_address'; +import { buildQuery } from '../../query'; +import { DriveID, EntityID, EntityKey, FolderID, TransactionID, UnixTime } from '../../types'; + +export interface ArFSMetadataEntityBuilderParams { + entityId: EntityID; + arweave: Arweave; + owner?: ArweaveAddress; +} +export type ArFSPublicMetadataEntityBuilderParams = ArFSMetadataEntityBuilderParams; +export interface ArFSPrivateMetadataEntityBuilderParams extends ArFSMetadataEntityBuilderParams { + key: EntityKey; +} + +export type ArFSMetadataEntityBuilderFactoryFunction< + T extends ArFSEntity, + B extends ArFSMetadataEntityBuilder, + P extends ArFSMetadataEntityBuilderParams +> = (params: P) => B; + +export abstract class ArFSMetadataEntityBuilder { + appName?: string; + appVersion?: string; + arFS?: string; + contentType?: ContentType; + driveId?: DriveID; + entityType?: EntityType; + name?: string; + txId?: TransactionID; + unixTime?: UnixTime; + protected readonly entityId: EntityID; + protected readonly arweave: Arweave; + protected readonly owner?: ArweaveAddress; + + constructor({ entityId, arweave, owner }: ArFSMetadataEntityBuilderParams) { + this.entityId = entityId; + this.arweave = arweave; + this.owner = owner; + } + + abstract getGqlQueryParameters(): GQLTagInterface[]; + protected abstract buildEntity(): Promise; + + /** + * Parses data for builder fields from either the provided GQL tags, or from a fresh request to Arweave for tag data + * + * @param node (optional) a pre-fetched GQL node containing the txID and tags that will be parsed out of the on-chain data + * + * @param owner (optional) filter all transactions out by owner's public arweave address + * + * @returns an array of unparsed tags + */ + protected async parseFromArweaveNode(node?: GQLNodeInterface, owner?: ArweaveAddress): Promise { + const unparsedTags: GQLTagInterface[] = []; + if (!node) { + const gqlQuery = buildQuery({ tags: this.getGqlQueryParameters(), owner }); + + const response = await this.arweave.api.post(graphQLURL, gqlQuery); + + const { data } = response.data; + const transactions: GQLTransactionsResultInterface = data.transactions; + const { edges } = transactions; + + if (!edges.length) { + throw new Error(`Entity with ID ${this.entityId} not found!`); + } + + node = edges[0].node; + } + this.txId = node.id; + const { tags } = node; + tags.forEach((tag: GQLTagInterface) => { + const key = tag.name; + const { value } = tag; + switch (key) { + case 'App-Name': + this.appName = value; + break; + case 'App-Version': + this.appVersion = value; + break; + case 'ArFS': + this.arFS = value; + break; + case 'Content-Type': + this.contentType = value as ContentType; + break; + case 'Drive-Id': + this.driveId = value; + break; + case 'Entity-Type': + this.entityType = value as EntityType; + break; + case 'Unix-Time': + this.unixTime = +value; + break; + default: + unparsedTags.push(tag); + break; + } + }); + + return unparsedTags; + } + + async build(node?: GQLNodeInterface): Promise { + await this.parseFromArweaveNode(node, this.owner); + return this.buildEntity(); + } +} + +export abstract class ArFSFileOrFolderBuilder extends ArFSMetadataEntityBuilder { + parentFolderId?: FolderID; + + protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise { + const unparsedTags: GQLTagInterface[] = []; + const tags = await super.parseFromArweaveNode(node); + tags.forEach((tag: GQLTagInterface) => { + const key = tag.name; + const { value } = tag; + switch (key) { + case 'Parent-Folder-Id': + this.parentFolderId = value; + break; + default: + unparsedTags.push(tag); + break; + } + }); + + return unparsedTags; + } +} diff --git a/src/utils/arfs_builders/arfs_drive_builders.ts b/src/utils/arfs_builders/arfs_drive_builders.ts new file mode 100644 index 00000000..dbd447df --- /dev/null +++ b/src/utils/arfs_builders/arfs_drive_builders.ts @@ -0,0 +1,365 @@ +import { + ArFSDriveEntity, + DriveAuthMode, + driveDecrypt, + DrivePrivacy, + GQLNodeInterface, + GQLTagInterface, + Utf8ArrayToStr +} from 'ardrive-core-js'; +import Arweave from 'arweave'; +import { ArFSPrivateDrive, ArFSPublicDrive } from '../../arfs_entities'; +import { EntityMetaDataTransactionData, PrivateKeyData } from '../../private_key_data'; +import { CipherIV, DriveKey, FolderID } from '../../types'; +import { + ArFSMetadataEntityBuilder, + ArFSMetadataEntityBuilderParams, + ArFSPrivateMetadataEntityBuilderParams +} from './arfs_builders'; + +interface DriveMetaDataTransactionData extends EntityMetaDataTransactionData { + name: string; + rootFolderId: FolderID; +} + +export const ENCRYPTED_DATA_PLACEHOLDER = 'ENCRYPTED'; + +export class ArFSPublicDriveBuilder extends ArFSMetadataEntityBuilder { + drivePrivacy?: DrivePrivacy; + rootFolderId?: FolderID; + + static fromArweaveNode(node: GQLNodeInterface, arweave: Arweave): ArFSPublicDriveBuilder { + const { tags } = node; + const driveId = tags.find((tag) => tag.name === 'Drive-Id')?.value; + if (!driveId) { + throw new Error('Drive-ID tag missing!'); + } + const driveBuilder = new ArFSPublicDriveBuilder({ entityId: driveId, arweave }); + return driveBuilder; + } + + getGqlQueryParameters(): GQLTagInterface[] { + return [ + { name: 'Drive-Id', value: this.entityId }, + { name: 'Entity-Type', value: 'drive' }, + { name: 'Drive-Privacy', value: 'public' } + ]; + } + + protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise { + const unparsedTags: GQLTagInterface[] = []; + const tags = await super.parseFromArweaveNode(node); + tags.forEach((tag: GQLTagInterface) => { + const key = tag.name; + const { value } = tag; + switch (key) { + case 'Drive-Privacy': + this.drivePrivacy = value as DrivePrivacy; + break; + default: + unparsedTags.push(tag); + break; + } + }); + return unparsedTags; + } + + protected async buildEntity(): Promise { + if ( + this.appName?.length && + this.appVersion?.length && + this.arFS?.length && + this.contentType?.length && + this.driveId?.length && + this.entityType?.length && + this.txId?.length && + this.unixTime && + this.driveId == this.entityId && + this.drivePrivacy?.length + ) { + const txData = await this.arweave.transactions.getData(this.txId, { decode: true }); + const dataString = await Utf8ArrayToStr(txData); + const dataJSON = await JSON.parse(dataString); + + // Get the drive name and root folder id + this.name = dataJSON.name; + this.rootFolderId = dataJSON.rootFolderId; + if (!this.name || !this.rootFolderId) { + throw new Error('Invalid drive state'); + } + + return new ArFSPublicDrive( + this.appName, + this.appVersion, + this.arFS, + this.contentType, + this.driveId, + this.entityType, + this.name, + this.txId, + this.unixTime, + this.drivePrivacy, + this.rootFolderId + ); + } + throw new Error('Invalid drive state'); + } +} + +export class ArFSPrivateDriveBuilder extends ArFSMetadataEntityBuilder { + drivePrivacy?: DrivePrivacy; + rootFolderId?: FolderID; + driveAuthMode?: DriveAuthMode; + cipher?: string; + cipherIV?: CipherIV; + private readonly driveKey: DriveKey; + + constructor({ entityId: driveId, arweave, key: driveKey, owner }: ArFSPrivateMetadataEntityBuilderParams) { + super({ entityId: driveId, arweave, owner }); + this.driveKey = driveKey; + } + + getGqlQueryParameters(): GQLTagInterface[] { + return [ + { name: 'Drive-Id', value: this.entityId }, + { name: 'Entity-Type', value: 'drive' }, + { name: 'Drive-Privacy', value: 'private' } + ]; + } + + static fromArweaveNode(node: GQLNodeInterface, arweave: Arweave, driveKey: DriveKey): ArFSPrivateDriveBuilder { + const { tags } = node; + const driveId = tags.find((tag) => tag.name === 'Drive-Id')?.value; + if (!driveId) { + throw new Error('Drive-ID tag missing!'); + } + const fileBuilder = new ArFSPrivateDriveBuilder({ entityId: driveId, arweave, key: driveKey }); + return fileBuilder; + } + + protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise { + const unparsedTags: GQLTagInterface[] = []; + const tags = await super.parseFromArweaveNode(node); + tags.forEach((tag: GQLTagInterface) => { + const key = tag.name; + const { value } = tag; + switch (key) { + case 'Cipher': + this.cipher = value; + break; + case 'Cipher-IV': + this.cipherIV = value; + break; + case 'Drive-Auth-Mode': + this.driveAuthMode = value as DriveAuthMode; + break; + case 'Drive-Privacy': + this.drivePrivacy = value as DrivePrivacy; + break; + default: + unparsedTags.push(tag); + break; + } + }); + return unparsedTags; + } + + protected async buildEntity(): Promise { + if ( + this.appName?.length && + this.appVersion?.length && + this.arFS?.length && + this.contentType?.length && + this.driveId?.length && + this.entityType?.length && + this.txId?.length && + this.unixTime && + this.drivePrivacy?.length && + this.driveAuthMode?.length && + this.cipher?.length && + this.cipherIV?.length + ) { + const txData = await this.arweave.transactions.getData(this.txId, { decode: true }); + const dataBuffer = Buffer.from(txData); + const decryptedDriveBuffer: Buffer = await driveDecrypt(this.cipherIV, this.driveKey, dataBuffer); + const decryptedDriveString: string = await Utf8ArrayToStr(decryptedDriveBuffer); + const decryptedDriveJSON: DriveMetaDataTransactionData = await JSON.parse(decryptedDriveString); + + this.name = decryptedDriveJSON.name; + this.rootFolderId = decryptedDriveJSON.rootFolderId; + + return new ArFSPrivateDrive( + this.appName, + this.appVersion, + this.arFS, + this.contentType, + this.driveId, + this.entityType, + this.name, + this.txId, + this.unixTime, + this.drivePrivacy, + this.rootFolderId, + this.driveAuthMode, + this.cipher, + this.cipherIV + ); + } + + throw new Error('Invalid drive state'); + } +} + +export interface SafeArFSPrivateMetadataEntityBuilderParams extends ArFSMetadataEntityBuilderParams { + privateKeyData: PrivateKeyData; +} + +export class SafeArFSDriveBuilder extends ArFSMetadataEntityBuilder { + drivePrivacy?: DrivePrivacy; + rootFolderId?: FolderID; + driveAuthMode?: DriveAuthMode; + cipher?: string; + cipherIV?: CipherIV; + + private readonly privateKeyData: PrivateKeyData; + + constructor({ entityId: driveId, arweave, privateKeyData }: SafeArFSPrivateMetadataEntityBuilderParams) { + super({ entityId: driveId, arweave }); + this.privateKeyData = privateKeyData; + } + + getGqlQueryParameters(): GQLTagInterface[] { + return [ + { name: 'Drive-Id', value: this.entityId }, + { name: 'Entity-Type', value: 'drive' } + ]; + } + + static fromArweaveNode( + node: GQLNodeInterface, + arweave: Arweave, + privateKeyData: PrivateKeyData + ): SafeArFSDriveBuilder { + const { tags } = node; + const driveId = tags.find((tag) => tag.name === 'Drive-Id')?.value; + if (!driveId) { + throw new Error('Drive-ID tag missing!'); + } + const driveBuilder = new SafeArFSDriveBuilder({ + entityId: driveId, + arweave, + // TODO: Make all private builders optionally take driveKey and fail gracefully, populating fields with 'ENCRYPTED' + privateKeyData + }); + return driveBuilder; + } + + protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise { + const unparsedTags: GQLTagInterface[] = []; + const tags = await super.parseFromArweaveNode(node); + tags.forEach((tag: GQLTagInterface) => { + const key = tag.name; + const { value } = tag; + switch (key) { + case 'Cipher': + this.cipher = value; + break; + case 'Cipher-IV': + this.cipherIV = value; + break; + case 'Drive-Auth-Mode': + this.driveAuthMode = value as DriveAuthMode; + break; + case 'Drive-Privacy': + this.drivePrivacy = value as DrivePrivacy; + break; + default: + unparsedTags.push(tag); + break; + } + }); + return unparsedTags; + } + + protected async buildEntity(): Promise { + if ( + this.appName?.length && + this.appVersion?.length && + this.arFS?.length && + this.contentType?.length && + this.driveId?.length && + this.entityType?.length && + this.txId?.length && + this.unixTime && + this.drivePrivacy?.length + ) { + const isPrivate = this.drivePrivacy === 'private'; + + const txData = await this.arweave.transactions.getData(this.txId, { decode: true }); + const dataBuffer = Buffer.from(txData); + + // Data JSON will be false when a private drive cannot be decrypted + const dataJSON: DriveMetaDataTransactionData = await (async () => { + if (isPrivate) { + // Type-check private properties + if (this.cipher?.length && this.driveAuthMode?.length && this.cipherIV?.length) { + const placeholderDriveData = { + name: ENCRYPTED_DATA_PLACEHOLDER, + rootFolderId: ENCRYPTED_DATA_PLACEHOLDER + }; + return this.privateKeyData.safelyDecryptToJson( + this.cipherIV, + this.entityId, + dataBuffer, + placeholderDriveData + ); + } + throw new Error('Invalid private drive state'); + } + // Drive is public, no decryption needed + const dataString = await Utf8ArrayToStr(txData); + return JSON.parse(dataString) as DriveMetaDataTransactionData; + })(); + + this.name = dataJSON.name; + this.rootFolderId = dataJSON.rootFolderId; + + if (isPrivate) { + return new ArFSPrivateDrive( + this.appName, + this.appVersion, + this.arFS, + this.contentType, + this.driveId, + this.entityType, + this.name, + this.txId, + this.unixTime, + this.drivePrivacy, + this.rootFolderId, + // These private fields are type-checked these within the dataJSON closure + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.driveAuthMode!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.cipher!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.cipherIV! + ); + } + return new ArFSPublicDrive( + this.appName, + this.appVersion, + this.arFS, + this.contentType, + this.driveId, + this.entityType, + this.name, + this.txId, + this.unixTime, + this.drivePrivacy, + this.rootFolderId + ); + } + throw new Error('Invalid drive state'); + } +} diff --git a/src/utils/arfs_builders/arfs_file_builders.ts b/src/utils/arfs_builders/arfs_file_builders.ts new file mode 100644 index 00000000..7984ad31 --- /dev/null +++ b/src/utils/arfs_builders/arfs_file_builders.ts @@ -0,0 +1,213 @@ +import { + ContentType, + deriveFileKey, + extToMime, + fileDecrypt, + GQLNodeInterface, + GQLTagInterface, + Utf8ArrayToStr +} from 'ardrive-core-js'; +import Arweave from 'arweave'; +import { ArFSPrivateFile, ArFSPublicFile } from '../../arfs_entities'; +import { ArweaveAddress } from '../../arweave_address'; +import { ByteCount, CipherIV, DriveKey, FileID, FileKey, TransactionID, UnixTime } from '../../types'; +import { ArFSFileOrFolderBuilder } from './arfs_builders'; + +interface FileMetaDataTransactionData { + name: string; + size: ByteCount; + lastModifiedDate: UnixTime; + dataTxId: TransactionID; + dataContentType: ContentType; +} +export abstract class ArFSFileBuilder extends ArFSFileOrFolderBuilder { + size?: ByteCount; + lastModifiedDate?: UnixTime; + dataTxId?: TransactionID; + dataContentType?: ContentType; + + getGqlQueryParameters(): GQLTagInterface[] { + return [ + { name: 'File-Id', value: this.entityId }, + { name: 'Entity-Type', value: 'file' } + ]; + } +} + +export class ArFSPublicFileBuilder extends ArFSFileBuilder { + static fromArweaveNode(node: GQLNodeInterface, arweave: Arweave): ArFSPublicFileBuilder { + const { tags } = node; + const fileId = tags.find((tag) => tag.name === 'File-Id')?.value; + if (!fileId) { + throw new Error('File-ID tag missing!'); + } + const fileBuilder = new ArFSPublicFileBuilder({ entityId: fileId, arweave }); + return fileBuilder; + } + + protected async buildEntity(): Promise { + if ( + this.appName?.length && + this.appVersion?.length && + this.arFS?.length && + this.contentType?.length && + this.driveId?.length && + this.entityType?.length && + this.txId?.length && + this.unixTime && + this.parentFolderId?.length && + this.entityId?.length + ) { + const txData = await this.arweave.transactions.getData(this.txId, { decode: true }); + const dataString = await Utf8ArrayToStr(txData); + const dataJSON: FileMetaDataTransactionData = await JSON.parse(dataString); + + // Get fields from data JSON + this.name = dataJSON.name; + this.size = dataJSON.size; + this.lastModifiedDate = dataJSON.lastModifiedDate; + this.dataTxId = dataJSON.dataTxId; + this.dataContentType = dataJSON.dataContentType ?? extToMime(this.name); + + if ( + !this.name || + this.size === undefined || + !this.lastModifiedDate || + !this.dataTxId || + !this.dataContentType + ) { + throw new Error('Invalid file state'); + } + + return Promise.resolve( + new ArFSPublicFile( + this.appName, + this.appVersion, + this.arFS, + this.contentType, + this.driveId, + this.entityType, + this.name, + this.txId, + this.unixTime, + this.parentFolderId, + this.entityId, + this.size, + this.lastModifiedDate, + this.dataTxId, + this.dataContentType + ) + ); + } + throw new Error('Invalid file state'); + } +} + +export class ArFSPrivateFileBuilder extends ArFSFileBuilder { + cipher?: string; + cipherIV?: CipherIV; + + constructor( + readonly fileId: FileID, + readonly arweave: Arweave, + private readonly driveKey: DriveKey, + readonly owner?: ArweaveAddress, + readonly fileKey?: FileKey + ) { + super({ entityId: fileId, arweave, owner }); + } + + static fromArweaveNode(node: GQLNodeInterface, arweave: Arweave, driveKey: DriveKey): ArFSPrivateFileBuilder { + const { tags } = node; + const fileId = tags.find((tag) => tag.name === 'File-Id')?.value; + if (!fileId) { + throw new Error('File-ID tag missing!'); + } + const fileBuilder = new ArFSPrivateFileBuilder(fileId, arweave, driveKey); + return fileBuilder; + } + + protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise { + const unparsedTags: GQLTagInterface[] = []; + const tags = await super.parseFromArweaveNode(node); + tags.forEach((tag: GQLTagInterface) => { + const key = tag.name; + const { value } = tag; + switch (key) { + case 'Cipher-IV': + this.cipherIV = value; + break; + case 'Cipher': + this.cipher = value; + break; + default: + unparsedTags.push(tag); + break; + } + }); + return unparsedTags; + } + + protected async buildEntity(): Promise { + if ( + this.appName?.length && + this.appVersion?.length && + this.arFS?.length && + this.contentType?.length && + this.driveId?.length && + this.entityType?.length && + this.txId?.length && + this.unixTime && + this.parentFolderId?.length && + this.entityId?.length && + this.cipher?.length && + this.cipherIV?.length + ) { + const txData = await this.arweave.transactions.getData(this.txId, { decode: true }); + const dataBuffer = Buffer.from(txData); + const fileKey = this.fileKey ?? (await deriveFileKey(this.fileId, this.driveKey)); + + const decryptedFileBuffer: Buffer = await fileDecrypt(this.cipherIV, fileKey, dataBuffer); + const decryptedFileString: string = await Utf8ArrayToStr(decryptedFileBuffer); + const decryptedFileJSON: FileMetaDataTransactionData = await JSON.parse(decryptedFileString); + + // Get fields from data JSON + this.name = decryptedFileJSON.name; + this.size = decryptedFileJSON.size; + this.lastModifiedDate = decryptedFileJSON.lastModifiedDate; + this.dataTxId = decryptedFileJSON.dataTxId; + this.dataContentType = decryptedFileJSON.dataContentType ?? extToMime(this.name); + + if ( + !this.name || + this.size === undefined || + !this.lastModifiedDate || + !this.dataTxId || + !this.dataContentType + ) { + throw new Error('Invalid file state'); + } + + return new ArFSPrivateFile( + this.appName, + this.appVersion, + this.arFS, + this.contentType, + this.driveId, + this.entityType, + this.name, + this.txId, + this.unixTime, + this.parentFolderId, + this.entityId, + this.size, + this.lastModifiedDate, + this.dataTxId, + this.dataContentType, + this.cipher, + this.cipherIV + ); + } + throw new Error('Invalid file state'); + } +} diff --git a/src/utils/arfs_builders/arfs_folder_builders.ts b/src/utils/arfs_builders/arfs_folder_builders.ts new file mode 100644 index 00000000..f9c36556 --- /dev/null +++ b/src/utils/arfs_builders/arfs_folder_builders.ts @@ -0,0 +1,173 @@ +import { fileDecrypt, GQLNodeInterface, GQLTagInterface, Utf8ArrayToStr } from 'ardrive-core-js'; +import Arweave from 'arweave'; +import { ArFSPrivateFolder, ArFSPublicFolder } from '../../arfs_entities'; +import { ArweaveAddress } from '../../arweave_address'; +import { CipherIV, DriveKey, FolderID } from '../../types'; +import { ArFSFileOrFolderBuilder } from './arfs_builders'; + +export abstract class ArFSFolderBuilder< + T extends ArFSPublicFolder | ArFSPrivateFolder +> extends ArFSFileOrFolderBuilder { + getGqlQueryParameters(): GQLTagInterface[] { + return [ + { name: 'Folder-Id', value: this.entityId }, + { name: 'Entity-Type', value: 'folder' } + ]; + } +} + +export class ArFSPublicFolderBuilder extends ArFSFolderBuilder { + static fromArweaveNode(node: GQLNodeInterface, arweave: Arweave): ArFSPublicFolderBuilder { + const { tags } = node; + const folderId = tags.find((tag) => tag.name === 'Folder-Id')?.value; + if (!folderId) { + throw new Error('Folder-ID tag missing!'); + } + const folderBuilder = new ArFSPublicFolderBuilder({ entityId: folderId, arweave }); + return folderBuilder; + } + + protected async buildEntity(): Promise { + if (!this.parentFolderId) { + // Root folders do not have a Parent-Folder-Id tag + this.parentFolderId = 'root folder'; + } + + if ( + this.appName?.length && + this.appVersion?.length && + this.arFS?.length && + this.contentType?.length && + this.driveId?.length && + this.entityType?.length && + this.txId?.length && + this.unixTime && + this.parentFolderId?.length && + this.entityId?.length + ) { + const txData = await this.arweave.transactions.getData(this.txId, { decode: true }); + const dataString = await Utf8ArrayToStr(txData); + const dataJSON = await JSON.parse(dataString); + + // Get the folder name + this.name = dataJSON.name; + if (!this.name) { + throw new Error('Invalid folder state'); + } + + return Promise.resolve( + new ArFSPublicFolder( + this.appName, + this.appVersion, + this.arFS, + this.contentType, + this.driveId, + this.entityType, + this.name, + this.txId, + this.unixTime, + this.parentFolderId, + this.entityId + ) + ); + } + throw new Error('Invalid folder state'); + } +} + +export class ArFSPrivateFolderBuilder extends ArFSFolderBuilder { + cipher?: string; + cipherIV?: CipherIV; + + constructor( + readonly folderId: FolderID, + readonly arweave: Arweave, + protected readonly driveKey: DriveKey, + readonly owner?: ArweaveAddress + ) { + super({ entityId: folderId, arweave, owner }); + } + + static fromArweaveNode(node: GQLNodeInterface, arweave: Arweave, driveKey: DriveKey): ArFSPrivateFolderBuilder { + const { tags } = node; + const folderId = tags.find((tag) => tag.name === 'Folder-Id')?.value; + if (!folderId) { + throw new Error('Folder-ID tag missing!'); + } + const folderBuilder = new ArFSPrivateFolderBuilder(folderId, arweave, driveKey); + return folderBuilder; + } + + protected async parseFromArweaveNode(node?: GQLNodeInterface): Promise { + const unparsedTags: GQLTagInterface[] = []; + const tags = await super.parseFromArweaveNode(node); + tags.forEach((tag: GQLTagInterface) => { + const key = tag.name; + const { value } = tag; + switch (key) { + case 'Cipher-IV': + this.cipherIV = value; + break; + case 'Cipher': + this.cipher = value; + break; + default: + unparsedTags.push(tag); + break; + } + }); + return unparsedTags; + } + + protected async buildEntity(): Promise { + if (!this.parentFolderId) { + // Root folders do not have a Parent-Folder-Id tag + this.parentFolderId = 'root folder'; + } + + if ( + this.appName?.length && + this.appVersion?.length && + this.arFS?.length && + this.contentType?.length && + this.driveId?.length && + this.entityType?.length && + this.txId?.length && + this.unixTime && + this.parentFolderId?.length && + this.entityId?.length && + this.cipher?.length && + this.cipherIV?.length + ) { + const txData = await this.arweave.transactions.getData(this.txId, { decode: true }); + const dataBuffer = Buffer.from(txData); + + const decryptedFolderBuffer: Buffer = await fileDecrypt(this.cipherIV, this.driveKey, dataBuffer); + const decryptedFolderString: string = await Utf8ArrayToStr(decryptedFolderBuffer); + const decryptedFolderJSON = await JSON.parse(decryptedFolderString); + + // Get the folder name + this.name = decryptedFolderJSON.name; + if (!this.name) { + throw new Error('Invalid folder state'); + } + + return new ArFSPrivateFolder( + this.appName, + this.appVersion, + this.arFS, + this.contentType, + this.driveId, + this.entityType, + this.name, + this.txId, + this.unixTime, + this.parentFolderId, + this.entityId, + this.cipher, + this.cipherIV + ); + } + throw new Error('Invalid folder state'); + } +} diff --git a/src/utils/arweave_oracle.ts b/src/utils/arweave_oracle.ts new file mode 100644 index 00000000..2a00fbd0 --- /dev/null +++ b/src/utils/arweave_oracle.ts @@ -0,0 +1,5 @@ +import { ByteCount } from '../types'; + +export interface ArweaveOracle { + getWinstonPriceForByteCount(byteCount: ByteCount): Promise; +} diff --git a/src/utils/data_price_regression.test.ts b/src/utils/data_price_regression.test.ts new file mode 100644 index 00000000..d4373069 --- /dev/null +++ b/src/utils/data_price_regression.test.ts @@ -0,0 +1,47 @@ +import { expect } from 'chai'; +import { ARDataPrice } from './ar_data_price'; +import { ARDataPriceRegression } from './data_price_regression'; + +describe('ARDataPriceRegression class', () => { + it('static constructor throws an error if no input data was supplied', () => { + expect(() => new ARDataPriceRegression([])).to.throw(Error); + }); + + it('static constructor can create a regression from a single datapoint', () => { + const inputDataPrice = new ARDataPrice(1, 1); + const predictedPrice = new ARDataPriceRegression([inputDataPrice]).predictedPriceForByteCount(1); + expect(predictedPrice).to.deep.equal(inputDataPrice); + }); + + it('predictedPriceForByteCount throws an error for negative and non-integer byte counts', () => { + const inputDataPrice = new ARDataPrice(1, 1); + const predictor = new ARDataPriceRegression([inputDataPrice]); + expect(() => predictor.predictedPriceForByteCount(-1)).to.throw(Error); + expect(() => predictor.predictedPriceForByteCount(0.5)).to.throw(Error); + }); + + it('predictedPriceForByteCount returns an accurate linear prediction', () => { + const predictor = new ARDataPriceRegression([ + new ARDataPrice(1, 1), + new ARDataPrice(100, 100), + new ARDataPrice(10000, 10000) + ]); + expect(predictor.predictedPriceForByteCount(0)).to.deep.equal(new ARDataPrice(0, 0)); + expect(predictor.predictedPriceForByteCount(1000000)).to.deep.equal(new ARDataPrice(1000000, 1000000)); + }); + + it('predictedPriceForByteCount returns a rounded up estimate when the Winston price would otherwise be predicted as non-integer', () => { + const predictor = new ARDataPriceRegression([new ARDataPrice(0, 0), new ARDataPrice(2, 3)]); + expect(predictor.predictedPriceForByteCount(1)).to.deep.equal(new ARDataPrice(1, 2)); + }); + + it('baseWinstonPrice returns the correct base value', () => { + const predictor = new ARDataPriceRegression([new ARDataPrice(0, 100), new ARDataPrice(5, 600)]); + expect(predictor.baseWinstonPrice()).to.equal(100); + }); + + it('marginalWinstonPrice returns the correct marginal value', () => { + const predictor = new ARDataPriceRegression([new ARDataPrice(0, 1), new ARDataPrice(5, 11)]); + expect(predictor.marginalWinstonPrice()).to.equal(2); + }); +}); diff --git a/src/utils/data_price_regression.ts b/src/utils/data_price_regression.ts new file mode 100644 index 00000000..c549e90f --- /dev/null +++ b/src/utils/data_price_regression.ts @@ -0,0 +1,60 @@ +import regression, { DataPoint } from 'regression'; +import { ByteCount } from '../types'; +import { ARDataPrice } from './ar_data_price'; + +/** + * A prediction tool for estimating the AR price (in Winston) for a data upload of a specified size based + * on a supplied set of observations of market prices. A linear prediction model is used for estimation. + */ +export class ARDataPriceRegression { + private readonly regression: regression.Result; + + /** + * Create a new price curve (linear) regression based on the supplied set of input price observations + * @param pricingData an array of recent data price observations + * @returns an ARDataPriceRegression that is ready for generating price predictions + * @throws {@link Error} for an empty pricing data array + */ + constructor(pricingData: ARDataPrice[]) { + if (!pricingData.length) { + throw new Error('Regression can not be run with an empty ARDataPrice list!'); + } + + const dataPoints: DataPoint[] = pricingData.map( + (pricingDatapoint) => [pricingDatapoint.numBytes, pricingDatapoint.winstonPrice] as DataPoint + ); + + this.regression = regression.linear(dataPoints); + } + + /** + * Predicts the AR (Winston) price for an upload with the specified size + * @param numBytes the size, in bytes, of the upload whose price we want to predict + * @returns the ARDataPrice predicted by the regression model for an upload of size `numBytes` + * @throws {@link Error} if `numBytes` is negative or not an integer + */ + predictedPriceForByteCount(numBytes: ByteCount): ARDataPrice { + if (numBytes < 0 || !Number.isInteger(numBytes)) { + throw new Error(`numBytes (${numBytes}) should be a positive integer`); + } + + const regressionResult = this.regression.predict(numBytes); + return new ARDataPrice(regressionResult[0], Math.ceil(regressionResult[1])); + } + + /** + * Returns the current base AR price in Winston for submitting an Arweave transaction, + * which has been calculated by the regression model + */ + baseWinstonPrice(): number { + return this.regression.equation[1]; + } + + /** + * Returns the current marginal AR price in Winston (winston price per byte), + * which has been calculated by the regression model + */ + marginalWinstonPrice(): number { + return this.regression.equation[0]; + } +} diff --git a/src/utils/filter_methods.ts b/src/utils/filter_methods.ts new file mode 100644 index 00000000..b455bad8 --- /dev/null +++ b/src/utils/filter_methods.ts @@ -0,0 +1,48 @@ +import { ArFSDriveEntity } from 'ardrive-core-js'; +import { ArFSFileOrFolderEntity } from '../arfs_entities'; + +/** + * @name lastRevisionFilter is a standard JS find/filter function intended to + * filter only the last revision of entities within an array + * + * @param {ArFSFileOrFolderEntity} entity the iterated entity + * @param {number} _index the iterated index + * @param {ArFSFileOrFolderEntity[]} allEntities the array of all entities + * @returns {boolean} + */ +export function latestRevisionFilter( + entity: ArFSFileOrFolderEntity, + _index: number, + allEntities: ArFSFileOrFolderEntity[] +): boolean { + const allRevisions = allEntities.filter((e) => e.entityId === entity.entityId); + const latestRevision = allRevisions[0]; + return entity.txId === latestRevision.txId; +} + +/** + * @name latestRevisionFilterForDrives is a standard JS find/filter function intended to + * filter only the last revision of entities within an array + * + * @param {ArFSDriveEntity} entity the iterated entity + * @param {number} _index the iterated index + * @param {ArFSDriveEntity[]} allEntities the array of all entities + * @returns {boolean} + */ +export function latestRevisionFilterForDrives( + entity: ArFSDriveEntity, + _index: number, + allEntities: ArFSDriveEntity[] +): boolean { + const allRevisions = allEntities.filter((e) => e.driveId === entity.driveId); + const latestRevision = allRevisions[0]; + return entity.txId === latestRevision.txId; +} + +export function fileFilter(entity: ArFSFileOrFolderEntity): boolean { + return entity.entityType === 'file'; +} + +export function folderFilter(entity: ArFSFileOrFolderEntity): boolean { + return entity.entityType === 'folder'; +} diff --git a/src/utils/gateway_oracle.ts b/src/utils/gateway_oracle.ts new file mode 100644 index 00000000..35893a8e --- /dev/null +++ b/src/utils/gateway_oracle.ts @@ -0,0 +1,11 @@ +import type { ArweaveOracle } from './arweave_oracle'; +import fetch from 'node-fetch'; +import { ByteCount } from '../types'; + +export class GatewayOracle implements ArweaveOracle { + async getWinstonPriceForByteCount(byteCount: ByteCount): Promise { + const response = await fetch(`https://arweave.net/price/${byteCount}`); + const winstonAsString = await response.text(); + return +winstonAsString; + } +} diff --git a/src/utils/mapper_functions.ts b/src/utils/mapper_functions.ts new file mode 100644 index 00000000..d77cbb91 --- /dev/null +++ b/src/utils/mapper_functions.ts @@ -0,0 +1,29 @@ +import { ArFSFileOrFolderEntity } from '../arfs_entities'; +import { FileID, FolderID } from '../types'; + +export interface EntityNamesAndIds { + files: FileNameAndId[]; + folders: FolderNameAndId[]; +} + +interface FolderNameAndId { + folderName: string; + folderId: FolderID; +} + +interface FileNameAndId { + fileName: string; + fileId: FileID; +} + +export function entityToNameMap(entity: ArFSFileOrFolderEntity): string { + return entity.name; +} + +export function folderToNameAndIdMap(entity: ArFSFileOrFolderEntity): FolderNameAndId { + return { folderId: entity.entityId, folderName: entity.name }; +} + +export function fileToNameAndIdMap(entity: ArFSFileOrFolderEntity): FileNameAndId { + return { fileId: entity.entityId, fileName: entity.name }; +} diff --git a/src/utils/sort_functions.ts b/src/utils/sort_functions.ts new file mode 100644 index 00000000..f742e963 --- /dev/null +++ b/src/utils/sort_functions.ts @@ -0,0 +1,3 @@ +export function alphabeticalOrder(a: string, b: string): number { + return a.localeCompare(b); +} diff --git a/src/utils/stubs.ts b/src/utils/stubs.ts new file mode 100644 index 00000000..382bd894 --- /dev/null +++ b/src/utils/stubs.ts @@ -0,0 +1,148 @@ +import { + ArFSPublicDrive, + ArFSPrivateDrive, + ArFSPublicFolder, + ArFSPrivateFolder, + ArFSPublicFile, + ArFSPrivateFile +} from '../arfs_entities'; +import { ArweaveAddress } from '../arweave_address'; +import { ArFS_O_11, DriveID, FolderID } from '../types'; + +export const stubArweaveAddress = (address = 'abcdefghijklmnopqrxtuvwxyz123456789ABCDEFGH'): ArweaveAddress => + new ArweaveAddress(address); + +export const stubTransactionID = '0000000000000000000000000000000000000000000'; + +export const stubEntityID = '00000000-0000-0000-0000-000000000000'; +export const stubEntityIDAlt = 'caa8b54a-eb5e-4134-8ae2-a3946a428ec7'; + +export const stubEntityIDRoot = '00000000-0000-0000-0000-000000000002'; +export const stubEntityIDParent = '00000000-0000-0000-0000-000000000003'; +export const stubEntityIDChild = '00000000-0000-0000-0000-000000000004'; +export const stubEntityIDGrandchild = '00000000-0000-0000-0000-000000000005'; + +export const stubPublicDrive = new ArFSPublicDrive( + 'Integration Test', + '1.0', + ArFS_O_11, + 'application/json', + stubEntityID, + 'drive', + 'STUB DRIVE', + stubTransactionID, + 0, + 'public', + stubEntityID +); + +export const stubPrivateDrive = new ArFSPrivateDrive( + 'Integration Test', + '1.0', + ArFS_O_11, + 'application/octet-stream', + stubEntityID, + 'drive', + 'STUB DRIVE', + stubTransactionID, + 0, + 'private', + stubEntityID, + 'password', + 'stubCipher', + 'stubIV' +); + +interface StubFolderParams { + folderId?: FolderID; + parentFolderId?: FolderID; + folderName?: string; + driveId?: DriveID; +} + +export const stubPublicFolder = ({ + folderId = stubEntityID, + parentFolderId = stubEntityID, + folderName = 'STUB NAME', + driveId = stubEntityID +}: StubFolderParams): ArFSPublicFolder => + new ArFSPublicFolder( + 'Integration Test', + '1.0', + ArFS_O_11, + 'application/json', + driveId, + 'folder', + folderName, + stubTransactionID, + 0, + parentFolderId, + folderId + ); + +export const stubPrivateFolder = ({ + folderId = stubEntityID, + parentFolderId = stubEntityID, + folderName = 'STUB NAME', + driveId = stubEntityID +}: StubFolderParams): ArFSPrivateFolder => + new ArFSPrivateFolder( + 'Integration Test', + '1.0', + ArFS_O_11, + 'application/json', + driveId, + 'folder', + folderName, + stubTransactionID, + 0, + parentFolderId, + folderId, + 'stubCipher', + 'stubIV' + ); + +interface StubFileParams { + driveId?: DriveID; + fileName?: string; +} + +export const stubPublicFile = ({ driveId = stubEntityID, fileName = 'STUB NAME' }: StubFileParams): ArFSPublicFile => + new ArFSPublicFile( + 'Integration Test', + '1.0', + ArFS_O_11, + 'application/json', + driveId, + 'file', + fileName, + stubTransactionID, + 0, + stubEntityID, + stubEntityID, + 1234567890, + 0, + stubTransactionID, + 'application/json' + ); + +export const stubPrivateFile = ({ driveId = stubEntityID, fileName = 'STUB NAME' }: StubFileParams): ArFSPrivateFile => + new ArFSPrivateFile( + 'Integration Test', + '1.0', + ArFS_O_11, + 'application/json', + driveId, + 'file', + fileName, + stubTransactionID, + 0, + stubEntityID, + stubEntityID, + 1234567890, + 0, + stubTransactionID, + 'application/json', + 'stubCipher', + 'stubIV' + ); diff --git a/src/utils/test_helpers.ts b/src/utils/test_helpers.ts new file mode 100644 index 00000000..a4cf39de --- /dev/null +++ b/src/utils/test_helpers.ts @@ -0,0 +1,35 @@ +import { expect } from 'chai'; + +interface expectAsyncErrorThrowParams { + promiseToError: Promise; + // TODO: Define error types, + // errorType: 'Error' | 'TypeError' | ... + errorType?: string; + errorMessage?: string; +} + +/** + * Test helper function that takes a promise and will expect a caught error + * + * @param promiseToError the promise on which to expect a thrown error + * @param errorType type of error to expect, defaults to 'Error' + * @param errorMessage exact error message to expect + * */ +export async function expectAsyncErrorThrow({ + promiseToError, + errorType = 'Error', + errorMessage +}: expectAsyncErrorThrowParams): Promise { + let error: null | Error = null; + try { + await promiseToError; + } catch (err) { + error = err; + } + + expect(error?.name).to.equal(errorType); + + if (errorMessage) { + expect(error?.message).to.equal(errorMessage); + } +} diff --git a/src/wallet.ts b/src/wallet.ts new file mode 100644 index 00000000..213e5442 --- /dev/null +++ b/src/wallet.ts @@ -0,0 +1,206 @@ +import { GQLTagInterface, JWKInterface } from 'ardrive-core-js'; +import * as B64js from 'base64-js'; +import * as crypto from 'crypto'; +import jwkToPem, { JWK } from 'jwk-to-pem'; +import Arweave from 'arweave'; +import * as mnemonicKeys from 'arweave-mnemonic-keys'; +import { + TransactionID, + Winston, + NetworkReward, + PublicKey, + SeedPhrase, + DEFAULT_APP_NAME, + DEFAULT_APP_VERSION, + RewardSettings +} from './types'; +import { CreateTransactionInterface } from 'arweave/node/common'; +import { ArweaveAddress } from './arweave_address'; + +export type ARTransferResult = { + trxID: TransactionID; + winston: Winston; + reward: NetworkReward; +}; + +export interface Wallet { + getPublicKey(): Promise; + getAddress(): Promise; + sign(data: Uint8Array): Promise; +} + +export class JWKWallet implements Wallet { + constructor(private readonly jwk: JWKInterface) {} + + getPublicKey(): Promise { + return Promise.resolve(this.jwk.n); + } + + getPrivateKey(): JWKInterface { + return this.jwk; + } + + async getAddress(): Promise { + const result = crypto + .createHash('sha256') + .update(b64UrlToBuffer(await this.getPublicKey())) + .digest(); + return Promise.resolve(new ArweaveAddress(bufferTob64Url(result))); + } + + // Use cases: generating drive keys, file keys, etc. + sign(data: Uint8Array): Promise { + const sign = crypto.createSign('sha256'); + sign.update(data); + const pem: string = jwkToPem(this.jwk as JWK, { private: true }); + const signature = sign.sign({ + key: pem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: 0 // We do not need to salt the signature since we combine with a random UUID + }); + return Promise.resolve(signature); + } +} + +export class WalletDAO { + constructor( + private readonly arweave: Arweave, + private readonly appName = DEFAULT_APP_NAME, + private readonly appVersion = DEFAULT_APP_VERSION + ) {} + + async generateSeedPhrase(): Promise { + const seedPhrase: SeedPhrase = await mnemonicKeys.generateMnemonic(); + return Promise.resolve(seedPhrase); + } + + async generateJWKWallet(seedPhrase: SeedPhrase): Promise { + const jwkWallet: JWKInterface = await mnemonicKeys.getKeyFromMnemonic(seedPhrase); + return Promise.resolve(new JWKWallet(jwkWallet)); + } + + async getWalletWinstonBalance(wallet: Wallet): Promise { + return this.getAddressWinstonBalance(await wallet.getAddress()); + } + + async getAddressWinstonBalance(address: ArweaveAddress): Promise { + return Promise.resolve(+(await this.arweave.wallets.getBalance(address.toString()))); + } + + async walletHasBalance(wallet: Wallet, winstonPrice: Winston): Promise { + const walletBalance = await this.getWalletWinstonBalance(wallet); + return +walletBalance > +winstonPrice; + } + + async sendARToAddress( + arAmount: number, + fromWallet: Wallet, + toAddress: ArweaveAddress, + rewardSettings: RewardSettings = {}, + dryRun = false, + [ + { value: appName = this.appName }, + { value: appVersion = this.appVersion }, + { value: trxType = 'transfer' }, + ...otherTags + ]: GQLTagInterface[], + assertBalance = false + ): Promise { + // TODO: Figure out how this works for other wallet types + const jwkWallet = fromWallet as JWKWallet; + const winston: Winston = this.arweave.ar.arToWinston(arAmount.toString()); + + // Create transaction + const trxAttributes: Partial = { target: toAddress.toString(), quantity: winston }; + + // If we provided our own reward settings, use them now + if (rewardSettings.reward) { + trxAttributes.reward = rewardSettings.reward; + } + + // TODO: Use a mock arweave server instead + if (process.env.NODE_ENV === 'test') { + trxAttributes.last_tx = 'STUB'; + } + const transaction = await this.arweave.createTransaction(trxAttributes, jwkWallet.getPrivateKey()); + if (rewardSettings.feeMultiple && rewardSettings.feeMultiple > 1.0) { + // Round up with ceil because fractional Winston will cause an Arweave API failure + transaction.reward = Math.ceil(+transaction.reward * rewardSettings.feeMultiple).toString(); + } + + if (assertBalance) { + const fromAddress = await fromWallet.getAddress(); + const balanceInWinston = await this.getAddressWinstonBalance(fromAddress); + const total = +transaction.reward + +transaction.quantity; + if (total > balanceInWinston) { + throw new Error( + [ + `Insufficient funds for this transaction`, + `quantity: ${transaction.quantity}`, + `minerReward: ${transaction.reward}`, + `balance: ${balanceInWinston}`, + `total: ${total}`, + `difference: ${total - balanceInWinston}` + ].join('\n\t') + ); + } + } + + // Tag file with data upload Tipping metadata + transaction.addTag('App-Name', appName); + transaction.addTag('App-Version', appVersion); + transaction.addTag('Type', trxType); + if (rewardSettings.feeMultiple && rewardSettings.feeMultiple > 1.0) { + transaction.addTag('Boost', rewardSettings.feeMultiple.toString()); + } + otherTags?.forEach((tag) => { + transaction.addTag(tag.name, tag.value); + }); + + // TODO: CHECK TAG LIMITS - i.e. upper limit of 2048bytes for all names and values + + // Sign file + await this.arweave.transactions.sign(transaction, jwkWallet.getPrivateKey()); + + // Submit the transaction + const response = await (async () => { + if (dryRun) { + return { status: 200, statusText: 'OK', data: '' }; + } else { + return this.arweave.transactions.post(transaction); + } + })(); + if (response.status === 200 || response.status === 202) { + return Promise.resolve({ + trxID: transaction.id, + winston, + reward: transaction.reward + }); + } else { + throw new Error(`Transaction failed. Response: ${response}`); + } + } +} + +export function bufferTob64Url(buffer: Uint8Array): string { + return b64UrlEncode(bufferTob64(buffer)); +} + +export function b64UrlEncode(b64UrlString: string): string { + return b64UrlString.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); +} + +export function bufferTob64(buffer: Uint8Array): string { + return B64js.fromByteArray(new Uint8Array(buffer)); +} + +export function b64UrlToBuffer(b64UrlString: string): Uint8Array { + return new Uint8Array(B64js.toByteArray(b64UrlDecode(b64UrlString))); +} + +export function b64UrlDecode(b64UrlString: string): string { + b64UrlString = b64UrlString.replace(/-/g, '+').replace(/_/g, '/'); + let padding; + b64UrlString.length % 4 == 0 ? (padding = 0) : (padding = 4 - (b64UrlString.length % 4)); + return b64UrlString.concat('='.repeat(padding)); +} diff --git a/test_wallet.json b/test_wallet.json new file mode 100644 index 00000000..1cae3f16 --- /dev/null +++ b/test_wallet.json @@ -0,0 +1,12 @@ +{ + "d": "PPD79CNSdvNPLQ7ns-SjJy5_jCvSDS9FMx65HOubCZSk8yXyDS1kjzpE5EIUx66fExlh6xjVq9hr7ZVvPmSqKYTU_N3HkUV0v49ZmmmHkxBgYPjUKz0MPn4DWaHGIGZRIuHMcm8NS8Y0hUZqj59UYIrjMBTcRW1ydbDuyUhWmg0xluCDOoxd73-xpnraV2-ZWE8Eks8SGMNlx4KZRvdzjx3VjOhe_WUuwcbkh1NX8_Ab2TIFkqlexrku10YZXSTYZRnvOISuDaD7d2CIrEaRGDNUK3gV6MwOxA8_rgk42Yh1TjxN3ZjhmgG50kjyatS8asmTOhStXq42in0qTA0Z848OThrwcA2avyNjhi_DVabQobQ2W2qDja4vw4C9UT50ob0me2xHSBojx7U5PsCmGGnDJb-97aaEe9T_IN11P9_3JabIqnLB2Q0vuwVe8TTSpD6ZIO01eQ4YD7m_d0oeWE23D5MkT3HrHr-p2twNede6QRDVOQ_4ruBC-2l4tp8sOaspAij1cud0rMAdkx1TuTcnnzTAMuaPaiPYAri5sd0YJxMjF3saGc2YUOHVFsWt7Kc0RZiMWCJP4QSbEeOwOT9OyCozfEqgGDdlf4z0g-NPFGXCioEbhLZsFvNkXFT33GTa1ACpSDi8qXLXgxPhDBT_NJ0ucLUjhTH6p0INX6k", + "dp": "xydNPLZs8DNgtBAV020GrgnM_6hiKv1G621fe-JvKfHXkJBm9_D0n6tq94eCdtNd8KFAXXOncj4tgpc22eHgE9Bt2LdXh7ppTcjY3M79UEMqSjybpoU-XpxC2a_lWJ2jT81hLb3kSyWh5Cyb-VPPujkJ1JRUfEC13PBll1mpcnkieZaGIIPYGCvJwDodlCH1ZuNje6qA3Q9eKUhV67QiXZ8g_nX1AwF1hLStX3N3bv13LONDQobfM9GWI05I0cyYBS3ydCbAQifqRP_LyPmsIuzMr4JVvoiG5R7eEmmqE6IHb1phUmgr2N2Jq18FbsimWx-RpfoI7KEy8GxEiuYfAw", + "dq": "mqVH3DGvjVMD0sbDqwWWhfwYlZfOpffPjWnowIbVpKmQAbkOqGvtfu12svjjkAUr9XNPYviC-gZZsugVThVwIcyYj6aUv8a1NwtNACGLjkeRz-YzITuJwo2MuwEbX9Eqg_GnG9zU1tXK3kTS1M7hnOugxewY8L4mbfQAM4I_h_1yRWr16twvxUTU9VOXNNOSrrJRozzN0_yZw9ApOfXDllS-ahzXNpWUYuSmKGw2PvbIbq9ZM8AJP5Lgmkb6jiFh15547EnnicM0sewEIYxZPNts3-lX6IxCayu1I_XJjVNt3V8z43bEESBqwvtip08Vs1aoJ8f2a8tjo942P1q8uQ", + "e": "AQAB", + "ext": true, + "kty": "RSA", + "n": "refMg4aHFNHl1MXu3W0Ss9ckv2hh1iWdLQKn0u4NkV_ejpQ5BJBk6KUy1fb11NUdeCWCck8P_gRSjdk3zK24PPwU-aHsxasgq_nFTDaCHQao3lPWZoVER-aEMsxA2svqnH2j-UL9bFH0lGuh_MQkHVSaIMXACs8ykzi-PFE8xv4OcnrcyZ7wJcit9e3F1rBaVWOvfryG1Mn3P6cwr1Om_LGZnNvFeeHcRhDKaHeqS2X96z41VZ5p5Y8fGt_ht2-ZBjOMAqErHqSspbt4JoPn0dtDjybIybyItQD0qrELjn6SRwvLaGNVFUh5rgi_O-Th4C_GnKKmTgYT2W2sDhU-_m8T4fI5aO19e653vZOaFKMbN7hN3EvPdxin1MVdOngParqBkS59eNZ-duL9hJl8Y0lh_LVNTRdJVMzgNNqHGoUwWkBbMD1Ot78aD3RUo2XaeKIfuqA6XHaBI0yBzdAmV3QjyOZwM8-0cdpbmlodI50-DGCZc9OPFja469MIRhl-BMSVy6Y1kaVW-kCNXMZz90iEphnsw_7SZ_tswoNq7pdmZCqMnzPGErh517Jm7M_TS2gFpkqdFdegAtjv-Qzt5b-Dhj_VU4arJA0FlC1BaX1CaT8G9h7RF1RP59I7wfqvbnH2rSduy869rybqpGXjtkGpugn05OmOwRJGbTl23G0", + "p": "5yR7281SVDDXG9Xh1MDMGXHifiQDqQrpecLDk7pJZqsdYl2nR1Fr_4v8zbXwULBJ7y01kWOH2Xi5hO8APHay98PDx9Mi6xO6_8mgbgDMaJVwklceGEJJG8fFhm4Gz9Ne5labDhBI8Q1kek9wVEnB-u9Ugm7aFDK7U-CxdoKQFpItZNY4rMtYQY499N-3F2etYhRUl9S3I2B8S_VjQTdlidVZuRk1hhWANP-5S1IjvsGWHUVjQz9wD4bVkJe7V90LgoSt5q4_P4V3mwlbPhzZEo9zei5idxJ_xiQOiBIno4hhdgOMYMpfeURdajQFvRotgekb1HCE_gT1NCfdLZIINw", + "q": "wJuJ7BSHhD6-2w65TzqaOHyJ8VQvtK5kFxZF6cY7pLBBSSgIGkixh9PkNnyd08oxNVZKfE_Q6yAlpG231RV-s8IWwBGdDlHzuqVkpVPVV5seTA0DouVBtUTnxk8oEr4tXghr9BwGtlEUbv_8Y2lUsK-ZLe41MGSu98e2Pj7zB-fXYYCDMMwSxEx9X6ZjU8o_kX1sUtC8dQvDNUWsQNY0867ponNn6kRH92dR2uuUZmypzE_7MF1lDiruJB3CQgdHK85-5PvBvrRjqV3FBgP7w3ZYrWHnX1svk96bkFnk9yGBtOhwkjzKqKokBJ9eQl42TlRRpdJWYgy5afhhwbFmew", + "qi": "QmKaBjToBnC9YDZhkeE28FjwGT2z-wwcO5NjWt_tpML9Aqdu8q34Xi4Opwb0n4UIV_4Y8_OqzhVWUXh9-8PcNQUywXYlZuqfIiizcs3KJJzWyWZt8c65R31GvVxcBlKuqos3V4U2c-Fx9XNfuztPw3gMD1ab-YIZ1SzEDQruag9X4HUSrD1NuaEQHL7LZ3njeomZHcEak3x25lvd4Px9LJgzBsAgehgVwvVi7wIyByYyXerzM2iZ3Tht_vjm2k1ojrQMl3JQh2d5U-olFomOUqe-qR5N1Niyb7SSkwwIR9ruLsljNx1NxFyZfhH495ila44I0kr3Iz_obwtPOt97Ow" +} \ No newline at end of file diff --git a/tests/integration/ardrive.int.test.ts b/tests/integration/ardrive.int.test.ts new file mode 100644 index 00000000..7e5360a7 --- /dev/null +++ b/tests/integration/ardrive.int.test.ts @@ -0,0 +1,816 @@ +import Arweave from 'arweave'; +import { expect } from 'chai'; +import { stub } from 'sinon'; +import { ArDrive, ArFSResult } from '../../src/ardrive'; +import { readJWKFile, urlEncodeHashKey } from '../../src/utils'; +import { + stubArweaveAddress, + stubEntityID, + stubEntityIDAlt, + stubEntityIDChild, + stubEntityIDGrandchild, + stubEntityIDParent, + stubEntityIDRoot, + stubPrivateDrive, + stubPrivateFile, + stubPrivateFolder, + stubPublicDrive, + stubPublicFile, + stubPublicFolder +} from '../../src/utils/stubs'; +import { ARDataPriceRegressionEstimator } from '../../src/utils/ar_data_price_regression_estimator'; +import { GatewayOracle } from '../../src/utils/gateway_oracle'; +import { JWKWallet, WalletDAO } from '../../src/wallet'; +import { expectAsyncErrorThrow } from '../../src/utils/test_helpers'; +import { ArDriveCommunityOracle } from '../../src/community/ardrive_community_oracle'; +import { ArFSDAO, PrivateDriveKeyData } from '../../src/arfsdao'; +import { deriveDriveKey, DrivePrivacy } from 'ardrive-core-js'; +import { DriveKey, FileID, Winston } from '../../src/types'; +import { ArFSFileToUpload, wrapFileOrFolder } from '../../src/arfs_file_wrapper'; + +const entityIdRegex = /^([a-f]|[0-9]){8}-([a-f]|[0-9]){4}-([a-f]|[0-9]){4}-([a-f]|[0-9]){4}-([a-f]|[0-9]){12}$/; +const trxIdRegex = /^([a-zA-Z]|[0-9]|-|_){43}$/; +const fileKeyRegex = /^([a-zA-Z]|[0-9]|-|_|\/|\+){43}$/; + +describe('ArDrive class - integrated', () => { + const wallet = readJWKFile('./test_wallet.json'); + + const getStubDriveKey = async (): Promise => { + return deriveDriveKey('stubPassword', stubEntityID, JSON.stringify((wallet as JWKWallet).getPrivateKey())); + }; + + const fakeArweave = Arweave.init({ + host: 'localhost', + port: 443, + protocol: 'https', + timeout: 600000 + }); + + const arweaveOracle = new GatewayOracle(); + const communityOracle = new ArDriveCommunityOracle(fakeArweave); + const priceEstimator = new ARDataPriceRegressionEstimator(true, arweaveOracle); + const walletDao = new WalletDAO(fakeArweave, 'Integration Test', '1.0'); + const arfsDao = new ArFSDAO(wallet, fakeArweave, true, 'Integration Test', '1.0'); + + const arDrive = new ArDrive( + wallet, + walletDao, + arfsDao, + communityOracle, + 'Integration Test', + '1.0', + priceEstimator, + 1.0, + true + ); + + const walletOwner = stubArweaveAddress(); + const unexpectedOwner = stubArweaveAddress('0987654321klmnopqrxtuvwxyz123456789ABCDEFGH'); + + const expectedDriveId = stubEntityID; + const unexpectedDriveId = stubEntityIDAlt; + const existingFileId = stubEntityIDAlt; + + beforeEach(() => { + // Set pricing algo up as x = y (bytes = Winston) + stub(arweaveOracle, 'getWinstonPriceForByteCount').callsFake((input) => Promise.resolve(input)); + + // Declare common stubs + stub(walletDao, 'walletHasBalance').resolves(true); + stub(wallet, 'getAddress').resolves(walletOwner); + stub(arfsDao, 'getDriveIDForEntityId').resolves(expectedDriveId); + }); + + describe('utility function', () => { + describe('sendCommunityTip', () => { + it('returns the correct TipResult', async () => { + stub(communityOracle, 'selectTokenHolder').resolves(stubArweaveAddress()); + + const result = await arDrive.sendCommunityTip('12345'); + + // Can't know the txID ahead of time without mocking arweave deeply + expect(result.tipData.txId).to.match(trxIdRegex); + expect(`${result.tipData.recipient}`).to.equal(`${stubArweaveAddress()}`); + expect(result.tipData.winston).to.equal('12345'); + expect(result.reward).to.equal('0'); + }); + }); + }); + + describe('drive function', () => { + describe('createPublicDrive', () => { + it('returns the correct ArFSResult', async () => { + const result = await arDrive.createPublicDrive('TEST_DRIVE'); + assertCreateDriveExpectations(result, 75, 21); + }); + }); + + describe('createPrivateDrive', () => { + it('returns the correct ArFSResult', async () => { + const stubDriveKey = await getStubDriveKey(); + const stubPrivateDriveData: PrivateDriveKeyData = { + driveId: stubEntityID, + driveKey: stubDriveKey + }; + + const result = await arDrive.createPrivateDrive('TEST_DRIVE', stubPrivateDriveData); + assertCreateDriveExpectations(result, 91, 37, urlEncodeHashKey(stubDriveKey)); + }); + }); + }); + + describe('folder function', () => { + describe('createPublicFolder', () => { + beforeEach(() => { + stub(arfsDao, 'getPublicEntityNamesInFolder').resolves(['CONFLICTING_NAME']); + }); + + it('throws an error if the owner of the drive conflicts with supplied wallet', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(unexpectedOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.createPublicFolder({ + folderName: 'TEST_FOLDER', + driveId: stubEntityID, + parentFolderId: stubEntityID + }), + errorMessage: 'Supplied wallet is not the owner of this drive!' + }); + }); + + it('throws an error if the folder name conflicts with another ENTITY name in the destination folder', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.createPublicFolder({ + folderName: 'CONFLICTING_NAME', + driveId: stubEntityID, + parentFolderId: stubEntityID + }), + errorMessage: 'Entity name already exists in destination folder!' + }); + }); + + it('returns the correct ArFSResult', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPublicDrive').resolves(stubPublicDrive); + + const result = await arDrive.createPublicFolder({ + folderName: 'TEST_FOLDER', + driveId: stubEntityID, + parentFolderId: stubEntityID + }); + assertCreateFolderExpectations(result, 22); + }); + }); + + describe('createPrivateFolder', () => { + beforeEach(() => { + stub(arfsDao, 'getPrivateEntityNamesInFolder').resolves(['CONFLICTING_NAME']); + }); + + it('throws an error if the owner of the drive conflicts with supplied wallet', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(unexpectedOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.createPrivateFolder({ + folderName: 'TEST_FOLDER', + driveId: stubEntityID, + parentFolderId: stubEntityID, + driveKey: await getStubDriveKey() + }), + errorMessage: 'Supplied wallet is not the owner of this drive!' + }); + }); + + it('throws an error if the folder name conflicts with another ENTITY name in the destination folder', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.createPrivateFolder({ + folderName: 'CONFLICTING_NAME', + driveId: stubEntityID, + parentFolderId: stubEntityID, + driveKey: await getStubDriveKey() + }), + errorMessage: 'Entity name already exists in destination folder!' + }); + }); + + it('returns the correct ArFSResult', async () => { + stub(arfsDao, 'getPrivateDrive').resolves(stubPrivateDrive); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + const stubDriveKey = await getStubDriveKey(); + const result = await arDrive.createPrivateFolder({ + folderName: 'TEST_FOLDER', + driveId: stubEntityID, + parentFolderId: stubEntityID, + driveKey: stubDriveKey + }); + assertCreateFolderExpectations(result, 38, urlEncodeHashKey(stubDriveKey)); + }); + }); + + describe('movePublicFolder', () => { + const folderHierarchy = { + rootFolder: stubPublicFolder({ folderId: stubEntityIDRoot, parentFolderId: 'root folder' }), + parentFolder: stubPublicFolder({ folderId: stubEntityIDParent, parentFolderId: stubEntityIDRoot }), + childFolder: stubPublicFolder({ folderId: stubEntityIDChild, parentFolderId: stubEntityIDParent }), + grandChildFolder: stubPublicFolder({ + folderId: stubEntityIDGrandchild, + parentFolderId: stubEntityIDChild + }) + }; + + beforeEach(() => { + stub(arfsDao, 'getPublicEntityNamesInFolder').resolves(['CONFLICTING_NAME']); + }); + + it('throws an error if the owner of the drive conflicts with supplied wallet', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(unexpectedOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePublicFolder({ + folderId: stubEntityID, + newParentFolderId: stubEntityIDAlt + }), + errorMessage: 'Supplied wallet is not the owner of this drive!' + }); + }); + + it('throws an error if the folder name conflicts with another ENTITY name in the destination folder', async () => { + stub(arfsDao, 'getPublicFolder').resolves(stubPublicFolder({ folderName: 'CONFLICTING_NAME' })); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getDriveIdForFolderId').resolves(stubEntityID); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePublicFolder({ + folderId: stubEntityID, + newParentFolderId: stubEntityIDAlt + }), + errorMessage: 'Entity name already exists in destination folder!' + }); + }); + + it('throws an error if it is being moved inside any of its children folders', async () => { + stub(arfsDao, 'getPublicFolder').resolves(folderHierarchy.rootFolder); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPublicChildrenFolderIds').resolves([ + folderHierarchy.parentFolder.entityId, + folderHierarchy.childFolder.entityId, + folderHierarchy.grandChildFolder.entityId + ]); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePublicFolder({ + folderId: folderHierarchy.parentFolder.entityId, + newParentFolderId: folderHierarchy.grandChildFolder.entityId + }), + errorMessage: 'Parent folder cannot be moved inside any of its children folders!' + }); + }); + + it('throws an error if the new parent folder id matches its current parent folder id', async () => { + stub(arfsDao, 'getPublicFolder').resolves(folderHierarchy.childFolder); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPublicChildrenFolderIds').resolves([folderHierarchy.grandChildFolder.entityId]); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePublicFolder({ + folderId: folderHierarchy.childFolder.entityId, + newParentFolderId: folderHierarchy.parentFolder.entityId + }), + errorMessage: `Folder already has parent folder with ID: ${folderHierarchy.parentFolder.entityId}` + }); + }); + + it('throws an error if the new parent folder id matches its own folder id', async () => { + stub(arfsDao, 'getPublicFolder').resolves(folderHierarchy.parentFolder); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPublicChildrenFolderIds').resolves([ + folderHierarchy.childFolder.entityId, + folderHierarchy.grandChildFolder.entityId + ]); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePublicFolder({ + folderId: folderHierarchy.parentFolder.entityId, + newParentFolderId: folderHierarchy.parentFolder.entityId + }), + errorMessage: 'Folders cannot be moved into themselves!' + }); + }); + + it('throws an error if the folder is being moved to a different drive', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPublicFolder').resolves(stubPublicFolder({ driveId: unexpectedDriveId })); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePublicFolder({ + folderId: 'not used here', + newParentFolderId: 'we will error for drive ID reasons' + }), + errorMessage: 'Entity must stay in the same drive!' + }); + }); + + it('returns the correct ArFSResult', async () => { + stub(arfsDao, 'getPublicFolder').resolves(folderHierarchy.grandChildFolder); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPublicChildrenFolderIds').resolves([]); + + const result = await arDrive.movePublicFolder({ + folderId: folderHierarchy.grandChildFolder.entityId, + newParentFolderId: folderHierarchy.parentFolder.entityId + }); + assertCreateFolderExpectations(result, 20); + }); + }); + + describe('movePrivateFolder', () => { + const folderHierarchy = { + rootFolder: stubPrivateFolder({ folderId: stubEntityIDRoot, parentFolderId: 'root folder' }), + parentFolder: stubPrivateFolder({ folderId: stubEntityIDParent, parentFolderId: stubEntityIDRoot }), + childFolder: stubPrivateFolder({ folderId: stubEntityIDChild, parentFolderId: stubEntityIDParent }), + grandChildFolder: stubPrivateFolder({ + folderId: stubEntityIDGrandchild, + parentFolderId: stubEntityIDChild + }) + }; + + beforeEach(() => { + stub(arfsDao, 'getPrivateEntityNamesInFolder').resolves(['CONFLICTING_NAME']); + }); + + it('throws an error if the owner of the drive conflicts with supplied wallet', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(unexpectedOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePrivateFolder({ + folderId: stubEntityID, + newParentFolderId: stubEntityIDAlt, + driveKey: await getStubDriveKey() + }), + errorMessage: 'Supplied wallet is not the owner of this drive!' + }); + }); + + it('throws an error if the folder name conflicts with another ENTITY name in the destination folder', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPrivateFolder').resolves(stubPrivateFolder({ folderName: 'CONFLICTING_NAME' })); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePrivateFolder({ + folderId: stubEntityID, + newParentFolderId: stubEntityIDAlt, + driveKey: await getStubDriveKey() + }), + errorMessage: 'Entity name already exists in destination folder!' + }); + }); + + it('throws an error if it is being moved inside any of its children folders', async () => { + stub(arfsDao, 'getPrivateFolder').resolves(folderHierarchy.rootFolder); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPrivateChildrenFolderIds').resolves([ + folderHierarchy.parentFolder.entityId, + folderHierarchy.childFolder.entityId, + folderHierarchy.grandChildFolder.entityId + ]); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePrivateFolder({ + folderId: folderHierarchy.parentFolder.entityId, + newParentFolderId: folderHierarchy.grandChildFolder.entityId, + driveKey: await getStubDriveKey() + }), + errorMessage: 'Parent folder cannot be moved inside any of its children folders!' + }); + }); + + it('throws an error if the new parent folder id matches its current parent folder id', async () => { + stub(arfsDao, 'getPrivateFolder').resolves(folderHierarchy.childFolder); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPrivateChildrenFolderIds').resolves([folderHierarchy.grandChildFolder.entityId]); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePrivateFolder({ + folderId: folderHierarchy.childFolder.entityId, + newParentFolderId: folderHierarchy.parentFolder.entityId, + driveKey: await getStubDriveKey() + }), + errorMessage: `Folder already has parent folder with ID: ${folderHierarchy.parentFolder.entityId}` + }); + }); + + it('throws an error if the new parent folder id matches its own folder id', async () => { + stub(arfsDao, 'getPrivateFolder').resolves(folderHierarchy.parentFolder); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPrivateChildrenFolderIds').resolves([ + folderHierarchy.childFolder.entityId, + folderHierarchy.grandChildFolder.entityId + ]); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePrivateFolder({ + folderId: folderHierarchy.parentFolder.entityId, + newParentFolderId: folderHierarchy.parentFolder.entityId, + driveKey: await getStubDriveKey() + }), + errorMessage: 'Folders cannot be moved into themselves!' + }); + }); + + it('throws an error if the folder is being moved to a different drive', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPrivateFolder').resolves(stubPrivateFolder({ driveId: unexpectedDriveId })); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePrivateFolder({ + folderId: 'not used here', + newParentFolderId: 'we will error for drive ID reasons', + driveKey: await getStubDriveKey() + }), + errorMessage: 'Entity must stay in the same drive!' + }); + }); + + it('returns the correct ArFSResult', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + stub(arfsDao, 'getPrivateFolder').resolves(folderHierarchy.grandChildFolder); + stub(arfsDao, 'getPrivateChildrenFolderIds').resolves([]); + + const result = await arDrive.movePrivateFolder({ + folderId: folderHierarchy.grandChildFolder.entityId, + newParentFolderId: folderHierarchy.parentFolder.entityId, + driveKey: await getStubDriveKey() + }); + assertCreateFolderExpectations(result, 36, urlEncodeHashKey(await getStubDriveKey())); + }); + }); + + describe('file function', () => { + describe('uploadPublicFile', () => { + const wrappedFile = wrapFileOrFolder('test_wallet.json') as ArFSFileToUpload; + + beforeEach(() => { + stub(communityOracle, 'getCommunityWinstonTip').resolves('1'); + stub(communityOracle, 'selectTokenHolder').resolves(stubArweaveAddress()); + + stub(arfsDao, 'getPublicEntityNamesAndIdsInFolder').resolves({ + files: [{ fileName: 'CONFLICTING_FILE_NAME', fileId: existingFileId }], + folders: [{ folderName: 'CONFLICTING_FOLDER_NAME', folderId: stubEntityID }] + }); + }); + + it('throws an error if the owner of the drive conflicts with supplied wallet', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(unexpectedOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.uploadPublicFile(stubEntityID, wrappedFile), + errorMessage: 'Supplied wallet is not the owner of this drive!' + }); + }); + + it('throws an error if destination folder has a conflicting FOLDER name', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.uploadPublicFile(stubEntityID, wrappedFile, 'CONFLICTING_FOLDER_NAME'), + errorMessage: 'Entity name already exists in destination folder!' + }); + }); + + it('returns the correct ArFSResult revision if destination folder has a conflicting FILE name', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + const result = await arDrive.uploadPublicFile(stubEntityID, wrappedFile, 'CONFLICTING_FILE_NAME'); + + // Pass expected existing file id, so that the file would be considered a revision + assertUploadFileExpectations(result, 3204, 171, 0, '1', 'public', existingFileId); + }); + + it('returns the correct ArFSResult', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + const result = await arDrive.uploadPublicFile(stubEntityID, wrappedFile); + assertUploadFileExpectations(result, 3204, 166, 0, '1', 'public'); + }); + }); + + describe('uploadPrivateFile', () => { + const wrappedFile = wrapFileOrFolder('test_wallet.json') as ArFSFileToUpload; + + beforeEach(() => { + stub(communityOracle, 'getCommunityWinstonTip').resolves('1'); + stub(communityOracle, 'selectTokenHolder').resolves(stubArweaveAddress()); + + stub(arfsDao, 'getPrivateEntityNamesAndIdsInFolder').resolves({ + files: [{ fileName: 'CONFLICTING_FILE_NAME', fileId: existingFileId }], + folders: [{ folderName: 'CONFLICTING_FOLDER_NAME', folderId: stubEntityID }] + }); + }); + + it('throws an error if the owner of the drive conflicts with supplied wallet', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(unexpectedOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.uploadPrivateFile(stubEntityID, wrappedFile, await getStubDriveKey()), + errorMessage: 'Supplied wallet is not the owner of this drive!' + }); + }); + + it('throws an error if destination folder has a conflicting FOLDER name', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.uploadPrivateFile( + stubEntityID, + wrappedFile, + await getStubDriveKey(), + 'CONFLICTING_FOLDER_NAME' + ), + errorMessage: 'Entity name already exists in destination folder!' + }); + }); + + it('returns the correct ArFSResult revision if destination folder has a conflicting FILE name', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + const result = await arDrive.uploadPrivateFile( + stubEntityID, + wrappedFile, + await getStubDriveKey(), + 'CONFLICTING_FILE_NAME' + ); + + // Pass expected existing file id, so that the file would be considered a revision + assertUploadFileExpectations(result, 3216, 187, 0, '1', 'private', existingFileId); + }); + + it('returns the correct ArFSResult', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + const stubDriveKey = await getStubDriveKey(); + + const result = await arDrive.uploadPrivateFile(stubEntityID, wrappedFile, stubDriveKey); + assertUploadFileExpectations(result, 3216, 182, 0, '1', 'private'); + }); + }); + + describe('movePublicFile', () => { + beforeEach(() => { + stub(arfsDao, 'getPublicEntityNamesInFolder').resolves(['CONFLICTING_NAME']); + }); + + it('throws an error if the owner of the drive conflicts with supplied wallet', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(unexpectedOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePublicFile(stubEntityID, stubEntityIDAlt), + errorMessage: 'Supplied wallet is not the owner of this drive!' + }); + }); + + it('throws an error if the destination folder has a conflicting entity name', async () => { + stub(arfsDao, 'getPublicFile').resolves(stubPublicFile({ fileName: 'CONFLICTING_NAME' })); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePublicFile(stubEntityID, stubEntityIDAlt), + errorMessage: 'Entity name already exists in destination folder!' + }); + }); + + it('throws an error if the new parent folder id matches its current parent folder id', async () => { + stub(arfsDao, 'getPublicFile').resolves(stubPublicFile({})); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + await expectAsyncErrorThrow({ promiseToError: arDrive.movePublicFile(stubEntityID, stubEntityID) }); + }); + + it('throws an error if the file is being moved to a different drive', async () => { + stub(arfsDao, 'getPublicFile').resolves(stubPublicFile({ driveId: unexpectedDriveId })); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePublicFile(stubEntityID, stubEntityID), + errorMessage: 'Entity must stay in the same drive!' + }); + }); + + it('returns the correct ArFSResult', async () => { + stub(arfsDao, 'getPublicFile').resolves(stubPublicFile({})); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + const result = await arDrive.movePublicFile(stubEntityID, stubEntityIDAlt); + assertMoveFileExpectations(result, 153, 'public'); + }); + }); + + describe('movePrivateFile', () => { + beforeEach(() => { + stub(arfsDao, 'getPrivateEntityNamesInFolder').resolves(['CONFLICTING_NAME']); + }); + + it('throws an error if the owner of the drive conflicts with supplied wallet', async () => { + stub(arfsDao, 'getOwnerForDriveId').resolves(unexpectedOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePrivateFile(stubEntityID, stubEntityIDAlt, await getStubDriveKey()), + errorMessage: 'Supplied wallet is not the owner of this drive!' + }); + }); + + it('throws an error if the destination folder has a conflicting entity name', async () => { + stub(arfsDao, 'getPrivateFile').resolves(stubPrivateFile({ fileName: 'CONFLICTING_NAME' })); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePrivateFile(stubEntityID, stubEntityIDAlt, await getStubDriveKey()), + errorMessage: 'Entity name already exists in destination folder!' + }); + }); + + it('throws an error if the new parent folder id matches its current parent folder id', async () => { + stub(arfsDao, 'getPrivateFile').resolves(stubPrivateFile({})); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePrivateFile(stubEntityID, stubEntityID, await getStubDriveKey()), + errorMessage: `File already has parent folder with ID: ${stubEntityID}` + }); + }); + + it('throws an error if the file is being moved to a different drive', async () => { + stub(arfsDao, 'getPrivateFile').resolves(stubPrivateFile({ driveId: unexpectedDriveId })); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + await expectAsyncErrorThrow({ + promiseToError: arDrive.movePrivateFile(stubEntityID, stubEntityID, await getStubDriveKey()), + errorMessage: 'Entity must stay in the same drive!' + }); + }); + + it('returns the correct ArFSResult', async () => { + stub(arfsDao, 'getPrivateFile').resolves(stubPrivateFile({})); + stub(arfsDao, 'getOwnerForDriveId').resolves(walletOwner); + + const result = await arDrive.movePrivateFile( + stubEntityID, + stubEntityIDAlt, + await getStubDriveKey() + ); + assertMoveFileExpectations(result, 169, 'private'); + }); + }); + }); + }); +}); + +function assertCreateDriveExpectations( + result: ArFSResult, + driveFee: number, + folderFee: number, + expectedDriveKey?: string +) { + // Ensure that 2 arfs entities were created + expect(result.created.length).to.equal(2); + + // Ensure that the drive entity looks healthy + const driveEntity = result.created[0]; + expect(driveEntity.dataTxId).to.be.undefined; + expect(driveEntity.entityId).to.match(entityIdRegex); + expect(driveEntity.key).to.equal(expectedDriveKey); + expect(driveEntity.metadataTxId).to.match(trxIdRegex); + expect(driveEntity.type).to.equal('drive'); + + // Ensure that the root folder entity looks healthy + const rootFolderEntity = result.created[1]; + expect(rootFolderEntity.dataTxId).to.be.undefined; + expect(rootFolderEntity.entityId).to.match(entityIdRegex); + expect(rootFolderEntity.key).to.equal(expectedDriveKey); + expect(rootFolderEntity.metadataTxId).to.match(trxIdRegex); + expect(rootFolderEntity.type).to.equal('folder'); + + // There should be no tips + expect(result.tips).to.be.empty; + + // Ensure that the fees look healthy + const feeKeys = Object.keys(result.fees); + expect(feeKeys.length).to.equal(2); + expect(feeKeys[0]).to.equal(driveEntity.metadataTxId); + expect(feeKeys[0]).to.match(trxIdRegex); + expect(result.fees[driveEntity.metadataTxId]).to.equal(driveFee); + expect(feeKeys[1]).to.equal(rootFolderEntity.metadataTxId); + expect(feeKeys[1]).to.match(trxIdRegex); + expect(result.fees[rootFolderEntity.metadataTxId]).to.equal(folderFee); +} + +function assertCreateFolderExpectations(result: ArFSResult, folderFee: number, expectedDriveKey?: string) { + // Ensure that 1 arfs entity was created + expect(result.created.length).to.equal(1); + + // Ensure that the folder entity looks healthy + const folderEntity = result.created[0]; + expect(folderEntity.dataTxId).to.be.undefined; + expect(folderEntity.entityId).to.match(entityIdRegex); + expect(folderEntity.key).to.equal(expectedDriveKey); + expect(folderEntity.metadataTxId).to.match(trxIdRegex); + expect(folderEntity.type).to.equal('folder'); + + // There should be no tips + expect(result.tips).to.be.empty; + + // Ensure that the fees look healthy + const feeKeys = Object.keys(result.fees); + expect(feeKeys.length).to.equal(1); + expect(feeKeys[0]).to.match(trxIdRegex); + expect(feeKeys[0]).to.equal(folderEntity.metadataTxId); + expect(result.fees[folderEntity.metadataTxId]).to.equal(folderFee); +} + +function assertUploadFileExpectations( + result: ArFSResult, + fileFee: number, + metadataFee: number, + tipFee: number, + expectedTip: Winston, + drivePrivacy: DrivePrivacy, + expectedFileId?: FileID +) { + // Ensure that 1 arfs entity was created + expect(result.created.length).to.equal(1); + + // Ensure that the file data entity looks healthy + const fileEntity = result.created[0]; + expect(fileEntity.dataTxId).to.match(trxIdRegex); + expect(fileEntity.entityId).to.match(entityIdRegex); + + if (expectedFileId) { + expect(fileEntity.entityId).to.equal(expectedFileId); + } + + switch (drivePrivacy) { + case 'public': + expect(fileEntity.key).to.equal(undefined); + break; + case 'private': + expect(fileEntity.key).to.match(fileKeyRegex); + } + expect(fileEntity.metadataTxId).to.match(trxIdRegex); + expect(fileEntity.type).to.equal('file'); + + // There should be 1 tip + expect(result.tips.length).to.equal(1); + const uploadTip = result.tips[0]; + expect(uploadTip.txId).to.match(trxIdRegex); + expect(uploadTip.winston).to.equal(expectedTip); + expect(uploadTip.recipient).to.match(trxIdRegex); + + // Ensure that the fees look healthy + expect(Object.keys(result.fees).length).to.equal(3); + + const feeKeys = Object.keys(result.fees); + expect(feeKeys[0]).to.match(trxIdRegex); + expect(feeKeys[0]).to.equal(fileEntity.dataTxId); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + expect(result.fees[fileEntity.dataTxId!]).to.equal(fileFee); + + expect(feeKeys[1]).to.match(trxIdRegex); + expect(feeKeys[1]).to.equal(fileEntity.metadataTxId); + expect(result.fees[fileEntity.metadataTxId]).to.equal(metadataFee); + + expect(feeKeys[2]).to.match(trxIdRegex); + expect(feeKeys[2]).to.equal(uploadTip.txId); + expect(result.fees[uploadTip.txId]).to.equal(tipFee); +} + +function assertMoveFileExpectations(result: ArFSResult, fileFee: number, drivePrivacy: DrivePrivacy) { + // Ensure that 1 arfs entity was created + expect(result.created.length).to.equal(1); + + // Ensure that the file entity looks healthy + const fileEntity = result.created[0]; + expect(fileEntity.dataTxId).to.match(trxIdRegex); + expect(fileEntity.entityId).to.match(entityIdRegex); + switch (drivePrivacy) { + case 'public': + expect(fileEntity.key).to.equal(undefined); + break; + case 'private': + expect(fileEntity.key).to.match(fileKeyRegex); + } + expect(fileEntity.metadataTxId).to.match(trxIdRegex); + expect(fileEntity.type).to.equal('file'); + + // There should be no tips + expect(result.tips).to.be.empty; + + // Ensure that the fees look healthy + const feeKeys = Object.keys(result.fees); + expect(feeKeys.length).to.equal(1); + expect(feeKeys[0]).to.match(trxIdRegex); + expect(feeKeys[0]).to.equal(fileEntity.metadataTxId); + expect(result.fees[fileEntity.metadataTxId]).to.equal(fileFee); +} diff --git a/tests/testSetup.ts b/tests/testSetup.ts new file mode 100644 index 00000000..fd7c08c1 --- /dev/null +++ b/tests/testSetup.ts @@ -0,0 +1,8 @@ +import { restore } from 'sinon'; + +// Restores the default sandbox after every test +exports.mochaHooks = { + afterEach() { + restore(); + } +}; diff --git a/tsconfig.json b/tsconfig.json index 1e528b85..051b26dd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,20 +1,20 @@ { - "compilerOptions": { - "target": "es6", - "module": "CommonJS", - "declaration": true, - "moduleResolution": "node", - "outDir": "./lib", - "strict": true, - "esModuleInterop": true, - /* Additional Checks */ - "pretty": true, - "sourceMap": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - }, - "include": ["src"], - "exclude": ["node_modules", "**/__tests__/*"] + "compilerOptions": { + "target": "es6", + "module": "CommonJS", + "declaration": true, + "moduleResolution": "node", + "outDir": "./lib", + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true, + /* Additional Checks */ + "pretty": true, + "sourceMap": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src", "tests"] } diff --git a/tsconfig.prod.json b/tsconfig.prod.json new file mode 100644 index 00000000..e8dd1383 --- /dev/null +++ b/tsconfig.prod.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "sourceMap": false + }, + "include": ["src"], + "exclude": ["tests", "**/*.test.ts"] +} diff --git a/yarn.lock b/yarn.lock index 2373ef0c..52e1f53a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,30 +14,268 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0": - version: 7.10.4 - resolution: "@babel/code-frame@npm:7.10.4" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/code-frame@npm:7.14.5" dependencies: - "@babel/highlight": ^7.10.4 - checksum: 05245d3b22a3ae849439195c4ee9ce9903dfd8c3fcb5124e77923c45e9f1ceac971cce4c61505974f411a9db432949531abe10ddee92937a0a9c306dc380a5b2 + "@babel/highlight": ^7.14.5 + checksum: 48c584cad9aa05ff16fa965b4572deae0343d51abe658a2fb72640e924c229d47f71f880a474cc1e14e613f88a4bfd576609b1e0d8073bbc4e50e60f7e678626 + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.15.0": + version: 7.15.0 + resolution: "@babel/compat-data@npm:7.15.0" + checksum: 76db9ec4fb897b6a1de1057411a65b134bc2016b156ebf7ce39fbe97b4f1eeaf661b81d457126a71164cbc2de31a06c68f74d0ebbdce3720a000139683f787d0 + languageName: node + linkType: hard + +"@babel/core@npm:7.15.5, @babel/core@npm:^7.7.5": + version: 7.15.5 + resolution: "@babel/core@npm:7.15.5" + dependencies: + "@babel/code-frame": ^7.14.5 + "@babel/generator": ^7.15.4 + "@babel/helper-compilation-targets": ^7.15.4 + "@babel/helper-module-transforms": ^7.15.4 + "@babel/helpers": ^7.15.4 + "@babel/parser": ^7.15.5 + "@babel/template": ^7.15.4 + "@babel/traverse": ^7.15.4 + "@babel/types": ^7.15.4 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.1.2 + semver: ^6.3.0 + source-map: ^0.5.0 + checksum: 84c787c8215f722598eed9f7b07e434df0a109520fa752c2a63f6ab4e9dd3b2ff08c31b3a030b329845fb6c1c771dba0180ecdf5b73b949ef173117bbb861bb8 + languageName: node + linkType: hard + +"@babel/generator@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/generator@npm:7.15.4" + dependencies: + "@babel/types": ^7.15.4 + jsesc: ^2.5.1 + source-map: ^0.5.0 + checksum: 5ee8687d492ce44b613109f5a9e40e6b344177286c0d2578d3a144ea54855f20e80124399207d027a2262c04b52a99c8a538e77289f3a0e7b204ec06e550f531 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-compilation-targets@npm:7.15.4" + dependencies: + "@babel/compat-data": ^7.15.0 + "@babel/helper-validator-option": ^7.14.5 + browserslist: ^4.16.6 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 539e4d209324fe56cd624aa3ae4a843bd479a644f28240322ec3b93154e35087731b43a16f13e1e0037c651afdf589a06b5937adc77941bbf71e062ce9d85b89 + languageName: node + linkType: hard + +"@babel/helper-function-name@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-function-name@npm:7.15.4" + dependencies: + "@babel/helper-get-function-arity": ^7.15.4 + "@babel/template": ^7.15.4 + "@babel/types": ^7.15.4 + checksum: 74ec8a86b1e4263f502447409098c71f1461e09794495e11514d659857c1d8794fb0552afadeaebb4a6c93b75f800ad48c531174a672126bd645b1f49f2fa13f + languageName: node + linkType: hard + +"@babel/helper-get-function-arity@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-get-function-arity@npm:7.15.4" + dependencies: + "@babel/types": ^7.15.4 + checksum: c60ed72a9cf00222e20a5c7608f37a649c88af469f3021bd497dada07ccad109772b75cdf83ac363c48d1f135e80c367683f6aa6a015d21e5c4b9fbaf9682e56 languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/helper-validator-identifier@npm:7.10.4" - checksum: 25098ef842e3ffecdd9a7216f6173da7ad7be1b0b3e454a9f6965055154b9ad7a4acd2f218ba3d2efc0821bdab97837b3cb815844af7d72f66f89d446a54efc6 +"@babel/helper-hoist-variables@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-hoist-variables@npm:7.15.4" + dependencies: + "@babel/types": ^7.15.4 + checksum: b500f154f9444b5251548deaa62f3553d0a2ff277ab1478e4f8ec94f2b669474cc968c62b3451a9b9739d48c5e8818d48f03d07d75fe14fa247e574ee6aa674b languageName: node linkType: hard -"@babel/highlight@npm:^7.10.4": - version: 7.10.4 - resolution: "@babel/highlight@npm:7.10.4" +"@babel/helper-member-expression-to-functions@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-member-expression-to-functions@npm:7.15.4" dependencies: - "@babel/helper-validator-identifier": ^7.10.4 + "@babel/types": ^7.15.4 + checksum: 7180912838c00f3199dfe5c6299975a0c966b80e1aad7a0a448298b619c292103bd90a7fce2c748c8933f8e4b042138bdf1868d614c8fc303cec13a51fd9706d + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-module-imports@npm:7.15.4" + dependencies: + "@babel/types": ^7.15.4 + checksum: efb532958154817da2f5f4e9c12818500f97c2256eb6b99f01c7c537e506b8b2e609b0444d91503956ed3102eaba50826607a3352ef563d04430f9b5f502061b + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-module-transforms@npm:7.15.4" + dependencies: + "@babel/helper-module-imports": ^7.15.4 + "@babel/helper-replace-supers": ^7.15.4 + "@babel/helper-simple-access": ^7.15.4 + "@babel/helper-split-export-declaration": ^7.15.4 + "@babel/helper-validator-identifier": ^7.14.9 + "@babel/template": ^7.15.4 + "@babel/traverse": ^7.15.4 + "@babel/types": ^7.15.4 + checksum: f895915ff778f4b45f77effa67b4feed1aad704a5b441bcb153b58f5ce5d1a8ba13752270f59a18c37583420f928ab3027ef3b966ffdb4ee3e61d883bb2a5538 + languageName: node + linkType: hard + +"@babel/helper-optimise-call-expression@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-optimise-call-expression@npm:7.15.4" + dependencies: + "@babel/types": ^7.15.4 + checksum: 96d837b5d28679cebd0da6d7d1ead844c8f59854eb228033f290ecfe9385ca8a0201d5f8ff21f7fb04b14d20c94931f115332f283e2d5e73ce49c632566b8651 + languageName: node + linkType: hard + +"@babel/helper-replace-supers@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-replace-supers@npm:7.15.4" + dependencies: + "@babel/helper-member-expression-to-functions": ^7.15.4 + "@babel/helper-optimise-call-expression": ^7.15.4 + "@babel/traverse": ^7.15.4 + "@babel/types": ^7.15.4 + checksum: 44291cc084e5c36f9d05948614f0cf8efd36feccba5a291f5644280a5fa03b5eeb2a6fc18caaafd990747499fa4c4115c6e94ae27c46cf2008b28e947566ab29 + languageName: node + linkType: hard + +"@babel/helper-simple-access@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-simple-access@npm:7.15.4" + dependencies: + "@babel/types": ^7.15.4 + checksum: ac7f1403b4345258181c38640ff725aece0dc880a909e60e8492f0c0a847d11b14743a6b01cc4b53528210e67931e8aa76c21487183265de35aa8186d19fec98 + languageName: node + linkType: hard + +"@babel/helper-split-export-declaration@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helper-split-export-declaration@npm:7.15.4" + dependencies: + "@babel/types": ^7.15.4 + checksum: 17179ddcbaff87aa2fc00ec0c65282265a171918c49b66a7ebfbc519687bf24d584fbf0e535250a1ff76050c425263982f1114c598ea73dbe582b42eb7ce10e3 + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.14.5, @babel/helper-validator-identifier@npm:^7.14.9": + version: 7.14.9 + resolution: "@babel/helper-validator-identifier@npm:7.14.9" + checksum: a4825ac127a83def2def864fbf14f3601037c8bc35e8347a413b21d06f0257e8a8b0cde88885262e6c0401f71d24b158ba8e8c4e08c1c915dbdba1db7ec8d043 + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/helper-validator-option@npm:7.14.5" + checksum: aded46b377d25f5966aab30506cdb95908bae18ea5d062e86c646e9afcef911327da80e637e06c3542ea2ba01cab903d18e91fc8a12fbf7b15e0b3e8ee0b9d3b + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/helpers@npm:7.15.4" + dependencies: + "@babel/template": ^7.15.4 + "@babel/traverse": ^7.15.4 + "@babel/types": ^7.15.4 + checksum: b6e700c85be893ad5594e9d3ec61aa510cc08d0faef493030c3fb9f61ddfc3ce75b94be07989129cf3bcaa62f12296127db2ea80d9b952bf1d4417909181b269 + languageName: node + linkType: hard + +"@babel/highlight@npm:^7.10.4, @babel/highlight@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/highlight@npm:7.14.5" + dependencies: + "@babel/helper-validator-identifier": ^7.14.5 chalk: ^2.0.0 js-tokens: ^4.0.0 - checksum: c167b938af9797e7630dd922398ceb1a079469085b9c0a7274f093f9f2b1ef9f0a5efec89592e81cbab7c87a537d32c238cea97d288b7af9a0d26b2bceb7a439 + checksum: a1ed599c2655eb0b13134875ba2626b547a2634940e532c86a02896fb403f197cd56d1adaa474c7859ae4f53fabc5f1621e90770e75d235ca3350952ba78aa5c + languageName: node + linkType: hard + +"@babel/parser@npm:^7.15.4, @babel/parser@npm:^7.15.5": + version: 7.15.6 + resolution: "@babel/parser@npm:7.15.6" + bin: + parser: ./bin/babel-parser.js + checksum: 2ec5923e17cfc59425d095b64cd68f33e4d4753597cad27e812dd0a825925efd6b8b3f5f0c99d0b92fccd3becd714287cb9b509d71ea2ebca943bccd273df47c + languageName: node + linkType: hard + +"@babel/template@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/template@npm:7.15.4" + dependencies: + "@babel/code-frame": ^7.14.5 + "@babel/parser": ^7.15.4 + "@babel/types": ^7.15.4 + checksum: d4d366d8812a8e461d43bc8cbd25a183a20317c49af129d1a5bee45ce2bca8f1e3f9baa80d19216173c7bfc391d5d4c55af9540ffab419741122d7182dd7092f + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.15.4": + version: 7.15.4 + resolution: "@babel/traverse@npm:7.15.4" + dependencies: + "@babel/code-frame": ^7.14.5 + "@babel/generator": ^7.15.4 + "@babel/helper-function-name": ^7.15.4 + "@babel/helper-hoist-variables": ^7.15.4 + "@babel/helper-split-export-declaration": ^7.15.4 + "@babel/parser": ^7.15.4 + "@babel/types": ^7.15.4 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: d023925a4683732b096447290b2c7348ef5203ce44fc7d2fed11d82f287d743c941a8f86217e1884d9949b47958e3fd2034931f3a6f17dc78f9100c3587f8b7b + languageName: node + linkType: hard + +"@babel/types@npm:^7.15.4, @babel/types@npm:^7.8.3": + version: 7.15.6 + resolution: "@babel/types@npm:7.15.6" + dependencies: + "@babel/helper-validator-identifier": ^7.14.9 + to-fast-properties: ^2.0.0 + checksum: 34c8048bdec27e9935fa22cb1bd02f7b7c92c3bce21e96a85a92791c7e648868bb39039f0a1293dadc3fda51242040077d65e0a3d9601993ea5e4d9dd0821da4 + languageName: node + linkType: hard + +"@cspotcode/source-map-consumer@npm:0.8.0": + version: 0.8.0 + resolution: "@cspotcode/source-map-consumer@npm:0.8.0" + checksum: fc32215a353c7b3d039f5d4b93329200264daeabb0739dcf53eb78f779e37e5f5c281aad281f86781c930011d354612aafab887304451a46f16b10f0f919297f + languageName: node + linkType: hard + +"@cspotcode/source-map-support@npm:0.6.1": + version: 0.6.1 + resolution: "@cspotcode/source-map-support@npm:0.6.1" + dependencies: + "@cspotcode/source-map-consumer": 0.8.0 + checksum: eda5227576981f3a16bd1b0940f11bffe96a7dad36da6f2e5e3a8ee5b3ecb153359df08084684509adc63ffe12e14894b6f6f8f9423780895cf7cd9d66c8bbd6 languageName: node linkType: hard @@ -58,15 +296,47 @@ __metadata: languageName: node linkType: hard -"@jest/types@npm:^25.5.0": - version: 25.5.0 - resolution: "@jest/types@npm:25.5.0" +"@istanbuljs/load-nyc-config@npm:^1.0.0": + version: 1.1.0 + resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" + dependencies: + camelcase: ^5.3.1 + find-up: ^4.1.0 + get-package-type: ^0.1.0 + js-yaml: ^3.13.1 + resolve-from: ^5.0.0 + checksum: f7f3b1c922bf5e36a7f747b2a80fedc9c2e1ebd7e03dc73082fca7c1066cc4e2e2ac39827aded6a087c32294e9c032ff3e50bc9041fcf757b4a38ca97418b652 + languageName: node + linkType: hard + +"@istanbuljs/nyc-config-typescript@npm:^1.0.1": + version: 1.0.1 + resolution: "@istanbuljs/nyc-config-typescript@npm:1.0.1" dependencies: - "@types/istanbul-lib-coverage": ^2.0.0 - "@types/istanbul-reports": ^1.1.1 - "@types/yargs": ^15.0.0 - chalk: ^3.0.0 - checksum: 33ad68320efb297c4bd98975105130e1b4096d631decfc5a093691e24f27fce0410b4a7c5a87b736873271ebc003e48e853529587e584b3152efca572139a4a3 + "@istanbuljs/schema": ^0.1.2 + peerDependencies: + nyc: ">=15" + source-map-support: "*" + ts-node: "*" + checksum: 28c19a10ee0d9d46cf8602ffe296c43997b3b6d153f6c15dda99e4a25f7ed56cc5bc43574a0d24aa3685e5155fb09a50291115662820a1849044c91dd5d513d3 + languageName: node + linkType: hard + +"@istanbuljs/schema@npm:^0.1.2": + version: 0.1.3 + resolution: "@istanbuljs/schema@npm:0.1.3" + checksum: d84c326335c37e3bd963e51d0e9631153961ff695524b1722317c9991f5153da283f819beab84a079695e2da8b3740e84c81db47c361cf12fff575968145d662 + languageName: node + linkType: hard + +"@lordvlad/asn1.js@npm:^5.1.1": + version: 5.1.1 + resolution: "@lordvlad/asn1.js@npm:5.1.1" + dependencies: + bn.js: ^4.0.0 + inherits: ^2.0.1 + minimalistic-assert: ^1.0.0 + checksum: 806d50b2eab95b2117afe3c31d6016e58e808a9ba627f6b147876987eb1c6319ce8cfdb1d44efa022ae0b007cf30f43b1ed5921b6827e9e65b1c8ab3224c885f languageName: node linkType: hard @@ -97,20 +367,167 @@ __metadata: languageName: node linkType: hard -"@ponicode/cli@npm:^0.0.1-18": - version: 0.0.1-18 - resolution: "@ponicode/cli@npm:0.0.1-18" +"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/aspromise@npm:1.1.2" + checksum: 83ced0798abea0c30ed07dc6338605ddfd8dc7b8b15d84586d155980f90476a8f049e7695d7de79d35dcada29a01710d24d0aa2104e8466412c0cbc6c14d9173 + languageName: node + linkType: hard + +"@protobufjs/base64@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/base64@npm:1.1.2" + checksum: ae9e84aaf69f5e6aedfb346153c236e6f6ca0e97612c680251fc3e48e6c60746ed553062ea97fdde9f0592fb085e467b9de377a848a224d990a4f0a5e7212fa4 + languageName: node + linkType: hard + +"@protobufjs/codegen@npm:^2.0.4": + version: 2.0.4 + resolution: "@protobufjs/codegen@npm:2.0.4" + checksum: a05d5f8892eeb7696c73f7585a9c3a89eea293b37a582cd1a5d650908393287b0ca068dc55e7bbecd3ec146b2f61afaff2f61e3cb488ea67b900114247b1b717 + languageName: node + linkType: hard + +"@protobufjs/eventemitter@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/eventemitter@npm:1.1.0" + checksum: 32a84e33f191a3ddc0ca4f8975484c0afaf10c2b27e0cb2008dabe267791420447c25d77b1409595aa6be660a908ada306d40cccb675aee7912c4bc82a9f512e + languageName: node + linkType: hard + +"@protobufjs/fetch@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/fetch@npm:1.1.0" dependencies: - "@types/connect": ^3.4.33 - "@types/convert-source-map": ^1.5.1 - "@types/jest": ^25.2.1 - "@types/js-base64": ^3.0.0 - convert-source-map: ^1.7.0 - cross-spawn: ^7.0.3 - js-base64: ^3.5.2 - bin: - ponicode: bin/ponicode - checksum: 5873f1fb0a89d13fcf0936eec98f2dc49204084011704040b1631c971c35c2026c575c1ae8b73817946b97744e07c53b549d1052b3155d58cade8679b3ff8588 + "@protobufjs/aspromise": ^1.1.1 + "@protobufjs/inquire": ^1.1.0 + checksum: d682e5d8a164d096d71b05866eb0852d4e1fc024900e3af96a57d9f6396402b96d81b7bd77cda6b983ebcbfd715ec12dd1c1582760b7658a9438c530f5f6f4db + languageName: node + linkType: hard + +"@protobufjs/float@npm:^1.0.2": + version: 1.0.2 + resolution: "@protobufjs/float@npm:1.0.2" + checksum: eee7278de2c4550fac7ec156cba3d4227226a08470426cba6eb04671be9aea035bdf79b7839fdad624171451a4d66e1cdf01e4980e57223ba4f1e34ccd187563 + languageName: node + linkType: hard + +"@protobufjs/inquire@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/inquire@npm:1.1.0" + checksum: 3541518cca945c2a95e2dcacacdc243fd64abc4cc51c2b691bd0534bd8a1b945c90368f57ba3fed1d6632e1e68d6dac6a7761738f89ccf60bb352b6624c8aacb + languageName: node + linkType: hard + +"@protobufjs/path@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/path@npm:1.1.2" + checksum: 22f10c5c22e5b3f7bb3e7f0af15bafa1fe42bad3705f27448464c6221d956d10c6c9dad96a48650ec5acba7b072a64ab94da52e21f00e6cd9612da761f02b845 + languageName: node + linkType: hard + +"@protobufjs/pool@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/pool@npm:1.1.0" + checksum: 5fc4af9e069b58cc39cfd6fded3bfe3de7113d35c1bc3458bfc6699fa42e90c2c6d96b5ae7c274470f272135122a9ab94c708eacfd81e1b0a593917841b07ec4 + languageName: node + linkType: hard + +"@protobufjs/utf8@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/utf8@npm:1.1.0" + checksum: 5b3fa7425fe2852f6e5643d9a53c6cfb4491630667176c0fc35ddb7a71101559241bafb6b80486144ed75da3d3624095d037c802b578dbdb4cce131cf1bcb7cf + languageName: node + linkType: hard + +"@sinonjs/commons@npm:^1.6.0, @sinonjs/commons@npm:^1.7.0, @sinonjs/commons@npm:^1.8.1, @sinonjs/commons@npm:^1.8.3": + version: 1.8.3 + resolution: "@sinonjs/commons@npm:1.8.3" + dependencies: + type-detect: 4.0.8 + checksum: a7f3181512f67bbb9059dc9247febfda6dea58fc2a918360b208c6fde193b0c2cbe628650b0d13b4ba69f144470788eb6c2ef8a84e050dce4808be8511da4316 + languageName: node + linkType: hard + +"@sinonjs/fake-timers@npm:^6.0.0, @sinonjs/fake-timers@npm:^6.0.1": + version: 6.0.1 + resolution: "@sinonjs/fake-timers@npm:6.0.1" + dependencies: + "@sinonjs/commons": ^1.7.0 + checksum: 64458b908773638dda08b555a00e6fbbbc679735348291dc1b7f437ada2f60242537fdc48e4ee82d2573d86984ec87e755b66a96c0ed9ebf0f46b4c6687ccde2 + languageName: node + linkType: hard + +"@sinonjs/fake-timers@npm:^7.0.4, @sinonjs/fake-timers@npm:^7.1.0, @sinonjs/fake-timers@npm:^7.1.2": + version: 7.1.2 + resolution: "@sinonjs/fake-timers@npm:7.1.2" + dependencies: + "@sinonjs/commons": ^1.7.0 + checksum: 5ce48e40db14d7e1419bae287b84559133d580cb56130b51d7479dff318bfafed87531f8b48f618f07e3c9a6113c9e3f00286805d55de64986b9cccb8eb6d5cf + languageName: node + linkType: hard + +"@sinonjs/samsam@npm:^5.3.1": + version: 5.3.1 + resolution: "@sinonjs/samsam@npm:5.3.1" + dependencies: + "@sinonjs/commons": ^1.6.0 + lodash.get: ^4.4.2 + type-detect: ^4.0.8 + checksum: 0277cd0b71146efed3d243edddc98362adcc25c13e0067b91500fd990792194be64b0ace6f4ba9f98cdec60ee46749e24aad44b46193cfa73096c5ef309b00a1 + languageName: node + linkType: hard + +"@sinonjs/samsam@npm:^6.0.2": + version: 6.0.2 + resolution: "@sinonjs/samsam@npm:6.0.2" + dependencies: + "@sinonjs/commons": ^1.6.0 + lodash.get: ^4.4.2 + type-detect: ^4.0.8 + checksum: 8113510c5b22bf5cdb66e50e519560b6532cfb31bdf87f3876328e3c16797ed9a65a3b5d5e0578e3da307e5124e332c977a09f9af509592ec7d75a7f3faf2018 + languageName: node + linkType: hard + +"@sinonjs/text-encoding@npm:^0.7.1": + version: 0.7.1 + resolution: "@sinonjs/text-encoding@npm:0.7.1" + checksum: fbc2abff23dce7f8842a64c61a4183a9300b03b8b4f03dc2739c8172fd3e401ed8f28fb1c5aac85b3cc0935f69bdb9823dd864a2e22110626751ff855879a76a + languageName: node + linkType: hard + +"@tsconfig/node10@npm:^1.0.7": + version: 1.0.8 + resolution: "@tsconfig/node10@npm:1.0.8" + checksum: 0336493b89fb7c06409a1247a3fb00fac2755f21f3f8ae4b9dd2457859abfc5e8ca42b6d9ca5a279fe81bc70fe1f3450eef61e5dd5a63a7b4a6946ff31874816 + languageName: node + linkType: hard + +"@tsconfig/node12@npm:^1.0.7": + version: 1.0.9 + resolution: "@tsconfig/node12@npm:1.0.9" + checksum: 5532bfb5df47ed3a507da533c731a2fb80ee2e886edadbf20e664dcd3172d5c159577a281d15733b8d0c30bfa4e6b48496bef0704192c085520bc76bb9938068 + languageName: node + linkType: hard + +"@tsconfig/node14@npm:^1.0.0": + version: 1.0.1 + resolution: "@tsconfig/node14@npm:1.0.1" + checksum: d0068287dba46dc98e7d49c229b0fee034fbac2bb4bc2efe12cc67227a1c68ec0728ca1e535dff7f033f7455de6c67e9b8f9d90f4fc3bb07c0d9ac08186fe65c + languageName: node + linkType: hard + +"@tsconfig/node16@npm:^1.0.2": + version: 1.0.2 + resolution: "@tsconfig/node16@npm:1.0.2" + checksum: 57310e88858be59b36603e5d9bd7bd1714d16402ef1e536c01521ad3b207833a2fc64d260d362cae07bf4e50759a3aba3958c2faf9c42b52c4c817be60a93123 + languageName: node + linkType: hard + +"@types/chai@npm:*, @types/chai@npm:^4.2.21": + version: 4.2.21 + resolution: "@types/chai@npm:4.2.21" + checksum: fc7d32fbae47ad6d3576bcb8cf24a243af03c93bb97cd9456a5415da888ab78a03800dcc4cc25a21ad1b493bcdaee7377f163bc4dd8db463c3530dafddea205c languageName: node linkType: hard @@ -137,22 +554,6 @@ __metadata: languageName: node linkType: hard -"@types/connect@npm:^3.4.33": - version: 3.4.34 - resolution: "@types/connect@npm:3.4.34" - dependencies: - "@types/node": "*" - checksum: 6f712a0408ed119a42037bcbc38c12e053d3ffe11337221a3966c9e29a21c8455e331fe2116c426361c0b2c0e44cb3fb1f83594326e42ba34ce4723d726b7d30 - languageName: node - linkType: hard - -"@types/convert-source-map@npm:^1.5.1": - version: 1.5.1 - resolution: "@types/convert-source-map@npm:1.5.1" - checksum: 36cd50ea42b5e5c41db2415e4f42bdbc426e6b963a7d0d0a511164b789d4140629fbc0b3c5a29306ad3731ab41708088bb63e95972060cd3983af1f22b33f414 - languageName: node - linkType: hard - "@types/inquirer@npm:^7.3.1": version: 7.3.1 resolution: "@types/inquirer@npm:7.3.1" @@ -163,74 +564,68 @@ __metadata: languageName: node linkType: hard -"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0": - version: 2.0.3 - resolution: "@types/istanbul-lib-coverage@npm:2.0.3" - checksum: d6f6dbf66d2d2d7d80d093329f0428ac279440510030bfd0080545bba6882433444430905e6e31eba299b890e50ccf2b6a7de9345d7d0ed52ff174f8ead48855 +"@types/json-schema@npm:^7.0.3": + version: 7.0.7 + resolution: "@types/json-schema@npm:7.0.7" + checksum: b9d2c509fa4e0b82f58e73f5e6ab76c60ff1884ba41bb82f37fb1cece226d4a3e5a62fedf78a43da0005373a6713d9abe61c1e592906402c41c08ad6ab26d52b languageName: node linkType: hard -"@types/istanbul-lib-report@npm:*": - version: 3.0.0 - resolution: "@types/istanbul-lib-report@npm:3.0.0" - dependencies: - "@types/istanbul-lib-coverage": "*" - checksum: 78aa9f859b6d1b2c02387b401e4e42fdec2e26ffede392e544da108abc6aff35c95b40821116ca46006d94c8b405ffd64465c32514549e997b04f8363de1af5e +"@types/jwk-to-pem@npm:^2.0.0": + version: 2.0.0 + resolution: "@types/jwk-to-pem@npm:2.0.0" + checksum: 647e62d34757d9909b46805d87baa6d49791a9a9da09d41e3f539f6df527090396dd8285022558819ffbc5b7d705f3c816c2ca4a90101fc4773df15ee43a67d5 languageName: node linkType: hard -"@types/istanbul-reports@npm:^1.1.1": - version: 1.1.2 - resolution: "@types/istanbul-reports@npm:1.1.2" - dependencies: - "@types/istanbul-lib-coverage": "*" - "@types/istanbul-lib-report": "*" - checksum: 92bd1f76a4ce16f5390c80b6b0e657171faf0003b0ff370b3c37739087c825d664493c9debf442c0871d864f1be15c88460f2399ae748186d1a944f16958aea4 +"@types/lodash@npm:^4": + version: 4.14.176 + resolution: "@types/lodash@npm:4.14.176" + checksum: cc3c9d7522a20814d1bbc5125a3d5a4a4eed72e7cf6a3b01b0810aa5dcff0cf23ab40ea4ab00fe2425dd0828cd915087d8d073757ab9c925e2872903dd3e15d0 languageName: node linkType: hard -"@types/jest@npm:^25.2.1": - version: 25.2.3 - resolution: "@types/jest@npm:25.2.3" - dependencies: - jest-diff: ^25.2.1 - pretty-format: ^25.2.1 - checksum: fe92624fd7b3b5358ffba8e59108b484de1d01df08dfc66bb597d59d09900db1950717adb8d42ce5f81ba615fa0b8de55fd5446808d92f75b45e08fcbe4cec6e +"@types/long@npm:^4.0.1": + version: 4.0.1 + resolution: "@types/long@npm:4.0.1" + checksum: ed2a125330dbf2b425b2e58b1e1151a37106ceeec973f9f191fba0bda83ed00fae6d858254022e8b6e7165fec5f0c3872b7cb5f425c753383fed54699fb63b38 languageName: node linkType: hard -"@types/js-base64@npm:^3.0.0": - version: 3.0.0 - resolution: "@types/js-base64@npm:3.0.0" - checksum: 7513271c3f772d58781d0d784aecc7bccd9e5907f27be9c0c259e367aa0dbdafa79c496b56303a5c390d5a73b4e6851835d5eef1365361592747650d73a0995c +"@types/mocha@npm:^9.0.0": + version: 9.0.0 + resolution: "@types/mocha@npm:9.0.0" + checksum: dac70c24e5e7f825a6f449035ebab1b7cfe99b5976b879a4039a516ade38fcc6c78fc4c4d953d27c62db1d9117372e129c6bfe08e2aab0c0a2a6e441335aba42 languageName: node linkType: hard -"@types/json-schema@npm:^7.0.3": - version: 7.0.7 - resolution: "@types/json-schema@npm:7.0.7" - checksum: b9d2c509fa4e0b82f58e73f5e6ab76c60ff1884ba41bb82f37fb1cece226d4a3e5a62fedf78a43da0005373a6713d9abe61c1e592906402c41c08ad6ab26d52b +"@types/node-fetch@npm:2.5.3": + version: 2.5.3 + resolution: "@types/node-fetch@npm:2.5.3" + dependencies: + "@types/node": "*" + checksum: c85f18372209de9b8633c2d3ad162f4604ed6eb53c7b4656c031b878ee2d996021591b9dd3183ae5a202ae5ed6354d5fcf1a92be39cafe21c4b550e9282c4826 languageName: node linkType: hard -"@types/jwk-to-pem@npm:^2.0.0": - version: 2.0.0 - resolution: "@types/jwk-to-pem@npm:2.0.0" - checksum: 647e62d34757d9909b46805d87baa6d49791a9a9da09d41e3f539f6df527090396dd8285022558819ffbc5b7d705f3c816c2ca4a90101fc4773df15ee43a67d5 +"@types/node@npm:*, @types/node@npm:>=13.7.0": + version: 16.7.13 + resolution: "@types/node@npm:16.7.13" + checksum: e22d3b58e5c4b18d058b18ab75bb1bf27b2dcb83b798eac3cdb7a3b12725f7ee1343abc7b5d020d39869669045654ce7d38e952832a1f39f061da0fbebbf43df languageName: node linkType: hard -"@types/node@npm:*": - version: 14.6.4 - resolution: "@types/node@npm:14.6.4" - checksum: bff274e362080628c96e51b1948be668b68117e35d6353691c753052e490efc0934a0c0a3ba5d2739ac70e745932a9f6b049b7d01a1d96c9ae0c93c3b1b88ed0 +"@types/node@npm:11.11.6": + version: 11.11.6 + resolution: "@types/node@npm:11.11.6" + checksum: 2b64bfc2344f60fdcf3baea2f9f2e065189cee3a6144d3dbdcb32cb6728dd64933808e2da8bd4d69033f04510f7de1397c08d54d8f89d505b3a957b24dcc583f languageName: node linkType: hard -"@types/node@npm:^14.14.32": - version: 14.14.37 - resolution: "@types/node@npm:14.14.37" - checksum: 5e2d9baf7594ebacaf016716515f30de0765169412787f981481c2fb8b468923149bb9e2e3219ee672399811672ceddc339a7372a61cf15bc656836a5494d991 +"@types/node@npm:^14.14.32, @types/node@npm:^14.6.1": + version: 14.17.15 + resolution: "@types/node@npm:14.17.15" + checksum: c9f5b6fa6a383ebecf57ad85d75e7c1df9c1e31ba8468f2193d63177f52f9472c08677f68959bae3defce038e9ebf3d8954edcae93f99225d6a74e6b88e68521 languageName: node linkType: hard @@ -241,13 +636,6 @@ __metadata: languageName: node linkType: hard -"@types/prompt-sync@npm:^4.1.0": - version: 4.1.0 - resolution: "@types/prompt-sync@npm:4.1.0" - checksum: 21bc6832fb849c71bd9ac940e43fc1f91acb7f79a7e5f610141ca0b3bf97ba392be1c541598a0874bc2e9812e010854955e039dfb0c3f587b959190bb5c4aff6 - languageName: node - linkType: hard - "@types/prompts@npm:^2.0.9": version: 2.4.0 resolution: "@types/prompts@npm:2.4.0" @@ -255,6 +643,57 @@ __metadata: languageName: node linkType: hard +"@types/regression@npm:^2.0.2": + version: 2.0.2 + resolution: "@types/regression@npm:2.0.2" + checksum: fa577d48c5a1eae5c9df305622d1104e7d457fb0d2802189d2228f70bf199b287f16201eaac363ba3550ac5fbd67b753217c50b5d286700e69479deb4ca4d72d + languageName: node + linkType: hard + +"@types/sinon-chai@npm:^3.2.4": + version: 3.2.5 + resolution: "@types/sinon-chai@npm:3.2.5" + dependencies: + "@types/chai": "*" + "@types/sinon": "*" + checksum: 0e482de52bc6a390921b940dd30786b9ddbc78d2c32a5590f094606ecf20523a5ea2fadc1f702e5e45479e1f7dc19be1061fdf9fe01ad26f466511cea418899e + languageName: node + linkType: hard + +"@types/sinon@npm:*, @types/sinon@npm:^10.0.2": + version: 10.0.2 + resolution: "@types/sinon@npm:10.0.2" + dependencies: + "@sinonjs/fake-timers": ^7.1.0 + checksum: 44d149b0ba4ebfbadd7452e31edb35aab782d74bb542a808e5b0cc72f40fd2db6b4179bb97a79d5e2dc1b870823a395cd21349037a7af336810c1662ee9f076f + languageName: node + linkType: hard + +"@types/sinon@npm:^9.0.5": + version: 9.0.11 + resolution: "@types/sinon@npm:9.0.11" + dependencies: + "@types/sinonjs__fake-timers": "*" + checksum: c84ccbd5ac05d8eb45151a9c0790cf9b9fc78bdfc2f3feecb8866dab530bed82301ba0340cfb73de7148f08a40c9c1b1b7a920e4c9efd79195b60a52ab3e577d + languageName: node + linkType: hard + +"@types/sinonjs__fake-timers@npm:*": + version: 6.0.3 + resolution: "@types/sinonjs__fake-timers@npm:6.0.3" + checksum: 2f2accb87ecddb8becbd3c3fa92a4272ee857356b7b677c6b7807a0d30e1490d9fb2fe074bc23d32bc90ec7f96b494485b722f1e81f266f3c21f4118646c796c + languageName: node + linkType: hard + +"@types/source-map-support@npm:^0": + version: 0.5.4 + resolution: "@types/source-map-support@npm:0.5.4" + dependencies: + source-map: ^0.6.0 + checksum: 448a3aa4fa18837f19881783e158ecaf135c8c266533506c2b50c86d6cd60e3c41b1060c4ec8c750fa38424772fccaf613fb1daa1b9428a8e62795750910051b + languageName: node + linkType: hard + "@types/through@npm:*": version: 0.0.30 resolution: "@types/through@npm:0.0.30" @@ -271,22 +710,6 @@ __metadata: languageName: node linkType: hard -"@types/yargs-parser@npm:*": - version: 15.0.0 - resolution: "@types/yargs-parser@npm:15.0.0" - checksum: 74bfaefde90fb28eace49469fa6c2da63161176cb6dfbd2cfea2c3cb3268e4ca6abe174ae3ff7e633a49a6d6d1a114901c78799a19d0cbc5a9b539585afe6c4f - languageName: node - linkType: hard - -"@types/yargs@npm:^15.0.0": - version: 15.0.5 - resolution: "@types/yargs@npm:15.0.5" - dependencies: - "@types/yargs-parser": "*" - checksum: 2133c8cb5878d13959844f98e546e69dacdf44cd9baf87d84c828a1a093febfc97c8f4df19cffd34a4a4f726a3cdb1851da4391176accf56534c5f8a1c271f46 - languageName: node - linkType: hard - "@typescript-eslint/eslint-plugin@npm:^4.18.0": version: 4.20.0 resolution: "@typescript-eslint/eslint-plugin@npm:4.20.0" @@ -387,6 +810,13 @@ __metadata: languageName: node linkType: hard +"@ungap/promise-all-settled@npm:1.1.2": + version: 1.1.2 + resolution: "@ungap/promise-all-settled@npm:1.1.2" + checksum: be6c80a2fcea2fc4ed9ccf707da9837c2f501ba21312bf9b39f8b0c4ac7e581ed37909f4f64f8dab0c51c0ae5bdf2a45b59d9a215050876b3f27e6844dba30b6 + languageName: node + linkType: hard + "@weavery/clarity@npm:^0.1.5": version: 0.1.5 resolution: "@weavery/clarity@npm:0.1.5" @@ -410,6 +840,13 @@ __metadata: languageName: node linkType: hard +"acorn-walk@npm:^8.1.1": + version: 8.2.0 + resolution: "acorn-walk@npm:8.2.0" + checksum: 93ffbdb8b35dda04313f1d6a94bdfdf8f929337f742e3a6d51b3026ced8471feb6a522646240809da44b0f576f4902ff2fa0c64292b3216478992aefcbde3278 + languageName: node + linkType: hard + "acorn@npm:^7.4.0": version: 7.4.1 resolution: "acorn@npm:7.4.1" @@ -419,6 +856,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.4.1": + version: 8.5.0 + resolution: "acorn@npm:8.5.0" + bin: + acorn: bin/acorn + checksum: 989ff8bf4b1f37d3c719c1dce785524344d2e3859ac9d851e25a0af9f4f8cddfb15f6b83560bf57508daebe76faaf2ee2a089ecb984ad4ac6236b594bc41abbb + languageName: node + linkType: hard + "aggregate-error@npm:^3.0.0": version: 3.1.0 resolution: "aggregate-error@npm:3.1.0" @@ -429,7 +875,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.10.0, ajv@npm:^6.12.4": +"ajv@npm:^6.10.0, ajv@npm:^6.12.3, ajv@npm:^6.12.4": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -441,18 +887,6 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.12.3": - version: 6.12.4 - resolution: "ajv@npm:6.12.4" - dependencies: - fast-deep-equal: ^3.1.1 - fast-json-stable-stringify: ^2.0.0 - json-schema-traverse: ^0.4.1 - uri-js: ^4.2.2 - checksum: 50d72b0a10326732072f5481b1b6bd5a43f8d770878b8f88ba5bb232abb745cefbf7f87a0e64679bd477d4a8bba0b3aea084675bd34943db5279c15907ee658f - languageName: node - linkType: hard - "ajv@npm:^8.0.1": version: 8.0.2 resolution: "ajv@npm:8.0.2" @@ -564,6 +998,13 @@ __metadata: languageName: node linkType: hard +"ansi-colors@npm:4.1.1, ansi-colors@npm:^4.1.1": + version: 4.1.1 + resolution: "ansi-colors@npm:4.1.1" + checksum: 50d8dfbce25602caea1b170ecf4c71c4c9c58d2d1e3186fb5712848c0610d05fe60b8bb6a9eaebd9b54f1db3baf6f603e04214cce597cc7799bc9f47fd9a797a + languageName: node + linkType: hard + "ansi-colors@npm:^0.2.0": version: 0.2.0 resolution: "ansi-colors@npm:0.2.0" @@ -593,16 +1034,9 @@ __metadata: ansi-strikethrough: ^0.1.1 ansi-underline: ^0.1.1 ansi-white: ^0.1.1 - ansi-yellow: ^0.1.1 - lazy-cache: ^2.0.1 - checksum: 695d9095de792f063b4a531a8d2093592a7dc6687af795e4afde8b5bd101873b9fef978fcc7c20f51630d1dc11ebe5cfaf21cfbbe5ed5a6d9edc4a86988d0726 - languageName: node - linkType: hard - -"ansi-colors@npm:^4.1.1": - version: 4.1.1 - resolution: "ansi-colors@npm:4.1.1" - checksum: 50d8dfbce25602caea1b170ecf4c71c4c9c58d2d1e3186fb5712848c0610d05fe60b8bb6a9eaebd9b54f1db3baf6f603e04214cce597cc7799bc9f47fd9a797a + ansi-yellow: ^0.1.1 + lazy-cache: ^2.0.1 + checksum: 695d9095de792f063b4a531a8d2093592a7dc6687af795e4afde8b5bd101873b9fef978fcc7c20f51630d1dc11ebe5cfaf21cfbbe5ed5a6d9edc4a86988d0726 languageName: node linkType: hard @@ -624,16 +1058,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.2.1": - version: 4.3.1 - resolution: "ansi-escapes@npm:4.3.1" - dependencies: - type-fest: ^0.11.0 - checksum: bcb39e57bd32af0236c4ded96aaf8ef5d86c5a4683762b0be998c68cd11d5afd93296f4b5e087a3557da82a899b7c4d081483d603a4d4647e6a6613bf1aded8a - languageName: node - linkType: hard - -"ansi-escapes@npm:^4.3.0": +"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -813,13 +1238,13 @@ __metadata: languageName: node linkType: hard -"anymatch@npm:~3.1.1": - version: 3.1.1 - resolution: "anymatch@npm:3.1.1" +"anymatch@npm:~3.1.2": + version: 3.1.2 + resolution: "anymatch@npm:3.1.2" dependencies: normalize-path: ^3.0.0 picomatch: ^2.0.4 - checksum: cf61bbaf7f34d9f94dd966230b7a7f8f1f24e3e2185540741a2561118e108206d85101ee2fc9876cd756475dbe6573d84d91115c3abdbf53a64e26a5f1f06b67 + checksum: cd6c08eb8d435741a9de6f5695c75cfba747a50772929ca588235535c6a57d37f2c2b34057768f015fd92abb88108b122ed2e399faac6ae30363a8ca0b6107d0 languageName: node linkType: hard @@ -832,6 +1257,15 @@ __metadata: languageName: node linkType: hard +"append-transform@npm:^2.0.0": + version: 2.0.0 + resolution: "append-transform@npm:2.0.0" + dependencies: + default-require-extensions: ^3.0.0 + checksum: 7fd09e1d1f25e7c6b54828bded68df6a228a9889bf4b2b5666482fc318cf4dbc361e7f4b58984f7bad98b47d0b992409d182b25c46ae16dc04e4c1e25d9a2fe9 + languageName: node + linkType: hard + "aproba@npm:^1.0.3": version: 1.2.0 resolution: "aproba@npm:1.2.0" @@ -839,6 +1273,13 @@ __metadata: languageName: node linkType: hard +"archy@npm:^1.0.0": + version: 1.0.0 + resolution: "archy@npm:1.0.0" + checksum: fed06a0487f79dd89f30a8558f3e8f88011025ded47b10e412a4fc8f842a4ddec6e51af5a117258f5b84bef587cff7d1e056df4f453a7d8752a46e25bf5be7dc + languageName: node + linkType: hard + "arconnect@npm:^0.2.8": version: 0.2.9 resolution: "arconnect@npm:0.2.9" @@ -852,34 +1293,50 @@ __metadata: version: 0.0.0-use.local resolution: "ardrive-cli@workspace:." dependencies: + "@istanbuljs/nyc-config-typescript": ^1.0.1 + "@types/chai": ^4.2.21 "@types/jwk-to-pem": ^2.0.0 + "@types/lodash": ^4 + "@types/mocha": ^9.0.0 "@types/node": ^14.14.32 - "@types/prompt-sync": ^4.1.0 + "@types/node-fetch": 2.5.3 "@types/prompts": ^2.0.9 + "@types/regression": ^2.0.2 + "@types/sinon": ^10.0.2 + "@types/source-map-support": ^0 "@types/uuid": ^8.3.0 "@typescript-eslint/eslint-plugin": ^4.18.0 "@typescript-eslint/parser": ^4.18.0 ardrive-core-js: 0.5.1 - arweave: 1.10.11 + arweave: ^1.10.16 arweave-bundles: ^1.0.3 - community-js: ^1.1.36 + arweave-mnemonic-keys: ^0.0.9 + base64-js: ^1.5.1 + chai: ^4.3.4 + commander: ^8.2.0 eslint: ^7.23.0 eslint-config-prettier: ^8.1.0 eslint-plugin-prettier: latest husky: ^=6 + jwk-to-pem: ^2.0.4 lint-staged: ^11.0.0 + lodash: ^4.17.21 + mocha: ^9.1.1 + node-fetch: 2.6.2 + nyc: ^15.1.0 prettier: ^2.2.1 - progress: ^2.0.3 - prompt-password: ^1.2.0 - prompt-sync: ^4.2.0 prompts: ^2.4.0 + regression: ^2.0.1 rimraf: ^3.0.2 - ts-node: ^9.1.1 - tsc-files: ^1.1.2 + sinon: ^11.1.2 + smartweave: ^0.4.45 + source-map-support: ^0.5.20 + ts-node: ^10.2.1 + ts-sinon: ^2.0.1 typescript: ^4.2.3 uuid: ^8.3.2 bin: - ardrive-cli: ./lib/index.js + ardrive: ./lib/index.js languageName: unknown linkType: soft @@ -937,6 +1394,13 @@ __metadata: languageName: node linkType: hard +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 160b7a25d2a7097fd5fdf25eb8a99e037340078f70e6c7cfdef305837ed14d54570b2b13261bcae26c8cd44ad6e9a7136a0110d815ac65a7891c69c7bf2f4afd + languageName: node + linkType: hard + "arr-flatten@npm:^1.1.0": version: 1.1.0 resolution: "arr-flatten@npm:1.1.0" @@ -976,31 +1440,17 @@ __metadata: languageName: node linkType: hard -"arweave@npm:1.10.11": - version: 1.10.11 - resolution: "arweave@npm:1.10.11" - dependencies: - asn1.js: ^5.4.1 - axios: ^0.21.1 - base64-js: ^1.3.1 - bignumber.js: ^9.0.1 - checksum: 40d32d1a683c8d0595650e20acfc780233e4de62f051c2dfca76cd0e4a230650e2d6b6246c4fb792787e1e51ab6655861bbfd3aad50287226a1d06c2f19e1d05 - languageName: node - linkType: hard - -"arweave@npm:^1.10.11, arweave@npm:^1.10.5": - version: 1.10.13 - resolution: "arweave@npm:1.10.13" +"arweave-mnemonic-keys@npm:^0.0.9": + version: 0.0.9 + resolution: "arweave-mnemonic-keys@npm:0.0.9" dependencies: - asn1.js: ^5.4.1 - axios: ^0.21.1 - base64-js: ^1.3.1 - bignumber.js: ^9.0.1 - checksum: 5da8a3f5cc70aca4bde5eb5fcdd6eee56ed66d07f0fd7c41ef9b6c56b6aaf9c30c88e331d51472995e58e597a9c7f6e5fcd1de9669489a289fe262d50cf9e3de + human-crypto-keys: ^0.1.4 + libp2p-crypto: ^0.19.0 + checksum: 4e04c4726b1937df46a5f688eb39075e3f4756ee826f21348b877f5dc79c7b77802888653504d32ef30feecbcfea03bbb74c3f527939e3be1b3be983c985b4fc languageName: node linkType: hard -"arweave@npm:^1.10.13, arweave@npm:^1.10.16": +"arweave@npm:^1.10.13, arweave@npm:^1.10.16, arweave@npm:^1.10.5": version: 1.10.16 resolution: "arweave@npm:1.10.16" dependencies: @@ -1013,7 +1463,7 @@ __metadata: languageName: node linkType: hard -"asn1.js@npm:^5.3.0, asn1.js@npm:^5.4.1": +"asn1.js@npm:^5.0.1, asn1.js@npm:^5.3.0, asn1.js@npm:^5.4.1": version: 5.4.1 resolution: "asn1.js@npm:5.4.1" dependencies: @@ -1041,6 +1491,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^1.1.0": + version: 1.1.0 + resolution: "assertion-error@npm:1.1.0" + checksum: 7bbc9fa2ff51618b0ea3c4ae13dbafe6dfb71b11c267aa93c749489355f55d0b844403bfc3dc8b9a98b0f21837fa2b59191c8a8f76e65303d06498fab4867f4c + languageName: node + linkType: hard + "astral-regex@npm:^2.0.0": version: 2.0.0 resolution: "astral-regex@npm:2.0.0" @@ -1085,10 +1542,10 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1": - version: 1.3.1 - resolution: "base64-js@npm:1.3.1" - checksum: 8a0cc69d7c7c0ab75c164d3e2eccc3dd65fbaba17bcf440aab54636afd31255287ac3cd16a111e98d741c4a6e0b5631774b0c32818355089e645df3ae96a49bb +"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: c1b41a26ddc6620eb7f1ee6c29c812f5942a4e328e74263f995872cfb8ca3aee08542beb25cd10fd7ef16e4f16603e25c35a26e776c01fd55277e5035e829e0e languageName: node linkType: hard @@ -1136,6 +1593,18 @@ __metadata: languageName: node linkType: hard +"bip39@npm:^3.0.2": + version: 3.0.4 + resolution: "bip39@npm:3.0.4" + dependencies: + "@types/node": 11.11.6 + create-hash: ^1.1.0 + pbkdf2: ^3.0.9 + randombytes: ^2.0.1 + checksum: 24359ef61f68a1abf6c6662aa49561673a42ffa024cab02eacd5e35fd09f999b3ef60d43ad89271fa193a23d88340c95d40894792c8e22439bd3b82f5e9905c4 + languageName: node + linkType: hard + "bl@npm:^4.0.3, bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -1147,14 +1616,7 @@ __metadata: languageName: node linkType: hard -"bn.js@npm:^4.0.0": - version: 4.11.9 - resolution: "bn.js@npm:4.11.9" - checksum: 31630d3560b28931010980886a0f657b37ce818ba237867cd838e89a1a0b71044fb4977aa56376616997b372bbb3f55d3bb25e5378c48c1d24a47bfb4235b60e - languageName: node - linkType: hard - -"bn.js@npm:^4.11.9": +"bn.js@npm:^4.0.0, bn.js@npm:^4.11.9": version: 4.12.0 resolution: "bn.js@npm:4.12.0" checksum: cfe7494de9c8a472c3534418f64f9c30a3f18134a8ad0c3ba4e0e934feb5d1a9110bba049a47ec6b79a1d649467df212f64ba6b22aaccbea0c3c208bef0b4110 @@ -1187,6 +1649,28 @@ __metadata: languageName: node linkType: hard +"browser-stdout@npm:1.3.1": + version: 1.3.1 + resolution: "browser-stdout@npm:1.3.1" + checksum: 2f91b1ad26f3401ae68801d901754331811e257aeed4ee0eb287cc2ff375c8fffadb2d14a16654eeb0729e3b1ae6d84642ba7a3119880843d774ee1db2b898b3 + languageName: node + linkType: hard + +"browserslist@npm:^4.16.6": + version: 4.17.0 + resolution: "browserslist@npm:4.17.0" + dependencies: + caniuse-lite: ^1.0.30001254 + colorette: ^1.3.0 + electron-to-chromium: ^1.3.830 + escalade: ^3.1.1 + node-releases: ^1.1.75 + bin: + browserslist: cli.js + checksum: e7c4b78520f1e6f547112891d4a2298fa2d9bcba0156fd62b8229d654cd7a6c72db74254d774a55cacf604fbee0966189ac6fb1ce71e8c1a24bb5ce1c2ed9644 + languageName: node + linkType: hard + "buffer-from@npm:^1.0.0": version: 1.1.1 resolution: "buffer-from@npm:1.1.1" @@ -1194,7 +1678,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.5.0": +"buffer@npm:^5.2.1, buffer@npm:^5.5.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -1204,6 +1688,18 @@ __metadata: languageName: node linkType: hard +"caching-transform@npm:^4.0.0": + version: 4.0.0 + resolution: "caching-transform@npm:4.0.0" + dependencies: + hasha: ^5.0.0 + make-dir: ^3.0.0 + package-hash: ^4.0.0 + write-file-atomic: ^3.0.0 + checksum: 0a9e0c317e5eeb86a39835aba163388849bfb640383630be6c06ca104e8610d135df673c2cfd9be6e25ebdf834bbe5f4c3fa0f3530468185646b84b03d872758 + languageName: node + linkType: hard + "call-bind@npm:^1.0.0": version: 1.0.2 resolution: "call-bind@npm:1.0.2" @@ -1221,6 +1717,27 @@ __metadata: languageName: node linkType: hard +"camelcase@npm:^5.0.0, camelcase@npm:^5.3.1": + version: 5.3.1 + resolution: "camelcase@npm:5.3.1" + checksum: 6a3350c4ea8ab6e5109e0b443cfaf43dc40abfad7b2d79dcafbbafbe9b6b4059b4365b17ad822e24cf08e6627c1ffb65a9651d05cef9fcc6f64b6a0c2f327feb + languageName: node + linkType: hard + +"camelcase@npm:^6.0.0": + version: 6.2.0 + resolution: "camelcase@npm:6.2.0" + checksum: 654700600a80cb1f06ab85b3e2fe80333f94b441884d40826becdac549774f51b0317c6dcb6040416df26241fa9481eb58d0c1659d4d6d5627dcd4259be61beb + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001254": + version: 1.0.30001257 + resolution: "caniuse-lite@npm:1.0.30001257" + checksum: 2d39f401d745845da6435ff98cc93bd8c247746287e299d0b28baf16374069ea3a42d78890fbefc51cc4c1cd475b00cc99a4ad7a75643eeba336adc917164026 + languageName: node + linkType: hard + "caseless@npm:~0.12.0": version: 0.12.0 resolution: "caseless@npm:0.12.0" @@ -1228,6 +1745,20 @@ __metadata: languageName: node linkType: hard +"chai@npm:^4.3.4": + version: 4.3.4 + resolution: "chai@npm:4.3.4" + dependencies: + assertion-error: ^1.1.0 + check-error: ^1.0.2 + deep-eql: ^3.0.1 + get-func-name: ^2.0.0 + pathval: ^1.1.1 + type-detect: ^4.0.5 + checksum: ea3e6547b9a8d367f203e83b71632ec6a801f6380393594907968479aea15e411e4317211f46e03eda092a97f6812118b69b05bbded51b01224d193cc3f4b9b3 + languageName: node + linkType: hard + "chalk@npm:^2.0.0": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -1239,37 +1770,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^3.0.0": - version: 3.0.0 - resolution: "chalk@npm:3.0.0" - dependencies: - ansi-styles: ^4.1.0 - supports-color: ^7.1.0 - checksum: 4018b0c812880da595d0d7b8159939527b72f58d3370e2fdc1a24d9abd460bab851695d7eca014082f110d5702d1221b05493fec430ccce375de907d50cc48c1 - languageName: node - linkType: hard - -"chalk@npm:^4.0.0, chalk@npm:^4.1.0": - version: 4.1.0 - resolution: "chalk@npm:4.1.0" - dependencies: - ansi-styles: ^4.1.0 - supports-color: ^7.1.0 - checksum: f860285b419f9e925c2db0f45ffa88aa8794c14b80cc5d01ff30930bcfc384996606362706f0829cf557f6d36152a5fb2d227ad63c4bc90e2ec9e9dbed4a3c07 - languageName: node - linkType: hard - -"chalk@npm:^4.1.1": - version: 4.1.1 - resolution: "chalk@npm:4.1.1" - dependencies: - ansi-styles: ^4.1.0 - supports-color: ^7.1.0 - checksum: 445c12db7aeed0046500a1e4184d31209a77d165654c885a789c41c8598a6a95bd2392180987cac572c967b93a2a730dda778bb7f87fa06ca63fd8592f8cc59f - languageName: node - linkType: hard - -"chalk@npm:^4.1.2": +"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -1286,6 +1787,13 @@ __metadata: languageName: node linkType: hard +"check-error@npm:^1.0.2": + version: 1.0.2 + resolution: "check-error@npm:1.0.2" + checksum: 1460ad12da600b277575f53b7512d6e59005a34e3235ae62cfe12ff5d83b68a6ff568841907eb5f041c3f468d1385ea9fcfd22344df02175d28ea8a8b9330940 + languageName: node + linkType: hard + "choices-separator@npm:^2.0.0": version: 2.0.0 resolution: "choices-separator@npm:2.0.0" @@ -1297,22 +1805,22 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.5.1": - version: 3.5.1 - resolution: "chokidar@npm:3.5.1" +"chokidar@npm:3.5.2, chokidar@npm:^3.5.1": + version: 3.5.2 + resolution: "chokidar@npm:3.5.2" dependencies: - anymatch: ~3.1.1 + anymatch: ~3.1.2 braces: ~3.0.2 - fsevents: ~2.3.1 - glob-parent: ~5.1.0 + fsevents: ~2.3.2 + glob-parent: ~5.1.2 is-binary-path: ~2.1.0 is-glob: ~4.0.1 normalize-path: ~3.0.0 - readdirp: ~3.5.0 + readdirp: ~3.6.0 dependenciesMeta: fsevents: optional: true - checksum: 61b3f710f9e7dc69d76f638d8b0d37bad586497444165125ca8062f7192695f35403b5f622cbd7dfdd06805201ceaba40ff90e53ea2974df9a8087861192a99b + checksum: 52fbff3acebf06ec0125872110f6c8403e66cd3d613264c83405496e199554d99380342d9b3a7ffd7910c53c5865e242ed7dd72fcb2e883d8e3ad3f3883aee6c languageName: node linkType: hard @@ -1330,6 +1838,16 @@ __metadata: languageName: node linkType: hard +"cipher-base@npm:^1.0.1, cipher-base@npm:^1.0.3": + version: 1.0.4 + resolution: "cipher-base@npm:1.0.4" + dependencies: + inherits: ^2.0.1 + safe-buffer: ^5.0.1 + checksum: ec80001ec91dbb7c5c08facc00ffc9c75fed7abd6d720c7a9c62c260aa2e5cb2655c183e011b50b8b711f755b1753c7fdd2ca44c091ee78d81c377ca74ed83c9 + languageName: node + linkType: hard + "clean-stack@npm:^2.0.0": version: 2.2.0 resolution: "clean-stack@npm:2.2.0" @@ -1382,6 +1900,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^6.0.0": + version: 6.0.0 + resolution: "cliui@npm:6.0.0" + dependencies: + string-width: ^4.2.0 + strip-ansi: ^6.0.0 + wrap-ansi: ^6.2.0 + checksum: e59d0642946dd300b1b002e69f43b32d55e682c84f6f2073705ffe77477b400aeabd4f4795467db0771a21d35ee070071f6a31925e4f83b52a7fe1f5c8e6e860 + languageName: node + linkType: hard + "cliui@npm:^7.0.2": version: 7.0.4 resolution: "cliui@npm:7.0.4" @@ -1405,7 +1934,7 @@ __metadata: languageName: node linkType: hard -"clone-deep@npm:^4.0.0": +"clone-deep@npm:^4.0.0, clone-deep@npm:^4.0.1": version: 4.0.1 resolution: "clone-deep@npm:4.0.1" dependencies: @@ -1481,10 +2010,10 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^1.2.2": - version: 1.2.2 - resolution: "colorette@npm:1.2.2" - checksum: e240f0c94b8d9f34b52bd17b50fc13a3b74f9e662edeaa2b0c65e06ec6b1fc6367fb42b834ec5a1d819d68b74a3d850f3bd3e284f9e614d6c4ffa122f83c6ec5 +"colorette@npm:^1.2.2, colorette@npm:^1.3.0": + version: 1.4.0 + resolution: "colorette@npm:1.4.0" + checksum: 7ef8e1ca16ca7ae4659722ecd103ff89388e1e1e4100ee41e892ad880364a2f8bb9aacbce6c96aa572f25e56a45a2ea7008ff69b8182bc6baf383cd269b963c0 languageName: node linkType: hard @@ -1504,15 +2033,17 @@ __metadata: languageName: node linkType: hard -"community-js@npm:^1.1.36": - version: 1.1.36 - resolution: "community-js@npm:1.1.36" - dependencies: - "@ponicode/cli": ^0.0.1-18 - arweave: ^1.10.11 - axios: ^0.21.1 - smartweave: ^0.4.27 - checksum: 940cd0d27e92f6517582fe6d56e287ec812bfeb24fb78bd634c2cb4b72970a75c6b9f23fa039eb9842794402602053be5bb7c560f42a41a2aa74a68647c6a0f7 +"commander@npm:^8.2.0": + version: 8.2.0 + resolution: "commander@npm:8.2.0" + checksum: e41e680f2afa0a409aa21d3cad6f06a3d9d2d1086e76223ebd600181b588085e6ad0c7387cf491cb96c60de0a0a50815130368b40bfde017744210d4a8fb75a7 + languageName: node + linkType: hard + +"commondir@npm:^1.0.1": + version: 1.0.1 + resolution: "commondir@npm:1.0.1" + checksum: 98f18ad14f0ea38e0866db365bc8496f2a74250cf47ec96b94913e1b0574c99b4ff837a9f05dbc68d82505fd06b52dfba4f6bbe6fbda43094296cfaf33b475a0 languageName: node linkType: hard @@ -1585,6 +2116,33 @@ __metadata: languageName: node linkType: hard +"create-hash@npm:^1.1.0, create-hash@npm:^1.1.2": + version: 1.2.0 + resolution: "create-hash@npm:1.2.0" + dependencies: + cipher-base: ^1.0.1 + inherits: ^2.0.1 + md5.js: ^1.3.4 + ripemd160: ^2.0.1 + sha.js: ^2.4.0 + checksum: 5565182efc3603e4d34c3ce13fd0765a058b27f91e49ba8e720e30ba8bfc53e9cd835e5343136000b6f210a979fe1041a4f3fe728e866e64f34db04b068fd725 + languageName: node + linkType: hard + +"create-hmac@npm:^1.1.4": + version: 1.1.7 + resolution: "create-hmac@npm:1.1.7" + dependencies: + cipher-base: ^1.0.3 + create-hash: ^1.1.0 + inherits: ^2.0.1 + ripemd160: ^2.0.0 + safe-buffer: ^5.0.1 + sha.js: ^2.4.8 + checksum: 98957676a93081678a2a915ae14898d65aac9b5651ffa55b8888484dd9d79c06d3cb3f85b137cd833ab536d87adee17394bb2b0efc591ea0e34110266d5bcd75 + languageName: node + linkType: hard + "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -1592,7 +2150,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: @@ -1603,6 +2161,21 @@ __metadata: languageName: node linkType: hard +"crypto-key-composer@npm:^0.1.0": + version: 0.1.3 + resolution: "crypto-key-composer@npm:0.1.3" + dependencies: + "@lordvlad/asn1.js": ^5.1.1 + buffer: ^5.2.1 + clone-deep: ^4.0.1 + deep-for-each: ^3.0.0 + es6-error: ^4.1.1 + matcher: ^2.0.0 + node-forge: ^0.8.1 + checksum: 48f6d45d2a85469b6e752fdf2aaccbb3edc58fee18adb3335ae9d603f8fe5cc78598a97d31c78f950b86396b4617275ef3110056fed7469cbf9ecd9f30874ac2 + languageName: node + linkType: hard + "d@npm:1, d@npm:^1.0.1": version: 1.0.1 resolution: "d@npm:1.0.1" @@ -1631,6 +2204,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:4.3.1": + version: 4.3.1 + resolution: "debug@npm:4.3.1" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 0d41ba5177510e8b388dfd7df143ab0f9312e4abdaba312595461511dac88e9ef8101939d33b4e6d37e10341af6a5301082e4d7d6f3deb4d57bc05fc7d296fad + languageName: node + linkType: hard + "debug@npm:^2.6.6, debug@npm:^2.6.8": version: 2.6.9 resolution: "debug@npm:2.6.9" @@ -1649,7 +2234,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^4.0.1, debug@npm:^4.3.1": +"debug@npm:^4.0.1, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1": version: 4.3.2 resolution: "debug@npm:4.3.2" dependencies: @@ -1661,12 +2246,17 @@ __metadata: languageName: node linkType: hard -"debug@npm:^4.1.1": - version: 4.1.1 - resolution: "debug@npm:4.1.1" - dependencies: - ms: ^2.1.1 - checksum: 3601a6ce96e4698ed3edf0ee6e67ef0317adfcdae2f66a43b23d1b14e8888b422337429b16dbbcba6801e7bfa6cbb8de3128fbacfb8ae1cd9bd7615ea6baf970 +"decamelize@npm:^1.2.0": + version: 1.2.0 + resolution: "decamelize@npm:1.2.0" + checksum: 8ca9d03ea8ac07920f4504e219d18edff2491bdd0a3e05a1e5ca2e9a0bf6333564231de3528b01d5e76c40a38c37bbc1e09cb5a0424714f53dd615ed78ced464 + languageName: node + linkType: hard + +"decamelize@npm:^4.0.0": + version: 4.0.0 + resolution: "decamelize@npm:4.0.0" + checksum: 3846161a3b6ba7043dd0e8cb8101676fcbd92a60bd228e63fe17116ebfaddff8d730e33c9e93a9e02c049c53e0e37013174225f27b95bb21d703359999a4aab9 languageName: node linkType: hard @@ -1686,6 +2276,15 @@ __metadata: languageName: node linkType: hard +"deep-eql@npm:^3.0.1": + version: 3.0.1 + resolution: "deep-eql@npm:3.0.1" + dependencies: + type-detect: ^4.0.0 + checksum: eff42bc2d4d889dec8fd925fa9c013fa4d641842b98b502f436b9f2865460c8330f79fb7dbdb924a1e844b97b40843ed952f3847fb1e388a35bd4ef6e3d64184 + languageName: node + linkType: hard + "deep-extend@npm:^0.6.0": version: 0.6.0 resolution: "deep-extend@npm:0.6.0" @@ -1693,6 +2292,15 @@ __metadata: languageName: node linkType: hard +"deep-for-each@npm:^3.0.0": + version: 3.0.0 + resolution: "deep-for-each@npm:3.0.0" + dependencies: + lodash.isplainobject: ^4.0.6 + checksum: 88c1dc9f15911494961508220bbd8dcbde1da48e03d90a2c19ae8801d6edeb088afea18811b385b6e96928234ba0a5151d7e7bab679b3723b5221abe8c065f51 + languageName: node + linkType: hard + "deep-is@npm:^0.1.3": version: 0.1.3 resolution: "deep-is@npm:0.1.3" @@ -1700,6 +2308,15 @@ __metadata: languageName: node linkType: hard +"default-require-extensions@npm:^3.0.0": + version: 3.0.0 + resolution: "default-require-extensions@npm:3.0.0" + dependencies: + strip-bom: ^4.0.0 + checksum: c53a7ddfa11c731f503ec99a40c7955802982ba06d8503f25a22bbc84aec8b1a976d4f431fe0c6378c3c862d6da2f38c0dfbb0b3951b2e3aa72778ed70d7a56c + languageName: node + linkType: hard + "defaults@npm:^1.0.3": version: 1.0.3 resolution: "defaults@npm:1.0.3" @@ -1760,14 +2377,14 @@ __metadata: languageName: node linkType: hard -"diff-sequences@npm:^25.2.6": - version: 25.2.6 - resolution: "diff-sequences@npm:25.2.6" - checksum: 332484fc00f6beca726d8dbc13095f6006527002bef936a07b4e6bbec681fbaac484e1a7ea4e9ab0d53e375d1cde9e642c8cce31dfe6329cfdf8f01f26b17505 +"diff@npm:5.0.0, diff@npm:^5.0.0": + version: 5.0.0 + resolution: "diff@npm:5.0.0" + checksum: ef241d3b20017b8a1a6f20d184035b836de662203638e16eb57267653a56392ea82e1f9c12b28836e6e22aa25c28c59847aaeb35dd65e77e75c822c7e848e7e8 languageName: node linkType: hard -"diff@npm:^4.0.1": +"diff@npm:^4.0.1, diff@npm:^4.0.2": version: 4.0.2 resolution: "diff@npm:4.0.2" checksum: 81b5cd7ddde6f0ba2a532d434cfdca365aedd6cc62bb133e851e66e071d40382a30924a07c1034bd3d5a2e332146f64514b73c06fe2ebc0490a67f0c98da79fb @@ -1802,7 +2419,14 @@ __metadata: languageName: node linkType: hard -"elliptic@npm:^6.5.3": +"electron-to-chromium@npm:^1.3.830": + version: 1.3.836 + resolution: "electron-to-chromium@npm:1.3.836" + checksum: 4cb506937e45878cbc4c40b589abf0880ed946a8deb71862f10e2df70246f72833c53aa76156f1af678d259443cfd9f12523be7e234a6c7a6e62a889ee49da44 + languageName: node + linkType: hard + +"elliptic@npm:^6.5.2, elliptic@npm:^6.5.3": version: 6.5.4 resolution: "elliptic@npm:6.5.4" dependencies: @@ -1849,6 +2473,13 @@ __metadata: languageName: node linkType: hard +"err-code@npm:^3.0.1": + version: 3.0.1 + resolution: "err-code@npm:3.0.1" + checksum: ddae1b8c69f2d53047e276fdfd078793aaf1eed5af5e1875a981b60c136a499d2574431f3486ecd2c50e1c4ad0e5d27fd7ea6b6e5e7472de3f5d9dc8042be847 + languageName: node + linkType: hard + "error-ex@npm:^1.3.1": version: 1.3.2 resolution: "error-ex@npm:1.3.2" @@ -1876,6 +2507,13 @@ __metadata: languageName: node linkType: hard +"es6-error@npm:^4.0.1, es6-error@npm:^4.1.1": + version: 4.1.1 + resolution: "es6-error@npm:4.1.1" + checksum: d7343d3f47834d71912278b5a7476028b7ef3db4ee5c8b7184d7204d2c3a48dd4ce68d197a14116f0d16c85f85d3d8ed1d8c137cf5bc9f33f672646755289688 + languageName: node + linkType: hard + "es6-iterator@npm:~0.1.3": version: 0.1.3 resolution: "es6-iterator@npm:0.1.3" @@ -1937,6 +2575,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:4.0.0": + version: 4.0.0 + resolution: "escape-string-regexp@npm:4.0.0" + checksum: c747be8d5ff7873127e3e0cffe7d2206a37208077fa9c30a3c1bb4f26bebd081c8c24d5fba7a99449f9d20670bea3dc5e1b6098b0f074b099bd38766271a272f + languageName: node + linkType: hard + "escape-string-regexp@npm:^1.0.5": version: 1.0.5 resolution: "escape-string-regexp@npm:1.0.5" @@ -1944,6 +2589,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^2.0.0": + version: 2.0.0 + resolution: "escape-string-regexp@npm:2.0.0" + checksum: f3500f264e864aef0c336a2efb3adb1cee9ba1abbe15d69f0d9dab423607cac91aa009b23011b4e6cfd6d6b79888873e21dad1882047aa2e1555dd307428c51d + languageName: node + linkType: hard + "eslint-config-prettier@npm:^8.1.0": version: 8.1.0 resolution: "eslint-config-prettier@npm:8.1.0" @@ -2120,6 +2772,13 @@ __metadata: languageName: node linkType: hard +"events@npm:^3.3.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: 56fa12567013e85b98782a1d971442ea29df057129d8a94432711fd68303357594ea37bfbe234860e28581a7768f943a8bea88c16b48aa01b96acf804bc01d52 + languageName: node + linkType: hard + "execa@npm:^5.0.0": version: 5.0.0 resolution: "execa@npm:5.0.0" @@ -2245,13 +2904,6 @@ __metadata: languageName: node linkType: hard -"figlet@npm:^1.5.0": - version: 1.5.0 - resolution: "figlet@npm:1.5.0" - checksum: 49839d8179dc0a7b6f105f13348e233df79cc2959088316560c1a85af0e99466b3e85d417de2a1f87d4dd4f42c7a734b01b8290aaf1c04c80f6fcffdf7c86bde - languageName: node - linkType: hard - "figlet@npm:^1.5.2": version: 1.5.2 resolution: "figlet@npm:1.5.2" @@ -2293,6 +2945,37 @@ __metadata: languageName: node linkType: hard +"find-cache-dir@npm:^3.2.0": + version: 3.3.2 + resolution: "find-cache-dir@npm:3.3.2" + dependencies: + commondir: ^1.0.1 + make-dir: ^3.0.2 + pkg-dir: ^4.1.0 + checksum: 5330b819225a54a105c7e186ceea5f64d313f7e830aa0abeea7d419379b454c286fcdced2f25768c31e87b6621488bab99b3d64c74ca831f93f8018a8b79bbe2 + languageName: node + linkType: hard + +"find-up@npm:5.0.0": + version: 5.0.0 + resolution: "find-up@npm:5.0.0" + dependencies: + locate-path: ^6.0.0 + path-exists: ^4.0.0 + checksum: cd0b77415bc59e5af31e4e1b29c6ff8d965d9ca3c60a4b74161f8f116c0d1ad8d35bc6e53bf8f92c69e704e98183f1628a363ed7d519eb28eff54378b8f167a7 + languageName: node + linkType: hard + +"find-up@npm:^4.0.0, find-up@npm:^4.1.0": + version: 4.1.0 + resolution: "find-up@npm:4.1.0" + dependencies: + locate-path: ^5.0.0 + path-exists: ^4.0.0 + checksum: d612d28e02eaca6cd7128fc9bc9b456e2547a3f9875b2b2ae2dbdc6b8cec52bc2885efcb3ac6c18954e838f4c8e20565d196784b190e1d38565f9dc39aade722 + languageName: node + linkType: hard + "flat-cache@npm:^3.0.4": version: 3.0.4 resolution: "flat-cache@npm:3.0.4" @@ -2303,6 +2986,15 @@ __metadata: languageName: node linkType: hard +"flat@npm:^5.0.2": + version: 5.0.2 + resolution: "flat@npm:5.0.2" + bin: + flat: cli.js + checksum: 549b3012e9c8e90da9eab25c283443a39a6c5b0a35a6d382827e4bfc5e88161d1dac5faf667f6255571a42edc58e679b432a1452b9bdbd7ab16718a740bef556 + languageName: node + linkType: hard + "flatted@npm:^3.1.0": version: 3.1.1 resolution: "flatted@npm:3.1.1" @@ -2356,6 +3048,16 @@ __metadata: languageName: node linkType: hard +"foreground-child@npm:^2.0.0": + version: 2.0.0 + resolution: "foreground-child@npm:2.0.0" + dependencies: + cross-spawn: ^7.0.0 + signal-exit: ^3.0.2 + checksum: 661c7adbc153c3f6dc1cd6b14f395442adfe22fce9bce65b7b436dfa2c95554879de50a0cfd30a40fc74a80fa541c2c7afe0890f1728cb608ce8d0689bcb0e32 + languageName: node + linkType: hard + "forever-agent@npm:~0.6.1": version: 0.6.1 resolution: "forever-agent@npm:0.6.1" @@ -2374,6 +3076,13 @@ __metadata: languageName: node linkType: hard +"fromentries@npm:^1.2.0": + version: 1.3.2 + resolution: "fromentries@npm:1.3.2" + checksum: 5cc722e4e3fd333ba75f31dd3ef80b4a6c405d8814e86e343b4676c1483c00f4f29b39aca462d268e918b3316a4fb03cea8022458fd8ad965f251362a129783f + languageName: node + linkType: hard + "fs-constants@npm:^1.0.0": version: 1.0.0 resolution: "fs-constants@npm:1.0.0" @@ -2397,7 +3106,7 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@~2.3.1#builtin": +"fsevents@patch:fsevents@~2.3.2#builtin": version: 2.3.2 resolution: "fsevents@patch:fsevents@npm%3A2.3.2#builtin::version=2.3.2&hash=11e9ea" dependencies: @@ -2406,7 +3115,7 @@ __metadata: languageName: node linkType: hard -fsevents@~2.3.1: +fsevents@~2.3.2: version: 2.3.2 resolution: "fsevents@npm:2.3.2" dependencies: @@ -2452,13 +3161,27 @@ fsevents@~2.3.1: languageName: node linkType: hard -"get-caller-file@npm:^2.0.5": +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: d523437689c97b3aba9c5cdeca4677d5fff9a29d620db693fea40d852bad63563110f16979d0170248439dbcd2ecee0780fb2533d3f0519f019081aa10767c60 + languageName: node + linkType: hard + +"get-caller-file@npm:^2.0.1, get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" checksum: 9dd9e1e2591039ee4c38c897365b904f66f1e650a8c1cb7b7db8ce667fa63e88cc8b13282b74df9d93de481114b3304a0487880d31cd926dfda6efe71455855d languageName: node linkType: hard +"get-func-name@npm:^2.0.0": + version: 2.0.0 + resolution: "get-func-name@npm:2.0.0" + checksum: c72d3857cd05338fc61e52c385da67d553e42c226e141da11684aecb3e017a753bf7120411731fbc30768edf68f9c359dc202757703b3a851da54b2b17a9828d + languageName: node + linkType: hard + "get-intrinsic@npm:^1.0.2": version: 1.1.1 resolution: "get-intrinsic@npm:1.1.1" @@ -2477,6 +3200,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"get-package-type@npm:^0.1.0": + version: 0.1.0 + resolution: "get-package-type@npm:0.1.0" + checksum: a5b8beaf68d8bcdb507e23b3d2b6458e54b9061e84e2a8a94b846c8e1d794beb47fdcbda895da16ae59225bb3ea1608c0719e4f986e8a987ec2f228eaf00d78b + languageName: node + linkType: hard + "get-stream@npm:^6.0.0": version: 6.0.1 resolution: "get-stream@npm:6.0.1" @@ -2500,7 +3230,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"glob-parent@npm:^5.0.0, glob-parent@npm:^5.1.0": +"glob-parent@npm:^5.0.0, glob-parent@npm:^5.1.0, glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" dependencies: @@ -2509,18 +3239,9 @@ fsevents@~2.3.1: languageName: node linkType: hard -"glob-parent@npm:~5.1.0": - version: 5.1.1 - resolution: "glob-parent@npm:5.1.1" - dependencies: - is-glob: ^4.0.1 - checksum: 2af6e196fba4071fb07ba261366e446ba2b320e6db0a2069cf8e12117c5811abc6721f08546148048882d01120df47e56aa5a965517a6e5ba19bfeb792655119 - languageName: node - linkType: hard - -"glob@npm:^7.1.3, glob@npm:^7.1.4": - version: 7.1.6 - resolution: "glob@npm:7.1.6" +"glob@npm:7.1.7, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6": + version: 7.1.7 + resolution: "glob@npm:7.1.7" dependencies: fs.realpath: ^1.0.0 inflight: ^1.0.4 @@ -2528,7 +3249,14 @@ fsevents@~2.3.1: minimatch: ^3.0.4 once: ^1.3.0 path-is-absolute: ^1.0.0 - checksum: 789977b52432865bd63846da5c75a6efc2c56abdc0cb5ffcdb8e91eeb67a58fa5594c1195d18b2b4aff99675b0739ed6bd61024b26562e0cca18c8f993efdc82 + checksum: 352f74f08247db5420161a2f68f2bd84b53228b5fcfc9dcc37cd54d3f19ec0232495d84aeff1286d0727059e9fdc1031400e00b971bdc59e30f8f82b199c9d02 + languageName: node + linkType: hard + +"globals@npm:^11.1.0": + version: 11.12.0 + resolution: "globals@npm:11.12.0" + checksum: 2563d3306a7e646fd9ec484b0ca29bf8847d9dc6ebbe86026f11e31bda04f420f6536c2decbd4cb96350379801d2cce352ab373c40be8b024324775b31f882f9 languageName: node linkType: hard @@ -2564,10 +3292,17 @@ fsevents@~2.3.1: languageName: node linkType: hard -"graceful-fs@npm:^4.2.3, graceful-fs@npm:~4.2.0": - version: 4.2.6 - resolution: "graceful-fs@npm:4.2.6" - checksum: 84d39c7756892553da990a9db7e45f844b3309b37b5a00174cbb4748476f2250c54f24594d4d252f64f085c65c2fdac7c809419bf6d55f0e6e42eb07ac0f5bf2 +"graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.2.3, graceful-fs@npm:~4.2.0": + version: 4.2.8 + resolution: "graceful-fs@npm:4.2.8" + checksum: b07e032c0a17e928d3e8ab0f0fea1492efd4568b55a3d2675aaaccf1619eca91156edfa0cb05e99b923e24edf5e26fdce22ffa58ec14d5b13a3b1392460f37f0 + languageName: node + linkType: hard + +"growl@npm:1.10.5": + version: 1.10.5 + resolution: "growl@npm:1.10.5" + checksum: e1dae8dde6e43aa7e18c864094f4690b3a48cf45779b9302a18c24b90a7038084ecffb931f89b52ce76fffd72123ae46755c0b5abca541de5eef75879e610257 languageName: node linkType: hard @@ -2625,7 +3360,18 @@ fsevents@~2.3.1: languageName: node linkType: hard -"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3": +"hash-base@npm:^3.0.0": + version: 3.1.0 + resolution: "hash-base@npm:3.1.0" + dependencies: + inherits: ^2.0.4 + readable-stream: ^3.6.0 + safe-buffer: ^5.2.0 + checksum: 9f4b0d183daf13f79ef60f117efc7004bb3570de48fe2d3c7d03c546313490decb2dff2b08d71b8a0049a7de4b79eda16096c2a96f33a7f4916e7616bce4dc11 + languageName: node + linkType: hard + +"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3, hash.js@npm:^1.1.7": version: 1.1.7 resolution: "hash.js@npm:1.1.7" dependencies: @@ -2635,6 +3381,25 @@ fsevents@~2.3.1: languageName: node linkType: hard +"hasha@npm:^5.0.0": + version: 5.2.2 + resolution: "hasha@npm:5.2.2" + dependencies: + is-stream: ^2.0.0 + type-fest: ^0.8.0 + checksum: 50adf6a312f802a3fac6858eb5835b0465931c46608e0719178033008153c4aef85cb682d82df6c02f069024bd588d918fd8cea30ca0584a8fbe7914b36962af + languageName: node + linkType: hard + +"he@npm:1.2.0": + version: 1.2.0 + resolution: "he@npm:1.2.0" + bin: + he: bin/he + checksum: 212122003c20c8c17ac0c83a419b4c8e835411ff6ab9195d053ea6e4a0597cc005b5b8eabcbd57b0b0c0fe676f0049e09315845fff4e051198845491cbba260e + languageName: node + linkType: hard + "hmac-drbg@npm:^1.0.1": version: 1.0.1 resolution: "hmac-drbg@npm:1.0.1" @@ -2646,6 +3411,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"html-escaper@npm:^2.0.0": + version: 2.0.2 + resolution: "html-escaper@npm:2.0.2" + checksum: a216ae96fa647155ce31ebf14e45b602eb84ab7b4a99d329d85d855d8a74d54c0c4146ac7eb4ada2761d3e22c067e73d6c66b54faefee37229ac025cfc97a513 + languageName: node + linkType: hard + "http-signature@npm:~1.2.0": version: 1.2.0 resolution: "http-signature@npm:1.2.0" @@ -2657,6 +3429,20 @@ fsevents@~2.3.1: languageName: node linkType: hard +"human-crypto-keys@npm:^0.1.4": + version: 0.1.4 + resolution: "human-crypto-keys@npm:0.1.4" + dependencies: + bip39: ^3.0.2 + crypto-key-composer: ^0.1.0 + hash.js: ^1.1.7 + hmac-drbg: ^1.0.1 + node-forge: ^0.8.2 + pify: ^4.0.1 + checksum: 594ae91b0fd80a2bac50e3caa707507c2029c7811008c5d550600cc242d53f52dabe8834cd347ea969219ae9207e26416ec3f9bb664ef6916068feb244024c61 + languageName: node + linkType: hard + "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -2758,27 +3544,6 @@ fsevents@~2.3.1: languageName: node linkType: hard -"inquirer@npm:^7.3.3": - version: 7.3.3 - resolution: "inquirer@npm:7.3.3" - dependencies: - ansi-escapes: ^4.2.1 - chalk: ^4.1.0 - cli-cursor: ^3.1.0 - cli-width: ^3.0.0 - external-editor: ^3.0.3 - figures: ^3.0.0 - lodash: ^4.17.19 - mute-stream: 0.0.8 - run-async: ^2.4.0 - rxjs: ^6.6.0 - string-width: ^4.1.0 - strip-ansi: ^6.0.0 - through: ^2.3.6 - checksum: fa0cbd9594a04e04c5c10a806e9a86b23986acdc7d07c75afdbc03412ff03b1d201efa83d9d64929afe99a901a093bfc9ae7ab13560f8e557cb98eddbe5bf37d - languageName: node - linkType: hard - "inquirer@npm:^8.1.2": version: 8.1.5 resolution: "inquirer@npm:8.1.5" @@ -2981,6 +3746,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"is-plain-obj@npm:^2.1.0": + version: 2.1.0 + resolution: "is-plain-obj@npm:2.1.0" + checksum: 2314302f9140d1e9607731d523f207d8000281aebbabe0083210342c0758976f75f0f5db405e55910bd4dc9a04baddbeab9d476290642b5a0d31431cc9bda4b3 + languageName: node + linkType: hard + "is-plain-object@npm:^2.0.4": version: 2.0.4 resolution: "is-plain-object@npm:2.0.4" @@ -3011,7 +3783,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"is-typedarray@npm:~1.0.0": +"is-typedarray@npm:^1.0.0, is-typedarray@npm:~1.0.0": version: 1.0.0 resolution: "is-typedarray@npm:1.0.0" checksum: 4e21156e7360a5916eded35c5938adf6278299a8055640864eebb251e4351cd605beccddf9af27477e19f753d453412fe0c21379bb54b55cfdf5add263076959 @@ -3025,13 +3797,20 @@ fsevents@~2.3.1: languageName: node linkType: hard -"is-windows@npm:^1.0.1": +"is-windows@npm:^1.0.1, is-windows@npm:^1.0.2": version: 1.0.2 resolution: "is-windows@npm:1.0.2" checksum: dd1ed8339a28c68fb52f05931c832488dafc90063e53b97a69ead219a5584d7f3e6e564731c2f983962ff5403afeb05365d88ce9af34c8dae76a14911020d73a languageName: node linkType: hard +"isarray@npm:0.0.1": + version: 0.0.1 + resolution: "isarray@npm:0.0.1" + checksum: daeda3c23646200b0b464b7a9030d10008d7701fc6b7a1b45cafe42b4f4d2dde20835b56f19a49e04bb218245b7f7a2bcc6d0f696cff3711e4eddaa2031c611f + languageName: node + linkType: hard + "isarray@npm:~1.0.0": version: 1.0.0 resolution: "isarray@npm:1.0.0" @@ -3046,6 +3825,16 @@ fsevents@~2.3.1: languageName: node linkType: hard +"iso-random-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "iso-random-stream@npm:2.0.0" + dependencies: + events: ^3.3.0 + readable-stream: ^3.4.0 + checksum: 5dc5c37ecbf54456b102b84bf722b1d353619f1a410b80de2e1f83f5a28ea718bd883d4a1ad83c40877b2116c6e9377ada9cc155f0d82c5c19ba09bc5f90a066 + languageName: node + linkType: hard + "isobject@npm:^3.0.0, isobject@npm:^3.0.1": version: 3.0.1 resolution: "isobject@npm:3.0.1" @@ -3060,29 +3849,78 @@ fsevents@~2.3.1: languageName: node linkType: hard -"jest-diff@npm:^25.2.1": - version: 25.5.0 - resolution: "jest-diff@npm:25.5.0" +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.0.0-alpha.1": + version: 3.0.0 + resolution: "istanbul-lib-coverage@npm:3.0.0" + checksum: c8effc09ae00fc7974a10ee245fa2c3eceda840e8f46245b80bddc7101b84cf2ac0bcce514aa47e338de610cad06af1b6e3c21f679aebf03e398651898ca9aad + languageName: node + linkType: hard + +"istanbul-lib-hook@npm:^3.0.0": + version: 3.0.0 + resolution: "istanbul-lib-hook@npm:3.0.0" dependencies: - chalk: ^3.0.0 - diff-sequences: ^25.2.6 - jest-get-type: ^25.2.6 - pretty-format: ^25.5.0 - checksum: 14a2634ecb159a9a2f061239db1cea0c889e7a72ab05bd1fa799db30efca2ce79291372823f5e3468d9bc856f404f312e44e89c171eea8132b5835d12f71d0b3 + append-transform: ^2.0.0 + checksum: 7b5fa357418e47a0c45fb058a521f20601d1a9cae349809fa1db9294c877e822b29bd59a873b0f9b590ccd3498d447b499bea525753c65f9a7fd48c8883b9da7 languageName: node linkType: hard -"jest-get-type@npm:^25.2.6": - version: 25.2.6 - resolution: "jest-get-type@npm:25.2.6" - checksum: 6051fcb75cdaa8fad66fd5a1e91d2c1597e9ccc54eecd5cd489fd73a00e322d28cb5859b656a8224a41eddab0ecfb875df9ec62f545a76afa1a55d3ba97fba6d +"istanbul-lib-instrument@npm:^4.0.0": + version: 4.0.3 + resolution: "istanbul-lib-instrument@npm:4.0.3" + dependencies: + "@babel/core": ^7.7.5 + "@istanbuljs/schema": ^0.1.2 + istanbul-lib-coverage: ^3.0.0 + semver: ^6.3.0 + checksum: 478e43e75d3a0e8af3902dd11a8606b665dda005e4aaf6d1919c6ed570a557dc253553a56a26466df02e5703e722fba6a37f4f847cc6d1d0e8314df024d1d76c languageName: node linkType: hard -"js-base64@npm:^3.5.2": - version: 3.6.0 - resolution: "js-base64@npm:3.6.0" - checksum: 6959097159b3569d0ad3eab980bf4c6c7761a0522448075cde1cdc05d4f3255b0c169313d009e344f2a9d8f36d174c5e8e1ba92e7d27c69247e4e91723d44ffe +"istanbul-lib-processinfo@npm:^2.0.2": + version: 2.0.2 + resolution: "istanbul-lib-processinfo@npm:2.0.2" + dependencies: + archy: ^1.0.0 + cross-spawn: ^7.0.0 + istanbul-lib-coverage: ^3.0.0-alpha.1 + make-dir: ^3.0.0 + p-map: ^3.0.0 + rimraf: ^3.0.0 + uuid: ^3.3.3 + checksum: ab1e7cb67f34c6ccc1e8c07f34a4b44ca9db12d96573713d31d182da04d6b637394caecb3127f3a005baab046e91afcc604dc31f7e0408f9a6e481ec8a607790 + languageName: node + linkType: hard + +"istanbul-lib-report@npm:^3.0.0": + version: 3.0.0 + resolution: "istanbul-lib-report@npm:3.0.0" + dependencies: + istanbul-lib-coverage: ^3.0.0 + make-dir: ^3.0.0 + supports-color: ^7.1.0 + checksum: aada59dfceae04005f684031a627f1e9730634262a5426837a9b60c49530d626dc727be5930e7ae6303ce0d4357fb8331eda0935b8c6b999df5d376bdc825991 + languageName: node + linkType: hard + +"istanbul-lib-source-maps@npm:^4.0.0": + version: 4.0.0 + resolution: "istanbul-lib-source-maps@npm:4.0.0" + dependencies: + debug: ^4.1.1 + istanbul-lib-coverage: ^3.0.0 + source-map: ^0.6.1 + checksum: 018b5feeb4a3eb32675abb0129e88e48009de6c0b1c1c7006e8dadd5b15e54f4c09cbbeba0febf8bd7bacd25a514abc61c91e4340479d859a0c185448f692099 + languageName: node + linkType: hard + +"istanbul-reports@npm:^3.0.2": + version: 3.0.2 + resolution: "istanbul-reports@npm:3.0.2" + dependencies: + html-escaper: ^2.0.0 + istanbul-lib-report: ^3.0.0 + checksum: d4ed416e13fe0fc709566439086660ddab58dce9d6a655053c5315715aac8225bc7e9fcae553c2c3d8cc66cd4b59498a50b92d543a4820c5be0e5ee30178cdf0 languageName: node linkType: hard @@ -3093,6 +3931,17 @@ fsevents@~2.3.1: languageName: node linkType: hard +"js-yaml@npm:4.1.0": + version: 4.1.0 + resolution: "js-yaml@npm:4.1.0" + dependencies: + argparse: ^2.0.1 + bin: + js-yaml: bin/js-yaml.js + checksum: 8973cf4296c944cc2551d1e3d3d064e7de0d0a6db3f7bafe40339ee9e5e0329560b52c4b8492b9b22365404c9be0822b62340ab49884e1dedfcc7ff80158abe0 + languageName: node + linkType: hard + "js-yaml@npm:^3.13.1": version: 3.14.0 resolution: "js-yaml@npm:3.14.0" @@ -3112,6 +3961,15 @@ fsevents@~2.3.1: languageName: node linkType: hard +"jsesc@npm:^2.5.1": + version: 2.5.2 + resolution: "jsesc@npm:2.5.2" + bin: + jsesc: bin/jsesc + checksum: ca91ec33d74c55959e4b6fdbfee2af5f38be74a752cf0a982702e3a16239f26c2abbe19f5f84b15592570dda01872e929a90738615bd445f7b9b859781cfcf68 + languageName: node + linkType: hard + "json-beautify@npm:^1.1.1": version: 1.1.1 resolution: "json-beautify@npm:1.1.1" @@ -3163,6 +4021,17 @@ fsevents@~2.3.1: languageName: node linkType: hard +"json5@npm:^2.1.2": + version: 2.2.0 + resolution: "json5@npm:2.2.0" + dependencies: + minimist: ^1.2.5 + bin: + json5: lib/cli.js + checksum: 07b1f90c2801dc52df2b0ac8d606cc400a85cda79130e754780fa2ab9805d0fb85a0e61b6a5cdd68e88e5d0c8f9109ec415af08283175556cdccaa8563853908 + languageName: node + linkType: hard + "jsprim@npm:^1.2.2": version: 1.4.1 resolution: "jsprim@npm:1.4.1" @@ -3175,6 +4044,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"just-extend@npm:^4.0.2": + version: 4.2.1 + resolution: "just-extend@npm:4.2.1" + checksum: 4fb49b328a74717db39f365b8d54861da36824c5e386fd5a6de2da57254a8f4ddc2902be3bbbf5c3bfd4ef46325fcad9bb61de591f05a1cf8c86ce653c6ac4ee + languageName: node + linkType: hard + "jwk-to-pem@npm:^2.0.4": version: 2.0.4 resolution: "jwk-to-pem@npm:2.0.4" @@ -3186,6 +4062,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"keypair@npm:^1.0.1": + version: 1.0.3 + resolution: "keypair@npm:1.0.3" + checksum: 1be4b14da79bab9fe6d443a8e264bb76376d6f2a0a1ea98b46249cf3ab9572104842582102bc50e02d2e737c51d466e92560a91b5797ebf24243a499b54b4709 + languageName: node + linkType: hard + "kind-of@npm:^3.0.2, kind-of@npm:^3.0.3": version: 3.2.2 resolution: "kind-of@npm:3.2.2" @@ -3242,6 +4125,25 @@ fsevents@~2.3.1: languageName: node linkType: hard +"libp2p-crypto@npm:^0.19.0": + version: 0.19.7 + resolution: "libp2p-crypto@npm:0.19.7" + dependencies: + err-code: ^3.0.1 + is-typedarray: ^1.0.0 + iso-random-stream: ^2.0.0 + keypair: ^1.0.1 + multiformats: ^9.4.5 + node-forge: ^0.10.0 + pem-jwk: ^2.0.0 + protobufjs: ^6.11.2 + secp256k1: ^4.0.0 + uint8arrays: ^3.0.0 + ursa-optional: ^0.10.1 + checksum: f4964e1214cd4e40efda33e7364dc3e4497edada22b0630e8f8a8877d22be1ac1beebb3c5c93039e07b8330ac62b1a1575aaa8b19d5a2d0620268c9a43ff78a6 + languageName: node + linkType: hard + "lines-and-columns@npm:^1.1.6": version: 1.1.6 resolution: "lines-and-columns@npm:1.1.6" @@ -3291,6 +4193,24 @@ fsevents@~2.3.1: languageName: node linkType: hard +"locate-path@npm:^5.0.0": + version: 5.0.0 + resolution: "locate-path@npm:5.0.0" + dependencies: + p-locate: ^4.1.0 + checksum: c58f49d45c8672d0a290dea0ce41fcb27205b3f2d61452ba335ef3b42ad36c10c31b1f061b46d96dd4b81e9a00e8a2897bc124d75623b80a9f6d36b1e754a6b5 + languageName: node + linkType: hard + +"locate-path@npm:^6.0.0": + version: 6.0.0 + resolution: "locate-path@npm:6.0.0" + dependencies: + p-locate: ^5.0.0 + checksum: 4c379638152e0e5fda9a8cc07005702f81fcb9899db0f66d691ac1e64193dea670af14e96c50f14d82d45959daa4c400cb712c158cffe22ae265bfc1b1e3a221 + languageName: node + linkType: hard + "lodash.clonedeep@npm:^4.5.0": version: 4.5.0 resolution: "lodash.clonedeep@npm:4.5.0" @@ -3305,6 +4225,27 @@ fsevents@~2.3.1: languageName: node linkType: hard +"lodash.flattendeep@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.flattendeep@npm:4.4.0" + checksum: 941b709524bb7f16a06237a9b7041d8fc93054b5d3770bdf9e0edfaccf5666ef0c4dcc6866676774d8819cc1ec3a882eacafd42156befd470c35899e96fa4272 + languageName: node + linkType: hard + +"lodash.get@npm:^4.4.2": + version: 4.4.2 + resolution: "lodash.get@npm:4.4.2" + checksum: 447e575e3caa5131ef44e5a0c135b1614f3c937d86b3be0568f9da7b0fd015010af3b6b4e41edf6e2698c9ce2dcc061ca71b31f274f799c991dceb018be16e4f + languageName: node + linkType: hard + +"lodash.isplainobject@npm:^4.0.6": + version: 4.0.6 + resolution: "lodash.isplainobject@npm:4.0.6" + checksum: 72a114b610ec32a42b8cb47680d1729398caea0ee0631c0b220b97b21e7df19312377cb077acb6593bf6c5abdbdb43c530aa66b440e30d53324986d386808cd0 + languageName: node + linkType: hard + "lodash.truncate@npm:^4.4.2": version: 4.4.2 resolution: "lodash.truncate@npm:4.4.2" @@ -3319,13 +4260,6 @@ fsevents@~2.3.1: languageName: node linkType: hard -"lodash@npm:^4.17.19": - version: 4.17.20 - resolution: "lodash@npm:4.17.20" - checksum: c62101d2500c383b5f174a7e9e6fe8098149ddd6e9ccfa85f36d4789446195f5c4afd3cfba433026bcaf3da271256566b04a2bf2618e5a39f6e67f8c12030cb6 - languageName: node - linkType: hard - "log-ok@npm:^0.1.1": version: 0.1.1 resolution: "log-ok@npm:0.1.1" @@ -3336,7 +4270,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"log-symbols@npm:^4.1.0": +"log-symbols@npm:4.1.0, log-symbols@npm:^4.1.0": version: 4.1.0 resolution: "log-symbols@npm:4.1.0" dependencies: @@ -3380,6 +4314,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"long@npm:^4.0.0": + version: 4.0.0 + resolution: "long@npm:4.0.0" + checksum: 9cebc1ee8b9ea15278c977f61250ac41becdf7216104905f0d198c147ed2a7a090a2d83bb212878d3dc3ccb0199e6428862f848ec4b0ecfeab4dfe24eccf21b3 + languageName: node + linkType: hard + "lru-cache@npm:^6.0.0": version: 6.0.0 resolution: "lru-cache@npm:6.0.0" @@ -3398,6 +4339,15 @@ fsevents@~2.3.1: languageName: node linkType: hard +"make-dir@npm:^3.0.0, make-dir@npm:^3.0.2": + version: 3.1.0 + resolution: "make-dir@npm:3.1.0" + dependencies: + semver: ^6.0.0 + checksum: 54b6f186c209c1b133d0d1710e6b04c41ebfcb0dac699e5a369ea1223f22c0574ef820b91db37cae6c245f5bda8aff9bfec94f6c23e7d75970446b34a58a79b0 + languageName: node + linkType: hard + "make-error@npm:^1.1.1": version: 1.3.6 resolution: "make-error@npm:1.3.6" @@ -3414,6 +4364,15 @@ fsevents@~2.3.1: languageName: node linkType: hard +"matcher@npm:^2.0.0": + version: 2.1.0 + resolution: "matcher@npm:2.1.0" + dependencies: + escape-string-regexp: ^2.0.0 + checksum: dc05a170efb4507a276c8b656c71fa86019a9fcdbedd6f17da81304729124f4121f98d113342ef5814e8d67a55da4872658eadf7d13a98192f36e9b5935e9750 + languageName: node + linkType: hard + "md5-file@npm:^5.0.0": version: 5.0.0 resolution: "md5-file@npm:5.0.0" @@ -3423,6 +4382,17 @@ fsevents@~2.3.1: languageName: node linkType: hard +"md5.js@npm:^1.3.4": + version: 1.3.5 + resolution: "md5.js@npm:1.3.5" + dependencies: + hash-base: ^3.0.0 + inherits: ^2.0.1 + safe-buffer: ^5.1.2 + checksum: ca0b260ea29746f1017ad16bc0e164299ae453d2d6a24d635cc6ec03e280f350b09faa4899bfed9387c81457ca55981e9a684336d89faa94b1d2a01903fae2ec + languageName: node + linkType: hard + "memoizee@npm:0.3.x": version: 0.3.10 resolution: "memoizee@npm:0.3.10" @@ -3452,17 +4422,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"micromatch@npm:^4.0.2": - version: 4.0.2 - resolution: "micromatch@npm:4.0.2" - dependencies: - braces: ^3.0.1 - picomatch: ^2.0.5 - checksum: 0cb0e11d647cbb65e398a0a8a1340a7fb751ae2722346219c435704cfac8b3275a94a6464236fe867f52ad46a24046d3bc4ac11b3d21ddb73bc44e27cf1e4904 - languageName: node - linkType: hard - -"micromatch@npm:^4.0.4": +"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4": version: 4.0.4 resolution: "micromatch@npm:4.0.4" dependencies: @@ -3472,13 +4432,6 @@ fsevents@~2.3.1: languageName: node linkType: hard -"mime-db@npm:1.44.0": - version: 1.44.0 - resolution: "mime-db@npm:1.44.0" - checksum: b4e3b2141418572fba9786f7e36324faef15e23032ad0871f56760cb304ee721ba4c8cc795d3c1cac69a2a8b94045c1d6b08c4a8d1ef6ba1226a3a5193915c57 - languageName: node - linkType: hard - "mime-db@npm:1.46.0": version: 1.46.0 resolution: "mime-db@npm:1.46.0" @@ -3486,16 +4439,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"mime-types@npm:^2.1.12, mime-types@npm:~2.1.19": - version: 2.1.27 - resolution: "mime-types@npm:2.1.27" - dependencies: - mime-db: 1.44.0 - checksum: 51fe2f2c08c10ac7a2f67e2ce5de30f6500faa88d095418a1ab6e90e30960db7c682a8ecce60d3d4e293ac52c4700ca99399833db998ea9ec83d6f0503b70a94 - languageName: node - linkType: hard - -"mime-types@npm:^2.1.29": +"mime-types@npm:^2.1.12, mime-types@npm:^2.1.29, mime-types@npm:~2.1.19": version: 2.1.29 resolution: "mime-types@npm:2.1.29" dependencies: @@ -3532,7 +4476,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"minimatch@npm:^3.0.4, minimatch@npm:~3.0.4": +"minimatch@npm:3.0.4, minimatch@npm:^3.0.4, minimatch@npm:~3.0.4": version: 3.0.4 resolution: "minimatch@npm:3.0.4" dependencies: @@ -3541,7 +4485,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"minimist@npm:^1.2.0, minimist@npm:^1.2.3": +"minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.5": version: 1.2.5 resolution: "minimist@npm:1.2.5" checksum: b77b8590147a4e217ff34266236bc39de23b52e6e33054076991ff674c7397a1380a7bde11111916f16f003a94aaa7e4f3d92595a32189644ff607fabc65a5b6 @@ -3593,6 +4537,42 @@ fsevents@~2.3.1: languageName: node linkType: hard +"mocha@npm:^9.1.1": + version: 9.1.1 + resolution: "mocha@npm:9.1.1" + dependencies: + "@ungap/promise-all-settled": 1.1.2 + ansi-colors: 4.1.1 + browser-stdout: 1.3.1 + chokidar: 3.5.2 + debug: 4.3.1 + diff: 5.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 7.1.7 + growl: 1.10.5 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 3.0.4 + ms: 2.1.3 + nanoid: 3.1.23 + serialize-javascript: 6.0.0 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + which: 2.0.2 + wide-align: 1.1.3 + workerpool: 6.1.5 + yargs: 16.2.0 + yargs-parser: 20.2.4 + yargs-unparser: 2.0.0 + bin: + _mocha: bin/_mocha + mocha: bin/mocha + checksum: c316e4e396aa097e603b3e188388c11683e9722f9066f49f7cc4628271a70a379405e7f59cde72ead81c055e76be6e7fd4c81e900632e4b7dc331e1f2b7586d2 + languageName: node + linkType: hard + "ms@npm:2.0.0": version: 2.0.0 resolution: "ms@npm:2.0.0" @@ -3600,13 +4580,27 @@ fsevents@~2.3.1: languageName: node linkType: hard -"ms@npm:2.1.2, ms@npm:^2.1.1": +"ms@npm:2.1.2": version: 2.1.2 resolution: "ms@npm:2.1.2" checksum: 9b65fb709bc30c0c07289dcbdb61ca032acbb9ea5698b55fa62e2cebb04c5953f1876a1f3f7f4bc2e91d4bf4d86003f3e207c3bc6ee2f716f99827e62389cd0e languageName: node linkType: hard +"ms@npm:2.1.3, ms@npm:^2.1.1": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 6e721e648a544154d5de4c114b32f573d8027ca8ec505cf6c1105e505986d6ac46934a1256735aa0eece8eb2f5b2a1230503b2dddd3b100f9f016fd8a4f15f33 + languageName: node + linkType: hard + +"multiformats@npm:^9.4.2, multiformats@npm:^9.4.5": + version: 9.4.7 + resolution: "multiformats@npm:9.4.7" + checksum: 370ed783ca023708b095c73aa77194c3304a839aabc7d4a923dbba715088766c9ebac8d92dec2119ac303679428a0ad0fff01d686106bd55dae433d1fbc17c9d + languageName: node + linkType: hard + "mute-stream@npm:0.0.7": version: 0.0.7 resolution: "mute-stream@npm:0.0.7" @@ -3621,6 +4615,24 @@ fsevents@~2.3.1: languageName: node linkType: hard +"nan@npm:^2.14.2": + version: 2.15.0 + resolution: "nan@npm:2.15.0" + dependencies: + node-gyp: latest + checksum: 85bc21e90bcf7a1f1349f1e4fcf73190dcb43e5e7aac1082259b8b18baff618fef04d9eaf6ce896963341daa92461fd12908e2cd41795984462e2eeca327d161 + languageName: node + linkType: hard + +"nanoid@npm:3.1.23": + version: 3.1.23 + resolution: "nanoid@npm:3.1.23" + bin: + nanoid: bin/nanoid.cjs + checksum: e6dea1da5a593ffdc8cf2676d1d02f0626f07a54a5947a7a1f5ff1fd07901b2f53044c285e98b87eb367f016fde285fd8785d54a2dceeab9c3721f4e618f8326 + languageName: node + linkType: hard + "napi-build-utils@npm:^1.0.1": version: 1.0.2 resolution: "napi-build-utils@npm:1.0.2" @@ -3667,6 +4679,32 @@ fsevents@~2.3.1: languageName: node linkType: hard +"nise@npm:^4.0.4": + version: 4.1.0 + resolution: "nise@npm:4.1.0" + dependencies: + "@sinonjs/commons": ^1.7.0 + "@sinonjs/fake-timers": ^6.0.0 + "@sinonjs/text-encoding": ^0.7.1 + just-extend: ^4.0.2 + path-to-regexp: ^1.7.0 + checksum: 87b37725d9e0e6dc951caa40685f8726d028ac48ca87e71773cbb705e2736e084cc3087530a92b9ebe87163212ee477d37318875984b1093f79bae600ce6d7c4 + languageName: node + linkType: hard + +"nise@npm:^5.1.0": + version: 5.1.0 + resolution: "nise@npm:5.1.0" + dependencies: + "@sinonjs/commons": ^1.7.0 + "@sinonjs/fake-timers": ^7.0.4 + "@sinonjs/text-encoding": ^0.7.1 + just-extend: ^4.0.2 + path-to-regexp: ^1.7.0 + checksum: 09c3ad41650a0eb0594e448f846b164abc75e198fac71b259ccd8145d6c47851a17305808c07a1c9c1f201f319ea2e672940c24526bdbf6caedf7bd7a9b713cd + languageName: node + linkType: hard + "node-abi@npm:^2.21.0": version: 2.30.0 resolution: "node-abi@npm:2.30.0" @@ -3676,10 +4714,44 @@ fsevents@~2.3.1: languageName: node linkType: hard -"node-fetch@npm:^2.6.1": - version: 2.6.1 - resolution: "node-fetch@npm:2.6.1" - checksum: cbb171635e538162b977eac5dfe7a1e07a9a02e991924377a6435502291e2f823d306b95aabc455caebf4a118ccf836868462bc70ccc3095af02bb9da61fda37 +"node-addon-api@npm:^2.0.0": + version: 2.0.2 + resolution: "node-addon-api@npm:2.0.2" + dependencies: + node-gyp: latest + checksum: c40748948918fc82b83a9f3442a98f4fa92bd9786bb1d1b507facf77a22fb7a4b5b9332102f6ad94c7e2828ae3cd353907cbaa8eaf4ce9c5edaa9d327d501301 + languageName: node + linkType: hard + +"node-fetch@npm:2.6.2, node-fetch@npm:^2.6.1": + version: 2.6.2 + resolution: "node-fetch@npm:2.6.2" + checksum: f48d84c89c861ae7ff6d0a363a6c85fb7159e51165b1b97819c1b750f1fbbe27765d7bbda37c7a0e453a736db25a396d3ec22057d257d3403a79e722710a97f7 + languageName: node + linkType: hard + +"node-forge@npm:^0.10.0": + version: 0.10.0 + resolution: "node-forge@npm:0.10.0" + checksum: c7a729933a0391e4f434d4455705e869340bf91c3cc6b51b3844a91a5ac9db6f8697f600ab1e62e25f990382b2c1592d93d31fd831bb1a0b1e66ce28d9d6d124 + languageName: node + linkType: hard + +"node-forge@npm:^0.8.1, node-forge@npm:^0.8.2": + version: 0.8.5 + resolution: "node-forge@npm:0.8.5" + checksum: 4bfa3755c2a25199d80262d0197c37251b33b3c27513607bbb41d7e411be4c907a4849a5a893101853c650cf8c111270383416fd34ac5bf851e9c3a20c8d89c8 + languageName: node + linkType: hard + +"node-gyp-build@npm:^4.2.0": + version: 4.2.3 + resolution: "node-gyp-build@npm:4.2.3" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 8512c25498b1605dbe9d2605cac7f8996d18c35097b3079b232674b0f3b8f1559be6f531ec3a00a94f90a0a69ccc452edb62f8f6dfff8caa3abe4b729cc21b06 languageName: node linkType: hard @@ -3703,6 +4775,22 @@ fsevents@~2.3.1: languageName: node linkType: hard +"node-preload@npm:^0.2.1": + version: 0.2.1 + resolution: "node-preload@npm:0.2.1" + dependencies: + process-on-spawn: ^1.0.0 + checksum: 72d4cccb333d32f15aa83177e651ba0ce7aea6424cc409ed9a1cc42c46edb6f4197acb433a48abd270461ab7b18b426a2c5c74905e95c75e02bbe48429124885 + languageName: node + linkType: hard + +"node-releases@npm:^1.1.75": + version: 1.1.75 + resolution: "node-releases@npm:1.1.75" + checksum: 0ea9d84f2cc899fc033866178f4a86b2ae7abf8fb728e37740f7a6464180ce4d3a5557b6b25fdb375d2b2380edad6c67f2395e84986e7d88cbb87896279e20f5 + languageName: node + linkType: hard + "noop-logger@npm:^0.1.1": version: 0.1.1 resolution: "noop-logger@npm:0.1.1" @@ -3756,6 +4844,43 @@ fsevents@~2.3.1: languageName: node linkType: hard +"nyc@npm:^15.1.0": + version: 15.1.0 + resolution: "nyc@npm:15.1.0" + dependencies: + "@istanbuljs/load-nyc-config": ^1.0.0 + "@istanbuljs/schema": ^0.1.2 + caching-transform: ^4.0.0 + convert-source-map: ^1.7.0 + decamelize: ^1.2.0 + find-cache-dir: ^3.2.0 + find-up: ^4.1.0 + foreground-child: ^2.0.0 + get-package-type: ^0.1.0 + glob: ^7.1.6 + istanbul-lib-coverage: ^3.0.0 + istanbul-lib-hook: ^3.0.0 + istanbul-lib-instrument: ^4.0.0 + istanbul-lib-processinfo: ^2.0.2 + istanbul-lib-report: ^3.0.0 + istanbul-lib-source-maps: ^4.0.0 + istanbul-reports: ^3.0.2 + make-dir: ^3.0.0 + node-preload: ^0.2.1 + p-map: ^3.0.0 + process-on-spawn: ^1.0.0 + resolve-from: ^5.0.0 + rimraf: ^3.0.0 + signal-exit: ^3.0.2 + spawn-wrap: ^2.0.0 + test-exclude: ^6.0.0 + yargs: ^15.0.2 + bin: + nyc: bin/nyc.js + checksum: f7f71b2b7f8f56be965bc057af767ec38d99b1659417a9276d8276ca2f08e914047f990a448a90deac410afd7dfc91454e7a7c752c76f9b9237cd06c5c15f9f4 + languageName: node + linkType: hard + "oauth-sign@npm:~0.9.0": version: 0.9.0 resolution: "oauth-sign@npm:0.9.0" @@ -3839,10 +4964,55 @@ fsevents@~2.3.1: languageName: node linkType: hard -"os-tmpdir@npm:~1.0.2": - version: 1.0.2 - resolution: "os-tmpdir@npm:1.0.2" - checksum: ca158a3c2e48748adc7736cdbe4c593723f8ed8581d2aae2f2a30fdb9417d4ba14bed1cd487d47561898a7b1ece88bce69745e9ce0303e1dea9ea7d22d1f1082 +"os-tmpdir@npm:~1.0.2": + version: 1.0.2 + resolution: "os-tmpdir@npm:1.0.2" + checksum: ca158a3c2e48748adc7736cdbe4c593723f8ed8581d2aae2f2a30fdb9417d4ba14bed1cd487d47561898a7b1ece88bce69745e9ce0303e1dea9ea7d22d1f1082 + languageName: node + linkType: hard + +"p-limit@npm:^2.2.0": + version: 2.3.0 + resolution: "p-limit@npm:2.3.0" + dependencies: + p-try: ^2.0.0 + checksum: 5f20492a25c5f93fca2930dbbf41fa1bee46ef70eaa6b49ad1f7b963f309e599bc40507e0a3a531eee4bcd10fec4dd4a63291d0e3b2d84ac97d7403d43d271a9 + languageName: node + linkType: hard + +"p-limit@npm:^3.0.2": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: ^0.1.0 + checksum: 5301db6a34fc1fe3714ae562c100a0567d8c16ce9db800f547bbe75efc045c40cd74c4a4c893279975dcf15afc1217c8d2c93fe957a156a3a43d7cce98eaad2e + languageName: node + linkType: hard + +"p-locate@npm:^4.1.0": + version: 4.1.0 + resolution: "p-locate@npm:4.1.0" + dependencies: + p-limit: ^2.2.0 + checksum: 57f9abef0b29f02ff88c0936a392c9a1fbdd08169e636e0d85b7407c108014d71578c0c6fe93fa49b5bf3857b20d6f16b96389e2b356f7f599d4d2150505844f + languageName: node + linkType: hard + +"p-locate@npm:^5.0.0": + version: 5.0.0 + resolution: "p-locate@npm:5.0.0" + dependencies: + p-limit: ^3.0.2 + checksum: a233d775c870e00c734adabd29f66f93824df076683c0d5a2dc16e5285b02d80c1bf3bab43b9881e4a5b16b37bb86f1922aebb094674703d30a4973041d5c0f6 + languageName: node + linkType: hard + +"p-map@npm:^3.0.0": + version: 3.0.0 + resolution: "p-map@npm:3.0.0" + dependencies: + aggregate-error: ^3.0.0 + checksum: f7ce4709f432323a11f7c808f96add4104774cb7ba88cc9a92b6b5b8ea8a7fa977d28c4e5619669f9cf1315e889769843c6a4772155b08dadbd20d504e4ce2a7 languageName: node linkType: hard @@ -3855,6 +5025,25 @@ fsevents@~2.3.1: languageName: node linkType: hard +"p-try@npm:^2.0.0": + version: 2.2.0 + resolution: "p-try@npm:2.2.0" + checksum: 20983f3765466c1ab617ed153cb53b70ac5df828d854a3334d185e20b37f436e9096f12bc1b7fc96d8908dc927a3685172d3d89e755774f57b7103460c54dcc5 + languageName: node + linkType: hard + +"package-hash@npm:^4.0.0": + version: 4.0.0 + resolution: "package-hash@npm:4.0.0" + dependencies: + graceful-fs: ^4.1.15 + hasha: ^5.0.0 + lodash.flattendeep: ^4.4.0 + release-zalgo: ^1.0.0 + checksum: f64c22b5d619c12defd472a38dff0b16deb7a167b073176d7c28d1868f0d0e88b05e09b438a655a1ba9ebb94f73e539202ab45136edc904d1bc3d25b45ce8570 + languageName: node + linkType: hard + "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -3876,6 +5065,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 6ab15000c5bea4f3e6e6b651983276e27ee42907ea29f5bd68f0d5c425c22f1664ab53c355099723f59b0bfd31aa52d29ea499e1843bf62543e045698f4c77b2 + languageName: node + linkType: hard + "path-is-absolute@npm:^1.0.0": version: 1.0.1 resolution: "path-is-absolute@npm:1.0.1" @@ -3890,6 +5086,15 @@ fsevents@~2.3.1: languageName: node linkType: hard +"path-to-regexp@npm:^1.7.0": + version: 1.8.0 + resolution: "path-to-regexp@npm:1.8.0" + dependencies: + isarray: 0.0.1 + checksum: 4c0d9aaf3fc55db0b2d9aab379856acbf4e437f2252bbc2a178aec9f707c8457f8084ea6243a80e0b37c8c1c20d23e918cd43e772a7e71142a8ad67af699686b + languageName: node + linkType: hard + "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -3897,6 +5102,37 @@ fsevents@~2.3.1: languageName: node linkType: hard +"pathval@npm:^1.1.1": + version: 1.1.1 + resolution: "pathval@npm:1.1.1" + checksum: 81cd01d46cd0cd90fb7a8e0d576f8cb4cbbd0832ed00da14362be32ad79e855cf8ecf7e92fc0c267fdfc2766bd1d8fb3de43a51081e142ffb1fc814958fb8d8f + languageName: node + linkType: hard + +"pbkdf2@npm:^3.0.9": + version: 3.1.2 + resolution: "pbkdf2@npm:3.1.2" + dependencies: + create-hash: ^1.1.2 + create-hmac: ^1.1.4 + ripemd160: ^2.0.1 + safe-buffer: ^5.0.1 + sha.js: ^2.4.8 + checksum: 12ac46c71db0613f5e45824d65598a31b4fb09bb860cde8024919778be7971468f2c6b010250d5bc9907a247f85580b560fd5b78b09263d207e8bdf3963c83c7 + languageName: node + linkType: hard + +"pem-jwk@npm:^2.0.0": + version: 2.0.0 + resolution: "pem-jwk@npm:2.0.0" + dependencies: + asn1.js: ^5.0.1 + bin: + pem-jwk: ./bin/pem-jwk.js + checksum: c3d2248e114049aaaf477551ed88dda4fe88a146d011245ac664c6c1c719ee8edb2c54c1853f9b7652a26b99362a66d56e6a3df2b3862c3034fd7dac2d4e5585 + languageName: node + linkType: hard + "performance-now@npm:^2.1.0": version: 2.1.0 resolution: "performance-now@npm:2.1.0" @@ -3904,20 +5140,29 @@ fsevents@~2.3.1: languageName: node linkType: hard -"picomatch@npm:^2.0.4, picomatch@npm:^2.0.5, picomatch@npm:^2.2.1": - version: 2.2.2 - resolution: "picomatch@npm:2.2.2" - checksum: 20fa75e0a58b39d83425b3db68744d5f6f361fd4fd66ec7745d884036d502abba0d553a637703af79939b844164b13e60eea339ccb043d7fbd74c3da2592b864 - languageName: node - linkType: hard - -"picomatch@npm:^2.2.3": +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3": version: 2.3.0 resolution: "picomatch@npm:2.3.0" checksum: 80113a0fb70cfa62730d5aa3fd3d45b76bf3985f8494080ab2de1cc1fa3ba96d77990c7553a81401e16c51c0eb19c27cf5bc94f2196155090f26c8a167968001 languageName: node linkType: hard +"pify@npm:^4.0.1": + version: 4.0.1 + resolution: "pify@npm:4.0.1" + checksum: 786486a8c94a7e1980ea56c59dcc05ebf0793740b71df9b9f273e48032e6301c5ecc5cc237c5a9ff45b13db27678b4d71aa37a2777bc11473c1310718b648e98 + languageName: node + linkType: hard + +"pkg-dir@npm:^4.1.0": + version: 4.2.0 + resolution: "pkg-dir@npm:4.2.0" + dependencies: + find-up: ^4.0.0 + checksum: 1956ebf3cf5cc36a5d20e93851fcadd5a786774eb08667078561e72e0ab8ace91fc36a028d5305f0bfe7c89f9bf51886e2a3c8cb2c2620accfa3feb8da3c256b + languageName: node + linkType: hard + "please-upgrade-node@npm:^3.2.0": version: 3.2.0 resolution: "please-upgrade-node@npm:3.2.0" @@ -3983,18 +5228,6 @@ fsevents@~2.3.1: languageName: node linkType: hard -"pretty-format@npm:^25.2.1, pretty-format@npm:^25.5.0": - version: 25.5.0 - resolution: "pretty-format@npm:25.5.0" - dependencies: - "@jest/types": ^25.5.0 - ansi-regex: ^5.0.0 - ansi-styles: ^4.0.0 - react-is: ^16.12.0 - checksum: f7cc631d51e22c809d429d20facfd886ba0b212d419d153467872f68688256c2c55563bf70e943b7347ec9180b41a1d19c4235dc171850f9d5382a52959c0245 - languageName: node - linkType: hard - "prng-well1024a@npm:~1.0.0": version: 1.0.1 resolution: "prng-well1024a@npm:1.0.1" @@ -4009,6 +5242,15 @@ fsevents@~2.3.1: languageName: node linkType: hard +"process-on-spawn@npm:^1.0.0": + version: 1.0.0 + resolution: "process-on-spawn@npm:1.0.0" + dependencies: + fromentries: ^1.2.0 + checksum: 91b18f68bb31180e12708e9a947963265ad65d147673d914519995f0fbc619f9fa953e96c91f0c363c3afefbe2937b1c5881c0c334e7c460154165193a10ae3b + languageName: node + linkType: hard + "progress@npm:^2.0.0, progress@npm:^2.0.3": version: 2.0.3 resolution: "progress@npm:2.0.3" @@ -4110,6 +5352,30 @@ fsevents@~2.3.1: languageName: node linkType: hard +"protobufjs@npm:^6.11.2": + version: 6.11.2 + resolution: "protobufjs@npm:6.11.2" + dependencies: + "@protobufjs/aspromise": ^1.1.2 + "@protobufjs/base64": ^1.1.2 + "@protobufjs/codegen": ^2.0.4 + "@protobufjs/eventemitter": ^1.1.0 + "@protobufjs/fetch": ^1.1.0 + "@protobufjs/float": ^1.0.2 + "@protobufjs/inquire": ^1.1.0 + "@protobufjs/path": ^1.1.2 + "@protobufjs/pool": ^1.1.0 + "@protobufjs/utf8": ^1.1.0 + "@types/long": ^4.0.1 + "@types/node": ">=13.7.0" + long: ^4.0.0 + bin: + pbjs: bin/pbjs + pbts: bin/pbts + checksum: 3c7c6875ca794ae9c31c532f42368b6fd6d446b20d696f261946feb3f3a9afe482a142afdca7c5bf0cf958b2d79cdbb8bbd56f55bd13b221920267d3332a062e + languageName: node + linkType: hard + "psl@npm:^1.1.28": version: 1.8.0 resolution: "psl@npm:1.8.0" @@ -4159,6 +5425,15 @@ fsevents@~2.3.1: languageName: node linkType: hard +"randombytes@npm:^2.0.1, randombytes@npm:^2.1.0": + version: 2.1.0 + resolution: "randombytes@npm:2.1.0" + dependencies: + safe-buffer: ^5.1.0 + checksum: ede2693af09732ceab1c273dd70db787f34a7b8d95bab13f1aca763483c0113452a78e53d61ff18d393dcea586d388e01f198a5132a4a85cebba31ec54164b75 + languageName: node + linkType: hard + "randy@npm:~1.5.1": version: 1.5.1 resolution: "randy@npm:1.5.1" @@ -4182,13 +5457,6 @@ fsevents@~2.3.1: languageName: node linkType: hard -"react-is@npm:^16.12.0": - version: 16.13.1 - resolution: "react-is@npm:16.13.1" - checksum: 11bcf1267a314a522615f626f3ce3727a3a24cdbf61c4d452add3550a7875326669631326cfb1ba3e92b6f72244c32ffecf93ad21c0cad8455d3e169d0e3f060 - languageName: node - linkType: hard - "readable-stream@npm:^2.0.6": version: 2.3.7 resolution: "readable-stream@npm:2.3.7" @@ -4204,7 +5472,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"readable-stream@npm:^3.0.2, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0": +"readable-stream@npm:^3.0.2, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": version: 3.6.0 resolution: "readable-stream@npm:3.6.0" dependencies: @@ -4215,12 +5483,12 @@ fsevents@~2.3.1: languageName: node linkType: hard -"readdirp@npm:~3.5.0": - version: 3.5.0 - resolution: "readdirp@npm:3.5.0" +"readdirp@npm:~3.6.0": + version: 3.6.0 + resolution: "readdirp@npm:3.6.0" dependencies: picomatch: ^2.2.1 - checksum: a64fe5606937d9655252230003362d95da05dbfd3baecedb4bb8c1bc0df497d051a192f9b75345c944e58a0b362c68349be602d6dbf05d03770e510b35a9f80f + checksum: 7da2fe8d5abf17ae0bf97a052718e16d29fa185f3e461153035728d93642326ae8e44c17b9a9b3a5fa616dff160e96be3184e0323efaac7211f80c0aab5f622b languageName: node linkType: hard @@ -4260,6 +5528,22 @@ fsevents@~2.3.1: languageName: node linkType: hard +"regression@npm:^2.0.1": + version: 2.0.1 + resolution: "regression@npm:2.0.1" + checksum: 61feb6f23b2715956219b1892ac985e64e47a0c5e2fa9e5361ea02d8b068c3a24637f7b33f9bc5424aac086316d2671b0dfa9a64fa8372200d757c65bc1e953c + languageName: node + linkType: hard + +"release-zalgo@npm:^1.0.0": + version: 1.0.0 + resolution: "release-zalgo@npm:1.0.0" + dependencies: + es6-error: ^4.0.1 + checksum: db2e7567a9e7203862292d46cc31a933993be6bc59889240fd0dc910621fae551510a2a336ee04395856b300a802e1ffca53e0a14d3663ed12610244ccd1190d + languageName: node + linkType: hard + "request@npm:^2.88.2": version: 2.88.2 resolution: "request@npm:2.88.2" @@ -4302,6 +5586,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"require-main-filename@npm:^2.0.0": + version: 2.0.0 + resolution: "require-main-filename@npm:2.0.0" + checksum: 8d3633149a7fef67d14613146247137fe1dc4cc969bf2d1adcd40e3c28056de503229f41e78cba5efebad3a223cbfb4215fd220d879148df10c6d9a877099dbd + languageName: node + linkType: hard + "resolve-from@npm:^4.0.0": version: 4.0.0 resolution: "resolve-from@npm:4.0.0" @@ -4309,6 +5600,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"resolve-from@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 0d29fc7012eb21f34d2637fa0602694f60e64c14bf5fbd5395b72f6ea5540a6906cbeef062edefc34c22fd802bfe8ae46ef936e6c4a3f1b1047390f9738dd76f + languageName: node + linkType: hard + "restore-cursor@npm:^3.1.0": version: 3.1.0 resolution: "restore-cursor@npm:3.1.0" @@ -4326,7 +5624,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"rimraf@npm:^3.0.2": +"rimraf@npm:^3.0.0, rimraf@npm:^3.0.2": version: 3.0.2 resolution: "rimraf@npm:3.0.2" dependencies: @@ -4337,6 +5635,16 @@ fsevents@~2.3.1: languageName: node linkType: hard +"ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1": + version: 2.0.2 + resolution: "ripemd160@npm:2.0.2" + dependencies: + hash-base: ^3.0.0 + inherits: ^2.0.1 + checksum: e0370fbe779b1f15d74c3e7dffc0ce40b57b845fc7e431fab8a571958d5fd9c91eb0038a252604600e20786d117badea0cc4cf8816b8a6be6b9166b565ad6797 + languageName: node + linkType: hard + "run-async@npm:^2.4.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" @@ -4353,7 +5661,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"rxjs@npm:^6.4.0, rxjs@npm:^6.6.0, rxjs@npm:^6.6.7": +"rxjs@npm:^6.4.0, rxjs@npm:^6.6.7": version: 6.6.7 resolution: "rxjs@npm:6.6.7" dependencies: @@ -4371,7 +5679,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: 0bb57f0d8f9d1fa4fe35ad8a2db1f83a027d48f2822d59ede88fd5cd4ddad83c0b497213feb7a70fbf90597a70c5217f735b0eb1850df40ce9b4ae81dd22b3f9 @@ -4392,6 +5700,18 @@ fsevents@~2.3.1: languageName: node linkType: hard +"secp256k1@npm:^4.0.0": + version: 4.0.2 + resolution: "secp256k1@npm:4.0.2" + dependencies: + elliptic: ^6.5.2 + node-addon-api: ^2.0.0 + node-gyp: latest + node-gyp-build: ^4.2.0 + checksum: 5bf0e0aa119602c96fb26c87b4707d314e6e85d5b6aca8b6286e781b3c573775cabd68e4e6d90dcabe915a4795743e2f208dcc556e7c11b1a89b19dc5c64434e + languageName: node + linkType: hard + "semver-compare@npm:^1.0.0": version: 1.0.0 resolution: "semver-compare@npm:1.0.0" @@ -4408,7 +5728,16 @@ fsevents@~2.3.1: languageName: node linkType: hard -"semver@npm:^7.2.1": +"semver@npm:^6.0.0, semver@npm:^6.3.0": + version: 6.3.0 + resolution: "semver@npm:6.3.0" + bin: + semver: ./bin/semver.js + checksum: f0d155c06a67cc7e500c92d929339f1c6efd4ce9fe398aee6acc00a2333489cca0f5b4e76ee7292beba237fcca4b5a3d4a6153471f105f56299801bdab37289f + languageName: node + linkType: hard + +"semver@npm:^7.2.1, semver@npm:^7.3.2": version: 7.3.5 resolution: "semver@npm:7.3.5" dependencies: @@ -4419,15 +5748,6 @@ fsevents@~2.3.1: languageName: node linkType: hard -"semver@npm:^7.3.2": - version: 7.3.2 - resolution: "semver@npm:7.3.2" - bin: - semver: bin/semver.js - checksum: bceb46d396d039afb5be2b2860bce1b0a43ecbadc72dde7ebe9c56dd9035ca50d9b8e086208ff9bbe53773ebde0bcfc6fc0842d7358398bca7054bb9ced801e3 - languageName: node - linkType: hard - "sentencer@npm:^0.2.1": version: 0.2.1 resolution: "sentencer@npm:0.2.1" @@ -4440,7 +5760,16 @@ fsevents@~2.3.1: languageName: node linkType: hard -"set-blocking@npm:~2.0.0": +"serialize-javascript@npm:6.0.0": + version: 6.0.0 + resolution: "serialize-javascript@npm:6.0.0" + dependencies: + randombytes: ^2.1.0 + checksum: e086a40bfcb9d341c37a4e52bc200d143b54397cf1bb486f38cd40cdbaac4b82437d981472df94dbcff6334269e0d82daffbd6b75dd50fe54a5a5da2273f2360 + languageName: node + linkType: hard + +"set-blocking@npm:^2.0.0, set-blocking@npm:~2.0.0": version: 2.0.0 resolution: "set-blocking@npm:2.0.0" checksum: 0ac2403b0c2d39bf452f6d5d17dfd3cb952b9113098e1231cc0614c436e2f465637e39d27cf3b93556f5c59795e9790fd7e98da784c5f9919edeba4295ffeb29 @@ -4465,6 +5794,18 @@ fsevents@~2.3.1: languageName: node linkType: hard +"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8": + version: 2.4.11 + resolution: "sha.js@npm:2.4.11" + dependencies: + inherits: ^2.0.1 + safe-buffer: ^5.0.1 + bin: + sha.js: ./bin.js + checksum: 7554240ab76e683f7115123eb4815aae16b5fc6f2cdff97009831ad5b17b107ffcef022526211f7306957bce7a67fa4d0ccad79a3040c5073414365595e90516 + languageName: node + linkType: hard + "shallow-clone@npm:^1.0.0": version: 1.0.0 resolution: "shallow-clone@npm:1.0.0" @@ -4526,6 +5867,34 @@ fsevents@~2.3.1: languageName: node linkType: hard +"sinon@npm:^11.1.2": + version: 11.1.2 + resolution: "sinon@npm:11.1.2" + dependencies: + "@sinonjs/commons": ^1.8.3 + "@sinonjs/fake-timers": ^7.1.2 + "@sinonjs/samsam": ^6.0.2 + diff: ^5.0.0 + nise: ^5.1.0 + supports-color: ^7.2.0 + checksum: dca6f9cbc7cb1af0b1f8e3fb14afad261f280b5cb29372f8e9b7432afc17d6c60e3f8895997bc97237855795fe82cf2c3b82e9d4f3554b5ae026874d52d7c71c + languageName: node + linkType: hard + +"sinon@npm:^9.0.3": + version: 9.2.4 + resolution: "sinon@npm:9.2.4" + dependencies: + "@sinonjs/commons": ^1.8.1 + "@sinonjs/fake-timers": ^6.0.1 + "@sinonjs/samsam": ^5.3.1 + diff: ^4.0.2 + nise: ^4.0.4 + supports-color: ^7.1.0 + checksum: c285c3750ab403331a1eba390cea139b1c91003648f254d64e14b80d9a38c3ccf788e066d084ecdc7da6de29d279967e25eacdb4e5bc44840d30262f73a0c201 + languageName: node + linkType: hard + "sisteransi@npm:^1.0.5": version: 1.0.5 resolution: "sisteransi@npm:1.0.5" @@ -4562,30 +5931,6 @@ fsevents@~2.3.1: languageName: node linkType: hard -"smartweave@npm:^0.4.27": - version: 0.4.27 - resolution: "smartweave@npm:0.4.27" - dependencies: - "@types/clui": ^0.3.0 - "@types/inquirer": ^7.3.1 - "@weavery/clarity": ^0.1.5 - arweave: ^1.10.11 - bignumber.js: ^9.0.1 - chalk: ^4.1.0 - clui: ^0.3.6 - figlet: ^1.5.0 - inquirer: ^7.3.3 - json-beautify: ^1.1.1 - loglevel: ^1.7.0 - sentencer: ^0.2.1 - yargs: ^16.2.0 - bin: - smartweave: lib/bin/smartweave.js - smartweave-cli: lib/bin/smartweave-cli.js - checksum: ea910348e0400ed7b3cd7122f56fd6f3caebb32ad7d195f64b4e49f5b540aca66fa03c5f696a63c0852b081a3aaa50339006661377319d515387e2ba1aba3b8f - languageName: node - linkType: hard - "smartweave@npm:^0.4.45": version: 0.4.45 resolution: "smartweave@npm:0.4.45" @@ -4610,23 +5955,44 @@ fsevents@~2.3.1: languageName: node linkType: hard -"source-map-support@npm:^0.5.17": - version: 0.5.19 - resolution: "source-map-support@npm:0.5.19" +"source-map-support@npm:^0.5.20": + version: 0.5.20 + resolution: "source-map-support@npm:0.5.20" dependencies: buffer-from: ^1.0.0 source-map: ^0.6.0 - checksum: 59d4efaae97755155b078413ecba63517e3ef054cc7ab767bbd30e6f3054be2ae8e8f5cce7eef53b7eb93e98fe27a58dd8f5e7abfb13144ba420ddaf5267bbb2 + checksum: 2c5821ee94732b2ddbb1e1d9e61ed76ef00f72415fa686ab96d5f5c711789d55424866fa7d6c493beea380e44418053d86ec490b82c101008eac311463da84a9 + languageName: node + linkType: hard + +"source-map@npm:^0.5.0": + version: 0.5.7 + resolution: "source-map@npm:0.5.7" + checksum: 737face96577a2184a42f141607fcc2c9db5620cb8517ae8ab3924476defa138fc26b0bab31e98cbd6f19211ecbf78400b59f801ff7a0f87aa9faa79f7433e10 languageName: node linkType: hard -"source-map@npm:^0.6.0": +"source-map@npm:^0.6.0, source-map@npm:^0.6.1": version: 0.6.1 resolution: "source-map@npm:0.6.1" checksum: 8647829a0611724114022be455ca1c8a2c8ae61df81c5b3667d9b398207226a1e21174fb7bbf0b4dbeb27ac358222afb5a14f1c74a62a62b8883b012e5eb1270 languageName: node linkType: hard +"spawn-wrap@npm:^2.0.0": + version: 2.0.0 + resolution: "spawn-wrap@npm:2.0.0" + dependencies: + foreground-child: ^2.0.0 + is-windows: ^1.0.2 + make-dir: ^3.0.0 + rimraf: ^3.0.0 + signal-exit: ^3.0.2 + which: ^2.0.1 + checksum: 463a408825b6d25a8b45a0d248c95b7db59706792c73989cb9aa9bc2443e649295b7d2a05df38affb2a1b6523870e0cdb473945a0255fcc4bdcbfcd5a8e00679 + languageName: node + linkType: hard + "sprintf-js@npm:~1.0.2": version: 1.0.3 resolution: "sprintf-js@npm:1.0.3" @@ -4769,6 +6135,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"strip-bom@npm:^4.0.0": + version: 4.0.0 + resolution: "strip-bom@npm:4.0.0" + checksum: 25a231aacba2c6ecf37d7389721ff214c7f979e97407c935eeb41f5c5513c80119aada86049408feab74d22e7f1b29d90c942d4d47a4e47868dd16daed035823 + languageName: node + linkType: hard + "strip-color@npm:^0.1.0": version: 0.1.0 resolution: "strip-color@npm:0.1.0" @@ -4783,7 +6156,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1": +"strip-json-comments@npm:3.1.1, strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" checksum: f16719ce25abc58a55ef82b1c27f541dcfa5d544f17158f62d10be21ff9bd22fde45a53c592b29d80ad3c97ccb67b7451c4833913fdaeadb508a40f5e0a9c206 @@ -4804,6 +6177,15 @@ fsevents@~2.3.1: languageName: node linkType: hard +"supports-color@npm:8.1.1": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: ^4.0.0 + checksum: 0219f5c91753fea8dc8046cd4b18d39458b5dc0c6421c67c1072209faae9ba93b89283252e3b05d5c18901fd9f8b95001e3247fb93e2265f66d584a676522c75 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -4813,7 +6195,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"supports-color@npm:^7.1.0": +"supports-color@npm:^7.1.0, supports-color@npm:^7.2.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" dependencies: @@ -4896,6 +6278,17 @@ fsevents@~2.3.1: languageName: node linkType: hard +"test-exclude@npm:^6.0.0": + version: 6.0.0 + resolution: "test-exclude@npm:6.0.0" + dependencies: + "@istanbuljs/schema": ^0.1.2 + glob: ^7.1.4 + minimatch: ^3.0.4 + checksum: 68294d10066726cbced152aeb8a39cf9fd199199c62afb39290b824f613090f2535fc6acbad7d78f1f34cf00f4f00d42fa14f02d6262b910a7c9e2db2ecfa388 + languageName: node + linkType: hard + "text-table@npm:^0.2.0": version: 0.2.0 resolution: "text-table@npm:0.2.0" @@ -4936,6 +6329,13 @@ fsevents@~2.3.1: languageName: node linkType: hard +"to-fast-properties@npm:^2.0.0": + version: 2.0.0 + resolution: "to-fast-properties@npm:2.0.0" + checksum: 40e61984243b183d575a2f3a87d008bd57102115701ee9037fd673e34becf12ee90262631857410169ca82f401a662ed94482235cea8f3b8dea48b87eaabc467 + languageName: node + linkType: hard + "to-object-path@npm:^0.3.0": version: 0.3.0 resolution: "to-object-path@npm:0.3.0" @@ -4973,46 +6373,55 @@ fsevents@~2.3.1: languageName: node linkType: hard -"ts-node@npm:^9.1.1": - version: 9.1.1 - resolution: "ts-node@npm:9.1.1" +"ts-node@npm:^10.2.1": + version: 10.2.1 + resolution: "ts-node@npm:10.2.1" dependencies: + "@cspotcode/source-map-support": 0.6.1 + "@tsconfig/node10": ^1.0.7 + "@tsconfig/node12": ^1.0.7 + "@tsconfig/node14": ^1.0.0 + "@tsconfig/node16": ^1.0.2 + acorn: ^8.4.1 + acorn-walk: ^8.1.1 arg: ^4.1.0 create-require: ^1.1.0 diff: ^4.0.1 make-error: ^1.1.1 - source-map-support: ^0.5.17 yn: 3.1.1 peerDependencies: + "@swc/core": ">=1.2.50" + "@swc/wasm": ">=1.2.50" + "@types/node": "*" typescript: ">=2.7" + peerDependenciesMeta: + "@swc/core": + optional: true + "@swc/wasm": + optional: true bin: ts-node: dist/bin.js + ts-node-cwd: dist/bin-cwd.js ts-node-script: dist/bin-script.js ts-node-transpile-only: dist/bin-transpile.js ts-script: dist/bin-script-deprecated.js - checksum: a90db4a342872cd0e7a80babdfcb15d2f7c06e700d735003098f7cc79db575c3380580c58a19ae0d0eaab553af083651d4237060c92170e6f8ac4e64693113ea - languageName: node - linkType: hard - -"tsc-files@npm:^1.1.2": - version: 1.1.2 - resolution: "tsc-files@npm:1.1.2" - peerDependencies: - typescript: ">=3" - bin: - tsc-files: lib/index.js - checksum: 6e5a3a53f53b5b8d608fc1de9f1877137ea0e154b90f3985e03810c1c3125e4513a9f64b6b17d4463e4f21691a7c22280f93963829dd3931f98dcc3691ecd0a4 + checksum: 528e79c827198c6333830b7f6a92a9af8ac7932c0acb55db5376046cd2fabddf95ff2e1dff572d0175c68d0781f4401142625f3fa3a515124407b8670f3d3835 languageName: node linkType: hard -"tslib@npm:^1.8.1": - version: 1.13.0 - resolution: "tslib@npm:1.13.0" - checksum: 5dc3bdaea3b67c76ef4a14c28fcb2171da7bcf292fd9c59a260098729626b1ce766c52b588f08e324ed9a0c52ea8a93a815920f980d75981abc9d850fbf310fb +"ts-sinon@npm:^2.0.1": + version: 2.0.1 + resolution: "ts-sinon@npm:2.0.1" + dependencies: + "@types/node": ^14.6.1 + "@types/sinon": ^9.0.5 + "@types/sinon-chai": ^3.2.4 + sinon: ^9.0.3 + checksum: c274652c3e16393b1767b20c03c8631d3226b56b1f100e75e04fec0ea891cdc221415d953e9ad95d5f3e55e7c91c9b223c7b73620853ccba311d70f9d517b718 languageName: node linkType: hard -"tslib@npm:^1.9.0": +"tslib@npm:^1.8.1, tslib@npm:^1.9.0": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: f44fe7f216946b17d3e3074df3746372703cf24e9127b4c045511456e8e4bf25515fb0a1bb3937676cc305651c5d4fcb6377b0588a4c6a957e748c4c28905d17 @@ -5062,10 +6471,10 @@ fsevents@~2.3.1: languageName: node linkType: hard -"type-fest@npm:^0.11.0": - version: 0.11.0 - resolution: "type-fest@npm:0.11.0" - checksum: 02e5cadf13590a5724cacf8d9133320efd173f6fb1b695fcb29e56551a315bf0f07ca988a780a1999b7b55bb3eaaa7f37223615207236d393af17bba6749dc95 +"type-detect@npm:4.0.8, type-detect@npm:^4.0.0, type-detect@npm:^4.0.5, type-detect@npm:^4.0.8": + version: 4.0.8 + resolution: "type-detect@npm:4.0.8" + checksum: e01dc6ac9098192a7859fb86c7b4073709a4e13a5cc02c54d54412378bb099563fda7a7a85640f33e3a7c2e8189182eb1511f263e67f402b2d63fe81afdde785 languageName: node linkType: hard @@ -5083,7 +6492,7 @@ fsevents@~2.3.1: languageName: node linkType: hard -"type-fest@npm:^0.8.1": +"type-fest@npm:^0.8.0, type-fest@npm:^0.8.1": version: 0.8.1 resolution: "type-fest@npm:0.8.1" checksum: f8c4b4249f52e8bea7a4fc55b3653c96c2d547240e4c772e001d02b7cc38b8c3eb493ab9fbe985a76a203cd1aa7044776b728a71ba12bf36e7131f989597885b @@ -5104,6 +6513,15 @@ fsevents@~2.3.1: languageName: node linkType: hard +"typedarray-to-buffer@npm:^3.1.5": + version: 3.1.5 + resolution: "typedarray-to-buffer@npm:3.1.5" + dependencies: + is-typedarray: ^1.0.0 + checksum: e6e0e6812acc3496612d81abe026bb6c71bfc0f3daa00716a3236fe37c46a81508de8306df8a29ae81e2a2c4293b6b8067c77b65003e0022134d544902b9acec + languageName: node + linkType: hard + "typedarray@npm:^0.0.6": version: 0.0.6 resolution: "typedarray@npm:0.0.6" @@ -5131,6 +6549,15 @@ typescript@^4.2.3: languageName: node linkType: hard +"uint8arrays@npm:^3.0.0": + version: 3.0.0 + resolution: "uint8arrays@npm:3.0.0" + dependencies: + multiformats: ^9.4.2 + checksum: 75361a84ae731b69c84fe50d7ec649bf058b013cef9a2be2392b92fb8cac6a1f1ce98abadfbe448aef7125f1d5fd9140dc221c8a16e8f666ea1fbb20347059e1 + languageName: node + linkType: hard + "underscore@npm:>=1.3.1": version: 1.12.1 resolution: "underscore@npm:1.12.1" @@ -5147,6 +6574,17 @@ typescript@^4.2.3: languageName: node linkType: hard +"ursa-optional@npm:^0.10.1": + version: 0.10.2 + resolution: "ursa-optional@npm:0.10.2" + dependencies: + bindings: ^1.5.0 + nan: ^2.14.2 + node-gyp: latest + checksum: f832f0ae69d4e78eceb8132e082366864b9d2988f7ec1b357d6a62bf7ca63236d1ae5b1fccd910f3ffaeba8b9b39bc08d4a5934461f9a1e400271a9f5138a753 + languageName: node + linkType: hard + "utf8@npm:^3.0.0": version: 3.0.0 resolution: "utf8@npm:3.0.0" @@ -5161,7 +6599,7 @@ typescript@^4.2.3: languageName: node linkType: hard -"uuid@npm:^3.3.2": +"uuid@npm:^3.3.2, uuid@npm:^3.3.3": version: 3.4.0 resolution: "uuid@npm:3.4.0" bin: @@ -5213,7 +6651,14 @@ typescript@^4.2.3: languageName: node linkType: hard -"which@npm:^2.0.1, which@npm:^2.0.2": +"which-module@npm:^2.0.0": + version: 2.0.0 + resolution: "which-module@npm:2.0.0" + checksum: 3d2107ab18c3c2a0ffa4f1a2a0a8862d0bb3fd5c72b10df9cbd75a15b496533bf4c4dc6fa65cefba6fdb8af7935ffb939ef4c8f2eb7835b03d1b93680e9101e9 + languageName: node + linkType: hard + +"which@npm:2.0.2, which@npm:^2.0.1, which@npm:^2.0.2": version: 2.0.2 resolution: "which@npm:2.0.2" dependencies: @@ -5224,7 +6669,7 @@ typescript@^4.2.3: languageName: node linkType: hard -"wide-align@npm:^1.1.0": +"wide-align@npm:1.1.3, wide-align@npm:^1.1.0": version: 1.1.3 resolution: "wide-align@npm:1.1.3" dependencies: @@ -5252,6 +6697,13 @@ typescript@^4.2.3: languageName: node linkType: hard +"workerpool@npm:6.1.5": + version: 6.1.5 + resolution: "workerpool@npm:6.1.5" + checksum: aaf220c463d32c146887d28b45f0291e803c8c41d77a559ff525347f178b3b53b3b029a240bb92529a72771093f7cf5afdc81a28396ecc2f2f3179ad31d03456 + languageName: node + linkType: hard + "wrap-ansi@npm:^6.2.0": version: 6.2.0 resolution: "wrap-ansi@npm:6.2.0" @@ -5281,6 +6733,25 @@ typescript@^4.2.3: languageName: node linkType: hard +"write-file-atomic@npm:^3.0.0": + version: 3.0.3 + resolution: "write-file-atomic@npm:3.0.3" + dependencies: + imurmurhash: ^0.1.4 + is-typedarray: ^1.0.0 + signal-exit: ^3.0.2 + typedarray-to-buffer: ^3.1.5 + checksum: a26a8699c30cdc81d041b2c1049c6773f1e8401edda365874e9ca2dcf1fcf024dfeb43eea5e08c2e9b4e77be08a160d37f8d6c5d8c2d3ceccdf3d06e5cb38d35 + languageName: node + linkType: hard + +"y18n@npm:^4.0.0": + version: 4.0.3 + resolution: "y18n@npm:4.0.3" + checksum: e6d08e9d148e71d620acbca1c10f4db86a3a960527a47e8fbe732ea8246076d0a54e1f6adf0f8f8fdeacb87c23dea52382f4243bf736d36c83bb7f2ee0ea7fcd + languageName: node + linkType: hard + "y18n@npm:^5.0.5": version: 5.0.5 resolution: "y18n@npm:5.0.5" @@ -5302,6 +6773,23 @@ typescript@^4.2.3: languageName: node linkType: hard +"yargs-parser@npm:20.2.4": + version: 20.2.4 + resolution: "yargs-parser@npm:20.2.4" + checksum: 00dd0f23b608aa16962f1b73ac6c461ae6c97e8e85ad24b0c9adfeb5ef20a93a484ba858a6a3200f487478427db27d555d7772463d793d6c27b1b8b614cf3d7e + languageName: node + linkType: hard + +"yargs-parser@npm:^18.1.2": + version: 18.1.3 + resolution: "yargs-parser@npm:18.1.3" + dependencies: + camelcase: ^5.0.0 + decamelize: ^1.2.0 + checksum: 33871721679053cc38165afc6356c06c3e820459589b5db78f315886105070eb90cbb583cd6515fa4231937d60c80262ca2b7c486d5942576802446318a39597 + languageName: node + linkType: hard + "yargs-parser@npm:^20.2.2": version: 20.2.7 resolution: "yargs-parser@npm:20.2.7" @@ -5309,7 +6797,19 @@ typescript@^4.2.3: languageName: node linkType: hard -"yargs@npm:^16.2.0": +"yargs-unparser@npm:2.0.0": + version: 2.0.0 + resolution: "yargs-unparser@npm:2.0.0" + dependencies: + camelcase: ^6.0.0 + decamelize: ^4.0.0 + flat: ^5.0.2 + is-plain-obj: ^2.1.0 + checksum: afa83ec3fe1b32db279a3e928074697d9ee62907cd421bcea76a47ae5d92648bc046e04bc2198324c2ff8b2f631b907660209c5d9a7fa5160dbd5105d3eb2838 + languageName: node + linkType: hard + +"yargs@npm:16.2.0": version: 16.2.0 resolution: "yargs@npm:16.2.0" dependencies: @@ -5324,6 +6824,25 @@ typescript@^4.2.3: languageName: node linkType: hard +"yargs@npm:^15.0.2": + version: 15.4.1 + resolution: "yargs@npm:15.4.1" + dependencies: + cliui: ^6.0.0 + decamelize: ^1.2.0 + find-up: ^4.1.0 + get-caller-file: ^2.0.1 + require-directory: ^2.1.1 + require-main-filename: ^2.0.0 + set-blocking: ^2.0.0 + string-width: ^4.2.0 + which-module: ^2.0.0 + y18n: ^4.0.0 + yargs-parser: ^18.1.2 + checksum: dbf687d6b938f01bbf11e158dde6df906282b70cd9295af0217ee8cefbd83ad09d49fa9458d0d5325b0e66f03df954a38986db96f91e5b46ccdbbaf9a0157b23 + languageName: node + linkType: hard + "yargs@npm:^17.1.1": version: 17.1.1 resolution: "yargs@npm:17.1.1" @@ -5345,3 +6864,10 @@ typescript@^4.2.3: checksum: bff63b80568d80c711670935427494dde47cdf97e8b04196b140ce0af519c81c5ee857eddad0caa8b422dd65aea0157bbfaacbb1546bebba623f0f383d5d9ae5 languageName: node linkType: hard + +"yocto-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "yocto-queue@npm:0.1.0" + checksum: 096c3b40beb2804659539be1605a35c58eb0c85285f94b77b3e924f42ee265c1a40bf9f4153770039517146b469a964d51742395f35ca8135fc9f7e4982b785d + languageName: node + linkType: hard