From 81f06996f9849c955fc3cb5bb5394dc2088f5732 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 11 Apr 2021 22:36:35 -0700 Subject: [PATCH 001/112] first commit --- .gitignore | 116 ++ README.md | 98 ++ docker-compose.yml | 19 + env.example | 6 + package-lock.json | 1171 +++++++++++++++++ package.json | 33 + src/app.js | 26 + src/config.js | 38 + src/controllers/fungibleTokens.js | 82 ++ src/database/getLeastRecentAccountKey.js | 22 + .../migration.sql | 7 + src/database/migrations/migration_lock.toml | 3 + src/database/schema.prisma | 18 + src/database/syncAccountKeys.js | 51 + src/errors/ApiError.js | 11 + src/errors/InvalidFungibleTokenError.js | 13 + src/errors/NotFoundError.js | 13 + src/errors/catchAsync.js | 5 + src/index.js | 33 + src/lib/flow/crypto.js | 64 + src/lib/flow/getSigner.js | 32 + src/lib/flow/sendTransaction.js | 26 + src/lib/fungibleTokens/index.js | 28 + .../fungibleTokens/templates/transferFLOW.js | 30 + .../fungibleTokens/templates/transferFUSD.js | 31 + src/lib/fungibleTokens/transfer.js | 61 + src/logger.js | 26 + src/middleware/errors.js | 33 + src/middleware/morgan.js | 18 + src/routes/v1/fungibleTokens/index.js | 18 + src/routes/v1/fungibleTokens/withdrawals.js | 15 + src/routes/v1/index.js | 8 + 32 files changed, 2155 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 env.example create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/app.js create mode 100644 src/config.js create mode 100644 src/controllers/fungibleTokens.js create mode 100644 src/database/getLeastRecentAccountKey.js create mode 100644 src/database/migrations/20210412042610_create_account_keys/migration.sql create mode 100644 src/database/migrations/migration_lock.toml create mode 100644 src/database/schema.prisma create mode 100644 src/database/syncAccountKeys.js create mode 100644 src/errors/ApiError.js create mode 100644 src/errors/InvalidFungibleTokenError.js create mode 100644 src/errors/NotFoundError.js create mode 100644 src/errors/catchAsync.js create mode 100644 src/index.js create mode 100644 src/lib/flow/crypto.js create mode 100644 src/lib/flow/getSigner.js create mode 100644 src/lib/flow/sendTransaction.js create mode 100644 src/lib/fungibleTokens/index.js create mode 100644 src/lib/fungibleTokens/templates/transferFLOW.js create mode 100644 src/lib/fungibleTokens/templates/transferFUSD.js create mode 100644 src/lib/fungibleTokens/transfer.js create mode 100644 src/logger.js create mode 100644 src/middleware/errors.js create mode 100644 src/middleware/morgan.js create mode 100644 src/routes/v1/fungibleTokens/index.js create mode 100644 src/routes/v1/fungibleTokens/withdrawals.js create mode 100644 src/routes/v1/index.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1f22b9c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,116 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/README.md b/README.md new file mode 100644 index 00000000..89d7ad78 --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +# Flow Wallet API Demo (Node.js + express) + +This is a demonstration of a RESTful API that +implements a simple custodial wallet for the Flow blockchain. + +## Functionality + +- [x] Single admin account (hot wallet) +- [x] Fungible token withdrawals from admin account (FLOW, FUSD) +- [ ] Fungible token deposits from admin account (FLOW, FUSD) +- [ ] Fungible token withdrawals from user account (FLOW, FUSD) +- [ ] Fungible token deposits from user account (FLOW, FUSD) +- [ ] Non-fungible token withdrawals from admin account +- [ ] Non-fungible token deposits from admin account +- [ ] Non-fungible token withdrawals from user account +- [ ] Non-fungible token deposits from user account + +## API Routes + +### Fungible Tokens + +Supported tokens: +- `FLOW` +- `FUSD` + +#### List all tokens + +`GET /v1/fungible-tokens` + +**Example** + +```sh +curl --request GET \ + --url http://localhost:3000/v1/fungible-tokens +``` + +#### Get details of a token + +`GET /v1/fungible-tokens/{tokenName}` + +**Parameters** + +- `tokenName`: The name of the fungible token (e.g. FLOW) + +**Example** + +```sh +curl --request GET \ + --url http://localhost:3000/v1/fungible-tokens/flow +``` + +#### List all withdrawals of a token type + +`GET /v1/fungible-tokens/{tokenName}/withdrawals` + +**Parameters** + +- `tokenName`: The name of the fungible token (e.g. FLOW) + +:warning: Not implemented + +#### Get details of a token withdrawal + +`GET /v1/fungible-tokens/{tokenName}/withdrawals/{transactionId}` + +**Parameters** + +- `tokenName`: The name of the fungible token (e.g. FLOW) +- `transactionId`: The Flow transaction ID for the withdrawal + +:warning: Not implemented + +#### Create a token withdrawal + +`POST /v1/fungible-tokens/{tokenName}/withdrawals` + +**Parameters** + +- `tokenName`: The name of the fungible token (e.g. FLOW) + +**Body (JSON)** + +- `amount`: The number of tokens to transfer (e.g. "123.456") + - Must be a fixed-point number with a maximum of 8 decimal places +- `recipient`: The Flow address of the recipient (e.g. "0xf8d6e0586b0a20c7") + +**Example** + +```sh +curl --request GET \ + --url http://localhost:3000/v1/fungible-tokens \ + --header 'Content-Type: application/json' \ + --data '{ "recipient": "0xf8d6e0586b0a20c7", "amount": "123.456" }' +``` + +### Non-Fungible Tokens + +TODO diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..51e36395 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +version: "3" +services: + db: + image: postgres + ports: + - "5432:5432" + environment: + - POSTGRES_DB=wallet + - POSTGRES_USER=wallet + - POSTGRES_PASSWORD=wallet + emulator: + image: gcr.io/flow-container-registry/emulator:v0.16.1 + ports: + - "8080:8080" + - "3569:3569" + environment: + - FLOW_SERVICEPRIVATEKEY=${SIGNER_PRIVATE_KEY} + - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 + - FLOW_SERVICEKEYHASHALGO=SHA3_256 diff --git a/env.example b/env.example new file mode 100644 index 00000000..45258816 --- /dev/null +++ b/env.example @@ -0,0 +1,6 @@ +ACCESS_API_HOST=http://localhost:8080 + +DATABASE_URL="postgresql://wallet:wallet@localhost:5432/wallet?schema=public" + +SIGNER_ADDRESS=0xf8d6e0586b0a20c7 +SIGNER_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..ef0006a7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1171 @@ +{ + "name": "flow-wallet-nodejs-demo", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "@improbable-eng/grpc-web": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.12.0.tgz", + "integrity": "sha512-uJjgMPngreRTYPBuo6gswMj1gK39Wbqre/RgE0XnSDXJRg6ST7ZhuS53dFE6Vc2CX4jxgl+cO+0B3op8LA4Q0Q==", + "requires": { + "browser-headers": "^0.4.0" + } + }, + "@improbable-eng/grpc-web-node-http-transport": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web-node-http-transport/-/grpc-web-node-http-transport-0.12.0.tgz", + "integrity": "sha512-+Kjz+Dktfz5LKTZA9ZW/Vlww6HF9KaKz4x2mVe1O8CJdOP2WfzC+KY8L6EWMqVLrV4MvdBuQdSgDmvSJz+OGuA==" + }, + "@onflow/config": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@onflow/config/-/config-0.0.2.tgz", + "integrity": "sha512-H/+yrAalzEnMWkubiWsDdWytKSzd+OfRCddTlaRUelxfXhcfw2QWegH9N8EzeKfKXcQ6PLzvu9vQwhFxCZTE8Q==", + "requires": { + "@onflow/util-actor": "0.0.2" + } + }, + "@onflow/decode": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@onflow/decode/-/decode-0.0.10.tgz", + "integrity": "sha512-+eInaMNzMtoA2Fkt27HW/GYJdqt7GjIW0gYDpkK6B5AYoRLj1NZTAIX0wV5BUoLCecMGzS1OVML79FxIwLPowQ==" + }, + "@onflow/encode": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@onflow/encode/-/encode-0.0.8.tgz", + "integrity": "sha512-I7p+/KNzsNCpA+AfIOKRKDnkc7kSW4Nr6Hapamh4ak8SDcNfcO4j8Dx+nTsRwhBuo/R/4XpxMpodC6V7+Vmw0w==", + "requires": { + "@onflow/rlp": "0.0.3" + } + }, + "@onflow/fcl": { + "version": "0.0.67", + "resolved": "https://registry.npmjs.org/@onflow/fcl/-/fcl-0.0.67.tgz", + "integrity": "sha512-A/1YD+eDx6+mjqENR0bvWSPPww99LYDlLM9jzFjxIyju58paHPCMA7Rav9nQVHfxWMCRAnkbK0yPanUPEyeaww==", + "requires": { + "@onflow/config": "0.0.2", + "@onflow/interaction": "0.0.10", + "@onflow/rlp": "0.0.3", + "@onflow/sdk-account": "0.0.8", + "@onflow/sdk-build-arguments": "0.0.0", + "@onflow/sdk-build-authorizations": "0.0.1", + "@onflow/sdk-build-get-account": "0.0.1", + "@onflow/sdk-build-get-block-by-height": "0.0.0", + "@onflow/sdk-build-get-block-by-id": "0.0.0", + "@onflow/sdk-build-get-events": "0.0.0", + "@onflow/sdk-build-get-latest-block": "0.0.0", + "@onflow/sdk-build-invariant": "0.0.0", + "@onflow/sdk-build-limit": "0.0.0", + "@onflow/sdk-build-params": "0.0.0", + "@onflow/sdk-build-payer": "0.0.0", + "@onflow/sdk-build-ping": "0.0.0", + "@onflow/sdk-build-proposer": "0.0.0", + "@onflow/sdk-build-ref": "0.0.0", + "@onflow/sdk-build-script": "0.0.0", + "@onflow/sdk-build-transaction": "0.0.0", + "@onflow/sdk-build-transaction-status": "0.0.0", + "@onflow/sdk-build-validator": "0.0.0", + "@onflow/sdk-decode": "0.0.2", + "@onflow/sdk-latest-block": "0.0.8", + "@onflow/sdk-resolve": "0.0.10", + "@onflow/sdk-send": "0.0.10", + "@onflow/util-actor": "0.0.2", + "@onflow/util-address": "0.0.0", + "@onflow/util-invariant": "0.0.0", + "@onflow/util-template": "0.0.1", + "@onflow/util-uid": "0.0.1" + } + }, + "@onflow/interaction": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@onflow/interaction/-/interaction-0.0.10.tgz", + "integrity": "sha512-3+Nhjn8zZjAXpxH+MuHpl4ZR0s9eQl4aZqmjat2sekDTNNl1dYhaWs+RT5CSxzehGzJ2W+aL7JxFn6exFoA0zA==" + }, + "@onflow/protobuf": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@onflow/protobuf/-/protobuf-0.1.8.tgz", + "integrity": "sha512-Taww31RrpJMr3fkWgF2HR2TcRbAoPbMjwRYt9IlYknyRX5YTzpaU35petdqDRfxwmfY9mRxHbiInujX4aV02Hw==", + "requires": { + "@improbable-eng/grpc-web": "^0.12.0", + "google-protobuf": "^3.11.4" + } + }, + "@onflow/response": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@onflow/response/-/response-0.0.2.tgz", + "integrity": "sha512-k5UD4nv9F6YAkDn0Gs0F6R2Izr6jekX9sGu6aPbTQCsfyv8kbG8GRhnfcugtwbGfEKmYO4Dj91QtWy246V9sUQ==" + }, + "@onflow/rlp": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@onflow/rlp/-/rlp-0.0.3.tgz", + "integrity": "sha512-oAf0VEiMjX8eC6Vd66j1BdGYTHOM6UBaS/sLSScnc7bKX5gICqe2gtEsCeJVE9rUzRk3GD3JqXRnPAW6YFWd/Q==" + }, + "@onflow/sdk-account": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@onflow/sdk-account/-/sdk-account-0.0.8.tgz", + "integrity": "sha512-fa0zJkb7xasvfCWyo1WqZNomAV4vKulhp9br0+E08cwdSJWcH2CrgWz75Q67YFpdnflhP0n3RzrAREcXt6Y54Q==", + "requires": { + "@onflow/sdk-build-get-account": "0.0.1", + "@onflow/sdk-decode": "0.0.2", + "@onflow/sdk-send": "0.0.10" + } + }, + "@onflow/sdk-build-arguments": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-arguments/-/sdk-build-arguments-0.0.0.tgz", + "integrity": "sha512-CAUboKBIIfpVpoZFGIHa7vGtTXuAeE/YRyyDGtdlvQcu+7AqumJ0Sz4MohUY+DyvcSTC9FdNs/d5XicS3EIo2A==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-authorizations": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-authorizations/-/sdk-build-authorizations-0.0.1.tgz", + "integrity": "sha512-N46d9Hv4Jtrb159+HPq9R8RCkao4DnUmbZYIxxM8rkuTUdRluDC52SKS1to9l0WnhJm5+div8E+dSVIg1MBfnw==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-get-account": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-get-account/-/sdk-build-get-account-0.0.1.tgz", + "integrity": "sha512-BOTLsAqhXwwRj9TYDmV40yBKHRWBToFY1WvJKUOI05oZBOBqDb2KB0L5ej3uKpA/XdmIH4HDmRMmxWU01Xy+FA==", + "requires": { + "@onflow/interaction": "0.0.10", + "@onflow/util-address": "0.0.0" + } + }, + "@onflow/sdk-build-get-block-by-height": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-get-block-by-height/-/sdk-build-get-block-by-height-0.0.0.tgz", + "integrity": "sha512-Efvnzj+eWRTp76RHH3z25G0R+3N8b5oK/addfyjsh5hK61Mk9DAN9JgHQdeglTWgNaOJ0dKrzBZ/SJP2Uz9RHA==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-get-block-by-id": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-get-block-by-id/-/sdk-build-get-block-by-id-0.0.0.tgz", + "integrity": "sha512-Zw5Gt9NNVUn9CrSUE+SpJO80HL3yUnFhZZcQHw3WvaTb3hDYpDlGs9ldPjYI+SrNxMJAg3KD5OzFnKHMEnmQLw==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-get-events": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-get-events/-/sdk-build-get-events-0.0.0.tgz", + "integrity": "sha512-xABLsjs2BftKezH2mc34k6GVIXgmUjdv5bY2MFQI3btkVXn/vVbg/WEB/JNOmXl4dIvZXFMD9ANncS6n0PvcSw==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-get-latest-block": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-get-latest-block/-/sdk-build-get-latest-block-0.0.0.tgz", + "integrity": "sha512-RnUkH3gqStboZ6pQXTeGq7CrMwaqD2p+z4rHqBlcbLF8pAiJQgFLDigQ2vUxrwkMJmnNqi3lJfMzX58dgDa2JA==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-invariant": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-invariant/-/sdk-build-invariant-0.0.0.tgz", + "integrity": "sha512-eB1Jf6NZQ7wKQMudc2a6T03/SFH3OyVm67mKzU+gWXOUBZwh2GZfaW8ujchrYwy0tFyyqzTP1nqEFws8cQyvJg==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-limit": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-limit/-/sdk-build-limit-0.0.0.tgz", + "integrity": "sha512-HDl6UD7M6QgkII8tMlVgYeHO8WxkjzeQq/d16ZBeux5IGA0jd3mdV0sZSB01n6dio82Ap+vW79uIgM3e1urs5A==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-params": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-params/-/sdk-build-params-0.0.0.tgz", + "integrity": "sha512-Y6TT0MbkzsemAOuwDoCkhUF286krqndGS9bzQ+ipaykGz6ibKDguXGzPCq+eRLA8+uVMsEWcUJ/UE6YxzuwUeg==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-payer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-payer/-/sdk-build-payer-0.0.0.tgz", + "integrity": "sha512-siVQV8XE1l94Bqotx2thB59rTWkfTBuvyVOY7oj1hibYMBcyg+xQozZRJEZEV8H5aQOXkSmP7JXlutfqqFqt0Q==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-ping": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-ping/-/sdk-build-ping-0.0.0.tgz", + "integrity": "sha512-NRcg5drVzBkviK339t3KQL6yIN5JuO9xsTlALgAgPduABv1RfxdNCRSxWtxfqozNSz8c11ki+KTdZHJIwmBZAg==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-proposer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-proposer/-/sdk-build-proposer-0.0.0.tgz", + "integrity": "sha512-voEjh9ioG/R4EKFx9QWOEplhYOfB5ddwuoYQq5/0R6hLKnylnnTFvnphtWIMlcwhOJWM17ViKyozvucS0MhlyQ==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-ref": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-ref/-/sdk-build-ref-0.0.0.tgz", + "integrity": "sha512-+5bPNaOWnNeB+6vSBgBW9ojg6pkK8nvPwqMWvJQ9nVKPhsCXDO8Y28PtFjX0/vWbt+lRR4vfgdbG8fGfLV7Nzg==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-script": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-script/-/sdk-build-script-0.0.0.tgz", + "integrity": "sha512-r7CG+Q9svfTUykH61t4epo4D290NYvKlryQ+Csjot5FGTsvssXEwELRqiUOkONW7KOH+GpDG7LRQlikTgnnREQ==", + "requires": { + "@onflow/interaction": "0.0.10", + "@onflow/util-template": "0.0.1" + } + }, + "@onflow/sdk-build-transaction": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-transaction/-/sdk-build-transaction-0.0.0.tgz", + "integrity": "sha512-19S6QB2LcFxOm5o91A8Q4Pcfdrk1bdvnasyNdG1Cel/SwKLPUoHmCoelFCHbZkIYlwnEdLQZm11plQIfo70o6w==", + "requires": { + "@onflow/interaction": "0.0.10", + "@onflow/util-template": "0.0.1" + } + }, + "@onflow/sdk-build-transaction-status": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-transaction-status/-/sdk-build-transaction-status-0.0.0.tgz", + "integrity": "sha512-hj1AJgJsK/PVmY5s7gzsqoekzswFudI7VWj1oee99NKqM+BRPGH7HBTuEIYP1PZZ7XCW1KsDy54QbqPfMmeIUQ==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-build-validator": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-build-validator/-/sdk-build-validator-0.0.0.tgz", + "integrity": "sha512-ZgYappRaqmDXKOP9By1ZODa3iQu6xlHN0I53UZ5HgLtQ8MXsVCvpsNR7Zj9rYeWdlfJDglQeWORmhluw7zssSw==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-decode": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@onflow/sdk-decode/-/sdk-decode-0.0.2.tgz", + "integrity": "sha512-qqz29UaGEVVmfols5VRUODL3m73rURs/VAEDbCFjlrGn2kPRCUOcYofazge88jYY/9rwYuDvueDEVDg4iLV2+A==", + "requires": { + "@onflow/config": "0.0.2", + "@onflow/decode": "0.0.10" + } + }, + "@onflow/sdk-latest-block": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@onflow/sdk-latest-block/-/sdk-latest-block-0.0.8.tgz", + "integrity": "sha512-zBuETIqF+1KIi9C4BOUp1zKmrUdLHdYHSnVeV5rgS+90GNy5f3nH3xsWXKg74KRs3ZD0QgoJRkf3zTODuHpjmw==", + "requires": { + "@onflow/sdk-build-get-latest-block": "0.0.0", + "@onflow/sdk-decode": "0.0.2", + "@onflow/sdk-send": "0.0.10" + } + }, + "@onflow/sdk-resolve": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve/-/sdk-resolve-0.0.10.tgz", + "integrity": "sha512-SnlAZfenZWoWasLSQepnMljTEe7T9GWYUwR1VgF0/tXxnjDtnbhH9hH2WDp1Ioyev8DA7V3Jqd4qygj+fOsnMg==", + "requires": { + "@onflow/interaction": "0.0.10", + "@onflow/sdk-resolve-accounts": "0.0.0", + "@onflow/sdk-resolve-arguments": "0.0.1", + "@onflow/sdk-resolve-cadence": "0.0.1", + "@onflow/sdk-resolve-ref-block-id": "0.0.7", + "@onflow/sdk-resolve-signatures": "0.0.5", + "@onflow/sdk-resolve-validators": "0.0.0" + } + }, + "@onflow/sdk-resolve-accounts": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-accounts/-/sdk-resolve-accounts-0.0.0.tgz", + "integrity": "sha512-IrYWNC8Am/b8dznsbuh+/LLTkaXrkjsFMnRDc7ANFC7wZoukWv3Wwf2U/5ybtaTAJax5SF/SNgLeS4Inx60uOA==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-resolve-arguments": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-arguments/-/sdk-resolve-arguments-0.0.1.tgz", + "integrity": "sha512-CbGEZo28l8uaHt8oA5O7yVOOdakjTFXICQCtBDnT+z8tEvDaqIR29rgE9uzXbDnPSCSxHSsxttO1nxQ8DzDEOQ==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-resolve-cadence": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-cadence/-/sdk-resolve-cadence-0.0.1.tgz", + "integrity": "sha512-nZf5JwwjCcG55GaB325Y5PwyhYq6PRo1eVLz3rxFP8RB3LTxaPJwuPK8QD5wfeGumM/7UvtbvuEBZwEqN9eKtQ==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-resolve-ref-block-id": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-ref-block-id/-/sdk-resolve-ref-block-id-0.0.7.tgz", + "integrity": "sha512-Q+eD27TgpIiMu8FXZ0tBCZCDRwWmtc85dKNuJZlqLHl8aZ/5H7BnNpTaj2O6TWzJAXakeBtDIdHz8Zeb1to5oA==", + "requires": { + "@onflow/decode": "0.0.10", + "@onflow/interaction": "0.0.10", + "@onflow/sdk-build-get-latest-block": "0.0.0", + "@onflow/send": "0.0.35" + } + }, + "@onflow/sdk-resolve-signatures": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-signatures/-/sdk-resolve-signatures-0.0.5.tgz", + "integrity": "sha512-rh/8ZVS3O3bMQSwVYfxewSLVVf5zUmcaCGR9AufPxZJYCH7YCrjbYzkEpsTZKUNxn4uzxp3X4gsPLvzKyZuiMQ==", + "requires": { + "@onflow/encode": "0.0.8", + "@onflow/interaction": "0.0.10", + "@onflow/util-address": "0.0.0" + } + }, + "@onflow/sdk-resolve-validators": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-validators/-/sdk-resolve-validators-0.0.0.tgz", + "integrity": "sha512-OiC6o8VgzZ2mn1qD3Bi12zbXk9AS8ALoLg4X8XnYJqe1HwCBVaAAy+dFL8jI7SVvgqOINTGxcz+6cF4UvfZUPw==", + "requires": { + "@onflow/interaction": "0.0.10" + } + }, + "@onflow/sdk-send": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@onflow/sdk-send/-/sdk-send-0.0.10.tgz", + "integrity": "sha512-jzJuLsEKnW6lsP2gxcgF5LPW2HVjnClSKGo5Y+w8VQdgGvWVWLkMqUMJF3Pf/X6pQ1oWXfFu9UNioRKQnFW2ZQ==", + "requires": { + "@onflow/config": "0.0.2", + "@onflow/interaction": "0.0.10", + "@onflow/sdk-resolve": "0.0.10", + "@onflow/send": "0.0.35" + } + }, + "@onflow/send": { + "version": "0.0.35", + "resolved": "https://registry.npmjs.org/@onflow/send/-/send-0.0.35.tgz", + "integrity": "sha512-yJbpC8tF1SOaBpNWi2CEoGQSSO/sH8+9VBWwbxolc4xUwmyl8F+1WpYdXesOSDQFCzSP5womt3xKYWG4LF7FGw==", + "requires": { + "@improbable-eng/grpc-web": "0.12.0", + "@improbable-eng/grpc-web-node-http-transport": "0.12.0", + "@onflow/config": "0.0.2", + "@onflow/interaction": "0.0.10", + "@onflow/protobuf": "0.1.8", + "@onflow/response": "0.0.2", + "@onflow/util-address": "^0.0.0" + } + }, + "@onflow/types": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@onflow/types/-/types-0.0.4.tgz", + "integrity": "sha512-47r0qTM45mR7FSvyEE0njGljkpgy3MCf/2W82pfZ/PTFGWr1nsGV/4j2bUfK9Sry4Ymz2W20iRZVP3xpqOhqGw==" + }, + "@onflow/util-actor": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@onflow/util-actor/-/util-actor-0.0.2.tgz", + "integrity": "sha512-NV3zPXQue3FqVgcIIMo6ifJOiP3hVSQTaR4ZrWLFU5iAZ/L73cTtBMbCB4BUFOe20ALtF2c9PFmpNVowCYV+nw==", + "requires": { + "queue-microtask": "1.1.2" + } + }, + "@onflow/util-address": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/util-address/-/util-address-0.0.0.tgz", + "integrity": "sha512-Lzbw/wV3O1fmfXYF2q6iGLgHz/7ATsLXOHceP5tzeEAKNf+srdtTNJv5jhNGhpFFAtQ6TcomXURVMhUg+/4YbA==" + }, + "@onflow/util-invariant": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@onflow/util-invariant/-/util-invariant-0.0.0.tgz", + "integrity": "sha512-ZCt+NqLdeHt9tZhb0DGxo6iSIS9oNUpLkd0PEMzUYUHr4UwrUO7VruV1AUW3PaF9V78DZ13fNZUiQEzdF76O/w==" + }, + "@onflow/util-template": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@onflow/util-template/-/util-template-0.0.1.tgz", + "integrity": "sha512-qlJ0oq+QujnZRCOGYaw5OKSDpiDIOpwQMYlMe4Mbz//Wn+LOmUghoKZGmRP+YCgp7BJ4aB6AWW/7kL83NDy50A==" + }, + "@onflow/util-uid": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@onflow/util-uid/-/util-uid-0.0.1.tgz", + "integrity": "sha512-SzBscBdyn1Zoks0Wo/w7J/Ds9IZ/T+KM/wyWMwWla4PnxwBFviy1BytEQY+sM5q1UNOvaGWgGEoRmH/oOCcglA==" + }, + "@prisma/client": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.20.1.tgz", + "integrity": "sha512-/IYPubBi55rNMHfE0wwglA6eTWEZD77oz+x+3Mm9ji2lDKdS1lnYKZ0wZX0E3AB8gTNL/zsGtfzmfjgn3ePyIw==", + "requires": { + "@prisma/engines-version": "2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e" + } + }, + "@prisma/engines": { + "version": "2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e.tgz", + "integrity": "sha512-zOWETm7DTRvlwf/CekPNSeJe6EC5bn2IFexd74wM9zgBXCZo+1sMDuNGtCqIt4Rzv8CcimEgyzrEFVq0LPV8qg==", + "dev": true + }, + "@prisma/engines-version": { + "version": "2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e.tgz", + "integrity": "sha512-fJhbGZXm2SPs/RsI79Ew4SFe+6QmChNdgU2I/SIjmU18bUgK8f1TBEWnVtFdBqEDHYPGxbpaianF7lp04KN7EA==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-headers": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/browser-headers/-/browser-headers-0.4.1.tgz", + "integrity": "sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==" + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "dedent-js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz", + "integrity": "sha1-vuX7fJ5yfYXf+iRZDRDsGrElUwU=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + } + } + }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + } + } + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, + "fecha": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", + "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "google-protobuf": { + "version": "3.15.7", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.7.tgz", + "integrity": "sha512-S/kTHcT98AV2FxEwtT5lvgffeS87BB6hloZm+pYKkpzwtySwNiKcqXZbxpq/Odh3Wib1RdOe/oY2EHdi17YrlQ==" + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-status": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-status/-/http-status-1.5.0.tgz", + "integrity": "sha512-wcGvY31MpFNHIkUcXHHnvrE4IKYlpvitJw5P/1u892gMBAM46muQ+RH7UN1d+Ntnfx5apnOnVY6vcLmrWHOLwg==" + }, + "http-status-codes": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.1.4.tgz", + "integrity": "sha512-MZVIsLKGVOVE1KEnldppe6Ij+vmemMuApDfjhVSLzyYP+td0bREEYyAoIw9yFePoBXManCuBqmiNP5FqJS5Xkg==" + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "logform": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", + "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" + }, + "mime-types": { + "version": "2.1.30", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", + "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "requires": { + "mime-db": "1.47.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "requires": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "requires": { + "fn.name": "1.x.x" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "prisma": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-2.20.1.tgz", + "integrity": "sha512-zyPvJSUfJrmciP2D/4aUrsyIefiH8AIJUeuq1a0X1df1AFw9QQ+ata/7VQdoP+RIQHnCb6Kln9kqfUw/fieljw==", + "dev": true, + "requires": { + "@prisma/engines": "2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "queue-microtask": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.1.2.tgz", + "integrity": "sha512-F9wwNePtXrzZenAB3ax0Y8TSKGvuB7Qw16J30hspEUTbfUM+H827XyN3rlpwhVmtm5wuZtbKIHjOnwDn7MUxWQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sha3": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", + "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", + "requires": { + "buffer": "6.0.3" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "winston": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "requires": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + } + }, + "winston-transport": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", + "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "requires": { + "readable-stream": "^2.3.7", + "triple-beam": "^1.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..f354c2c6 --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "flow-wallet-nodejs-demo", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./src/index.js", + "db-migrate-dev": "prisma migrate dev --preview-feature", + "db-migrate-deploy": "prisma migrate deploy --preview-feature", + "db-sync-account-keys": "node ./src/database/syncAccountKeys.js" + }, + "prisma": { + "schema": "src/database/schema.prisma" + }, + "dependencies": { + "@onflow/fcl": "0.0.67", + "@onflow/types": "0.0.4", + "@prisma/client": "^2.20.1", + "debug": "~2.6.9", + "dedent-js": "^1.0.1", + "dotenv": "^8.2.0", + "elliptic": "^6.5.4", + "express": "~4.16.1", + "http-errors": "~1.6.3", + "http-status": "^1.5.0", + "http-status-codes": "^2.1.4", + "morgan": "~1.9.1", + "sha3": "^2.1.4", + "winston": "^3.3.3" + }, + "devDependencies": { + "prisma": "^2.20.1" + } +} diff --git a/src/app.js b/src/app.js new file mode 100644 index 00000000..a68a92df --- /dev/null +++ b/src/app.js @@ -0,0 +1,26 @@ +require('dotenv').config() + +const express = require('express'); +const morganMiddleware = require('./middleware/morgan'); +const errorsMiddleware = require('./middleware/errors'); +const NotFoundError = require('./errors/NotFoundError'); + +const routes = require('./routes/v1'); + +const app = express(); + +app.use(morganMiddleware); +app.use(express.json()); +app.use(express.urlencoded({ extended: false })); + +app.use('/v1', routes); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + next(new NotFoundError()); +}); + +// error handler +app.use(errorsMiddleware); + +module.exports = app; diff --git a/src/config.js b/src/config.js new file mode 100644 index 00000000..d017179f --- /dev/null +++ b/src/config.js @@ -0,0 +1,38 @@ +const chainEmulator = "emulator" +const chainTestnet = "testnet" + +const contractsEmulator = { + FlowToken: "0x0ae53cb6e3f42a79", + FungibleToken: "0xee82856bf20e2aa6", + FUSD: "0xf8d6e0586b0a20c7", +} + +const contractsTestnet = { + FlowToken: "0x7e60df042a9c0868", + FungibleToken: "0x9a0766d93b6608b7", + FUSD: "0xe223d8a629e49c68", +} + +function getContracts(chain) { + chain = chain || chainEmulator + + switch (chain) { + case chainEmulator: + return contractsEmulator + case chainTestnet: + return contractsTestnet + } + + throw `Invalid chain: ${chain}` +} + +module.exports = { + env: process.env.NODE_ENV, + port: process.env.PORT || 3000, + accessApiHost: process.env.ACCESS_API_HOST || "http://localhost:8080", + signerAddress: process.env.SIGNER_ADDRESS || "0xf8d6e0586b0a20c7", + signerPrivateKey: process.env.SIGNER_PRIVATE_KEY, + signerSigAlgo: process.env.SIGNER_SIG_ALGO || "ECDSA_P256", + signerHashAlgo: process.env.SIGNER_HASH_ALGO || "SHA3_256", + contracts: getContracts(process.env.CHAIN), +} diff --git a/src/controllers/fungibleTokens.js b/src/controllers/fungibleTokens.js new file mode 100644 index 00000000..d52094b3 --- /dev/null +++ b/src/controllers/fungibleTokens.js @@ -0,0 +1,82 @@ +const httpStatus = require('http-status'); +const config = require('../config'); +const { tokens, isValidToken, getTokenTransferFunc } = require('../lib/fungibleTokens'); +const getSigner = require('../lib/flow/getSigner'); +const getLeastRecentAccountKey = require('../database/getLeastRecentAccountKey'); +const catchAsync = require('../errors/catchAsync'); +const ApiError = require('../errors/ApiError'); +const InvalidFungibleTokenError = require('../errors/InvalidFungibleTokenError'); + +const makeToken = (tokenName) => ({ name: tokenName }); +const allTokens = tokens.map((tokenName) => makeToken(tokenName)); + +const getTokens = catchAsync(async (req, res) => res.json(allTokens)); + +const getToken = catchAsync(async (req, res) => { + const tokenName = req.params.tokenName; + + if (!isValidToken(tokenName)) { + throw new InvalidFungibleTokenError(tokenName); + } + + const token = makeToken(tokenName); + + res.json(token); +}); + +// TODO: implement withdrawal getters +const getWithdrawals = catchAsync(async (req, res) => { res.send("TODO: implement me") }); +const getWithdrawal = catchAsync(async (req, res) => { res.send("TODO: implement me") }); + +const createWithdrawal = catchAsync(async (req, res) => { + const tokenName = req.params.tokenName; + + if (!isValidToken(tokenName)) { + throw new InvalidFungibleTokenError(tokenName); + } + + // TODO: validate recipient and amount + const { recipient, amount } = req.body; + + const signerKeyIndex = await getLeastRecentAccountKey(); + + const signer = getSigner( + config.signerAddress, + config.signerPrivateKey, + config.signerSigAlgo, + config.signerHashAlgo, + signerKeyIndex, + ) + + const transfer = getTokenTransferFunc(tokenName) + + try { + const transactionId = await transfer( + recipient, + amount, + signer, + config.contracts, + ) + + const response = { + transactionId, + recipient, + amount, + } + + res.json(response); + } catch (e) { + throw new ApiError( + httpStatus.INTERNAL_SERVER_ERROR, + 'failed to complete withdrawal', + ) + } +}); + +module.exports = { + getTokens, + getToken, + getWithdrawals, + getWithdrawal, + createWithdrawal, +}; diff --git a/src/database/getLeastRecentAccountKey.js b/src/database/getLeastRecentAccountKey.js new file mode 100644 index 00000000..ae96a34f --- /dev/null +++ b/src/database/getLeastRecentAccountKey.js @@ -0,0 +1,22 @@ +const {PrismaClient} = require("@prisma/client"); + +const prisma = new PrismaClient() + +const getLeastRecentAccountKeySql = ` +UPDATE account_keys +SET last_used_at = current_timestamp +WHERE index = ( + SELECT index + FROM account_keys + ORDER BY last_used_at + LIMIT 1 +) +RETURNING index +` + +const getLeastRecentAccountKey = async () => { + const results = await prisma.$queryRaw(getLeastRecentAccountKeySql) + return results[0].index +} + +module.exports = getLeastRecentAccountKey; diff --git a/src/database/migrations/20210412042610_create_account_keys/migration.sql b/src/database/migrations/20210412042610_create_account_keys/migration.sql new file mode 100644 index 00000000..cf8e12d0 --- /dev/null +++ b/src/database/migrations/20210412042610_create_account_keys/migration.sql @@ -0,0 +1,7 @@ +-- CreateTable +CREATE TABLE "account_keys" ( + "index" INTEGER NOT NULL, + "last_used_at" TIMESTAMP(3) NOT NULL, + + PRIMARY KEY ("index") +); diff --git a/src/database/migrations/migration_lock.toml b/src/database/migrations/migration_lock.toml new file mode 100644 index 00000000..fbffa92c --- /dev/null +++ b/src/database/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/src/database/schema.prisma b/src/database/schema.prisma new file mode 100644 index 00000000..9016c317 --- /dev/null +++ b/src/database/schema.prisma @@ -0,0 +1,18 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model AccountKey { + @@map(name: "account_keys") + + index Int @id + last_used_at DateTime @updatedAt +} diff --git a/src/database/syncAccountKeys.js b/src/database/syncAccountKeys.js new file mode 100644 index 00000000..e9317f12 --- /dev/null +++ b/src/database/syncAccountKeys.js @@ -0,0 +1,51 @@ +const fcl = require('@onflow/fcl'); +const dotenv = require('dotenv'); + +dotenv.config() + +const { PrismaClient } = require('@prisma/client'); + +const prisma = new PrismaClient() + +const signerAddress = process.env.SIGNER_ADDRESS +const accessAPIHost = process.env.ACCESS_API_HOST + +fcl.config().put("accessNode.api", accessAPIHost) + +async function getAccount(address) { + const { account } = await fcl.send([fcl.getAccount(address)]) + return account +} + +async function main() { + const account = await getAccount(signerAddress) + + console.log(`Fetched account information for ${signerAddress} from ${accessAPIHost}\n`) + + // truncate existing keys + const { count } = await prisma.accountKey.deleteMany({where: {}}) + + console.log(`Removed ${count} existing key(s) from DB\n`) + + await Promise.all( + account.keys.map(async key => { + + console.log(`- Inserting key ${key.index}`) + + await prisma.accountKey.create({ + data: { index: key.index }, + }) + + }) + ) + + console.log(`\nInserted ${account.keys.length} key(s) into DB`) +} + +main() + .catch(e => { + throw e + }) + .finally(async () => { + await prisma.$disconnect() + }) diff --git a/src/errors/ApiError.js b/src/errors/ApiError.js new file mode 100644 index 00000000..10728b6d --- /dev/null +++ b/src/errors/ApiError.js @@ -0,0 +1,11 @@ +class ApiError extends Error { + constructor(statusCode, message) { + super(message); + + this.statusCode = statusCode; + + Error.captureStackTrace(this, this.constructor); + } +} + +module.exports = ApiError; diff --git a/src/errors/InvalidFungibleTokenError.js b/src/errors/InvalidFungibleTokenError.js new file mode 100644 index 00000000..919eb6b0 --- /dev/null +++ b/src/errors/InvalidFungibleTokenError.js @@ -0,0 +1,13 @@ +const { StatusCodes } = require('http-status-codes'); +const ApiError = require("./ApiError"); + +class InvalidFungibleTokenError extends ApiError { + constructor(token) { + const statusCode = StatusCodes.BAD_REQUEST; + const message = `${token} is not valid fungible token`; + + super(statusCode, message); + } +} + +module.exports = InvalidFungibleTokenError; diff --git a/src/errors/NotFoundError.js b/src/errors/NotFoundError.js new file mode 100644 index 00000000..05083a8b --- /dev/null +++ b/src/errors/NotFoundError.js @@ -0,0 +1,13 @@ +const { StatusCodes } = require('http-status-codes'); +const ApiError = require("./ApiError"); + +class NotFoundError extends ApiError { + constructor() { + const statusCode = StatusCodes.NOT_FOUND; + const message = "not found"; + + super(statusCode, message); + } +} + +module.exports = NotFoundError; diff --git a/src/errors/catchAsync.js b/src/errors/catchAsync.js new file mode 100644 index 00000000..387efc6d --- /dev/null +++ b/src/errors/catchAsync.js @@ -0,0 +1,5 @@ +const catchAsync = (fn) => (req, res, next) => { + Promise.resolve(fn(req, res, next)).catch((err) => next(err)); +}; + +module.exports = catchAsync; diff --git a/src/index.js b/src/index.js new file mode 100644 index 00000000..1490968f --- /dev/null +++ b/src/index.js @@ -0,0 +1,33 @@ +const app = require('./app'); +const config = require('./config'); +const logger = require('./logger'); + +const server = app.listen(config.port, () => { + logger.info(`Listening to port ${config.port}`); +}); + +const exitHandler = () => { + if (server) { + server.close(() => { + logger.info('Server closed'); + process.exit(1); + }); + } else { + process.exit(1); + } +}; + +const unexpectedErrorHandler = (error) => { + logger.error(error); + exitHandler(); +}; + +process.on('uncaughtException', unexpectedErrorHandler); +process.on('unhandledRejection', unexpectedErrorHandler); + +process.on('SIGTERM', () => { + logger.info('SIGTERM received'); + if (server) { + server.close(); + } +}); diff --git a/src/lib/flow/crypto.js b/src/lib/flow/crypto.js new file mode 100644 index 00000000..88b50372 --- /dev/null +++ b/src/lib/flow/crypto.js @@ -0,0 +1,64 @@ +const {createHash} = require("crypto"); +const {SHA3} = require("sha3"); +const {ec: EC} = require("elliptic"); + +const sigAlgos = { + ECDSA_P256: "ECDSA_P256", + ECDSA_secp256k1: "ECDSA_secp256k1", +} + +const hashAlgos = { + SHA2_256: "SHA2_256", + SHA3_256: "SHA3_256", +} + +const signers = { + ECDSA_P256: () => new EC("p256"), + ECDSA_secp256k1: () => new EC("secp256k1"), +} + +function hashSHA2(msg) { + const sha = createHash("sha256") + sha.update(Buffer.from(msg, "hex")) + return sha.digest() +} + +function hashSHA3(msg) { + const sha = new SHA3(256) + sha.update(Buffer.from(msg, "hex")) + return sha.digest() +} + +const hashers = { + SHA2_256: hashSHA2, + SHA3_256: hashSHA3, +} + +const getSigner = sigAlgo => signers[sigAlgo]() +const getHasher = hashAlgo => hashers[hashAlgo] + +function encodeSignature(sig) { + const n = 32 + const r = sig.r.toArrayLike(Buffer, "be", n) + const s = sig.s.toArrayLike(Buffer, "be", n) + + return Buffer.concat([r, s]).toString("hex") +} + +function signWithPrivateKey(privateKey, sigAlgo, hashAlgo, msg) { + const signer = getSigner(sigAlgo) + const hasher = getHasher(hashAlgo) + + const key = signer.keyFromPrivate(Buffer.from(privateKey, "hex")) + const digest = hasher(msg) + + const sig = key.sign(digest) + + return encodeSignature(sig) +} + +module.exports = { + sigAlgos, + hashAlgos, + signWithPrivateKey, +}; diff --git a/src/lib/flow/getSigner.js b/src/lib/flow/getSigner.js new file mode 100644 index 00000000..8006817a --- /dev/null +++ b/src/lib/flow/getSigner.js @@ -0,0 +1,32 @@ +const fcl = require("@onflow/fcl"); + +const {signWithPrivateKey} = require("./crypto"); + +function getSigner( + signerAddress, + signerPrivateKey, + signerSigAlgo, + signerHashAlgo, + signerKeyIndex, +) { + return async (account = {}) => { + return { + ...account, + tempId: "SIGNER", + addr: fcl.sansPrefix(signerAddress), + keyId: signerKeyIndex, + signingFunction: data => ({ + addr: fcl.withPrefix(signerAddress), + keyId: signerKeyIndex, + signature: signWithPrivateKey( + signerPrivateKey, + signerSigAlgo, + signerHashAlgo, + data.message + ), + }), + } + } +} + +module.exports = getSigner; diff --git a/src/lib/flow/sendTransaction.js b/src/lib/flow/sendTransaction.js new file mode 100644 index 00000000..b9461cfd --- /dev/null +++ b/src/lib/flow/sendTransaction.js @@ -0,0 +1,26 @@ +const fcl = require("@onflow/fcl"); + +async function sendTransaction({ + transaction, + args, + proposer, + authorizations, + payer, +}) { + const response = await fcl.send([ + fcl.transaction(transaction), + fcl.args(args), + fcl.proposer(proposer), + fcl.authorizations(authorizations), + fcl.payer(payer), + fcl.limit(1000), + ]) + + const { transactionId } = response; + + await fcl.tx(response).onceSealed() + + return transactionId +} + +module.exports = sendTransaction; diff --git a/src/lib/fungibleTokens/index.js b/src/lib/fungibleTokens/index.js new file mode 100644 index 00000000..1de7e281 --- /dev/null +++ b/src/lib/fungibleTokens/index.js @@ -0,0 +1,28 @@ +const { transferFLOW, transferFUSD } = require('./transfer'); + +const tokenFLOW = "flow" +const tokenFUSD = "fusd" + +const tokens = [ + tokenFLOW, + tokenFUSD, +]; + +const transferFuncs = { + [tokenFLOW]: transferFLOW, + [tokenFUSD]: transferFUSD, +} + +function isValidToken(token) { + return token in transferFuncs; +} + +function getTokenTransferFunc(token) { + return transferFuncs[token]; +} + +module.exports = { + tokens, + isValidToken, + getTokenTransferFunc +}; diff --git a/src/lib/fungibleTokens/templates/transferFLOW.js b/src/lib/fungibleTokens/templates/transferFLOW.js new file mode 100644 index 00000000..0bd2ef22 --- /dev/null +++ b/src/lib/fungibleTokens/templates/transferFLOW.js @@ -0,0 +1,30 @@ +const dedent = require('dedent-js'); + +function template(contracts) { + return dedent` + import FungibleToken from ${contracts.FungibleToken} + import FlowToken from ${contracts.FlowToken} + + transaction(recipient: Address, amount: UFix64) { + + let transferVault: @FungibleToken.Vault + + prepare(signer: AuthAccount) { + let vaultRef = signer + .borrow<&FlowToken.Vault>(from: /storage/flowTokenVault)! + + self.transferVault <- vaultRef.withdraw(amount: amount) + } + + execute { + let receiverRef = getAccount(recipient) + .getCapability(/public/flowTokenReceiver) + .borrow<&{FungibleToken.Receiver}>()! + + receiverRef.deposit(from: <-self.transferVault) + } + } + ` +} + +module.exports = template; diff --git a/src/lib/fungibleTokens/templates/transferFUSD.js b/src/lib/fungibleTokens/templates/transferFUSD.js new file mode 100644 index 00000000..78bc00b0 --- /dev/null +++ b/src/lib/fungibleTokens/templates/transferFUSD.js @@ -0,0 +1,31 @@ +const dedent = require('dedent-js'); + +function template(contracts) { + return dedent` + import FungibleToken from ${contracts.FungibleToken} + import FUSD from ${contracts.FUSD} + + transaction(recipient: Address, amount: UFix64) { + + let transferVault: @FungibleToken.Vault + + prepare(signer: AuthAccount) { + let vaultRef = signer + .borrow<&FUSD.Vault>(from: /storage/fusdVault)! + + self.transferVault <- vaultRef.withdraw(amount: amount) + } + + execute { + let receiverRef = getAccount(recipient) + .getCapability(/public/fusdReceiver) + .borrow<&{FungibleToken.Receiver}>()! + + receiverRef.deposit(from: <-self.transferVault) + } + } + ` +} + +module.exports = template; + \ No newline at end of file diff --git a/src/lib/fungibleTokens/transfer.js b/src/lib/fungibleTokens/transfer.js new file mode 100644 index 00000000..daa4840d --- /dev/null +++ b/src/lib/fungibleTokens/transfer.js @@ -0,0 +1,61 @@ +const fcl = require("@onflow/fcl"); +const t = require("@onflow/types"); + +const sendTransaction = require("../flow/sendTransaction"); + +const transferFLOWTemplate = require("./templates/transferFLOW"); +const transferFUSDTemplate = require("./templates/transferFUSD"); + +async function transfer( + template, + recipient, + amount, + authorization, + contracts +) { + return await sendTransaction({ + transaction: template(contracts), + args: [ + fcl.arg(fcl.withPrefix(recipient), t.Address), + fcl.arg(amount, t.UFix64), + ], + authorizations: [authorization], + payer: authorization, + proposer: authorization, + }) +} + +async function transferFLOW( + recipient, + amount, + authorization, + contracts +) { + return transfer( + transferFLOWTemplate, + recipient, + amount, + authorization, + contracts + ) +} + +async function transferFUSD( + recipient, + amount, + authorization, + contracts +) { + return transfer( + transferFUSDTemplate, + recipient, + amount, + authorization, + contracts + ) +} + +module.exports = { + transferFLOW, + transferFUSD, +}; diff --git a/src/logger.js b/src/logger.js new file mode 100644 index 00000000..bba9a7ce --- /dev/null +++ b/src/logger.js @@ -0,0 +1,26 @@ +const winston = require('winston'); +const config = require('./config'); + +const enumerateErrorFormat = winston.format((info) => { + if (info instanceof Error) { + Object.assign(info, { message: info.stack }); + } + return info; +}); + +const logger = winston.createLogger({ + level: config.env === 'development' ? 'debug' : 'info', + format: winston.format.combine( + enumerateErrorFormat(), + config.env === 'development' ? winston.format.colorize() : winston.format.uncolorize(), + winston.format.splat(), + winston.format.printf(({ level, message }) => `${level}: ${message}`) + ), + transports: [ + new winston.transports.Console({ + stderrLevels: ['error'], + }), + ], +}); + +module.exports = logger; diff --git a/src/middleware/errors.js b/src/middleware/errors.js new file mode 100644 index 00000000..e0cec68d --- /dev/null +++ b/src/middleware/errors.js @@ -0,0 +1,33 @@ +const httpStatus = require('http-status'); +const config = require('../config'); +const logger = require('../logger'); +const ApiError = require('../errors/ApiError'); + +const errorsMiddleware = (err, req, res, next) => { + + let { statusCode, message } = err; + + if (!(err instanceof ApiError)) { + statusCode = httpStatus.INTERNAL_SERVER_ERROR; + message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR]; + } else if ( + config.env === 'production' && + statusCode === httpStatus.INTERNAL_SERVER_ERROR + ) { + message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR]; + } + + const response = { + code: statusCode, + message, + ...(config.env === 'development' && { stack: err.stack }), + }; + + if (config.env === 'development') { + logger.error(err); + } + + res.status(statusCode).send(response); +}; + +module.exports = errorsMiddleware; diff --git a/src/middleware/morgan.js b/src/middleware/morgan.js new file mode 100644 index 00000000..ac1bd27b --- /dev/null +++ b/src/middleware/morgan.js @@ -0,0 +1,18 @@ +const morgan = require("morgan"); +const logger = require("../logger"); + +const stream = { + write: (message) => logger.info(message), +}; + +const skip = () => { + const env = process.env.NODE_ENV || "development"; + return env !== "development"; +}; + +const morganMiddleware = morgan( + ":method :url :status :res[content-length] - :response-time ms", + { stream, skip } +); + +module.exports = morganMiddleware; diff --git a/src/routes/v1/fungibleTokens/index.js b/src/routes/v1/fungibleTokens/index.js new file mode 100644 index 00000000..0dee3a57 --- /dev/null +++ b/src/routes/v1/fungibleTokens/index.js @@ -0,0 +1,18 @@ +const express = require('express'); +const fungibleTokensController = require('../../../controllers/fungibleTokens'); +const withdrawals = require('./withdrawals') + +const router = express.Router(); + +router + .route('/') + .get(fungibleTokensController.getTokens); + +router + .route('/:tokenName') + .get(fungibleTokensController.getToken) + +router + .use('/:tokenName/withdrawals', withdrawals) + +module.exports = router; diff --git a/src/routes/v1/fungibleTokens/withdrawals.js b/src/routes/v1/fungibleTokens/withdrawals.js new file mode 100644 index 00000000..00708a6a --- /dev/null +++ b/src/routes/v1/fungibleTokens/withdrawals.js @@ -0,0 +1,15 @@ +const express = require('express'); +const fungibleTokensController = require('../../../controllers/fungibleTokens'); + +const router = express.Router({mergeParams: true}); + +router + .route('/') + .get(fungibleTokensController.getWithdrawals) + .post(fungibleTokensController.createWithdrawal); + +router + .route('/:transactionId') + .get(fungibleTokensController.getWithdrawal) + +module.exports = router; diff --git a/src/routes/v1/index.js b/src/routes/v1/index.js new file mode 100644 index 00000000..1a71385f --- /dev/null +++ b/src/routes/v1/index.js @@ -0,0 +1,8 @@ +const express = require('express'); +const fungibleTokensRoute = require('./fungibleTokens'); + +const router = express.Router(); + +router.use('/fungible-tokens', fungibleTokensRoute); + +module.exports = router; From 14ce280d11defeff853e3465760f17b27d2c8e1e Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 11 Apr 2021 22:39:55 -0700 Subject: [PATCH 002/112] Update README.md --- README.md | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 89d7ad78..34e2077c 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,15 @@ implements a simple custodial wallet for the Flow blockchain. ## Functionality - [x] Single admin account (hot wallet) -- [x] Fungible token withdrawals from admin account (FLOW, FUSD) -- [ ] Fungible token deposits from admin account (FLOW, FUSD) -- [ ] Fungible token withdrawals from user account (FLOW, FUSD) -- [ ] Fungible token deposits from user account (FLOW, FUSD) -- [ ] Non-fungible token withdrawals from admin account -- [ ] Non-fungible token deposits from admin account -- [ ] Non-fungible token withdrawals from user account -- [ ] Non-fungible token deposits from user account +- [x] Send fungible token withdrawals from admin account (FLOW, FUSD) +- [ ] Detect fungible token deposits to admin account (FLOW, FUSD) +- [ ] Create user accounts from admin account +- [ ] Send fungible token withdrawals from a user account (FLOW, FUSD) +- [ ] Detect fungible token deposits to a user account (FLOW, FUSD) +- [ ] Send non-fungible token withdrawals from admin account +- [ ] Detect non-fungible token deposits to admin account +- [ ] Send non-fungible token withdrawals from a user account +- [ ] Detect non-fungible token deposits to aa user account ## API Routes @@ -34,6 +35,8 @@ curl --request GET \ --url http://localhost:3000/v1/fungible-tokens ``` +--- + #### Get details of a token `GET /v1/fungible-tokens/{tokenName}` @@ -49,18 +52,24 @@ curl --request GET \ --url http://localhost:3000/v1/fungible-tokens/flow ``` +--- + #### List all withdrawals of a token type +:warning: _Not yet implemented_ + `GET /v1/fungible-tokens/{tokenName}/withdrawals` **Parameters** - `tokenName`: The name of the fungible token (e.g. FLOW) -:warning: Not implemented +--- #### Get details of a token withdrawal +:warning: _Not yet implemented_ + `GET /v1/fungible-tokens/{tokenName}/withdrawals/{transactionId}` **Parameters** @@ -68,7 +77,7 @@ curl --request GET \ - `tokenName`: The name of the fungible token (e.g. FLOW) - `transactionId`: The Flow transaction ID for the withdrawal -:warning: Not implemented +--- #### Create a token withdrawal @@ -93,6 +102,8 @@ curl --request GET \ --data '{ "recipient": "0xf8d6e0586b0a20c7", "amount": "123.456" }' ``` +--- + ### Non-Fungible Tokens TODO From 62d7be5589eb55ec14bfe3bbe1732fdeb2f7239c Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 11 Apr 2021 22:41:15 -0700 Subject: [PATCH 003/112] Update README.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 34e2077c..6dbe6924 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Supported tokens: `GET /v1/fungible-tokens` -**Example** +Example ```sh curl --request GET \ @@ -41,11 +41,11 @@ curl --request GET \ `GET /v1/fungible-tokens/{tokenName}` -**Parameters** +Parameters - `tokenName`: The name of the fungible token (e.g. FLOW) -**Example** +Example ```sh curl --request GET \ @@ -60,7 +60,7 @@ curl --request GET \ `GET /v1/fungible-tokens/{tokenName}/withdrawals` -**Parameters** +Parameters - `tokenName`: The name of the fungible token (e.g. FLOW) @@ -72,7 +72,7 @@ curl --request GET \ `GET /v1/fungible-tokens/{tokenName}/withdrawals/{transactionId}` -**Parameters** +Parameters - `tokenName`: The name of the fungible token (e.g. FLOW) - `transactionId`: The Flow transaction ID for the withdrawal @@ -83,17 +83,17 @@ curl --request GET \ `POST /v1/fungible-tokens/{tokenName}/withdrawals` -**Parameters** +Parameters - `tokenName`: The name of the fungible token (e.g. FLOW) -**Body (JSON)** +Body (JSON) - `amount`: The number of tokens to transfer (e.g. "123.456") - Must be a fixed-point number with a maximum of 8 decimal places - `recipient`: The Flow address of the recipient (e.g. "0xf8d6e0586b0a20c7") -**Example** +Example ```sh curl --request GET \ @@ -106,4 +106,4 @@ curl --request GET \ ### Non-Fungible Tokens -TODO +:warning: _Not yet implemented_ From 30ac164f14edeb6c35c97388a8a87b7022ed5d8c Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 11 Apr 2021 22:47:18 -0700 Subject: [PATCH 004/112] Update README.md --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 6dbe6924..3ecca7a7 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,17 @@ curl --request GET \ --url http://localhost:3000/v1/fungible-tokens ``` +```json +[ + { + "name": "flow" + }, + { + "name": "fusd" + } +] +``` + --- #### Get details of a token @@ -52,6 +63,12 @@ curl --request GET \ --url http://localhost:3000/v1/fungible-tokens/flow ``` +```json +{ + "name": "flow" +} +``` + --- #### List all withdrawals of a token type @@ -102,6 +119,14 @@ curl --request GET \ --data '{ "recipient": "0xf8d6e0586b0a20c7", "amount": "123.456" }' ``` +```json +{ + "transactionId": "18647b584a03345f3b2d2c4d9ab2c4179ae1b124a7f62ef9f33910e5ca8b353c", + "recipient": "0xf8d6e0586b0a20c7", + "amount": "123.456" +} +``` + --- ### Non-Fungible Tokens From 3bd1aeaa653fb7d47b8899c1c1673692abae9641 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 11 Apr 2021 22:55:00 -0700 Subject: [PATCH 005/112] Update README.md --- README.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/README.md b/README.md index 3ecca7a7..8ebaa1d5 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,49 @@ implements a simple custodial wallet for the Flow blockchain. - [ ] Send non-fungible token withdrawals from a user account - [ ] Detect non-fungible token deposits to aa user account +## Getting Started + +### Install the Flow CLI + +First, install the [Flow CLI](https://docs.onflow.org/flow-cli/install/). + +### Install dependencies and configure environment + +```sh +npm install + +cp env.example .env +``` + +### Start the database and emulator + +Use Docker Compose to launch Postgres and the [Flow Emulator](https://docs.onflow.org/emulator): + +```sh +docker-compose up -d +``` + +### Deploy token contracts to the emulator + +```sh +flow project deploy --network=emulator +``` + +### Setup the database + +```sh +npm run db-migrate-dev + +# sync the account keys table +npm run db-sync-account-keys +``` + +### Start the server! + +```sh +npm run start +``` + ## API Routes ### Fungible Tokens From 979e8f8d6e39349e1a2fe0de7f59e9a716789231 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 11 Apr 2021 23:10:26 -0700 Subject: [PATCH 006/112] Add FUSD Cadence contract --- cadence/contracts/FUSD.cdc | 226 ++++++++++++++++++++++++++++ cadence/contracts/FlowToken.cdc | 198 ++++++++++++++++++++++++ cadence/contracts/FungibleToken.cdc | 199 ++++++++++++++++++++++++ flow.json | 56 +++++++ 4 files changed, 679 insertions(+) create mode 100644 cadence/contracts/FUSD.cdc create mode 100644 cadence/contracts/FlowToken.cdc create mode 100644 cadence/contracts/FungibleToken.cdc create mode 100644 flow.json diff --git a/cadence/contracts/FUSD.cdc b/cadence/contracts/FUSD.cdc new file mode 100644 index 00000000..b75c8fb6 --- /dev/null +++ b/cadence/contracts/FUSD.cdc @@ -0,0 +1,226 @@ +import FungibleToken from "./FungibleToken.cdc" + +pub contract FUSD: FungibleToken { + + // Event that is emitted when the contract is created + pub event TokensInitialized(initialSupply: UFix64) + + // Event that is emitted when tokens are withdrawn from a Vault + pub event TokensWithdrawn(amount: UFix64, from: Address?) + + // Event that is emitted when tokens are deposited to a Vault + pub event TokensDeposited(amount: UFix64, to: Address?) + + // Event that is emitted when new tokens are minted + pub event TokensMinted(amount: UFix64) + + // The storage path for the admin resource + pub let AdminStoragePath: StoragePath + + // The storage Path for minters' MinterProxy + pub let MinterProxyStoragePath: StoragePath + + // The public path for minters' MinterProxy capability + pub let MinterProxyPublicPath: PublicPath + + // Event that is emitted when a new minter resource is created + pub event MinterCreated() + + // Total supply of fusd in existence + pub var totalSupply: UFix64 + + // Vault + // + // Each user stores an instance of only the Vault in their storage + // The functions in the Vault are governed by the pre and post conditions + // in FungibleToken when they are called. + // The checks happen at runtime whenever a function is called. + // + // Resources can only be created in the context of the contract that they + // are defined in, so there is no way for a malicious user to create Vaults + // out of thin air. A special Minter resource needs to be defined to mint + // new tokens. + // + pub resource Vault: FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance { + + // holds the balance of a users tokens + pub var balance: UFix64 + + // initialize the balance at resource creation time + init(balance: UFix64) { + self.balance = balance + } + + // withdraw + // + // Function that takes an integer amount as an argument + // and withdraws that amount from the Vault. + // It creates a new temporary Vault that is used to hold + // the money that is being transferred. It returns the newly + // created Vault to the context that called so it can be deposited + // elsewhere. + // + pub fun withdraw(amount: UFix64): @FungibleToken.Vault { + self.balance = self.balance - amount + emit TokensWithdrawn(amount: amount, from: self.owner?.address) + return <-create Vault(balance: amount) + } + + // deposit + // + // Function that takes a Vault object as an argument and adds + // its balance to the balance of the owners Vault. + // It is allowed to destroy the sent Vault because the Vault + // was a temporary holder of the tokens. The Vault's balance has + // been consumed and therefore can be destroyed. + pub fun deposit(from: @FungibleToken.Vault) { + let vault <- from as! @FUSD.Vault + self.balance = self.balance + vault.balance + emit TokensDeposited(amount: vault.balance, to: self.owner?.address) + vault.balance = 0.0 + destroy vault + } + + destroy() { + FUSD.totalSupply = FUSD.totalSupply - self.balance + } + } + + // createEmptyVault + // + // Function that creates a new Vault with a balance of zero + // and returns it to the calling context. A user must call this function + // and store the returned Vault in their storage in order to allow their + // account to be able to receive deposits of this token type. + // + pub fun createEmptyVault(): @FUSD.Vault { + return <-create Vault(balance: 0.0) + } + + // Minter + // + // Resource object that can mint new tokens. + // The admin stores this and passes it to the minter account as a capability wrapper resource. + // + pub resource Minter { + + // mintTokens + // + // Function that mints new tokens, adds them to the total supply, + // and returns them to the calling context. + // + pub fun mintTokens(amount: UFix64): @FUSD.Vault { + pre { + amount > 0.0: "Amount minted must be greater than zero" + } + FUSD.totalSupply = FUSD.totalSupply + amount + emit TokensMinted(amount: amount) + return <-create Vault(balance: amount) + } + + } + + pub resource interface MinterProxyPublic { + pub fun setMinterCapability(cap: Capability<&Minter>) + } + + // MinterProxy + // + // Resource object holding a capability that can be used to mint new tokens. + // The resource that this capability represents can be deleted by the admin + // in order to unilaterally revoke minting capability if needed. + + pub resource MinterProxy: MinterProxyPublic { + + // access(self) so nobody else can copy the capability and use it. + access(self) var minterCapability: Capability<&Minter>? + + // Anyone can call this, but only the admin can create Minter capabilities, + // so the type system constrains this to being called by the admin. + pub fun setMinterCapability(cap: Capability<&Minter>) { + self.minterCapability = cap + } + + pub fun mintTokens(amount: UFix64): @FUSD.Vault { + return <- self.minterCapability! + .borrow()! + .mintTokens(amount:amount) + } + + init() { + self.minterCapability = nil + } + + } + + // createMinterProxy + // + // Function that creates a MinterProxy. + // Anyone can call this, but the MinterProxy cannot mint without a Minter capability, + // and only the admin can provide that. + // + pub fun createMinterProxy(): @MinterProxy { + return <- create MinterProxy() + } + + // Administrator + // + // A resource that allows new minters to be created + // + // We will only want one minter for now, but might need to add or replace them in future. + // The Minter/Minter Proxy structure enables this. + // Ideally we would create this structure in a single function, generate the paths from the address + // and cache all of this information to enable easy revocation but String/Path comversion isn't yet supported. + // + pub resource Administrator { + + // createNewMinter + // + // Function that creates a Minter resource. + // This should be stored at a unique path in storage then a capability to it wrapped + // in a MinterProxy to be stored in a minter account's storage. + // This is done by the minter account running: + // transactions/fusd/minter/setup_minter_account.cdc + // then the admin account running: + // transactions/fusd/admin/deposit_minter_capability.cdc + // + pub fun createNewMinter(): @Minter { + emit MinterCreated() + return <- create Minter() + } + + } + + init() { + self.AdminStoragePath = /storage/fusdAdmin + self.MinterProxyPublicPath = /public/fusdMinterProxy + self.MinterProxyStoragePath = /storage/fusdMinterProxy + + self.totalSupply = 0.0 + + let admin <- create Administrator() + + // Emit an event that shows that the contract was initialized + emit TokensInitialized(initialSupply: 0.0) + + let minter <- admin.createNewMinter() + + let mintedVault <- minter.mintTokens(amount: 1000000.0) + + destroy minter + + self.account.save(<-admin, to: self.AdminStoragePath) + + self.account.save(<-mintedVault, to: /storage/fusdVault) + + self.account.link<&FUSD.Vault{FungibleToken.Receiver}>( + /public/fusdReceiver, + target: /storage/fusdVault + ) + + self.account.link<&FUSD.Vault{FungibleToken.Balance}>( + /public/fusdBalance, + target: /storage/fusdVault + ) + } +} diff --git a/cadence/contracts/FlowToken.cdc b/cadence/contracts/FlowToken.cdc new file mode 100644 index 00000000..42743d11 --- /dev/null +++ b/cadence/contracts/FlowToken.cdc @@ -0,0 +1,198 @@ +import FungibleToken from "./FungibleToken.cdc" + +pub contract FlowToken: FungibleToken { + + // Total supply of Flow tokens in existence + pub var totalSupply: UFix64 + + // Event that is emitted when the contract is created + pub event TokensInitialized(initialSupply: UFix64) + + // Event that is emitted when tokens are withdrawn from a Vault + pub event TokensWithdrawn(amount: UFix64, from: Address?) + + // Event that is emitted when tokens are deposited to a Vault + pub event TokensDeposited(amount: UFix64, to: Address?) + + // Event that is emitted when new tokens are minted + pub event TokensMinted(amount: UFix64) + + // Event that is emitted when tokens are destroyed + pub event TokensBurned(amount: UFix64) + + // Event that is emitted when a new minter resource is created + pub event MinterCreated(allowedAmount: UFix64) + + // Event that is emitted when a new burner resource is created + pub event BurnerCreated() + + // Vault + // + // Each user stores an instance of only the Vault in their storage + // The functions in the Vault and governed by the pre and post conditions + // in FungibleToken when they are called. + // The checks happen at runtime whenever a function is called. + // + // Resources can only be created in the context of the contract that they + // are defined in, so there is no way for a malicious user to create Vaults + // out of thin air. A special Minter resource needs to be defined to mint + // new tokens. + // + pub resource Vault: FungibleToken.Provider, FungibleToken.Receiver, FungibleToken.Balance { + + // holds the balance of a users tokens + pub var balance: UFix64 + + // initialize the balance at resource creation time + init(balance: UFix64) { + self.balance = balance + } + + // withdraw + // + // Function that takes an integer amount as an argument + // and withdraws that amount from the Vault. + // It creates a new temporary Vault that is used to hold + // the money that is being transferred. It returns the newly + // created Vault to the context that called so it can be deposited + // elsewhere. + // + pub fun withdraw(amount: UFix64): @FungibleToken.Vault { + self.balance = self.balance - amount + emit TokensWithdrawn(amount: amount, from: self.owner?.address) + return <-create Vault(balance: amount) + } + + // deposit + // + // Function that takes a Vault object as an argument and adds + // its balance to the balance of the owners Vault. + // It is allowed to destroy the sent Vault because the Vault + // was a temporary holder of the tokens. The Vault's balance has + // been consumed and therefore can be destroyed. + pub fun deposit(from: @FungibleToken.Vault) { + let vault <- from as! @FlowToken.Vault + self.balance = self.balance + vault.balance + emit TokensDeposited(amount: vault.balance, to: self.owner?.address) + vault.balance = 0.0 + destroy vault + } + + destroy() { + FlowToken.totalSupply = FlowToken.totalSupply - self.balance + } + } + + // createEmptyVault + // + // Function that creates a new Vault with a balance of zero + // and returns it to the calling context. A user must call this function + // and store the returned Vault in their storage in order to allow their + // account to be able to receive deposits of this token type. + // + pub fun createEmptyVault(): @FungibleToken.Vault { + return <-create Vault(balance: 0.0) + } + + pub resource Administrator { + // createNewMinter + // + // Function that creates and returns a new minter resource + // + pub fun createNewMinter(allowedAmount: UFix64): @Minter { + emit MinterCreated(allowedAmount: allowedAmount) + return <-create Minter(allowedAmount: allowedAmount) + } + + // createNewBurner + // + // Function that creates and returns a new burner resource + // + pub fun createNewBurner(): @Burner { + emit BurnerCreated() + return <-create Burner() + } + } + + // Minter + // + // Resource object that token admin accounts can hold to mint new tokens. + // + pub resource Minter { + + // the amount of tokens that the minter is allowed to mint + pub var allowedAmount: UFix64 + + // mintTokens + // + // Function that mints new tokens, adds them to the total supply, + // and returns them to the calling context. + // + pub fun mintTokens(amount: UFix64): @FlowToken.Vault { + pre { + amount > UFix64(0): "Amount minted must be greater than zero" + amount <= self.allowedAmount: "Amount minted must be less than the allowed amount" + } + FlowToken.totalSupply = FlowToken.totalSupply + amount + self.allowedAmount = self.allowedAmount - amount + emit TokensMinted(amount: amount) + return <-create Vault(balance: amount) + } + + init(allowedAmount: UFix64) { + self.allowedAmount = allowedAmount + } + } + + // Burner + // + // Resource object that token admin accounts can hold to burn tokens. + // + pub resource Burner { + + // burnTokens + // + // Function that destroys a Vault instance, effectively burning the tokens. + // + // Note: the burned tokens are automatically subtracted from the + // total supply in the Vault destructor. + // + pub fun burnTokens(from: @FungibleToken.Vault) { + let vault <- from as! @FlowToken.Vault + let amount = vault.balance + destroy vault + emit TokensBurned(amount: amount) + } + } + + init(adminAccount: AuthAccount) { + self.totalSupply = 0.0 + + // Create the Vault with the total supply of tokens and save it in storage + // + let vault <- create Vault(balance: self.totalSupply) + adminAccount.save(<-vault, to: /storage/flowTokenVault) + + // Create a public capability to the stored Vault that only exposes + // the `deposit` method through the `Receiver` interface + // + adminAccount.link<&FlowToken.Vault{FungibleToken.Receiver}>( + /public/flowTokenReceiver, + target: /storage/flowTokenVault + ) + + // Create a public capability to the stored Vault that only exposes + // the `balance` field through the `Balance` interface + // + adminAccount.link<&FlowToken.Vault{FungibleToken.Balance}>( + /public/flowTokenBalance, + target: /storage/flowTokenVault + ) + + let admin <- create Administrator() + adminAccount.save(<-admin, to: /storage/flowTokenAdmin) + + // Emit an event that shows that the contract was initialized + emit TokensInitialized(initialSupply: self.totalSupply) + } +} diff --git a/cadence/contracts/FungibleToken.cdc b/cadence/contracts/FungibleToken.cdc new file mode 100644 index 00000000..6fddb0ed --- /dev/null +++ b/cadence/contracts/FungibleToken.cdc @@ -0,0 +1,199 @@ +/** + +# The Flow Fungible Token standard + +## `FungibleToken` contract interface + +The interface that all fungible token contracts would have to conform to. +If a users wants to deploy a new token contract, their contract +would need to implement the FungibleToken interface. + +Their contract would have to follow all the rules and naming +that the interface specifies. + +## `Vault` resource + +Each account that owns tokens would need to have an instance +of the Vault resource stored in their account storage. + +The Vault resource has methods that the owner and other users can call. + +## `Provider`, `Receiver`, and `Balance` resource interfaces + +These interfaces declare pre-conditions and post-conditions that restrict +the execution of the functions in the Vault. + +They are separate because it gives the user the ability to share +a reference to their Vault that only exposes the fields functions +in one or more of the interfaces. + +It also gives users the ability to make custom resources that implement +these interfaces to do various things with the tokens. +For example, a faucet can be implemented by conforming +to the Provider interface. + +By using resources and interfaces, users of FungibleToken contracts +can send and receive tokens peer-to-peer, without having to interact +with a central ledger smart contract. To send tokens to another user, +a user would simply withdraw the tokens from their Vault, then call +the deposit function on another user's Vault to complete the transfer. + +*/ + +/// FungibleToken +/// +/// The interface that fungible token contracts implement. +/// +pub contract interface FungibleToken { + + /// The total number of tokens in existence. + /// It is up to the implementer to ensure that the total supply + /// stays accurate and up to date + /// + pub var totalSupply: UFix64 + + /// TokensInitialized + /// + /// The event that is emitted when the contract is created + /// + pub event TokensInitialized(initialSupply: UFix64) + + /// TokensWithdrawn + /// + /// The event that is emitted when tokens are withdrawn from a Vault + /// + pub event TokensWithdrawn(amount: UFix64, from: Address?) + + /// TokensDeposited + /// + /// The event that is emitted when tokens are deposited into a Vault + /// + pub event TokensDeposited(amount: UFix64, to: Address?) + + /// Provider + /// + /// The interface that enforces the requirements for withdrawing + /// tokens from the implementing type. + /// + /// It does not enforce requirements on `balance` here, + /// because it leaves open the possibility of creating custom providers + /// that do not necessarily need their own balance. + /// + pub resource interface Provider { + + /// withdraw subtracts tokens from the owner's Vault + /// and returns a Vault with the removed tokens. + /// + /// The function's access level is public, but this is not a problem + /// because only the owner storing the resource in their account + /// can initially call this function. + /// + /// The owner may grant other accounts access by creating a private + /// capability that allows specific other users to access + /// the provider resource through a reference. + /// + /// The owner may also grant all accounts access by creating a public + /// capability that allows all users to access the provider + /// resource through a reference. + /// + pub fun withdraw(amount: UFix64): @Vault { + post { + // `result` refers to the return value + result.balance == amount: + "Withdrawal amount must be the same as the balance of the withdrawn Vault" + } + } + } + + /// Receiver + /// + /// The interface that enforces the requirements for depositing + /// tokens into the implementing type. + /// + /// We do not include a condition that checks the balance because + /// we want to give users the ability to make custom receivers that + /// can do custom things with the tokens, like split them up and + /// send them to different places. + /// + pub resource interface Receiver { + + /// deposit takes a Vault and deposits it into the implementing resource type + /// + pub fun deposit(from: @Vault) + } + + /// Balance + /// + /// The interface that contains the `balance` field of the Vault + /// and enforces that when new Vaults are created, the balance + /// is initialized correctly. + /// + pub resource interface Balance { + + /// The total balance of a vault + /// + pub var balance: UFix64 + + init(balance: UFix64) { + post { + self.balance == balance: + "Balance must be initialized to the initial balance" + } + } + } + + /// Vault + /// + /// The resource that contains the functions to send and receive tokens. + /// + pub resource Vault: Provider, Receiver, Balance { + + // The declaration of a concrete type in a contract interface means that + // every Fungible Token contract that implements the FungibleToken interface + // must define a concrete `Vault` resource that conforms to the `Provider`, `Receiver`, + // and `Balance` interfaces, and declares their required fields and functions + + /// The total balance of the vault + /// + pub var balance: UFix64 + + // The conforming type must declare an initializer + // that allows prioviding the initial balance of the Vault + // + init(balance: UFix64) + + /// withdraw subtracts `amount` from the Vault's balance + /// and returns a new Vault with the subtracted balance + /// + pub fun withdraw(amount: UFix64): @Vault { + pre { + self.balance >= amount: + "Amount withdrawn must be less than or equal than the balance of the Vault" + } + post { + // use the special function `before` to get the value of the `balance` field + // at the beginning of the function execution + // + self.balance == before(self.balance) - amount: + "New Vault balance must be the difference of the previous balance and the withdrawn Vault" + } + } + + /// deposit takes a Vault and adds its balance to the balance of this Vault + /// + pub fun deposit(from: @Vault) { + post { + self.balance == before(self.balance) + before(from.balance): + "New Vault balance must be the sum of the previous balance and the deposited Vault" + } + } + } + + /// createEmptyVault allows any user to create a new Vault that has a zero balance + /// + pub fun createEmptyVault(): @Vault { + post { + result.balance == 0.0: "The newly created Vault must have zero balance" + } + } +} diff --git a/flow.json b/flow.json new file mode 100644 index 00000000..d427dfb6 --- /dev/null +++ b/flow.json @@ -0,0 +1,56 @@ +{ + "emulators": { + "default": { + "port": 3569, + "serviceAccount": "emulator-account" + } + }, + "contracts": { + "FungibleToken": { + "source": "./cadence/contracts/FungibleToken.cdc", + "aliases": { + "emulator": "0xee82856bf20e2aa6", + "testnet": "0x9a0766d93b6608b7" + } + }, + "FlowToken": { + "source": "./cadence/contracts/FlowToken.cdc", + "aliases": { + "emulator": "0x0ae53cb6e3f42a79", + "testnet": "0x7e60df042a9c0868" + } + }, + "FUSD": { + "source": "./cadence/contracts/FUSD.cdc", + "aliases": { + "testnet": "0xe223d8a629e49c68" + } + } + }, + "deployments": { + "emulator": { + "emulator-account": [ "FUSD" ] + } + }, + "networks": { + "emulator": { + "host": "127.0.0.1:3569", + "chain": "flow-emulator" + }, + "mainnet": { + "host": "access.mainnet.nodes.onflow.org:9000", + "chain": "flow-mainnet" + }, + "testnet": { + "host": "access.devnet.nodes.onflow.org:9000", + "chain": "flow-testnet" + } + }, + "accounts": { + "emulator-account": { + "address": "f8d6e0586b0a20c7", + "keys": "91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068", + "chain": "flow-emulator" + } + } +} From 39210b414cad6cad3f8a40a963f0ebed83bf3531 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 11 Apr 2021 23:11:17 -0700 Subject: [PATCH 007/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8ebaa1d5..815c1a7b 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ docker-compose up -d flow project deploy --network=emulator ``` -### Setup the database +### Set up the database ```sh npm run db-migrate-dev From b8e27189a8ebe0e322811a2e8604ab7d9e8d11e7 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 11 Apr 2021 23:24:28 -0700 Subject: [PATCH 008/112] Rename signer to admin --- docker-compose.yml | 2 +- env.example | 4 ++-- package.json | 3 ++- src/config.js | 16 ++++++++-------- src/controllers/fungibleTokens.js | 12 ++++++------ src/database/syncAccountKeys.js | 6 +++--- 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 51e36395..f000385e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,6 @@ services: - "8080:8080" - "3569:3569" environment: - - FLOW_SERVICEPRIVATEKEY=${SIGNER_PRIVATE_KEY} + - FLOW_SERVICEPRIVATEKEY=${ADMIN_PRIVATE_KEY} - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 - FLOW_SERVICEKEYHASHALGO=SHA3_256 diff --git a/env.example b/env.example index 45258816..60bb70ff 100644 --- a/env.example +++ b/env.example @@ -2,5 +2,5 @@ ACCESS_API_HOST=http://localhost:8080 DATABASE_URL="postgresql://wallet:wallet@localhost:5432/wallet?schema=public" -SIGNER_ADDRESS=0xf8d6e0586b0a20c7 -SIGNER_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068 +ADMIN_ADDRESS=0xf8d6e0586b0a20c7 +ADMIN_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068 diff --git a/package.json b/package.json index f354c2c6..0c6d452a 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "version": "0.0.0", "private": true, "scripts": { - "start": "node ./src/index.js", + "start": "npm run deploy-contracts && NODE_ENV=development node ./src/index.js", + "deploy-contracts": "flow project deploy --network=emulator --update", "db-migrate-dev": "prisma migrate dev --preview-feature", "db-migrate-deploy": "prisma migrate deploy --preview-feature", "db-sync-account-keys": "node ./src/database/syncAccountKeys.js" diff --git a/src/config.js b/src/config.js index d017179f..6d45d767 100644 --- a/src/config.js +++ b/src/config.js @@ -27,12 +27,12 @@ function getContracts(chain) { } module.exports = { - env: process.env.NODE_ENV, - port: process.env.PORT || 3000, - accessApiHost: process.env.ACCESS_API_HOST || "http://localhost:8080", - signerAddress: process.env.SIGNER_ADDRESS || "0xf8d6e0586b0a20c7", - signerPrivateKey: process.env.SIGNER_PRIVATE_KEY, - signerSigAlgo: process.env.SIGNER_SIG_ALGO || "ECDSA_P256", - signerHashAlgo: process.env.SIGNER_HASH_ALGO || "SHA3_256", - contracts: getContracts(process.env.CHAIN), + env: process.env.NODE_ENV, + port: process.env.PORT || 3000, + accessApiHost: process.env.ACCESS_API_HOST || "http://localhost:8080", + adminAddress: process.env.ADMIN_ADDRESS || "0xf8d6e0586b0a20c7", + adminPrivateKey: process.env.ADMIN_PRIVATE_KEY, + adminSigAlgo: process.env.ADMIN_SIG_ALGO || "ECDSA_P256", + adminHashAlgo: process.env.ADMIN_HASH_ALGO || "SHA3_256", + contracts: getContracts(process.env.CHAIN), } diff --git a/src/controllers/fungibleTokens.js b/src/controllers/fungibleTokens.js index d52094b3..bc8f38c8 100644 --- a/src/controllers/fungibleTokens.js +++ b/src/controllers/fungibleTokens.js @@ -38,14 +38,14 @@ const createWithdrawal = catchAsync(async (req, res) => { // TODO: validate recipient and amount const { recipient, amount } = req.body; - const signerKeyIndex = await getLeastRecentAccountKey(); + const adminKeyIndex = await getLeastRecentAccountKey(); const signer = getSigner( - config.signerAddress, - config.signerPrivateKey, - config.signerSigAlgo, - config.signerHashAlgo, - signerKeyIndex, + config.adminAddress, + config.adminPrivateKey, + config.adminSigAlgo, + config.adminHashAlgo, + adminKeyIndex, ) const transfer = getTokenTransferFunc(tokenName) diff --git a/src/database/syncAccountKeys.js b/src/database/syncAccountKeys.js index e9317f12..a8a9f64b 100644 --- a/src/database/syncAccountKeys.js +++ b/src/database/syncAccountKeys.js @@ -7,7 +7,7 @@ const { PrismaClient } = require('@prisma/client'); const prisma = new PrismaClient() -const signerAddress = process.env.SIGNER_ADDRESS +const adminAddress = process.env.ADMIN_ADDRESS const accessAPIHost = process.env.ACCESS_API_HOST fcl.config().put("accessNode.api", accessAPIHost) @@ -18,9 +18,9 @@ async function getAccount(address) { } async function main() { - const account = await getAccount(signerAddress) + const account = await getAccount(adminAddress) - console.log(`Fetched account information for ${signerAddress} from ${accessAPIHost}\n`) + console.log(`Fetched account information for ${adminAddress} from ${accessAPIHost}\n`) // truncate existing keys const { count } = await prisma.accountKey.deleteMany({where: {}}) From 7ed7b11929bb59257461c314a8f997d7b48bd9e0 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 12 Apr 2021 11:10:55 -0700 Subject: [PATCH 009/112] Update README.md --- README.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 815c1a7b..f8447b93 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ curl --request GET \ Parameters -- `tokenName`: The name of the fungible token (e.g. FLOW) +- `tokenName`: The name of the fungible token (e.g. "flow") Example @@ -122,7 +122,7 @@ curl --request GET \ Parameters -- `tokenName`: The name of the fungible token (e.g. FLOW) +- `tokenName`: The name of the fungible token (e.g. "flow") --- @@ -134,7 +134,7 @@ Parameters Parameters -- `tokenName`: The name of the fungible token (e.g. FLOW) +- `tokenName`: The name of the fungible token (e.g. "flow") - `transactionId`: The Flow transaction ID for the withdrawal --- @@ -145,7 +145,7 @@ Parameters Parameters -- `tokenName`: The name of the fungible token (e.g. FLOW) +- `tokenName`: The name of the fungible token (e.g. "flow") Body (JSON) @@ -175,3 +175,70 @@ curl --request GET \ ### Non-Fungible Tokens :warning: _Not yet implemented_ + +#### List all tokens + +:warning: _Not yet implemented_ + +`GET /v1/non-fungible-tokens` + +Example + +```sh +curl --request GET \ + --url http://localhost:3000/v1/non-fungible-tokens +``` + +--- + +#### Get details of a token + +:warning: _Not yet implemented_ + +`GET /v1/non-fungible-tokens/{tokenName}` + +Parameters + +- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") + +--- + +#### List all withdrawals of a token type + +:warning: _Not yet implemented_ + +`GET /v1/non-fungible-tokens/{tokenName}/withdrawals` + +Parameters + +- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") + +--- + +#### Get details of a token withdrawal + +:warning: _Not yet implemented_ + +`GET /v1/non-fungible-tokens/{tokenName}/withdrawals/{transactionId}` + +Parameters + +- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") +- `transactionId`: The Flow transaction ID for the withdrawal + +--- + +#### Create a token withdrawal + +:warning: _Not yet implemented_ + +`POST /v1/non-fungible-tokens/{tokenName}/withdrawals` + +Parameters + +- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") + +Body (JSON) + +- `recipient`: The Flow address of the recipient (e.g. "0xf8d6e0586b0a20c7") + From 810cad5182e379da1079fb01d4e5b7bf454aaf1f Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 12 Apr 2021 11:15:16 -0700 Subject: [PATCH 010/112] Remove unneeded dependencies and add linter --- .eslintrc.json | 20 + .prettierrc | 6 + package-lock.json | 1582 ++++++++++++++++++++++- package.json | 13 +- src/errors/InvalidFungibleTokenError.js | 4 +- src/errors/NotFoundError.js | 4 +- 6 files changed, 1620 insertions(+), 9 deletions(-) create mode 100644 .eslintrc.json create mode 100644 .prettierrc diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..7747714d --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,20 @@ +{ + "parser": "babel-eslint", + "extends": [ + "plugin:react/recommended", + "plugin:prettier/recommended" + ], + "plugins": [ + "react", + "prettier" + ], + "rules": { + "react/react-in-jsx-scope": "off", + "react/prop-types": "off" + }, + "settings": { + "react": { + "version": "detect" + } + } +} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..7082e437 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": false, + "trailingComma": "es5", + "bracketSpacing": false, + "arrowParens": "avoid" +} diff --git a/package-lock.json b/package-lock.json index ef0006a7..c731bd5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,133 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "@babel/generator": { + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", + "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", + "dev": true, + "requires": { + "@babel/types": "^7.13.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", + "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.13.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", + "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", + "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + } + }, + "@babel/traverse": { + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", + "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.13.9", + "@babel/helper-function-name": "^7.12.13", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.13.15", + "@babel/types": "^7.13.14", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", + "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, "@dabh/diagnostics": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", @@ -14,6 +141,49 @@ "kuler": "^2.0.0" } }, + "@eslint/eslintrc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", + "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "@improbable-eng/grpc-web": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.12.0.tgz", @@ -439,16 +609,121 @@ "negotiator": "0.6.2" } }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.5" + } + }, + "array.prototype.flatmap": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", + "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "function-bind": "^1.1.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, "async": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -467,6 +742,16 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -486,6 +771,33 @@ "ieee754": "^1.2.1" } }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, "color": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", @@ -531,6 +843,12 @@ "text-hex": "1.0.x" } }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -551,6 +869,17 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -564,6 +893,21 @@ "resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz", "integrity": "sha1-vuX7fJ5yfYXf+iRZDRDsGrElUwU=" }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -574,6 +918,15 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dotenv": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", @@ -605,6 +958,12 @@ } } }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", @@ -615,11 +974,346 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "es-abstract": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", + "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.2", + "is-string": "^1.0.5", + "object-inspect": "^1.9.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.0" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.24.0.tgz", + "integrity": "sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.21", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + }, + "globals": { + "version": "13.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", + "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz", + "integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==", + "dev": true + }, + "eslint-plugin-prettier": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz", + "integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-plugin-react": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.23.2.tgz", + "integrity": "sha512-AfjgFQB+nYszudkxRkTFu0UR1zEQig0ArVMPloKhxwlwkzaw/fBiH0QWcBBhZONlXqQC51+nfqFrkn4EzHcGBw==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "array.prototype.flatmap": "^1.2.4", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.0.4", + "object.entries": "^1.1.3", + "object.fromentries": "^2.0.4", + "object.values": "^1.1.3", + "prop-types": "^15.7.2", + "resolve": "^2.0.0-next.3", + "string.prototype.matchall": "^4.0.4" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -710,6 +1404,30 @@ } } }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, "fast-safe-stringify": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", @@ -720,6 +1438,15 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, "finalhandler": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -734,6 +1461,22 @@ "unpipe": "~1.0.0" } }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, "fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", @@ -749,11 +1492,96 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, - "google-protobuf": { + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "google-protobuf": { "version": "3.15.7", "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.7.tgz", "integrity": "sha512-S/kTHcT98AV2FxEwtT5lvgffeS87BB6hloZm+pYKkpzwtySwNiKcqXZbxpq/Odh3Wib1RdOe/oY2EHdi17YrlQ==" }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, "hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -799,11 +1627,54 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -814,21 +1685,199 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "is-bigint": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", + "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==", + "dev": true + }, + "is-boolean-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", + "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "dev": true + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", + "dev": true + }, + "is-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", + "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.1" + } + }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "jsx-ast-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", + "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==", + "dev": true, + "requires": { + "array-includes": "^3.1.2", + "object.assign": "^4.1.2" + } + }, "kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, "logform": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", @@ -848,6 +1897,24 @@ } } }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -891,6 +1958,15 @@ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -908,11 +1984,83 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", + "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", + "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has": "^1.0.3" + } + }, + "object.values": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", + "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has": "^1.0.3" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -926,6 +2074,15 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, "one-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", @@ -934,16 +2091,78 @@ "fn.name": "1.x.x" } }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "prisma": { "version": "2.20.1", "resolved": "https://registry.npmjs.org/prisma/-/prisma-2.20.1.tgz", @@ -958,6 +2177,23 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -967,6 +2203,12 @@ "ipaddr.js": "1.9.1" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -982,6 +2224,12 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -992,6 +2240,53 @@ "util-deprecate": "^1.0.1" } }, + "regexp.prototype.flags": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -1002,6 +2297,15 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -1046,6 +2350,32 @@ "buffer": "6.0.3" } }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -1054,6 +2384,55 @@ "is-arrayish": "^0.3.1" } }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -1064,6 +2443,52 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "string.prototype.matchall": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz", + "integrity": "sha512-pknFIWVachNcyqRfaQSeu/FUfpvJTe4uskUSZ9Wc1RijsPuzbZ8TyYT8WCNnntCjUEqQ3vUHMAfVj2+wLAisPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has-symbols": "^1.0.1", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.3.1", + "side-channel": "^1.0.4" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -1079,16 +2504,104 @@ } } }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.9.tgz", + "integrity": "sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "lodash.clonedeep": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ajv": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.1.0.tgz", + "integrity": "sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1098,11 +2611,32 @@ "mime-types": "~2.1.24" } }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1113,11 +2647,39 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "winston": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", @@ -1166,6 +2728,24 @@ } } } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } } diff --git a/package.json b/package.json index 0c6d452a..1852b0c2 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "deploy-contracts": "flow project deploy --network=emulator --update", "db-migrate-dev": "prisma migrate dev --preview-feature", "db-migrate-deploy": "prisma migrate deploy --preview-feature", - "db-sync-account-keys": "node ./src/database/syncAccountKeys.js" + "db-sync-account-keys": "node ./src/database/syncAccountKeys.js", + "lint": "eslint .", + "lint-fix": "eslint . --fix" }, "prisma": { "schema": "src/database/schema.prisma" @@ -16,19 +18,22 @@ "@onflow/fcl": "0.0.67", "@onflow/types": "0.0.4", "@prisma/client": "^2.20.1", - "debug": "~2.6.9", "dedent-js": "^1.0.1", "dotenv": "^8.2.0", "elliptic": "^6.5.4", "express": "~4.16.1", - "http-errors": "~1.6.3", "http-status": "^1.5.0", - "http-status-codes": "^2.1.4", "morgan": "~1.9.1", "sha3": "^2.1.4", "winston": "^3.3.3" }, "devDependencies": { + "babel-eslint": "^10.1.0", + "eslint": "^7.21.0", + "eslint-config-prettier": "^8.1.0", + "eslint-plugin-prettier": "^3.3.1", + "eslint-plugin-react": "^7.22.0", + "prettier": "^2.2.1", "prisma": "^2.20.1" } } diff --git a/src/errors/InvalidFungibleTokenError.js b/src/errors/InvalidFungibleTokenError.js index 919eb6b0..77adcb06 100644 --- a/src/errors/InvalidFungibleTokenError.js +++ b/src/errors/InvalidFungibleTokenError.js @@ -1,9 +1,9 @@ -const { StatusCodes } = require('http-status-codes'); +const httpStatus = require('http-status'); const ApiError = require("./ApiError"); class InvalidFungibleTokenError extends ApiError { constructor(token) { - const statusCode = StatusCodes.BAD_REQUEST; + const statusCode = httpStatus.BAD_REQUEST; const message = `${token} is not valid fungible token`; super(statusCode, message); diff --git a/src/errors/NotFoundError.js b/src/errors/NotFoundError.js index 05083a8b..55fdf2c4 100644 --- a/src/errors/NotFoundError.js +++ b/src/errors/NotFoundError.js @@ -1,9 +1,9 @@ -const { StatusCodes } = require('http-status-codes'); +const httpStatus = require('http-status'); const ApiError = require("./ApiError"); class NotFoundError extends ApiError { constructor() { - const statusCode = StatusCodes.NOT_FOUND; + const statusCode = httpStatus.NOT_FOUND; const message = "not found"; super(statusCode, message); From 06ee5e64137f1f497f36b037b764301401692025 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 12 Apr 2021 11:16:37 -0700 Subject: [PATCH 011/112] Lint and fix all files --- .eslintrc.json | 13 +--- package.json | 1 - src/app.js | 32 ++++----- src/config.js | 14 ++-- src/controllers/fungibleTokens.js | 70 +++++++++++-------- src/database/getLeastRecentAccountKey.js | 4 +- src/database/syncAccountKeys.js | 18 ++--- src/errors/ApiError.js | 8 +-- src/errors/InvalidFungibleTokenError.js | 12 ++-- src/errors/NotFoundError.js | 12 ++-- src/errors/catchAsync.js | 8 +-- src/index.js | 40 +++++------ src/lib/flow/crypto.js | 8 +-- src/lib/flow/getSigner.js | 8 +-- src/lib/flow/sendTransaction.js | 6 +- src/lib/fungibleTokens/index.js | 15 ++-- .../fungibleTokens/templates/transferFLOW.js | 4 +- .../fungibleTokens/templates/transferFUSD.js | 7 +- src/lib/fungibleTokens/transfer.js | 34 +++------ src/logger.js | 26 +++---- src/middleware/errors.js | 33 +++++---- src/middleware/morgan.js | 20 +++--- src/routes/v1/fungibleTokens/index.js | 21 +++--- src/routes/v1/fungibleTokens/withdrawals.js | 16 ++--- src/routes/v1/index.js | 10 +-- 25 files changed, 205 insertions(+), 235 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 7747714d..c6fa6f97 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,20 +1,9 @@ { "parser": "babel-eslint", "extends": [ - "plugin:react/recommended", "plugin:prettier/recommended" ], "plugins": [ - "react", "prettier" - ], - "rules": { - "react/react-in-jsx-scope": "off", - "react/prop-types": "off" - }, - "settings": { - "react": { - "version": "detect" - } - } + ] } diff --git a/package.json b/package.json index 1852b0c2..f96dbed3 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "eslint": "^7.21.0", "eslint-config-prettier": "^8.1.0", "eslint-plugin-prettier": "^3.3.1", - "eslint-plugin-react": "^7.22.0", "prettier": "^2.2.1", "prisma": "^2.20.1" } diff --git a/src/app.js b/src/app.js index a68a92df..aa249779 100644 --- a/src/app.js +++ b/src/app.js @@ -1,26 +1,26 @@ -require('dotenv').config() +require("dotenv").config() -const express = require('express'); -const morganMiddleware = require('./middleware/morgan'); -const errorsMiddleware = require('./middleware/errors'); -const NotFoundError = require('./errors/NotFoundError'); +const express = require("express") +const morganMiddleware = require("./middleware/morgan") +const errorsMiddleware = require("./middleware/errors") +const NotFoundError = require("./errors/NotFoundError") -const routes = require('./routes/v1'); +const routes = require("./routes/v1") -const app = express(); +const app = express() -app.use(morganMiddleware); -app.use(express.json()); -app.use(express.urlencoded({ extended: false })); +app.use(morganMiddleware) +app.use(express.json()) +app.use(express.urlencoded({extended: false})) -app.use('/v1', routes); +app.use("/v1", routes) // catch 404 and forward to error handler -app.use(function(req, res, next) { - next(new NotFoundError()); -}); +app.use(function (req, res, next) { + next(new NotFoundError()) +}) // error handler -app.use(errorsMiddleware); +app.use(errorsMiddleware) -module.exports = app; +module.exports = app diff --git a/src/config.js b/src/config.js index 6d45d767..8b85fd4b 100644 --- a/src/config.js +++ b/src/config.js @@ -27,12 +27,12 @@ function getContracts(chain) { } module.exports = { - env: process.env.NODE_ENV, - port: process.env.PORT || 3000, - accessApiHost: process.env.ACCESS_API_HOST || "http://localhost:8080", - adminAddress: process.env.ADMIN_ADDRESS || "0xf8d6e0586b0a20c7", + env: process.env.NODE_ENV, + port: process.env.PORT || 3000, + accessApiHost: process.env.ACCESS_API_HOST || "http://localhost:8080", + adminAddress: process.env.ADMIN_ADDRESS || "0xf8d6e0586b0a20c7", adminPrivateKey: process.env.ADMIN_PRIVATE_KEY, - adminSigAlgo: process.env.ADMIN_SIG_ALGO || "ECDSA_P256", - adminHashAlgo: process.env.ADMIN_HASH_ALGO || "SHA3_256", - contracts: getContracts(process.env.CHAIN), + adminSigAlgo: process.env.ADMIN_SIG_ALGO || "ECDSA_P256", + adminHashAlgo: process.env.ADMIN_HASH_ALGO || "SHA3_256", + contracts: getContracts(process.env.CHAIN), } diff --git a/src/controllers/fungibleTokens.js b/src/controllers/fungibleTokens.js index bc8f38c8..a3fad324 100644 --- a/src/controllers/fungibleTokens.js +++ b/src/controllers/fungibleTokens.js @@ -1,77 +1,85 @@ -const httpStatus = require('http-status'); -const config = require('../config'); -const { tokens, isValidToken, getTokenTransferFunc } = require('../lib/fungibleTokens'); -const getSigner = require('../lib/flow/getSigner'); -const getLeastRecentAccountKey = require('../database/getLeastRecentAccountKey'); -const catchAsync = require('../errors/catchAsync'); -const ApiError = require('../errors/ApiError'); -const InvalidFungibleTokenError = require('../errors/InvalidFungibleTokenError'); +const httpStatus = require("http-status") +const config = require("../config") +const { + tokens, + isValidToken, + getTokenTransferFunc, +} = require("../lib/fungibleTokens") +const getSigner = require("../lib/flow/getSigner") +const getLeastRecentAccountKey = require("../database/getLeastRecentAccountKey") +const catchAsync = require("../errors/catchAsync") +const ApiError = require("../errors/ApiError") +const InvalidFungibleTokenError = require("../errors/InvalidFungibleTokenError") -const makeToken = (tokenName) => ({ name: tokenName }); -const allTokens = tokens.map((tokenName) => makeToken(tokenName)); +const makeToken = tokenName => ({name: tokenName}) +const allTokens = tokens.map(tokenName => makeToken(tokenName)) -const getTokens = catchAsync(async (req, res) => res.json(allTokens)); +const getTokens = catchAsync(async (req, res) => res.json(allTokens)) const getToken = catchAsync(async (req, res) => { - const tokenName = req.params.tokenName; + const tokenName = req.params.tokenName if (!isValidToken(tokenName)) { - throw new InvalidFungibleTokenError(tokenName); + throw new InvalidFungibleTokenError(tokenName) } - const token = makeToken(tokenName); + const token = makeToken(tokenName) - res.json(token); -}); + res.json(token) +}) // TODO: implement withdrawal getters -const getWithdrawals = catchAsync(async (req, res) => { res.send("TODO: implement me") }); -const getWithdrawal = catchAsync(async (req, res) => { res.send("TODO: implement me") }); +const getWithdrawals = catchAsync(async (req, res) => { + res.send("TODO: implement me") +}) +const getWithdrawal = catchAsync(async (req, res) => { + res.send("TODO: implement me") +}) const createWithdrawal = catchAsync(async (req, res) => { - const tokenName = req.params.tokenName; + const tokenName = req.params.tokenName if (!isValidToken(tokenName)) { - throw new InvalidFungibleTokenError(tokenName); + throw new InvalidFungibleTokenError(tokenName) } // TODO: validate recipient and amount - const { recipient, amount } = req.body; + const {recipient, amount} = req.body - const adminKeyIndex = await getLeastRecentAccountKey(); + const adminKeyIndex = await getLeastRecentAccountKey() const signer = getSigner( config.adminAddress, config.adminPrivateKey, config.adminSigAlgo, config.adminHashAlgo, - adminKeyIndex, + adminKeyIndex ) const transfer = getTokenTransferFunc(tokenName) - + try { const transactionId = await transfer( recipient, amount, signer, - config.contracts, + config.contracts ) - + const response = { transactionId, recipient, amount, } - - res.json(response); + + res.json(response) } catch (e) { throw new ApiError( httpStatus.INTERNAL_SERVER_ERROR, - 'failed to complete withdrawal', + "failed to complete withdrawal" ) } -}); +}) module.exports = { getTokens, @@ -79,4 +87,4 @@ module.exports = { getWithdrawals, getWithdrawal, createWithdrawal, -}; +} diff --git a/src/database/getLeastRecentAccountKey.js b/src/database/getLeastRecentAccountKey.js index ae96a34f..272e7250 100644 --- a/src/database/getLeastRecentAccountKey.js +++ b/src/database/getLeastRecentAccountKey.js @@ -1,4 +1,4 @@ -const {PrismaClient} = require("@prisma/client"); +const {PrismaClient} = require("@prisma/client") const prisma = new PrismaClient() @@ -19,4 +19,4 @@ const getLeastRecentAccountKey = async () => { return results[0].index } -module.exports = getLeastRecentAccountKey; +module.exports = getLeastRecentAccountKey diff --git a/src/database/syncAccountKeys.js b/src/database/syncAccountKeys.js index a8a9f64b..f180d2be 100644 --- a/src/database/syncAccountKeys.js +++ b/src/database/syncAccountKeys.js @@ -1,9 +1,9 @@ -const fcl = require('@onflow/fcl'); -const dotenv = require('dotenv'); +const fcl = require("@onflow/fcl") +const dotenv = require("dotenv") dotenv.config() -const { PrismaClient } = require('@prisma/client'); +const {PrismaClient} = require("@prisma/client") const prisma = new PrismaClient() @@ -13,29 +13,29 @@ const accessAPIHost = process.env.ACCESS_API_HOST fcl.config().put("accessNode.api", accessAPIHost) async function getAccount(address) { - const { account } = await fcl.send([fcl.getAccount(address)]) + const {account} = await fcl.send([fcl.getAccount(address)]) return account } async function main() { const account = await getAccount(adminAddress) - console.log(`Fetched account information for ${adminAddress} from ${accessAPIHost}\n`) + console.log( + `Fetched account information for ${adminAddress} from ${accessAPIHost}\n` + ) // truncate existing keys - const { count } = await prisma.accountKey.deleteMany({where: {}}) + const {count} = await prisma.accountKey.deleteMany({where: {}}) console.log(`Removed ${count} existing key(s) from DB\n`) await Promise.all( account.keys.map(async key => { - console.log(`- Inserting key ${key.index}`) await prisma.accountKey.create({ - data: { index: key.index }, + data: {index: key.index}, }) - }) ) diff --git a/src/errors/ApiError.js b/src/errors/ApiError.js index 10728b6d..b96cfb39 100644 --- a/src/errors/ApiError.js +++ b/src/errors/ApiError.js @@ -1,11 +1,11 @@ class ApiError extends Error { constructor(statusCode, message) { - super(message); + super(message) - this.statusCode = statusCode; + this.statusCode = statusCode - Error.captureStackTrace(this, this.constructor); + Error.captureStackTrace(this, this.constructor) } } -module.exports = ApiError; +module.exports = ApiError diff --git a/src/errors/InvalidFungibleTokenError.js b/src/errors/InvalidFungibleTokenError.js index 77adcb06..4961247f 100644 --- a/src/errors/InvalidFungibleTokenError.js +++ b/src/errors/InvalidFungibleTokenError.js @@ -1,13 +1,13 @@ -const httpStatus = require('http-status'); -const ApiError = require("./ApiError"); +const httpStatus = require("http-status") +const ApiError = require("./ApiError") class InvalidFungibleTokenError extends ApiError { constructor(token) { - const statusCode = httpStatus.BAD_REQUEST; - const message = `${token} is not valid fungible token`; + const statusCode = httpStatus.BAD_REQUEST + const message = `${token} is not valid fungible token` - super(statusCode, message); + super(statusCode, message) } } -module.exports = InvalidFungibleTokenError; +module.exports = InvalidFungibleTokenError diff --git a/src/errors/NotFoundError.js b/src/errors/NotFoundError.js index 55fdf2c4..58fb3829 100644 --- a/src/errors/NotFoundError.js +++ b/src/errors/NotFoundError.js @@ -1,13 +1,13 @@ -const httpStatus = require('http-status'); -const ApiError = require("./ApiError"); +const httpStatus = require("http-status") +const ApiError = require("./ApiError") class NotFoundError extends ApiError { constructor() { - const statusCode = httpStatus.NOT_FOUND; - const message = "not found"; + const statusCode = httpStatus.NOT_FOUND + const message = "not found" - super(statusCode, message); + super(statusCode, message) } } -module.exports = NotFoundError; +module.exports = NotFoundError diff --git a/src/errors/catchAsync.js b/src/errors/catchAsync.js index 387efc6d..e9c88664 100644 --- a/src/errors/catchAsync.js +++ b/src/errors/catchAsync.js @@ -1,5 +1,5 @@ -const catchAsync = (fn) => (req, res, next) => { - Promise.resolve(fn(req, res, next)).catch((err) => next(err)); -}; +const catchAsync = fn => (req, res, next) => { + Promise.resolve(fn(req, res, next)).catch(err => next(err)) +} -module.exports = catchAsync; +module.exports = catchAsync diff --git a/src/index.js b/src/index.js index 1490968f..ff7d334d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,33 +1,33 @@ -const app = require('./app'); -const config = require('./config'); -const logger = require('./logger'); +const app = require("./app") +const config = require("./config") +const logger = require("./logger") const server = app.listen(config.port, () => { - logger.info(`Listening to port ${config.port}`); -}); + logger.info(`Listening to port ${config.port}`) +}) const exitHandler = () => { if (server) { server.close(() => { - logger.info('Server closed'); - process.exit(1); - }); + logger.info("Server closed") + process.exit(1) + }) } else { - process.exit(1); + process.exit(1) } -}; +} -const unexpectedErrorHandler = (error) => { - logger.error(error); - exitHandler(); -}; +const unexpectedErrorHandler = error => { + logger.error(error) + exitHandler() +} -process.on('uncaughtException', unexpectedErrorHandler); -process.on('unhandledRejection', unexpectedErrorHandler); +process.on("uncaughtException", unexpectedErrorHandler) +process.on("unhandledRejection", unexpectedErrorHandler) -process.on('SIGTERM', () => { - logger.info('SIGTERM received'); +process.on("SIGTERM", () => { + logger.info("SIGTERM received") if (server) { - server.close(); + server.close() } -}); +}) diff --git a/src/lib/flow/crypto.js b/src/lib/flow/crypto.js index 88b50372..7813792d 100644 --- a/src/lib/flow/crypto.js +++ b/src/lib/flow/crypto.js @@ -1,6 +1,6 @@ -const {createHash} = require("crypto"); -const {SHA3} = require("sha3"); -const {ec: EC} = require("elliptic"); +const {createHash} = require("crypto") +const {SHA3} = require("sha3") +const {ec: EC} = require("elliptic") const sigAlgos = { ECDSA_P256: "ECDSA_P256", @@ -61,4 +61,4 @@ module.exports = { sigAlgos, hashAlgos, signWithPrivateKey, -}; +} diff --git a/src/lib/flow/getSigner.js b/src/lib/flow/getSigner.js index 8006817a..3f880dcf 100644 --- a/src/lib/flow/getSigner.js +++ b/src/lib/flow/getSigner.js @@ -1,13 +1,13 @@ -const fcl = require("@onflow/fcl"); +const fcl = require("@onflow/fcl") -const {signWithPrivateKey} = require("./crypto"); +const {signWithPrivateKey} = require("./crypto") function getSigner( signerAddress, signerPrivateKey, signerSigAlgo, signerHashAlgo, - signerKeyIndex, + signerKeyIndex ) { return async (account = {}) => { return { @@ -29,4 +29,4 @@ function getSigner( } } -module.exports = getSigner; +module.exports = getSigner diff --git a/src/lib/flow/sendTransaction.js b/src/lib/flow/sendTransaction.js index b9461cfd..f5fe978a 100644 --- a/src/lib/flow/sendTransaction.js +++ b/src/lib/flow/sendTransaction.js @@ -1,4 +1,4 @@ -const fcl = require("@onflow/fcl"); +const fcl = require("@onflow/fcl") async function sendTransaction({ transaction, @@ -16,11 +16,11 @@ async function sendTransaction({ fcl.limit(1000), ]) - const { transactionId } = response; + const {transactionId} = response await fcl.tx(response).onceSealed() return transactionId } -module.exports = sendTransaction; +module.exports = sendTransaction diff --git a/src/lib/fungibleTokens/index.js b/src/lib/fungibleTokens/index.js index 1de7e281..a956c906 100644 --- a/src/lib/fungibleTokens/index.js +++ b/src/lib/fungibleTokens/index.js @@ -1,12 +1,9 @@ -const { transferFLOW, transferFUSD } = require('./transfer'); +const {transferFLOW, transferFUSD} = require("./transfer") const tokenFLOW = "flow" const tokenFUSD = "fusd" -const tokens = [ - tokenFLOW, - tokenFUSD, -]; +const tokens = [tokenFLOW, tokenFUSD] const transferFuncs = { [tokenFLOW]: transferFLOW, @@ -14,15 +11,15 @@ const transferFuncs = { } function isValidToken(token) { - return token in transferFuncs; + return token in transferFuncs } function getTokenTransferFunc(token) { - return transferFuncs[token]; + return transferFuncs[token] } module.exports = { tokens, isValidToken, - getTokenTransferFunc -}; + getTokenTransferFunc, +} diff --git a/src/lib/fungibleTokens/templates/transferFLOW.js b/src/lib/fungibleTokens/templates/transferFLOW.js index 0bd2ef22..2033e45f 100644 --- a/src/lib/fungibleTokens/templates/transferFLOW.js +++ b/src/lib/fungibleTokens/templates/transferFLOW.js @@ -1,4 +1,4 @@ -const dedent = require('dedent-js'); +const dedent = require("dedent-js") function template(contracts) { return dedent` @@ -27,4 +27,4 @@ function template(contracts) { ` } -module.exports = template; +module.exports = template diff --git a/src/lib/fungibleTokens/templates/transferFUSD.js b/src/lib/fungibleTokens/templates/transferFUSD.js index 78bc00b0..5093bc01 100644 --- a/src/lib/fungibleTokens/templates/transferFUSD.js +++ b/src/lib/fungibleTokens/templates/transferFUSD.js @@ -1,4 +1,4 @@ -const dedent = require('dedent-js'); +const dedent = require("dedent-js") function template(contracts) { return dedent` @@ -26,6 +26,5 @@ function template(contracts) { } ` } - -module.exports = template; - \ No newline at end of file + +module.exports = template diff --git a/src/lib/fungibleTokens/transfer.js b/src/lib/fungibleTokens/transfer.js index daa4840d..695b7907 100644 --- a/src/lib/fungibleTokens/transfer.js +++ b/src/lib/fungibleTokens/transfer.js @@ -1,18 +1,12 @@ -const fcl = require("@onflow/fcl"); -const t = require("@onflow/types"); +const fcl = require("@onflow/fcl") +const t = require("@onflow/types") -const sendTransaction = require("../flow/sendTransaction"); +const sendTransaction = require("../flow/sendTransaction") -const transferFLOWTemplate = require("./templates/transferFLOW"); -const transferFUSDTemplate = require("./templates/transferFUSD"); +const transferFLOWTemplate = require("./templates/transferFLOW") +const transferFUSDTemplate = require("./templates/transferFUSD") -async function transfer( - template, - recipient, - amount, - authorization, - contracts -) { +async function transfer(template, recipient, amount, authorization, contracts) { return await sendTransaction({ transaction: template(contracts), args: [ @@ -25,12 +19,7 @@ async function transfer( }) } -async function transferFLOW( - recipient, - amount, - authorization, - contracts -) { +async function transferFLOW(recipient, amount, authorization, contracts) { return transfer( transferFLOWTemplate, recipient, @@ -40,12 +29,7 @@ async function transferFLOW( ) } -async function transferFUSD( - recipient, - amount, - authorization, - contracts -) { +async function transferFUSD(recipient, amount, authorization, contracts) { return transfer( transferFUSDTemplate, recipient, @@ -58,4 +42,4 @@ async function transferFUSD( module.exports = { transferFLOW, transferFUSD, -}; +} diff --git a/src/logger.js b/src/logger.js index bba9a7ce..9c298a85 100644 --- a/src/logger.js +++ b/src/logger.js @@ -1,26 +1,28 @@ -const winston = require('winston'); -const config = require('./config'); +const winston = require("winston") +const config = require("./config") -const enumerateErrorFormat = winston.format((info) => { +const enumerateErrorFormat = winston.format(info => { if (info instanceof Error) { - Object.assign(info, { message: info.stack }); + Object.assign(info, {message: info.stack}) } - return info; -}); + return info +}) const logger = winston.createLogger({ - level: config.env === 'development' ? 'debug' : 'info', + level: config.env === "development" ? "debug" : "info", format: winston.format.combine( enumerateErrorFormat(), - config.env === 'development' ? winston.format.colorize() : winston.format.uncolorize(), + config.env === "development" + ? winston.format.colorize() + : winston.format.uncolorize(), winston.format.splat(), - winston.format.printf(({ level, message }) => `${level}: ${message}`) + winston.format.printf(({level, message}) => `${level}: ${message}`) ), transports: [ new winston.transports.Console({ - stderrLevels: ['error'], + stderrLevels: ["error"], }), ], -}); +}) -module.exports = logger; +module.exports = logger diff --git a/src/middleware/errors.js b/src/middleware/errors.js index e0cec68d..10018061 100644 --- a/src/middleware/errors.js +++ b/src/middleware/errors.js @@ -1,33 +1,32 @@ -const httpStatus = require('http-status'); -const config = require('../config'); -const logger = require('../logger'); -const ApiError = require('../errors/ApiError'); +const httpStatus = require("http-status") +const config = require("../config") +const logger = require("../logger") +const ApiError = require("../errors/ApiError") const errorsMiddleware = (err, req, res, next) => { - - let { statusCode, message } = err; + let {statusCode, message} = err if (!(err instanceof ApiError)) { - statusCode = httpStatus.INTERNAL_SERVER_ERROR; - message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR]; + statusCode = httpStatus.INTERNAL_SERVER_ERROR + message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR] } else if ( - config.env === 'production' && + config.env === "production" && statusCode === httpStatus.INTERNAL_SERVER_ERROR ) { - message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR]; + message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR] } const response = { code: statusCode, message, - ...(config.env === 'development' && { stack: err.stack }), - }; + ...(config.env === "development" && {stack: err.stack}), + } - if (config.env === 'development') { - logger.error(err); + if (config.env === "development") { + logger.error(err) } - res.status(statusCode).send(response); -}; + res.status(statusCode).send(response) +} -module.exports = errorsMiddleware; +module.exports = errorsMiddleware diff --git a/src/middleware/morgan.js b/src/middleware/morgan.js index ac1bd27b..cfcc8a9c 100644 --- a/src/middleware/morgan.js +++ b/src/middleware/morgan.js @@ -1,18 +1,18 @@ -const morgan = require("morgan"); -const logger = require("../logger"); +const morgan = require("morgan") +const logger = require("../logger") const stream = { - write: (message) => logger.info(message), -}; + write: message => logger.info(message), +} const skip = () => { - const env = process.env.NODE_ENV || "development"; - return env !== "development"; -}; + const env = process.env.NODE_ENV || "development" + return env !== "development" +} const morganMiddleware = morgan( ":method :url :status :res[content-length] - :response-time ms", - { stream, skip } -); + {stream, skip} +) -module.exports = morganMiddleware; +module.exports = morganMiddleware diff --git a/src/routes/v1/fungibleTokens/index.js b/src/routes/v1/fungibleTokens/index.js index 0dee3a57..1cd037d4 100644 --- a/src/routes/v1/fungibleTokens/index.js +++ b/src/routes/v1/fungibleTokens/index.js @@ -1,18 +1,13 @@ -const express = require('express'); -const fungibleTokensController = require('../../../controllers/fungibleTokens'); -const withdrawals = require('./withdrawals') +const express = require("express") +const fungibleTokensController = require("../../../controllers/fungibleTokens") +const withdrawals = require("./withdrawals") -const router = express.Router(); +const router = express.Router() -router - .route('/') - .get(fungibleTokensController.getTokens); +router.route("/").get(fungibleTokensController.getTokens) -router - .route('/:tokenName') - .get(fungibleTokensController.getToken) +router.route("/:tokenName").get(fungibleTokensController.getToken) -router - .use('/:tokenName/withdrawals', withdrawals) +router.use("/:tokenName/withdrawals", withdrawals) -module.exports = router; +module.exports = router diff --git a/src/routes/v1/fungibleTokens/withdrawals.js b/src/routes/v1/fungibleTokens/withdrawals.js index 00708a6a..e1f96967 100644 --- a/src/routes/v1/fungibleTokens/withdrawals.js +++ b/src/routes/v1/fungibleTokens/withdrawals.js @@ -1,15 +1,13 @@ -const express = require('express'); -const fungibleTokensController = require('../../../controllers/fungibleTokens'); +const express = require("express") +const fungibleTokensController = require("../../../controllers/fungibleTokens") -const router = express.Router({mergeParams: true}); +const router = express.Router({mergeParams: true}) router - .route('/') + .route("/") .get(fungibleTokensController.getWithdrawals) - .post(fungibleTokensController.createWithdrawal); + .post(fungibleTokensController.createWithdrawal) -router - .route('/:transactionId') - .get(fungibleTokensController.getWithdrawal) +router.route("/:transactionId").get(fungibleTokensController.getWithdrawal) -module.exports = router; +module.exports = router diff --git a/src/routes/v1/index.js b/src/routes/v1/index.js index 1a71385f..ba8704c3 100644 --- a/src/routes/v1/index.js +++ b/src/routes/v1/index.js @@ -1,8 +1,8 @@ -const express = require('express'); -const fungibleTokensRoute = require('./fungibleTokens'); +const express = require("express") +const fungibleTokensRoute = require("./fungibleTokens") -const router = express.Router(); +const router = express.Router() -router.use('/fungible-tokens', fungibleTokensRoute); +router.use("/fungible-tokens", fungibleTokensRoute) -module.exports = router; +module.exports = router From c0e94b9cda64c366fa1bfd574c511aa4d6fbef8c Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 12 Apr 2021 11:17:25 -0700 Subject: [PATCH 012/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8447b93..93506d72 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Flow Wallet API Demo (Node.js + express) This is a demonstration of a RESTful API that -implements a simple custodial wallet for the Flow blockchain. +implements a simple custodial wallet service for the Flow blockchain. ## Functionality From 7fb313cbb72b363cfc33b243be9f4a9f73e514e9 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 12 Apr 2021 11:20:37 -0700 Subject: [PATCH 013/112] Update start script --- README.md | 3 --- package.json | 3 ++- src/index.js | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 815c1a7b..ba37c5ea 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,6 @@ flow project deploy --network=emulator ```sh npm run db-migrate-dev - -# sync the account keys table -npm run db-sync-account-keys ``` ### Start the server! diff --git a/package.json b/package.json index f96dbed3..152174c1 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "version": "0.0.0", "private": true, "scripts": { - "start": "npm run deploy-contracts && NODE_ENV=development node ./src/index.js", + "start": "npm run deploy-contracts && npm run db-sync-account-keys && npm run start-api", + "start-api": "NODE_ENV=development node ./src/index.js", "deploy-contracts": "flow project deploy --network=emulator --update", "db-migrate-dev": "prisma migrate dev --preview-feature", "db-migrate-deploy": "prisma migrate deploy --preview-feature", diff --git a/src/index.js b/src/index.js index ff7d334d..76d40209 100644 --- a/src/index.js +++ b/src/index.js @@ -3,7 +3,7 @@ const config = require("./config") const logger = require("./logger") const server = app.listen(config.port, () => { - logger.info(`Listening to port ${config.port}`) + logger.info(`Listening on port ${config.port}`) }) const exitHandler = () => { From 192b1bceb7aae659e772511e2d574d9de21ecf5e Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 12 Apr 2021 11:21:14 -0700 Subject: [PATCH 014/112] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7df28d40..7a3ded3c 100644 --- a/README.md +++ b/README.md @@ -44,13 +44,13 @@ docker-compose up -d flow project deploy --network=emulator ``` -### Set up the database +### Migrate the database ```sh npm run db-migrate-dev ``` -### Start the server! +### Start the server ```sh npm run start From 61e7e7f196561e68ca86efd89a587f796a61d5a7 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 12 Apr 2021 11:22:07 -0700 Subject: [PATCH 015/112] Remove unneeded deploy step from README --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 7a3ded3c..4685a93c 100644 --- a/README.md +++ b/README.md @@ -38,12 +38,6 @@ Use Docker Compose to launch Postgres and the [Flow Emulator](https://docs.onflo docker-compose up -d ``` -### Deploy token contracts to the emulator - -```sh -flow project deploy --network=emulator -``` - ### Migrate the database ```sh From 09dd3b2ba2dcf20ac0083573f06a601898b7e21b Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 13 Apr 2021 20:23:46 -0700 Subject: [PATCH 016/112] Update README.md --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4685a93c..fc931033 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,20 @@ implements a simple custodial wallet service for the Flow blockchain. ## Functionality +### Admin + - [x] Single admin account (hot wallet) +- [ ] Create user accounts from admin account + +### Fungible Tokens + - [x] Send fungible token withdrawals from admin account (FLOW, FUSD) - [ ] Detect fungible token deposits to admin account (FLOW, FUSD) -- [ ] Create user accounts from admin account - [ ] Send fungible token withdrawals from a user account (FLOW, FUSD) - [ ] Detect fungible token deposits to a user account (FLOW, FUSD) + +## Non-Fungible Tokens + - [ ] Send non-fungible token withdrawals from admin account - [ ] Detect non-fungible token deposits to admin account - [ ] Send non-fungible token withdrawals from a user account From 1ec3b13eaf282181a747a2d8858ac136906aa2ae Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Wed, 14 Apr 2021 20:47:50 -0700 Subject: [PATCH 017/112] Rename package to match repo name --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c731bd5c..29cda2db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "flow-wallet-nodejs-demo", + "name": "flow-wallet-api-node-demo", "version": "0.0.0", "lockfileVersion": 1, "requires": true, diff --git a/package.json b/package.json index 152174c1..4359694a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "flow-wallet-nodejs-demo", + "name": "flow-wallet-api-node-demo", "version": "0.0.0", "private": true, "scripts": { From 0348c67f4788ca877166549875fcd1f680bc6e3b Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Wed, 14 Apr 2021 22:01:26 -0700 Subject: [PATCH 018/112] Add nodemon --- README.md | 7 - package-lock.json | 1169 ++++++++++++++++++++++++++++++++------------- package.json | 5 +- 3 files changed, 853 insertions(+), 328 deletions(-) diff --git a/README.md b/README.md index fc931033..c5b0d0ff 100644 --- a/README.md +++ b/README.md @@ -46,12 +46,6 @@ Use Docker Compose to launch Postgres and the [Flow Emulator](https://docs.onflo docker-compose up -d ``` -### Migrate the database - -```sh -npm run db-migrate-dev -``` - ### Start the server ```sh @@ -240,4 +234,3 @@ Parameters Body (JSON) - `recipient`: The Flow address of the recipient (e.g. "0xf8d6e0586b0a20c7") - diff --git a/package-lock.json b/package-lock.json index 29cda2db..e8631dde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -600,6 +600,27 @@ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e.tgz", "integrity": "sha512-fJhbGZXm2SPs/RsI79Ew4SFe+6QmChNdgU2I/SIjmU18bUgK8f1TBEWnVtFdBqEDHYPGxbpaianF7lp04KN7EA==" }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -633,6 +654,55 @@ "uri-js": "^4.2.2" } }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -654,6 +724,16 @@ "color-convert": "^1.9.0" } }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -668,31 +748,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" - } - }, - "array.prototype.flatmap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", - "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "function-bind": "^1.1.1" - } - }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -737,11 +792,84 @@ "safe-buffer": "5.1.2" } }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -752,6 +880,15 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -771,6 +908,38 @@ "ieee754": "^1.2.1" } }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -787,6 +956,12 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -798,6 +973,43 @@ "supports-color": "^5.3.0" } }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "color": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", @@ -849,6 +1061,20 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -880,6 +1106,12 @@ "which": "^2.0.1" } }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -888,25 +1120,37 @@ "ms": "2.0.0" } }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "dedent-js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz", "integrity": "sha1-vuX7fJ5yfYXf+iRZDRDsGrElUwU=" }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "define-properties": { + "defer-to-connect": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true }, "depd": { "version": "1.1.2", @@ -927,11 +1171,26 @@ "esutils": "^2.0.2" } }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, "dotenv": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -974,6 +1233,15 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, "enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -983,40 +1251,11 @@ "ansi-colors": "^4.1.1" } }, - "es-abstract": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", - "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.2", - "is-string": "^1.0.5", - "object-inspect": "^1.9.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.0" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true }, "escape-html": { "version": "1.0.3", @@ -1185,47 +1424,6 @@ "prettier-linter-helpers": "^1.0.0" } }, - "eslint-plugin-react": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.23.2.tgz", - "integrity": "sha512-AfjgFQB+nYszudkxRkTFu0UR1zEQig0ArVMPloKhxwlwkzaw/fBiH0QWcBBhZONlXqQC51+nfqFrkn4EzHcGBw==", - "dev": true, - "requires": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", - "doctrine": "^2.1.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.3", - "object.fromentries": "^2.0.4", - "object.values": "^1.1.3", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.4" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - } - } - }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -1447,6 +1645,15 @@ "flat-cache": "^3.0.4" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "finalhandler": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -1498,6 +1705,13 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -1521,6 +1735,15 @@ "has-symbols": "^1.0.1" } }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -1544,6 +1767,15 @@ "is-glob": "^4.0.1" } }, + "global-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", + "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", + "dev": true, + "requires": { + "ini": "1.3.7" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -1555,6 +1787,31 @@ "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.7.tgz", "integrity": "sha512-S/kTHcT98AV2FxEwtT5lvgffeS87BB6hloZm+pYKkpzwtySwNiKcqXZbxpq/Odh3Wib1RdOe/oY2EHdi17YrlQ==" }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1564,12 +1821,6 @@ "function-bind": "^1.1.1" } }, - "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1582,6 +1833,12 @@ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "dev": true }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, "hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -1601,6 +1858,12 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -1617,11 +1880,6 @@ "resolved": "https://registry.npmjs.org/http-status/-/http-status-1.5.0.tgz", "integrity": "sha512-wcGvY31MpFNHIkUcXHHnvrE4IKYlpvitJw5P/1u892gMBAM46muQ+RH7UN1d+Ntnfx5apnOnVY6vcLmrWHOLwg==" }, - "http-status-codes": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.1.4.tgz", - "integrity": "sha512-MZVIsLKGVOVE1KEnldppe6Ij+vmemMuApDfjhVSLzyYP+td0bREEYyAoIw9yFePoBXManCuBqmiNP5FqJS5Xkg==" - }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -1633,6 +1891,12 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -1643,6 +1907,12 @@ "resolve-from": "^4.0.0" } }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -1664,16 +1934,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } + "ini": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", + "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", + "dev": true }, "ipaddr.js": { "version": "1.9.1", @@ -1685,11 +1950,14 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, - "is-bigint": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", - "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==", - "dev": true + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } }, "is-boolean-object": { "version": "1.1.0", @@ -1700,11 +1968,14 @@ "call-bind": "^1.0.0" } }, - "is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } }, "is-core-module": { "version": "2.2.0", @@ -1715,12 +1986,6 @@ "has": "^1.0.3" } }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1742,10 +2007,26 @@ "is-extglob": "^2.1.1" } }, - "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "is-installed-globally": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", + "dev": true, + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + } + }, + "is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, "is-number-object": { @@ -1754,15 +2035,17 @@ "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", "dev": true }, - "is-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", - "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.1" - } + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true }, "is-stream": { "version": "2.0.0", @@ -1775,14 +2058,17 @@ "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", "dev": true }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true }, "isarray": { "version": "1.0.0", @@ -1817,6 +2103,12 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1829,14 +2121,13 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "jsx-ast-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", - "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==", + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", "dev": true, "requires": { - "array-includes": "^3.1.2", - "object.assign": "^4.1.2" + "json-buffer": "3.0.0" } }, "kuler": { @@ -1844,6 +2135,15 @@ "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -1897,14 +2197,11 @@ } } }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true }, "lru-cache": { "version": "6.0.0", @@ -1915,6 +2212,23 @@ "yallist": "^4.0.0" } }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -1948,6 +2262,12 @@ "mime-db": "1.47.0" } }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -1967,6 +2287,12 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, "morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -1995,71 +2321,67 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "nodemon": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", + "integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" + "chokidar": "^3.2.2", + "debug": "^3.2.6", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.0.4", + "pstree.remy": "^1.1.7", + "semver": "^5.7.1", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.3", + "update-notifier": "^4.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, - "object.entries": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz", - "integrity": "sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg==", + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", "dev": true, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "has": "^1.0.3" + "abbrev": "1" } }, - "object.fromentries": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", - "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" - } + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, - "object.values": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", - "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" - } + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true }, "on-finished": { "version": "2.3.0", @@ -2105,6 +2427,32 @@ "word-wrap": "^1.2.3" } }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2142,12 +2490,24 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "picomatch": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", + "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", + "dev": true + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, "prettier": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", @@ -2183,17 +2543,6 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" - } - }, "proxy-addr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", @@ -2203,12 +2552,37 @@ "ipaddr.js": "1.9.1" } }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -2224,11 +2598,25 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } }, "readable-stream": { "version": "3.6.0", @@ -2240,14 +2628,13 @@ "util-deprecate": "^1.0.1" } }, - "regexp.prototype.flags": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", - "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "picomatch": "^2.2.1" } }, "regexpp": { @@ -2256,6 +2643,24 @@ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", "dev": true }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -2278,6 +2683,15 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -2306,6 +2720,23 @@ "lru-cache": "^6.0.0" } }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -2365,16 +2796,11 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true }, "simple-swizzle": { "version": "0.2.2", @@ -2454,41 +2880,6 @@ "strip-ansi": "^6.0.0" } }, - "string.prototype.matchall": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz", - "integrity": "sha512-pknFIWVachNcyqRfaQSeu/FUfpvJTe4uskUSZ9Wc1RijsPuzbZ8TyYT8WCNnntCjUEqQ3vUHMAfVj2+wLAisPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has-symbols": "^1.0.1", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.3.1", - "side-channel": "^1.0.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -2565,6 +2956,12 @@ } } }, + "term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true + }, "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -2582,6 +2979,30 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + } + }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", @@ -2611,16 +3032,31 @@ "mime-types": "~2.1.24" } }, - "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" + "is-typedarray": "^1.0.0" + } + }, + "undefsafe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", + "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", + "dev": true, + "requires": { + "debug": "^2.2.0" + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" } }, "unpipe": { @@ -2628,6 +3064,78 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, + "update-notifier": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", + "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", + "dev": true, + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2637,6 +3145,15 @@ "punycode": "^2.1.0" } }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2667,17 +3184,13 @@ "isexe": "^2.0.0" } }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", "dev": true, "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "string-width": "^4.0.0" } }, "winston": { @@ -2741,6 +3254,24 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index 4359694a..3d5cc913 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "version": "0.0.0", "private": true, "scripts": { - "start": "npm run deploy-contracts && npm run db-sync-account-keys && npm run start-api", - "start-api": "NODE_ENV=development node ./src/index.js", + "start": "npm run deploy-contracts && npm run db-migrate-dev && npm run db-sync-account-keys && npm run start-api", + "start-api": "NODE_ENV=development nodemon ./src/index.js", "deploy-contracts": "flow project deploy --network=emulator --update", "db-migrate-dev": "prisma migrate dev --preview-feature", "db-migrate-deploy": "prisma migrate deploy --preview-feature", @@ -33,6 +33,7 @@ "eslint": "^7.21.0", "eslint-config-prettier": "^8.1.0", "eslint-plugin-prettier": "^3.3.1", + "nodemon": "^2.0.7", "prettier": "^2.2.1", "prisma": "^2.20.1" } From 67e4eb7c1892a4140fc4775b6cc12da4bd71510e Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Wed, 14 Apr 2021 22:43:41 -0700 Subject: [PATCH 019/112] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c5b0d0ff..651582be 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,14 @@ implements a simple custodial wallet service for the Flow blockchain. ### Admin - [x] Single admin account (hot wallet) -- [ ] Create user accounts from admin account +- [ ] [Create user accounts from admin account](https://github.com/onflow/flow-wallet-api-node-demo/issues/1) ### Fungible Tokens - [x] Send fungible token withdrawals from admin account (FLOW, FUSD) -- [ ] Detect fungible token deposits to admin account (FLOW, FUSD) -- [ ] Send fungible token withdrawals from a user account (FLOW, FUSD) -- [ ] Detect fungible token deposits to a user account (FLOW, FUSD) +- [ ] [Detect fungible token deposits to admin account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/2) +- [ ] [Send fungible token withdrawals from a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/3) +- [ ] [Detect fungible token deposits to a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/4) ## Non-Fungible Tokens From 9f33ee0bdd7153f0ebb3056f7ee24048dc69f96c Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 15 Apr 2021 11:33:41 -0700 Subject: [PATCH 020/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 651582be..7d261958 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ implements a simple custodial wallet service for the Flow blockchain. - [ ] [Send fungible token withdrawals from a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/3) - [ ] [Detect fungible token deposits to a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/4) -## Non-Fungible Tokens +### Non-Fungible Tokens - [ ] Send non-fungible token withdrawals from admin account - [ ] Detect non-fungible token deposits to admin account From 33a3eb120032f4d146f73baffca5bcfa4daf9f38 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 15 Apr 2021 22:08:43 -0700 Subject: [PATCH 021/112] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7d261958..79fac7a0 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@ implements a simple custodial wallet service for the Flow blockchain. ## Functionality -### Admin +### 1. Admin - [x] Single admin account (hot wallet) - [ ] [Create user accounts from admin account](https://github.com/onflow/flow-wallet-api-node-demo/issues/1) -### Fungible Tokens +### 2. Fungible Tokens - [x] Send fungible token withdrawals from admin account (FLOW, FUSD) - [ ] [Detect fungible token deposits to admin account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/2) From 54c056fd5fec0d9c0f733891658a3c8d9c00bea3 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 15 Apr 2021 22:08:54 -0700 Subject: [PATCH 022/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79fac7a0..46b5f147 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ implements a simple custodial wallet service for the Flow blockchain. - [ ] [Send fungible token withdrawals from a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/3) - [ ] [Detect fungible token deposits to a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/4) -### Non-Fungible Tokens +### 3. Non-Fungible Tokens - [ ] Send non-fungible token withdrawals from admin account - [ ] Detect non-fungible token deposits to admin account From 393c2eec165616ff0cca25d0e1b9d335a9934a1e Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 15 Apr 2021 22:11:17 -0700 Subject: [PATCH 023/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 46b5f147..253d7a73 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ implements a simple custodial wallet service for the Flow blockchain. ### 1. Admin - [x] Single admin account (hot wallet) -- [ ] [Create user accounts from admin account](https://github.com/onflow/flow-wallet-api-node-demo/issues/1) +- [ ] [Create user accounts (using admin account)](https://github.com/onflow/flow-wallet-api-node-demo/issues/1) ### 2. Fungible Tokens From ba84a04f1eb9150f7f3b54c46dd87844db4a97c2 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 15 Apr 2021 22:21:16 -0700 Subject: [PATCH 024/112] Convert to typescript --- package-lock.json | 583 ++++++++++++++++++ package.json | 11 +- src/app.js | 26 - src/app.ts | 26 + src/{config.js => config.ts} | 2 +- .../{fungibleTokens.js => fungibleTokens.ts} | 38 +- ...ountKey.js => getLeastRecentAccountKey.ts} | 6 +- ...{syncAccountKeys.js => syncAccountKeys.ts} | 6 +- src/errors/ApiError.js | 11 - src/errors/ApiError.ts | 12 + src/errors/InvalidFungibleTokenError.js | 13 - src/errors/InvalidFungibleTokenError.ts | 11 + src/errors/NotFoundError.js | 13 - src/errors/NotFoundError.ts | 11 + src/errors/{catchAsync.js => catchAsync.ts} | 2 +- src/{index.js => index.ts} | 6 +- src/lib/flow/{crypto.js => crypto.ts} | 17 +- src/lib/flow/{getSigner.js => getSigner.ts} | 8 +- ...{sendTransaction.js => sendTransaction.ts} | 6 +- src/lib/fungibleTokens/index.js | 25 - src/lib/fungibleTokens/index.ts | 19 + .../{transferFLOW.js => transferFLOW.ts} | 6 +- .../{transferFUSD.js => transferFUSD.ts} | 6 +- .../{transfer.js => transfer.ts} | 19 +- src/{logger.js => logger.ts} | 6 +- src/middleware/{errors.js => errors.ts} | 10 +- src/middleware/{morgan.js => morgan.ts} | 6 +- .../v1/fungibleTokens/{index.js => index.ts} | 8 +- .../{withdrawals.js => withdrawals.ts} | 6 +- src/routes/v1/index.js | 8 - src/routes/v1/index.ts | 8 + tsconfig.json | 8 + 32 files changed, 752 insertions(+), 191 deletions(-) delete mode 100644 src/app.js create mode 100644 src/app.ts rename src/{config.js => config.ts} (98%) rename src/controllers/{fungibleTokens.js => fungibleTokens.ts} (61%) rename src/database/{getLeastRecentAccountKey.js => getLeastRecentAccountKey.ts} (70%) rename src/database/{syncAccountKeys.js => syncAccountKeys.ts} (89%) delete mode 100644 src/errors/ApiError.js create mode 100644 src/errors/ApiError.ts delete mode 100644 src/errors/InvalidFungibleTokenError.js create mode 100644 src/errors/InvalidFungibleTokenError.ts delete mode 100644 src/errors/NotFoundError.js create mode 100644 src/errors/NotFoundError.ts rename src/errors/{catchAsync.js => catchAsync.ts} (80%) rename src/{index.js => index.ts} (85%) rename src/lib/flow/{crypto.js => crypto.ts} (79%) rename src/lib/flow/{getSigner.js => getSigner.ts} (80%) rename src/lib/flow/{sendTransaction.js => sendTransaction.ts} (78%) delete mode 100644 src/lib/fungibleTokens/index.js create mode 100644 src/lib/fungibleTokens/index.ts rename src/lib/fungibleTokens/templates/{transferFLOW.js => transferFLOW.ts} (87%) rename src/lib/fungibleTokens/templates/{transferFUSD.js => transferFUSD.ts} (86%) rename src/lib/fungibleTokens/{transfer.js => transfer.ts} (55%) rename src/{logger.js => logger.ts} (86%) rename src/middleware/{errors.js => errors.ts} (76%) rename src/middleware/{morgan.js => morgan.ts} (73%) rename src/routes/v1/fungibleTokens/{index.js => index.ts} (53%) rename src/routes/v1/fungibleTokens/{withdrawals.js => withdrawals.ts} (64%) delete mode 100644 src/routes/v1/index.js create mode 100644 src/routes/v1/index.ts create mode 100644 tsconfig.json diff --git a/package-lock.json b/package-lock.json index e8631dde..46321e0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -615,6 +615,94 @@ "defer-to-connect": "^1.0.1" } }, + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/express": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", + "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", + "integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "@types/node": { + "version": "14.14.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.39.tgz", + "integrity": "sha512-Qipn7rfTxGEDqZiezH+wxqWYR8vcXq5LRpZrETD19Gs4o8LbklbmqotSUsMU+s5G3PJwMRDfNEYoxrcBwIxOuw==", + "dev": true + }, + "@types/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "@types/serve-static": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", + "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "dev": true + }, + "@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -734,6 +822,12 @@ "picomatch": "^2.0.4" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -743,6 +837,12 @@ "sprintf-js": "~1.0.2" } }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -908,6 +1008,12 @@ "ieee754": "^1.2.1" } }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, "cacheable-request": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", @@ -962,6 +1068,24 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -1095,6 +1219,12 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1112,6 +1242,25 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1120,6 +1269,12 @@ "ms": "2.0.0" } }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, "decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", @@ -1162,6 +1317,12 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1191,6 +1352,15 @@ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", "dev": true }, + "dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", + "dev": true, + "requires": { + "xtend": "^4.0.0" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1251,6 +1421,23 @@ "ansi-colors": "^4.1.1" } }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + } + } + }, "escape-goat": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", @@ -1668,6 +1855,16 @@ "unpipe": "~1.0.0" } }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -1735,6 +1932,12 @@ "has-symbols": "^1.0.1" } }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -1858,6 +2061,12 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, "http-cache-semantics": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", @@ -1919,6 +2128,15 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1992,6 +2210,12 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -2064,6 +2288,12 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, "is-yarn-global": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", @@ -2154,6 +2384,19 @@ "type-check": "~0.4.0" } }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -2197,6 +2440,16 @@ } } }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -2229,11 +2482,41 @@ } } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -2293,6 +2576,12 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, "morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -2371,6 +2660,26 @@ "abbrev": "1" } }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -2383,6 +2692,12 @@ "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", "dev": true }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -2462,11 +2777,29 @@ "callsites": "^3.0.0" } }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2490,12 +2823,44 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, "picomatch": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", "dev": true }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2618,6 +2983,27 @@ } } }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -2637,6 +3023,16 @@ "picomatch": "^2.2.1" } }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, "regexpp": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", @@ -2661,6 +3057,15 @@ "rc": "^1.2.8" } }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -2853,6 +3258,56 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -2904,6 +3359,24 @@ "ansi-regex": "^5.0.0" } }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3003,11 +3476,93 @@ "nopt": "~1.0.10" } }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "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" + } + }, + "ts-node-dev": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.6.tgz", + "integrity": "sha512-RTUi7mHMNQospArGz07KiraQcdgUVNXKsgO2HAi7FoiyPMdTDqdniB6K1dqyaIxT7c9v/VpSbfBZPS6uVpaFLQ==", + "dev": true, + "requires": { + "chokidar": "^3.5.1", + "dateformat": "~1.0.4-1.2.3", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.5", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^9.0.0", + "tsconfig": "^7.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "requires": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3041,6 +3596,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", + "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", + "dev": true + }, "undefsafe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", @@ -3170,6 +3731,16 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3272,11 +3843,23 @@ "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index 3d5cc913..6d23243f 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,11 @@ "private": true, "scripts": { "start": "npm run deploy-contracts && npm run db-migrate-dev && npm run db-sync-account-keys && npm run start-api", - "start-api": "NODE_ENV=development nodemon ./src/index.js", + "start-api": "NODE_ENV=development ts-node-dev --respawn ./src/index.ts", "deploy-contracts": "flow project deploy --network=emulator --update", "db-migrate-dev": "prisma migrate dev --preview-feature", "db-migrate-deploy": "prisma migrate deploy --preview-feature", - "db-sync-account-keys": "node ./src/database/syncAccountKeys.js", + "db-sync-account-keys": "ts-node ./src/database/syncAccountKeys.ts", "lint": "eslint .", "lint-fix": "eslint . --fix" }, @@ -29,12 +29,17 @@ "winston": "^3.3.3" }, "devDependencies": { + "@types/express": "^4.17.11", + "@types/node": "^14.14.39", "babel-eslint": "^10.1.0", "eslint": "^7.21.0", "eslint-config-prettier": "^8.1.0", "eslint-plugin-prettier": "^3.3.1", "nodemon": "^2.0.7", "prettier": "^2.2.1", - "prisma": "^2.20.1" + "prisma": "^2.20.1", + "ts-node": "^9.1.1", + "ts-node-dev": "^1.1.6", + "typescript": "^4.2.4" } } diff --git a/src/app.js b/src/app.js deleted file mode 100644 index aa249779..00000000 --- a/src/app.js +++ /dev/null @@ -1,26 +0,0 @@ -require("dotenv").config() - -const express = require("express") -const morganMiddleware = require("./middleware/morgan") -const errorsMiddleware = require("./middleware/errors") -const NotFoundError = require("./errors/NotFoundError") - -const routes = require("./routes/v1") - -const app = express() - -app.use(morganMiddleware) -app.use(express.json()) -app.use(express.urlencoded({extended: false})) - -app.use("/v1", routes) - -// catch 404 and forward to error handler -app.use(function (req, res, next) { - next(new NotFoundError()) -}) - -// error handler -app.use(errorsMiddleware) - -module.exports = app diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 00000000..b83d04e9 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,26 @@ +require("dotenv").config() + +import * as express from "express" +import morganMiddleware from "./middleware/morgan" +import errorsMiddleware from "./middleware/errors" +import NotFoundError from "./errors/NotFoundError" + +import routes from "./routes/v1" + +const app = express() + +app.use(morganMiddleware) +app.use(express.json()) +app.use(express.urlencoded({extended: false})) + +app.use("/v1", routes) + +// catch 404 and forward to error handler +app.use((req, res, next) => { + next(new NotFoundError()) +}) + +// error handler +app.use(errorsMiddleware) + +export default app diff --git a/src/config.js b/src/config.ts similarity index 98% rename from src/config.js rename to src/config.ts index 8b85fd4b..0e16e7b4 100644 --- a/src/config.js +++ b/src/config.ts @@ -26,7 +26,7 @@ function getContracts(chain) { throw `Invalid chain: ${chain}` } -module.exports = { +export default { env: process.env.NODE_ENV, port: process.env.PORT || 3000, accessApiHost: process.env.ACCESS_API_HOST || "http://localhost:8080", diff --git a/src/controllers/fungibleTokens.js b/src/controllers/fungibleTokens.ts similarity index 61% rename from src/controllers/fungibleTokens.js rename to src/controllers/fungibleTokens.ts index a3fad324..e22af0de 100644 --- a/src/controllers/fungibleTokens.js +++ b/src/controllers/fungibleTokens.ts @@ -1,22 +1,22 @@ -const httpStatus = require("http-status") -const config = require("../config") -const { +import * as httpStatus from "http-status" +import config from "../config" +import { tokens, isValidToken, getTokenTransferFunc, -} = require("../lib/fungibleTokens") -const getSigner = require("../lib/flow/getSigner") -const getLeastRecentAccountKey = require("../database/getLeastRecentAccountKey") -const catchAsync = require("../errors/catchAsync") -const ApiError = require("../errors/ApiError") -const InvalidFungibleTokenError = require("../errors/InvalidFungibleTokenError") +} from "../lib/fungibleTokens" +import getSigner from "../lib/flow/getSigner" +import getLeastRecentAccountKey from "../database/getLeastRecentAccountKey" +import catchAsync from "../errors/catchAsync" +import ApiError from "../errors/ApiError" +import InvalidFungibleTokenError from "../errors/InvalidFungibleTokenError" const makeToken = tokenName => ({name: tokenName}) const allTokens = tokens.map(tokenName => makeToken(tokenName)) -const getTokens = catchAsync(async (req, res) => res.json(allTokens)) +export const getTokens = catchAsync(async (req, res) => res.json(allTokens)) -const getToken = catchAsync(async (req, res) => { +export const getToken = catchAsync(async (req, res) => { const tokenName = req.params.tokenName if (!isValidToken(tokenName)) { @@ -29,14 +29,16 @@ const getToken = catchAsync(async (req, res) => { }) // TODO: implement withdrawal getters -const getWithdrawals = catchAsync(async (req, res) => { +export const getWithdrawals = catchAsync(async (req, res) => { res.send("TODO: implement me") }) -const getWithdrawal = catchAsync(async (req, res) => { + +// TODO: implement withdrawal getters +export const getWithdrawal = catchAsync(async (req, res) => { res.send("TODO: implement me") }) -const createWithdrawal = catchAsync(async (req, res) => { +export const createWithdrawal = catchAsync(async (req, res) => { const tokenName = req.params.tokenName if (!isValidToken(tokenName)) { @@ -80,11 +82,3 @@ const createWithdrawal = catchAsync(async (req, res) => { ) } }) - -module.exports = { - getTokens, - getToken, - getWithdrawals, - getWithdrawal, - createWithdrawal, -} diff --git a/src/database/getLeastRecentAccountKey.js b/src/database/getLeastRecentAccountKey.ts similarity index 70% rename from src/database/getLeastRecentAccountKey.js rename to src/database/getLeastRecentAccountKey.ts index 272e7250..41de90d5 100644 --- a/src/database/getLeastRecentAccountKey.js +++ b/src/database/getLeastRecentAccountKey.ts @@ -1,4 +1,4 @@ -const {PrismaClient} = require("@prisma/client") +import {PrismaClient} from "@prisma/client" const prisma = new PrismaClient() @@ -14,9 +14,7 @@ WHERE index = ( RETURNING index ` -const getLeastRecentAccountKey = async () => { +export default async function getLeastRecentAccountKey() { const results = await prisma.$queryRaw(getLeastRecentAccountKeySql) return results[0].index } - -module.exports = getLeastRecentAccountKey diff --git a/src/database/syncAccountKeys.js b/src/database/syncAccountKeys.ts similarity index 89% rename from src/database/syncAccountKeys.js rename to src/database/syncAccountKeys.ts index f180d2be..50a479fa 100644 --- a/src/database/syncAccountKeys.js +++ b/src/database/syncAccountKeys.ts @@ -1,9 +1,9 @@ -const fcl = require("@onflow/fcl") -const dotenv = require("dotenv") +import * as fcl from "@onflow/fcl" +import * as dotenv from "dotenv" dotenv.config() -const {PrismaClient} = require("@prisma/client") +import {PrismaClient} from "@prisma/client" const prisma = new PrismaClient() diff --git a/src/errors/ApiError.js b/src/errors/ApiError.js deleted file mode 100644 index b96cfb39..00000000 --- a/src/errors/ApiError.js +++ /dev/null @@ -1,11 +0,0 @@ -class ApiError extends Error { - constructor(statusCode, message) { - super(message) - - this.statusCode = statusCode - - Error.captureStackTrace(this, this.constructor) - } -} - -module.exports = ApiError diff --git a/src/errors/ApiError.ts b/src/errors/ApiError.ts new file mode 100644 index 00000000..e0dc7766 --- /dev/null +++ b/src/errors/ApiError.ts @@ -0,0 +1,12 @@ +export default class ApiError extends Error { + + statusCode: number + + constructor(statusCode: number, message: string) { + super(message) + + this.statusCode = statusCode + + Error.captureStackTrace(this, this.constructor) + } +} diff --git a/src/errors/InvalidFungibleTokenError.js b/src/errors/InvalidFungibleTokenError.js deleted file mode 100644 index 4961247f..00000000 --- a/src/errors/InvalidFungibleTokenError.js +++ /dev/null @@ -1,13 +0,0 @@ -const httpStatus = require("http-status") -const ApiError = require("./ApiError") - -class InvalidFungibleTokenError extends ApiError { - constructor(token) { - const statusCode = httpStatus.BAD_REQUEST - const message = `${token} is not valid fungible token` - - super(statusCode, message) - } -} - -module.exports = InvalidFungibleTokenError diff --git a/src/errors/InvalidFungibleTokenError.ts b/src/errors/InvalidFungibleTokenError.ts new file mode 100644 index 00000000..faeeaea6 --- /dev/null +++ b/src/errors/InvalidFungibleTokenError.ts @@ -0,0 +1,11 @@ +import * as httpStatus from "http-status" +import ApiError from "./ApiError" + +export default class InvalidFungibleTokenError extends ApiError { + constructor(token: string) { + const statusCode = httpStatus.BAD_REQUEST + const message = `${token} is not valid fungible token` + + super(statusCode, message) + } +} diff --git a/src/errors/NotFoundError.js b/src/errors/NotFoundError.js deleted file mode 100644 index 58fb3829..00000000 --- a/src/errors/NotFoundError.js +++ /dev/null @@ -1,13 +0,0 @@ -const httpStatus = require("http-status") -const ApiError = require("./ApiError") - -class NotFoundError extends ApiError { - constructor() { - const statusCode = httpStatus.NOT_FOUND - const message = "not found" - - super(statusCode, message) - } -} - -module.exports = NotFoundError diff --git a/src/errors/NotFoundError.ts b/src/errors/NotFoundError.ts new file mode 100644 index 00000000..9ace0cf1 --- /dev/null +++ b/src/errors/NotFoundError.ts @@ -0,0 +1,11 @@ +import * as httpStatus from "http-status" +import ApiError from "./ApiError" + +export default class NotFoundError extends ApiError { + constructor() { + const statusCode = httpStatus.NOT_FOUND + const message = "not found" + + super(statusCode, message) + } +} diff --git a/src/errors/catchAsync.js b/src/errors/catchAsync.ts similarity index 80% rename from src/errors/catchAsync.js rename to src/errors/catchAsync.ts index e9c88664..17352486 100644 --- a/src/errors/catchAsync.js +++ b/src/errors/catchAsync.ts @@ -2,4 +2,4 @@ const catchAsync = fn => (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(err => next(err)) } -module.exports = catchAsync +export default catchAsync diff --git a/src/index.js b/src/index.ts similarity index 85% rename from src/index.js rename to src/index.ts index 76d40209..6e52c998 100644 --- a/src/index.js +++ b/src/index.ts @@ -1,6 +1,6 @@ -const app = require("./app") -const config = require("./config") -const logger = require("./logger") +import app from "./app" +import config from "./config" +import logger from "./logger" const server = app.listen(config.port, () => { logger.info(`Listening on port ${config.port}`) diff --git a/src/lib/flow/crypto.js b/src/lib/flow/crypto.ts similarity index 79% rename from src/lib/flow/crypto.js rename to src/lib/flow/crypto.ts index 7813792d..7848d00b 100644 --- a/src/lib/flow/crypto.js +++ b/src/lib/flow/crypto.ts @@ -1,13 +1,13 @@ -const {createHash} = require("crypto") -const {SHA3} = require("sha3") -const {ec: EC} = require("elliptic") +import {createHash} from "crypto" +import {SHA3} from "sha3" +import {ec as EC} from "elliptic" -const sigAlgos = { +export const sigAlgos = { ECDSA_P256: "ECDSA_P256", ECDSA_secp256k1: "ECDSA_secp256k1", } -const hashAlgos = { +export const hashAlgos = { SHA2_256: "SHA2_256", SHA3_256: "SHA3_256", } @@ -45,7 +45,7 @@ function encodeSignature(sig) { return Buffer.concat([r, s]).toString("hex") } -function signWithPrivateKey(privateKey, sigAlgo, hashAlgo, msg) { +export function signWithPrivateKey(privateKey, sigAlgo, hashAlgo, msg) { const signer = getSigner(sigAlgo) const hasher = getHasher(hashAlgo) @@ -57,8 +57,3 @@ function signWithPrivateKey(privateKey, sigAlgo, hashAlgo, msg) { return encodeSignature(sig) } -module.exports = { - sigAlgos, - hashAlgos, - signWithPrivateKey, -} diff --git a/src/lib/flow/getSigner.js b/src/lib/flow/getSigner.ts similarity index 80% rename from src/lib/flow/getSigner.js rename to src/lib/flow/getSigner.ts index 3f880dcf..ace42a93 100644 --- a/src/lib/flow/getSigner.js +++ b/src/lib/flow/getSigner.ts @@ -1,8 +1,8 @@ -const fcl = require("@onflow/fcl") +import * as fcl from "@onflow/fcl" -const {signWithPrivateKey} = require("./crypto") +import {signWithPrivateKey} from "./crypto" -function getSigner( +export default function getSigner( signerAddress, signerPrivateKey, signerSigAlgo, @@ -28,5 +28,3 @@ function getSigner( } } } - -module.exports = getSigner diff --git a/src/lib/flow/sendTransaction.js b/src/lib/flow/sendTransaction.ts similarity index 78% rename from src/lib/flow/sendTransaction.js rename to src/lib/flow/sendTransaction.ts index f5fe978a..6e4af4df 100644 --- a/src/lib/flow/sendTransaction.js +++ b/src/lib/flow/sendTransaction.ts @@ -1,6 +1,6 @@ -const fcl = require("@onflow/fcl") +import * as fcl from "@onflow/fcl" -async function sendTransaction({ +export default async function sendTransaction({ transaction, args, proposer, @@ -22,5 +22,3 @@ async function sendTransaction({ return transactionId } - -module.exports = sendTransaction diff --git a/src/lib/fungibleTokens/index.js b/src/lib/fungibleTokens/index.js deleted file mode 100644 index a956c906..00000000 --- a/src/lib/fungibleTokens/index.js +++ /dev/null @@ -1,25 +0,0 @@ -const {transferFLOW, transferFUSD} = require("./transfer") - -const tokenFLOW = "flow" -const tokenFUSD = "fusd" - -const tokens = [tokenFLOW, tokenFUSD] - -const transferFuncs = { - [tokenFLOW]: transferFLOW, - [tokenFUSD]: transferFUSD, -} - -function isValidToken(token) { - return token in transferFuncs -} - -function getTokenTransferFunc(token) { - return transferFuncs[token] -} - -module.exports = { - tokens, - isValidToken, - getTokenTransferFunc, -} diff --git a/src/lib/fungibleTokens/index.ts b/src/lib/fungibleTokens/index.ts new file mode 100644 index 00000000..783a701b --- /dev/null +++ b/src/lib/fungibleTokens/index.ts @@ -0,0 +1,19 @@ +import {transferFLOW, transferFUSD} from "./transfer" + +const tokenFLOW = "flow" +const tokenFUSD = "fusd" + +export const tokens = [tokenFLOW, tokenFUSD] + +const transferFuncs = { + [tokenFLOW]: transferFLOW, + [tokenFUSD]: transferFUSD, +} + +export function isValidToken(token) { + return token in transferFuncs +} + +export function getTokenTransferFunc(token) { + return transferFuncs[token] +} diff --git a/src/lib/fungibleTokens/templates/transferFLOW.js b/src/lib/fungibleTokens/templates/transferFLOW.ts similarity index 87% rename from src/lib/fungibleTokens/templates/transferFLOW.js rename to src/lib/fungibleTokens/templates/transferFLOW.ts index 2033e45f..92261ea3 100644 --- a/src/lib/fungibleTokens/templates/transferFLOW.js +++ b/src/lib/fungibleTokens/templates/transferFLOW.ts @@ -1,6 +1,6 @@ -const dedent = require("dedent-js") +import * as dedent from "dedent-js" -function template(contracts) { +export default function template(contracts) { return dedent` import FungibleToken from ${contracts.FungibleToken} import FlowToken from ${contracts.FlowToken} @@ -26,5 +26,3 @@ function template(contracts) { } ` } - -module.exports = template diff --git a/src/lib/fungibleTokens/templates/transferFUSD.js b/src/lib/fungibleTokens/templates/transferFUSD.ts similarity index 86% rename from src/lib/fungibleTokens/templates/transferFUSD.js rename to src/lib/fungibleTokens/templates/transferFUSD.ts index 5093bc01..761cc81d 100644 --- a/src/lib/fungibleTokens/templates/transferFUSD.js +++ b/src/lib/fungibleTokens/templates/transferFUSD.ts @@ -1,6 +1,6 @@ -const dedent = require("dedent-js") +import * as dedent from "dedent-js" -function template(contracts) { +export default function template(contracts) { return dedent` import FungibleToken from ${contracts.FungibleToken} import FUSD from ${contracts.FUSD} @@ -26,5 +26,3 @@ function template(contracts) { } ` } - -module.exports = template diff --git a/src/lib/fungibleTokens/transfer.js b/src/lib/fungibleTokens/transfer.ts similarity index 55% rename from src/lib/fungibleTokens/transfer.js rename to src/lib/fungibleTokens/transfer.ts index 695b7907..28ec67cf 100644 --- a/src/lib/fungibleTokens/transfer.js +++ b/src/lib/fungibleTokens/transfer.ts @@ -1,10 +1,10 @@ -const fcl = require("@onflow/fcl") -const t = require("@onflow/types") +import * as fcl from "@onflow/fcl" +import * as t from "@onflow/types" -const sendTransaction = require("../flow/sendTransaction") +import sendTransaction from "../flow/sendTransaction" -const transferFLOWTemplate = require("./templates/transferFLOW") -const transferFUSDTemplate = require("./templates/transferFUSD") +import transferFLOWTemplate from "./templates/transferFLOW" +import transferFUSDTemplate from "./templates/transferFUSD" async function transfer(template, recipient, amount, authorization, contracts) { return await sendTransaction({ @@ -19,7 +19,7 @@ async function transfer(template, recipient, amount, authorization, contracts) { }) } -async function transferFLOW(recipient, amount, authorization, contracts) { +export async function transferFLOW(recipient, amount, authorization, contracts) { return transfer( transferFLOWTemplate, recipient, @@ -29,7 +29,7 @@ async function transferFLOW(recipient, amount, authorization, contracts) { ) } -async function transferFUSD(recipient, amount, authorization, contracts) { +export async function transferFUSD(recipient, amount, authorization, contracts) { return transfer( transferFUSDTemplate, recipient, @@ -38,8 +38,3 @@ async function transferFUSD(recipient, amount, authorization, contracts) { contracts ) } - -module.exports = { - transferFLOW, - transferFUSD, -} diff --git a/src/logger.js b/src/logger.ts similarity index 86% rename from src/logger.js rename to src/logger.ts index 9c298a85..3c739f26 100644 --- a/src/logger.js +++ b/src/logger.ts @@ -1,5 +1,5 @@ -const winston = require("winston") -const config = require("./config") +import * as winston from "winston" +import config from "./config" const enumerateErrorFormat = winston.format(info => { if (info instanceof Error) { @@ -25,4 +25,4 @@ const logger = winston.createLogger({ ], }) -module.exports = logger +export default logger; diff --git a/src/middleware/errors.js b/src/middleware/errors.ts similarity index 76% rename from src/middleware/errors.js rename to src/middleware/errors.ts index 10018061..21ab2b81 100644 --- a/src/middleware/errors.js +++ b/src/middleware/errors.ts @@ -1,7 +1,7 @@ -const httpStatus = require("http-status") -const config = require("../config") -const logger = require("../logger") -const ApiError = require("../errors/ApiError") +import * as httpStatus from "http-status" +import config from "../config" +import logger from "../logger" +import ApiError from "../errors/ApiError" const errorsMiddleware = (err, req, res, next) => { let {statusCode, message} = err @@ -29,4 +29,4 @@ const errorsMiddleware = (err, req, res, next) => { res.status(statusCode).send(response) } -module.exports = errorsMiddleware +export default errorsMiddleware diff --git a/src/middleware/morgan.js b/src/middleware/morgan.ts similarity index 73% rename from src/middleware/morgan.js rename to src/middleware/morgan.ts index cfcc8a9c..ac1c7161 100644 --- a/src/middleware/morgan.js +++ b/src/middleware/morgan.ts @@ -1,5 +1,5 @@ -const morgan = require("morgan") -const logger = require("../logger") +import * as morgan from "morgan" +import logger from "../logger" const stream = { write: message => logger.info(message), @@ -15,4 +15,4 @@ const morganMiddleware = morgan( {stream, skip} ) -module.exports = morganMiddleware +export default morganMiddleware diff --git a/src/routes/v1/fungibleTokens/index.js b/src/routes/v1/fungibleTokens/index.ts similarity index 53% rename from src/routes/v1/fungibleTokens/index.js rename to src/routes/v1/fungibleTokens/index.ts index 1cd037d4..45445932 100644 --- a/src/routes/v1/fungibleTokens/index.js +++ b/src/routes/v1/fungibleTokens/index.ts @@ -1,6 +1,6 @@ -const express = require("express") -const fungibleTokensController = require("../../../controllers/fungibleTokens") -const withdrawals = require("./withdrawals") +import * as express from "express" +import * as fungibleTokensController from "../../../controllers/fungibleTokens" +import withdrawals from "./withdrawals" const router = express.Router() @@ -10,4 +10,4 @@ router.route("/:tokenName").get(fungibleTokensController.getToken) router.use("/:tokenName/withdrawals", withdrawals) -module.exports = router +export default router diff --git a/src/routes/v1/fungibleTokens/withdrawals.js b/src/routes/v1/fungibleTokens/withdrawals.ts similarity index 64% rename from src/routes/v1/fungibleTokens/withdrawals.js rename to src/routes/v1/fungibleTokens/withdrawals.ts index e1f96967..6b522f17 100644 --- a/src/routes/v1/fungibleTokens/withdrawals.js +++ b/src/routes/v1/fungibleTokens/withdrawals.ts @@ -1,5 +1,5 @@ -const express = require("express") -const fungibleTokensController = require("../../../controllers/fungibleTokens") +import * as express from "express" +import * as fungibleTokensController from "../../../controllers/fungibleTokens" const router = express.Router({mergeParams: true}) @@ -10,4 +10,4 @@ router router.route("/:transactionId").get(fungibleTokensController.getWithdrawal) -module.exports = router +export default router diff --git a/src/routes/v1/index.js b/src/routes/v1/index.js deleted file mode 100644 index ba8704c3..00000000 --- a/src/routes/v1/index.js +++ /dev/null @@ -1,8 +0,0 @@ -const express = require("express") -const fungibleTokensRoute = require("./fungibleTokens") - -const router = express.Router() - -router.use("/fungible-tokens", fungibleTokensRoute) - -module.exports = router diff --git a/src/routes/v1/index.ts b/src/routes/v1/index.ts new file mode 100644 index 00000000..155cb3e1 --- /dev/null +++ b/src/routes/v1/index.ts @@ -0,0 +1,8 @@ +import * as express from "express" +import fungibleTokensRoute from "./fungibleTokens" + +const router = express.Router() + +router.use("/fungible-tokens", fungibleTokensRoute) + +export default router diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..f1fa2cee --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "outDir": "./built", + "target": "es2020", + "moduleResolution": "node", + }, + "include": ["./src/**/*"] +} From b255bbaea37cd87fe5bf9e74468e9bcf9e11613c Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 15 Apr 2021 23:40:51 -0700 Subject: [PATCH 025/112] Add types --- .eslintrc.js | 11 + .eslintrc.json | 9 - package-lock.json | 1073 ++++++---------------- package.json | 3 +- src/app.ts | 3 +- src/config.ts | 12 +- src/controllers/fungibleTokens.ts | 8 +- src/database/getLeastRecentAccountKey.ts | 2 +- src/errors/ApiError.ts | 1 - src/errors/catchAsync.ts | 8 +- src/lib/flow/crypto.ts | 26 +- src/lib/flow/getSigner.ts | 15 +- src/lib/flow/index.ts | 20 + src/lib/flow/sendTransaction.ts | 17 +- src/lib/fungibleTokens/transfer.ts | 14 +- src/logger.ts | 2 +- 16 files changed, 408 insertions(+), 816 deletions(-) create mode 100644 .eslintrc.js delete mode 100644 .eslintrc.json create mode 100644 src/lib/flow/index.ts diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..008514dc --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,11 @@ +/* eslint-env node */ +module.exports = { + root: true, + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint", "prettier"], + extends: [ + "eslint:recommended", + "plugin:prettier/recommended", + "plugin:@typescript-eslint/recommended", + ], +} diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index c6fa6f97..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "parser": "babel-eslint", - "extends": [ - "plugin:prettier/recommended" - ], - "plugins": [ - "prettier" - ] -} diff --git a/package-lock.json b/package-lock.json index 46321e0a..b1ec7fc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -197,6 +197,32 @@ "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web-node-http-transport/-/grpc-web-node-http-transport-0.12.0.tgz", "integrity": "sha512-+Kjz+Dktfz5LKTZA9ZW/Vlww6HF9KaKz4x2mVe1O8CJdOP2WfzC+KY8L6EWMqVLrV4MvdBuQdSgDmvSJz+OGuA==" }, + "@nodelib/fs.scandir": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + } + }, "@onflow/config": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/@onflow/config/-/config-0.0.2.tgz", @@ -600,21 +626,6 @@ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e.tgz", "integrity": "sha512-fJhbGZXm2SPs/RsI79Ew4SFe+6QmChNdgU2I/SIjmU18bUgK8f1TBEWnVtFdBqEDHYPGxbpaianF7lp04KN7EA==" }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, "@types/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", @@ -657,6 +668,12 @@ "@types/range-parser": "*" } }, + "@types/json-schema": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", + "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "dev": true + }, "@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -703,12 +720,148 @@ "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", "dev": true }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "@typescript-eslint/eslint-plugin": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz", + "integrity": "sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.22.0", + "@typescript-eslint/scope-manager": "4.22.0", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "lodash": "^4.17.15", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz", + "integrity": "sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/scope-manager": "4.22.0", + "@typescript-eslint/types": "4.22.0", + "@typescript-eslint/typescript-estree": "4.22.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.0.tgz", + "integrity": "sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.22.0", + "@typescript-eslint/types": "4.22.0", + "@typescript-eslint/typescript-estree": "4.22.0", + "debug": "^4.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz", + "integrity": "sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.22.0", + "@typescript-eslint/visitor-keys": "4.22.0" + } + }, + "@typescript-eslint/types": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.0.tgz", + "integrity": "sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA==", "dev": true }, + "@typescript-eslint/typescript-estree": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz", + "integrity": "sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.22.0", + "@typescript-eslint/visitor-keys": "4.22.0", + "debug": "^4.1.1", + "globby": "^11.0.1", + "is-glob": "^4.0.1", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz", + "integrity": "sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.22.0", + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + } + } + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -742,55 +895,6 @@ "uri-js": "^4.2.2" } }, - "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "dev": true, - "requires": { - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -848,6 +952,12 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -903,73 +1013,6 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, - "boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "dev": true, - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1014,38 +1057,6 @@ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - } - } - }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -1062,12 +1073,6 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, "camelcase-keys": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", @@ -1113,27 +1118,6 @@ "readdirp": "~3.5.0" } }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, "color": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", @@ -1185,20 +1169,6 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -1236,12 +1206,6 @@ "which": "^2.0.1" } }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true - }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -1275,38 +1239,17 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, "dedent-js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz", "integrity": "sha1-vuX7fJ5yfYXf+iRZDRDsGrElUwU=" }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1323,6 +1266,23 @@ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + }, + "dependencies": { + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + } + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1332,26 +1292,11 @@ "esutils": "^2.0.2" } }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, "dotenv": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, "dynamic-dedupe": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", @@ -1403,15 +1348,6 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, "enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -1438,12 +1374,6 @@ } } }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true - }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1801,6 +1731,20 @@ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", "dev": true }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1818,6 +1762,15 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "fecha": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", @@ -1938,15 +1891,6 @@ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", "dev": true }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -1970,45 +1914,39 @@ "is-glob": "^4.0.1" } }, - "global-dirs": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz", - "integrity": "sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==", - "dev": true, - "requires": { - "ini": "1.3.7" - } - }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "globby": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } + } + }, "google-protobuf": { "version": "3.15.7", "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.7.tgz", "integrity": "sha512-S/kTHcT98AV2FxEwtT5lvgffeS87BB6hloZm+pYKkpzwtySwNiKcqXZbxpq/Odh3Wib1RdOe/oY2EHdi17YrlQ==" }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, "graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", @@ -2036,12 +1974,6 @@ "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", "dev": true }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true - }, "hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -2067,12 +1999,6 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -2100,12 +2026,6 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", - "dev": true - }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -2116,12 +2036,6 @@ "resolve-from": "^4.0.0" } }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true - }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -2152,12 +2066,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, - "ini": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", - "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", - "dev": true - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2186,15 +2094,6 @@ "call-bind": "^1.0.0" } }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, "is-core-module": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", @@ -2231,44 +2130,16 @@ "is-extglob": "^2.1.1" } }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "dev": true, - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, - "is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", - "dev": true - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", "dev": true }, "is-stream": { @@ -2282,24 +2153,12 @@ "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", "dev": true }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", "dev": true }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -2333,12 +2192,6 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2351,29 +2204,11 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, "kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", - "dev": true, - "requires": { - "package-json": "^6.3.0" - } - }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2450,12 +2285,6 @@ "signal-exit": "^3.0.0" } }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2465,23 +2294,6 @@ "yallist": "^4.0.0" } }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -2522,11 +2334,27 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, "mime": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", @@ -2545,12 +2373,6 @@ "mime-db": "1.47.0" } }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -2610,56 +2432,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, - "nodemon": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", - "integrity": "sha512-XHzK69Awgnec9UzHr1kc8EomQh4sjTQ8oRf8TsGrSmHDx9/UmiGG9E/mM3BuTfNeFwdNBvrqQq/RHL0xIeyFOA==", - "dev": true, - "requires": { - "chokidar": "^3.2.2", - "debug": "^3.2.6", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", - "semver": "^5.7.1", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.3", - "update-notifier": "^4.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -2686,12 +2458,6 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2742,32 +2508,6 @@ "word-wrap": "^1.2.3" } }, - "p-cancelable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true - }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2867,12 +2607,6 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "prepend-http": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true - }, "prettier": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", @@ -2917,37 +2651,12 @@ "ipaddr.js": "1.9.1" } }, - "pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, - "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, - "requires": { - "escape-goat": "^2.0.0" - } - }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -2963,26 +2672,6 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - } - } - }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -3039,24 +2728,6 @@ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", "dev": true }, - "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "dev": true, - "requires": { - "rc": "^1.2.8" - } - }, "repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", @@ -3088,14 +2759,11 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" - } + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true }, "rimraf": { "version": "3.0.2", @@ -3106,6 +2774,23 @@ "glob": "^7.1.3" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + }, + "dependencies": { + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -3125,23 +2810,6 @@ "lru-cache": "^6.0.0" } }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, "send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -3215,6 +2883,12 @@ "is-arrayish": "^0.3.1" } }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -3429,12 +3103,6 @@ } } }, - "term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "dev": true - }, "text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", @@ -3452,12 +3120,6 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3467,15 +3129,6 @@ "is-number": "^7.0.0" } }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "requires": { - "nopt": "~1.0.10" - } - }, "tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -3563,6 +3216,21 @@ } } }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3587,116 +3255,17 @@ "mime-types": "~2.1.24" } }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, "typescript": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", "dev": true }, - "undefsafe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", - "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", - "dev": true, - "requires": { - "debug": "^2.2.0" - } - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, - "update-notifier": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", - "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", - "dev": true, - "requires": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -3706,15 +3275,6 @@ "punycode": "^2.1.0" } }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3755,15 +3315,6 @@ "isexe": "^2.0.0" } }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "requires": { - "string-width": "^4.0.0" - } - }, "winston": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", @@ -3825,24 +3376,6 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 6d23243f..dd2ad8dd 100644 --- a/package.json +++ b/package.json @@ -31,11 +31,12 @@ "devDependencies": { "@types/express": "^4.17.11", "@types/node": "^14.14.39", + "@typescript-eslint/eslint-plugin": "^4.22.0", + "@typescript-eslint/parser": "^4.22.0", "babel-eslint": "^10.1.0", "eslint": "^7.21.0", "eslint-config-prettier": "^8.1.0", "eslint-plugin-prettier": "^3.3.1", - "nodemon": "^2.0.7", "prettier": "^2.2.1", "prisma": "^2.20.1", "ts-node": "^9.1.1", diff --git a/src/app.ts b/src/app.ts index b83d04e9..a38ecad1 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,5 @@ -require("dotenv").config() +import * as dotenv from "dotenv" +dotenv.config() import * as express from "express" import morganMiddleware from "./middleware/morgan" diff --git a/src/config.ts b/src/config.ts index 0e16e7b4..2821e674 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,3 +1,5 @@ +import { HashAlgorithm, SignatureAlgorithm } from "./lib/flow/crypto" + const chainEmulator = "emulator" const chainTestnet = "testnet" @@ -26,13 +28,15 @@ function getContracts(chain) { throw `Invalid chain: ${chain}` } -export default { +const config = { env: process.env.NODE_ENV, port: process.env.PORT || 3000, accessApiHost: process.env.ACCESS_API_HOST || "http://localhost:8080", adminAddress: process.env.ADMIN_ADDRESS || "0xf8d6e0586b0a20c7", adminPrivateKey: process.env.ADMIN_PRIVATE_KEY, - adminSigAlgo: process.env.ADMIN_SIG_ALGO || "ECDSA_P256", - adminHashAlgo: process.env.ADMIN_HASH_ALGO || "SHA3_256", + adminSigAlgo: SignatureAlgorithm[(process.env.ADMIN_SIG_ALGO || "ECDSA_P256")], + adminHashAlgo: (process.env.ADMIN_HASH_ALGO || "SHA3_256") as HashAlgorithm, contracts: getContracts(process.env.CHAIN), -} +} + +export default config; diff --git a/src/controllers/fungibleTokens.ts b/src/controllers/fungibleTokens.ts index e22af0de..d828b14b 100644 --- a/src/controllers/fungibleTokens.ts +++ b/src/controllers/fungibleTokens.ts @@ -1,11 +1,7 @@ import * as httpStatus from "http-status" import config from "../config" -import { - tokens, - isValidToken, - getTokenTransferFunc, -} from "../lib/fungibleTokens" -import getSigner from "../lib/flow/getSigner" +import {tokens, isValidToken, getTokenTransferFunc} from "../lib/fungibleTokens" +import {getSigner} from "../lib/flow" import getLeastRecentAccountKey from "../database/getLeastRecentAccountKey" import catchAsync from "../errors/catchAsync" import ApiError from "../errors/ApiError" diff --git a/src/database/getLeastRecentAccountKey.ts b/src/database/getLeastRecentAccountKey.ts index 41de90d5..af75d43e 100644 --- a/src/database/getLeastRecentAccountKey.ts +++ b/src/database/getLeastRecentAccountKey.ts @@ -14,7 +14,7 @@ WHERE index = ( RETURNING index ` -export default async function getLeastRecentAccountKey() { +export default async function getLeastRecentAccountKey(): Promise { const results = await prisma.$queryRaw(getLeastRecentAccountKeySql) return results[0].index } diff --git a/src/errors/ApiError.ts b/src/errors/ApiError.ts index e0dc7766..ee0b4599 100644 --- a/src/errors/ApiError.ts +++ b/src/errors/ApiError.ts @@ -1,5 +1,4 @@ export default class ApiError extends Error { - statusCode: number constructor(statusCode: number, message: string) { diff --git a/src/errors/catchAsync.ts b/src/errors/catchAsync.ts index 17352486..f5533908 100644 --- a/src/errors/catchAsync.ts +++ b/src/errors/catchAsync.ts @@ -1,4 +1,10 @@ -const catchAsync = fn => (req, res, next) => { +import * as express from "express" + +const catchAsync = (fn: express.RequestHandler) => ( + req: express.Request, + res: express.Response, + next: express.NextFunction +): void => { Promise.resolve(fn(req, res, next)).catch(err => next(err)) } diff --git a/src/lib/flow/crypto.ts b/src/lib/flow/crypto.ts index 7848d00b..e566e04f 100644 --- a/src/lib/flow/crypto.ts +++ b/src/lib/flow/crypto.ts @@ -2,14 +2,14 @@ import {createHash} from "crypto" import {SHA3} from "sha3" import {ec as EC} from "elliptic" -export const sigAlgos = { - ECDSA_P256: "ECDSA_P256", - ECDSA_secp256k1: "ECDSA_secp256k1", +export enum SignatureAlgorithm { + ECDSA_P256 = "ECDSA_P256", + ECDSA_secp256k1 = "ECDSA_secp256k1", } -export const hashAlgos = { - SHA2_256: "SHA2_256", - SHA3_256: "SHA3_256", +export enum HashAlgorithm { + SHA2_256 = "SHA2_256", + SHA3_256 = "SHA3_256", } const signers = { @@ -37,15 +37,20 @@ const hashers = { const getSigner = sigAlgo => signers[sigAlgo]() const getHasher = hashAlgo => hashers[hashAlgo] -function encodeSignature(sig) { +function encodeSignature(rawSig): string { const n = 32 - const r = sig.r.toArrayLike(Buffer, "be", n) - const s = sig.s.toArrayLike(Buffer, "be", n) + const r = rawSig.r.toArrayLike(Buffer, "be", n) + const s = rawSig.s.toArrayLike(Buffer, "be", n) return Buffer.concat([r, s]).toString("hex") } -export function signWithPrivateKey(privateKey, sigAlgo, hashAlgo, msg) { +export function signWithPrivateKey( + privateKey: string, + sigAlgo: SignatureAlgorithm, + hashAlgo: HashAlgorithm, + msg: string +): string { const signer = getSigner(sigAlgo) const hasher = getHasher(hashAlgo) @@ -56,4 +61,3 @@ export function signWithPrivateKey(privateKey, sigAlgo, hashAlgo, msg) { return encodeSignature(sig) } - diff --git a/src/lib/flow/getSigner.ts b/src/lib/flow/getSigner.ts index ace42a93..2bec1d2b 100644 --- a/src/lib/flow/getSigner.ts +++ b/src/lib/flow/getSigner.ts @@ -1,14 +1,15 @@ import * as fcl from "@onflow/fcl" -import {signWithPrivateKey} from "./crypto" +import {Account, AccountAuthorization} from "./index" +import {SignatureAlgorithm, HashAlgorithm, signWithPrivateKey} from "./crypto" export default function getSigner( - signerAddress, - signerPrivateKey, - signerSigAlgo, - signerHashAlgo, - signerKeyIndex -) { + signerAddress: string, + signerPrivateKey: string, + signerSigAlgo: SignatureAlgorithm, + signerHashAlgo: HashAlgorithm, + signerKeyIndex: number +): (account?: Account) => Promise { return async (account = {}) => { return { ...account, diff --git a/src/lib/flow/index.ts b/src/lib/flow/index.ts new file mode 100644 index 00000000..74ab6f3b --- /dev/null +++ b/src/lib/flow/index.ts @@ -0,0 +1,20 @@ +import getSigner from "./getSigner" + +export interface Account { + addr?: string +} + +export interface AccountSignature { + addr: string + keyId: number + signature: string +} + +export interface AccountAuthorization { + tempId: string + addr: string + keyId: number + signingFunction: (data: {message: string}) => AccountSignature +} + +export {getSigner} diff --git a/src/lib/flow/sendTransaction.ts b/src/lib/flow/sendTransaction.ts index 6e4af4df..b096543a 100644 --- a/src/lib/flow/sendTransaction.ts +++ b/src/lib/flow/sendTransaction.ts @@ -1,12 +1,27 @@ import * as fcl from "@onflow/fcl" +import {AccountAuthorization} from "./index" + +interface Argument { + value: string + xform: any +} + +interface Transaction { + transaction: string, + args: Argument[], + proposer: string, + authorizations: AccountAuthorization[], + payer: string +} + export default async function sendTransaction({ transaction, args, proposer, authorizations, payer, -}) { +}: Transaction): Promise { const response = await fcl.send([ fcl.transaction(transaction), fcl.args(args), diff --git a/src/lib/fungibleTokens/transfer.ts b/src/lib/fungibleTokens/transfer.ts index 28ec67cf..6f63942d 100644 --- a/src/lib/fungibleTokens/transfer.ts +++ b/src/lib/fungibleTokens/transfer.ts @@ -19,7 +19,12 @@ async function transfer(template, recipient, amount, authorization, contracts) { }) } -export async function transferFLOW(recipient, amount, authorization, contracts) { +export async function transferFLOW( + recipient, + amount, + authorization, + contracts +) { return transfer( transferFLOWTemplate, recipient, @@ -29,7 +34,12 @@ export async function transferFLOW(recipient, amount, authorization, contracts) ) } -export async function transferFUSD(recipient, amount, authorization, contracts) { +export async function transferFUSD( + recipient, + amount, + authorization, + contracts +) { return transfer( transferFUSDTemplate, recipient, diff --git a/src/logger.ts b/src/logger.ts index 3c739f26..3db0f4cf 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -25,4 +25,4 @@ const logger = winston.createLogger({ ], }) -export default logger; +export default logger From aba0232822bec43edf222aec563dcdfe337a1460 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 16 Apr 2021 00:30:40 -0700 Subject: [PATCH 026/112] Rename getSigner to getAccountAuthorization --- src/controllers/fungibleTokens.ts | 6 +++--- src/lib/flow/{getSigner.ts => getAccountAuthorization.ts} | 2 +- src/lib/flow/index.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/lib/flow/{getSigner.ts => getAccountAuthorization.ts} (94%) diff --git a/src/controllers/fungibleTokens.ts b/src/controllers/fungibleTokens.ts index d828b14b..0c0ac6c2 100644 --- a/src/controllers/fungibleTokens.ts +++ b/src/controllers/fungibleTokens.ts @@ -1,7 +1,7 @@ import * as httpStatus from "http-status" import config from "../config" import {tokens, isValidToken, getTokenTransferFunc} from "../lib/fungibleTokens" -import {getSigner} from "../lib/flow" +import {getAccountAuthorization} from "../lib/flow" import getLeastRecentAccountKey from "../database/getLeastRecentAccountKey" import catchAsync from "../errors/catchAsync" import ApiError from "../errors/ApiError" @@ -46,7 +46,7 @@ export const createWithdrawal = catchAsync(async (req, res) => { const adminKeyIndex = await getLeastRecentAccountKey() - const signer = getSigner( + const authorization = getAccountAuthorization( config.adminAddress, config.adminPrivateKey, config.adminSigAlgo, @@ -60,7 +60,7 @@ export const createWithdrawal = catchAsync(async (req, res) => { const transactionId = await transfer( recipient, amount, - signer, + authorization, config.contracts ) diff --git a/src/lib/flow/getSigner.ts b/src/lib/flow/getAccountAuthorization.ts similarity index 94% rename from src/lib/flow/getSigner.ts rename to src/lib/flow/getAccountAuthorization.ts index 2bec1d2b..889a6894 100644 --- a/src/lib/flow/getSigner.ts +++ b/src/lib/flow/getAccountAuthorization.ts @@ -3,7 +3,7 @@ import * as fcl from "@onflow/fcl" import {Account, AccountAuthorization} from "./index" import {SignatureAlgorithm, HashAlgorithm, signWithPrivateKey} from "./crypto" -export default function getSigner( +export default function getAccountAuthorization( signerAddress: string, signerPrivateKey: string, signerSigAlgo: SignatureAlgorithm, diff --git a/src/lib/flow/index.ts b/src/lib/flow/index.ts index 74ab6f3b..e0ca9a5a 100644 --- a/src/lib/flow/index.ts +++ b/src/lib/flow/index.ts @@ -1,4 +1,4 @@ -import getSigner from "./getSigner" +import getAccountAuthorization from "./getAccountAuthorization" export interface Account { addr?: string @@ -17,4 +17,4 @@ export interface AccountAuthorization { signingFunction: (data: {message: string}) => AccountSignature } -export {getSigner} +export {getAccountAuthorization} From 85a546ec98fa48144f232fc0dcd38d92a1f4d56d Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 18 Apr 2021 18:57:39 -0700 Subject: [PATCH 027/112] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 253d7a73..83f70ded 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,12 @@ implements a simple custodial wallet service for the Flow blockchain. ### 3. Non-Fungible Tokens +- [ ] Set up admin account with non-fungible token collections (`NFT.Collection`) - [ ] Send non-fungible token withdrawals from admin account - [ ] Detect non-fungible token deposits to admin account +- [ ] Set up a user account with non-fungible token collections (`NFT.Collection`) - [ ] Send non-fungible token withdrawals from a user account -- [ ] Detect non-fungible token deposits to aa user account +- [ ] Detect non-fungible token deposits to a user account ## Getting Started From 7336f8a17049798144935391b877ef724c887a45 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 19 Apr 2021 00:02:30 -0700 Subject: [PATCH 028/112] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 83f70ded..d10e2b1a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Flow Wallet API Demo (Node.js + express) +> :warning: This demo is a work in progress. + + This is a demonstration of a RESTful API that implements a simple custodial wallet service for the Flow blockchain. From 934a91745ecf1b56a8d1100cc766edbde4fe8f35 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 19 Apr 2021 22:55:45 -0700 Subject: [PATCH 029/112] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d10e2b1a..663a9e83 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ curl --request GET \ #### List all withdrawals of a token type -:warning: _Not yet implemented_ +> :warning: Not yet implemented `GET /v1/fungible-tokens/{tokenName}/withdrawals` @@ -126,7 +126,7 @@ Parameters #### Get details of a token withdrawal -:warning: _Not yet implemented_ +> :warning: Not yet implemented `GET /v1/fungible-tokens/{tokenName}/withdrawals/{transactionId}` @@ -172,11 +172,11 @@ curl --request GET \ ### Non-Fungible Tokens -:warning: _Not yet implemented_ +> :warning: Not yet implemented #### List all tokens -:warning: _Not yet implemented_ +> :warning: Not yet implemented `GET /v1/non-fungible-tokens` @@ -191,7 +191,7 @@ curl --request GET \ #### Get details of a token -:warning: _Not yet implemented_ +> :warning: Not yet implemented `GET /v1/non-fungible-tokens/{tokenName}` @@ -203,7 +203,7 @@ Parameters #### List all withdrawals of a token type -:warning: _Not yet implemented_ +> :warning: Not yet implemented `GET /v1/non-fungible-tokens/{tokenName}/withdrawals` @@ -215,7 +215,7 @@ Parameters #### Get details of a token withdrawal -:warning: _Not yet implemented_ +> :warning: Not yet implemented `GET /v1/non-fungible-tokens/{tokenName}/withdrawals/{transactionId}` @@ -228,7 +228,7 @@ Parameters #### Create a token withdrawal -:warning: _Not yet implemented_ +> :warning: Not yet implemented `POST /v1/non-fungible-tokens/{tokenName}/withdrawals` From 036f15a6e05c77e2315e461088667041558ca5ab Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 20 Apr 2021 17:43:38 -0700 Subject: [PATCH 030/112] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 663a9e83..d9c88fd0 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ implements a simple custodial wallet service for the Flow blockchain. - [ ] [Detect fungible token deposits to admin account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/2) - [ ] [Send fungible token withdrawals from a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/3) - [ ] [Detect fungible token deposits to a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/4) +- [ ] View the fungible token balance of the admin account +- [ ] View the fungible token balance of a user account ### 3. Non-Fungible Tokens @@ -28,6 +30,8 @@ implements a simple custodial wallet service for the Flow blockchain. - [ ] Set up a user account with non-fungible token collections (`NFT.Collection`) - [ ] Send non-fungible token withdrawals from a user account - [ ] Detect non-fungible token deposits to a user account +- [ ] View the non-fungible tokens owned by the admin account +- [ ] View the non-fungible tokens owned by a user account ## Getting Started From b25a07477f73993c074e40f05f1b546ce129527c Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 22 Apr 2021 01:18:41 -0700 Subject: [PATCH 031/112] Restructure code to eventually support accounts --- README.md | 129 +++++++++++++++--- package-lock.json | 14 +- package.json | 6 +- src/controllers/accounts.ts | 25 ++++ src/controllers/fungibleTokens.ts | 15 +- ...Key.ts => getLeastRecentAdminSignerKey.ts} | 10 +- .../migration.sql | 17 +++ src/database/schema.prisma | 9 +- ...cAccountKeys.ts => syncAdminSignerKeys.ts} | 10 +- ...ntAuthorization.ts => getAuthorization.ts} | 6 +- src/lib/flow/index.ts | 6 +- .../v1/{ => accounts}/fungibleTokens/index.ts | 4 +- .../fungibleTokens/withdrawals.ts | 2 +- src/routes/v1/accounts/index.ts | 13 ++ src/routes/v1/index.ts | 4 +- src/services/accounts.ts | 67 +++++++++ 16 files changed, 273 insertions(+), 64 deletions(-) create mode 100644 src/controllers/accounts.ts rename src/database/{getLeastRecentAccountKey.ts => getLeastRecentAdminSignerKey.ts} (59%) create mode 100644 src/database/migrations/20210421075756_admin_signer_keys/migration.sql rename src/database/{syncAccountKeys.ts => syncAdminSignerKeys.ts} (71%) rename src/lib/flow/{getAccountAuthorization.ts => getAuthorization.ts} (81%) rename src/routes/v1/{ => accounts}/fungibleTokens/index.ts (67%) rename src/routes/v1/{ => accounts}/fungibleTokens/withdrawals.ts (78%) create mode 100644 src/routes/v1/accounts/index.ts create mode 100644 src/services/accounts.ts diff --git a/README.md b/README.md index d9c88fd0..ed0e3f38 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,80 @@ npm run start ## API Routes +### Accounts + +#### List all accounts + +> :warning: Not yet implemented + +`GET /v1/accounts` + +Example + +```sh +curl --request GET \ + --url http://localhost:3000/v1/accounts +``` + +```json +[ + { + "address": "0xf8d6e0586b0a20c7" + }, + { + "address": "0xe467b9dd11fa00df" + } +] +``` + +--- + +### Get an account + +> :warning: Not yet implemented + +`GET /v1/accounts/{address}` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") + +Example + +```sh +curl --request GET \ + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7 +``` + +```json +{ + "address": "0xf8d6e0586b0a20c7" +} +``` + +--- + +### Create an account + +> :warning: Not yet implemented + +`POST /v1/accounts` + +Example + +```sh +curl --request POST \ + --url http://localhost:3000/v1/accounts +``` + +```json +{ + "address": "0xe467b9dd11fa00df" +} +``` + +--- + ### Fungible Tokens Supported tokens: @@ -71,13 +145,17 @@ Supported tokens: #### List all tokens -`GET /v1/fungible-tokens` +`GET /v1/accounts/{address}/fungible-tokens` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") Example ```sh curl --request GET \ - --url http://localhost:3000/v1/fungible-tokens + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens ``` ```json @@ -93,24 +171,26 @@ curl --request GET \ --- -#### Get details of a token +#### Get details of a token type -`GET /v1/fungible-tokens/{tokenName}` +`GET /v1/accounts/{address}/fungible-tokens/{tokenName}` Parameters +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - `tokenName`: The name of the fungible token (e.g. "flow") Example ```sh curl --request GET \ - --url http://localhost:3000/v1/fungible-tokens/flow + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens/flow ``` ```json { - "name": "flow" + "name": "flow", + "balance": "42.0" } ``` @@ -120,10 +200,11 @@ curl --request GET \ > :warning: Not yet implemented -`GET /v1/fungible-tokens/{tokenName}/withdrawals` +`GET /v1/accounts/{v1/accounts/{address}/fungible-tokens}/fungible-tokens/{tokenName}/withdrawals` Parameters +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - `tokenName`: The name of the fungible token (e.g. "flow") --- @@ -132,10 +213,11 @@ Parameters > :warning: Not yet implemented -`GET /v1/fungible-tokens/{tokenName}/withdrawals/{transactionId}` +`GET /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals/{transactionId}` Parameters +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - `tokenName`: The name of the fungible token (e.g. "flow") - `transactionId`: The Flow transaction ID for the withdrawal @@ -143,10 +225,11 @@ Parameters #### Create a token withdrawal -`POST /v1/fungible-tokens/{tokenName}/withdrawals` +`POST /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals` Parameters +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - `tokenName`: The name of the fungible token (e.g. "flow") Body (JSON) @@ -158,16 +241,16 @@ Body (JSON) Example ```sh -curl --request GET \ - --url http://localhost:3000/v1/fungible-tokens \ +curl --request POST \ + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens/fusd/withdrawls \ --header 'Content-Type: application/json' \ - --data '{ "recipient": "0xf8d6e0586b0a20c7", "amount": "123.456" }' + --data '{ "recipient": "0xe467b9dd11fa00df", "amount": "123.456" }' ``` ```json { "transactionId": "18647b584a03345f3b2d2c4d9ab2c4179ae1b124a7f62ef9f33910e5ca8b353c", - "recipient": "0xf8d6e0586b0a20c7", + "recipient": "0xe467b9dd11fa00df", "amount": "123.456" } ``` @@ -182,13 +265,17 @@ curl --request GET \ > :warning: Not yet implemented -`GET /v1/non-fungible-tokens` +`GET /v1/accounts/{address}/non-fungible-tokens` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") Example ```sh curl --request GET \ - --url http://localhost:3000/v1/non-fungible-tokens + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/non-fungible-tokens ``` --- @@ -197,10 +284,11 @@ curl --request GET \ > :warning: Not yet implemented -`GET /v1/non-fungible-tokens/{tokenName}` +`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}` Parameters +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") --- @@ -209,10 +297,11 @@ Parameters > :warning: Not yet implemented -`GET /v1/non-fungible-tokens/{tokenName}/withdrawals` +`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals` Parameters +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") --- @@ -221,10 +310,11 @@ Parameters > :warning: Not yet implemented -`GET /v1/non-fungible-tokens/{tokenName}/withdrawals/{transactionId}` +`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals/{transactionId}` Parameters +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") - `transactionId`: The Flow transaction ID for the withdrawal @@ -234,10 +324,11 @@ Parameters > :warning: Not yet implemented -`POST /v1/non-fungible-tokens/{tokenName}/withdrawals` +`POST /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals` Parameters +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") Body (JSON) diff --git a/package-lock.json b/package-lock.json index b1ec7fc8..fb0666f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -608,11 +608,11 @@ "integrity": "sha512-SzBscBdyn1Zoks0Wo/w7J/Ds9IZ/T+KM/wyWMwWla4PnxwBFviy1BytEQY+sM5q1UNOvaGWgGEoRmH/oOCcglA==" }, "@prisma/client": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.20.1.tgz", - "integrity": "sha512-/IYPubBi55rNMHfE0wwglA6eTWEZD77oz+x+3Mm9ji2lDKdS1lnYKZ0wZX0E3AB8gTNL/zsGtfzmfjgn3ePyIw==", + "version": "2.21.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.21.2.tgz", + "integrity": "sha512-UjkOXYpxLuHyoMDsP2m0LTcxhrjQa1dEOLFe3aDrO/BLrs/2yUxyPdtwSKxizRXFzuXSGkKIK225vcjZRuMpAg==", "requires": { - "@prisma/engines-version": "2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e" + "@prisma/engines-version": "2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d" } }, "@prisma/engines": { @@ -622,9 +622,9 @@ "dev": true }, "@prisma/engines-version": { - "version": "2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e.tgz", - "integrity": "sha512-fJhbGZXm2SPs/RsI79Ew4SFe+6QmChNdgU2I/SIjmU18bUgK8f1TBEWnVtFdBqEDHYPGxbpaianF7lp04KN7EA==" + "version": "2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d.tgz", + "integrity": "sha512-9/fE1gdPWmjbMjXUJjrTMt848TsgEnSjZCcJ1wu9OAcRlAKKJBLehftqC3gSEShDijvMYgeTdGU5snMpwmv4vg==" }, "@types/body-parser": { "version": "1.19.0", diff --git a/package.json b/package.json index dd2ad8dd..69c40c9a 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "version": "0.0.0", "private": true, "scripts": { - "start": "npm run deploy-contracts && npm run db-migrate-dev && npm run db-sync-account-keys && npm run start-api", + "start": "npm run deploy-contracts && npm run db-migrate-dev && npm run db-sync-admin-keys && npm run start-api", "start-api": "NODE_ENV=development ts-node-dev --respawn ./src/index.ts", "deploy-contracts": "flow project deploy --network=emulator --update", "db-migrate-dev": "prisma migrate dev --preview-feature", "db-migrate-deploy": "prisma migrate deploy --preview-feature", - "db-sync-account-keys": "ts-node ./src/database/syncAccountKeys.ts", + "db-sync-admin-keys": "ts-node ./src/database/syncAdminSignerKeys.ts", "lint": "eslint .", "lint-fix": "eslint . --fix" }, @@ -18,7 +18,7 @@ "dependencies": { "@onflow/fcl": "0.0.67", "@onflow/types": "0.0.4", - "@prisma/client": "^2.20.1", + "@prisma/client": "^2.21.2", "dedent-js": "^1.0.1", "dotenv": "^8.2.0", "elliptic": "^6.5.4", diff --git a/src/controllers/accounts.ts b/src/controllers/accounts.ts new file mode 100644 index 00000000..7c608fb5 --- /dev/null +++ b/src/controllers/accounts.ts @@ -0,0 +1,25 @@ +import config from "../config" +import catchAsync from "../errors/catchAsync" +import NotFoundError from "../errors/NotFoundError" +import { getAccountByAddress, queryAccounts } from "../services/accounts" + +export const getAccounts = catchAsync(async (req, res) => { + const accounts = await queryAccounts() + + res.json(accounts) +}) + +export const getAccount = catchAsync(async (req, res) => { + const address = req.params.address + + const account = await getAccountByAddress(address) + + if (account === null) { + throw new NotFoundError() + } + + res.json(account) +}) + +// TODO: implement create account function +export const createAccount = catchAsync(async (req, res) => {}) diff --git a/src/controllers/fungibleTokens.ts b/src/controllers/fungibleTokens.ts index 0c0ac6c2..255e8aa1 100644 --- a/src/controllers/fungibleTokens.ts +++ b/src/controllers/fungibleTokens.ts @@ -1,8 +1,7 @@ import * as httpStatus from "http-status" import config from "../config" import {tokens, isValidToken, getTokenTransferFunc} from "../lib/fungibleTokens" -import {getAccountAuthorization} from "../lib/flow" -import getLeastRecentAccountKey from "../database/getLeastRecentAccountKey" +import {getAccountAuthorization} from "../services/accounts" import catchAsync from "../errors/catchAsync" import ApiError from "../errors/ApiError" import InvalidFungibleTokenError from "../errors/InvalidFungibleTokenError" @@ -35,6 +34,7 @@ export const getWithdrawal = catchAsync(async (req, res) => { }) export const createWithdrawal = catchAsync(async (req, res) => { + const address = req.params.address const tokenName = req.params.tokenName if (!isValidToken(tokenName)) { @@ -44,15 +44,7 @@ export const createWithdrawal = catchAsync(async (req, res) => { // TODO: validate recipient and amount const {recipient, amount} = req.body - const adminKeyIndex = await getLeastRecentAccountKey() - - const authorization = getAccountAuthorization( - config.adminAddress, - config.adminPrivateKey, - config.adminSigAlgo, - config.adminHashAlgo, - adminKeyIndex - ) + const authorization = await getAccountAuthorization(address) const transfer = getTokenTransferFunc(tokenName) @@ -72,6 +64,7 @@ export const createWithdrawal = catchAsync(async (req, res) => { res.json(response) } catch (e) { + console.log(e) throw new ApiError( httpStatus.INTERNAL_SERVER_ERROR, "failed to complete withdrawal" diff --git a/src/database/getLeastRecentAccountKey.ts b/src/database/getLeastRecentAdminSignerKey.ts similarity index 59% rename from src/database/getLeastRecentAccountKey.ts rename to src/database/getLeastRecentAdminSignerKey.ts index af75d43e..aac7e3bc 100644 --- a/src/database/getLeastRecentAccountKey.ts +++ b/src/database/getLeastRecentAdminSignerKey.ts @@ -3,18 +3,18 @@ import {PrismaClient} from "@prisma/client" const prisma = new PrismaClient() const getLeastRecentAccountKeySql = ` -UPDATE account_keys -SET last_used_at = current_timestamp +UPDATE admin_signer_keys +SET updated_at = current_timestamp WHERE index = ( SELECT index - FROM account_keys - ORDER BY last_used_at + FROM admin_signer_keys + ORDER BY updated_at LIMIT 1 ) RETURNING index ` -export default async function getLeastRecentAccountKey(): Promise { +export default async function getLeastRecentAdminSignerKey(): Promise { const results = await prisma.$queryRaw(getLeastRecentAccountKeySql) return results[0].index } diff --git a/src/database/migrations/20210421075756_admin_signer_keys/migration.sql b/src/database/migrations/20210421075756_admin_signer_keys/migration.sql new file mode 100644 index 00000000..29f9c10d --- /dev/null +++ b/src/database/migrations/20210421075756_admin_signer_keys/migration.sql @@ -0,0 +1,17 @@ +/* + Warnings: + + - You are about to drop the `account_keys` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropTable +DROP TABLE "account_keys"; + +-- CreateTable +CREATE TABLE "admin_signer_keys" ( + "index" INTEGER NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + PRIMARY KEY ("index") +); diff --git a/src/database/schema.prisma b/src/database/schema.prisma index 9016c317..473c5de9 100644 --- a/src/database/schema.prisma +++ b/src/database/schema.prisma @@ -10,9 +10,10 @@ generator client { provider = "prisma-client-js" } -model AccountKey { - @@map(name: "account_keys") +model AdminSignerKey { + @@map(name: "admin_signer_keys") - index Int @id - last_used_at DateTime @updatedAt + index Int @id + created_at DateTime @default(now()) + updated_at DateTime @updatedAt } diff --git a/src/database/syncAccountKeys.ts b/src/database/syncAdminSignerKeys.ts similarity index 71% rename from src/database/syncAccountKeys.ts rename to src/database/syncAdminSignerKeys.ts index 50a479fa..5d950ff8 100644 --- a/src/database/syncAccountKeys.ts +++ b/src/database/syncAdminSignerKeys.ts @@ -21,25 +21,25 @@ async function main() { const account = await getAccount(adminAddress) console.log( - `Fetched account information for ${adminAddress} from ${accessAPIHost}\n` + `Fetched admin account information for ${adminAddress} from ${accessAPIHost}\n` ) // truncate existing keys - const {count} = await prisma.accountKey.deleteMany({where: {}}) + const {count} = await prisma.adminSignerKey.deleteMany({where: {}}) - console.log(`Removed ${count} existing key(s) from DB\n`) + console.log(`Removed ${count} existing admin key(s) from DB\n`) await Promise.all( account.keys.map(async key => { console.log(`- Inserting key ${key.index}`) - await prisma.accountKey.create({ + await prisma.adminSignerKey.create({ data: {index: key.index}, }) }) ) - console.log(`\nInserted ${account.keys.length} key(s) into DB`) + console.log(`\nInserted ${account.keys.length} admin key(s) into DB`) } main() diff --git a/src/lib/flow/getAccountAuthorization.ts b/src/lib/flow/getAuthorization.ts similarity index 81% rename from src/lib/flow/getAccountAuthorization.ts rename to src/lib/flow/getAuthorization.ts index 889a6894..0012bd27 100644 --- a/src/lib/flow/getAccountAuthorization.ts +++ b/src/lib/flow/getAuthorization.ts @@ -1,15 +1,15 @@ import * as fcl from "@onflow/fcl" -import {Account, AccountAuthorization} from "./index" +import {AccountAuthorizer} from "./index" import {SignatureAlgorithm, HashAlgorithm, signWithPrivateKey} from "./crypto" -export default function getAccountAuthorization( +export default function getAuthorization( signerAddress: string, signerPrivateKey: string, signerSigAlgo: SignatureAlgorithm, signerHashAlgo: HashAlgorithm, signerKeyIndex: number -): (account?: Account) => Promise { +): AccountAuthorizer { return async (account = {}) => { return { ...account, diff --git a/src/lib/flow/index.ts b/src/lib/flow/index.ts index e0ca9a5a..b8d0bc0b 100644 --- a/src/lib/flow/index.ts +++ b/src/lib/flow/index.ts @@ -1,4 +1,4 @@ -import getAccountAuthorization from "./getAccountAuthorization" +import getAuthorization from "./getAuthorization" export interface Account { addr?: string @@ -17,4 +17,6 @@ export interface AccountAuthorization { signingFunction: (data: {message: string}) => AccountSignature } -export {getAccountAuthorization} +export type AccountAuthorizer = (account?: Account) => Promise + +export {getAuthorization} diff --git a/src/routes/v1/fungibleTokens/index.ts b/src/routes/v1/accounts/fungibleTokens/index.ts similarity index 67% rename from src/routes/v1/fungibleTokens/index.ts rename to src/routes/v1/accounts/fungibleTokens/index.ts index 45445932..e086b0b2 100644 --- a/src/routes/v1/fungibleTokens/index.ts +++ b/src/routes/v1/accounts/fungibleTokens/index.ts @@ -1,8 +1,8 @@ import * as express from "express" -import * as fungibleTokensController from "../../../controllers/fungibleTokens" +import * as fungibleTokensController from "../../../../controllers/fungibleTokens" import withdrawals from "./withdrawals" -const router = express.Router() +const router = express.Router({mergeParams: true}) router.route("/").get(fungibleTokensController.getTokens) diff --git a/src/routes/v1/fungibleTokens/withdrawals.ts b/src/routes/v1/accounts/fungibleTokens/withdrawals.ts similarity index 78% rename from src/routes/v1/fungibleTokens/withdrawals.ts rename to src/routes/v1/accounts/fungibleTokens/withdrawals.ts index 6b522f17..c76739c4 100644 --- a/src/routes/v1/fungibleTokens/withdrawals.ts +++ b/src/routes/v1/accounts/fungibleTokens/withdrawals.ts @@ -1,5 +1,5 @@ import * as express from "express" -import * as fungibleTokensController from "../../../controllers/fungibleTokens" +import * as fungibleTokensController from "../../../../controllers/fungibleTokens" const router = express.Router({mergeParams: true}) diff --git a/src/routes/v1/accounts/index.ts b/src/routes/v1/accounts/index.ts new file mode 100644 index 00000000..ce5f0dc8 --- /dev/null +++ b/src/routes/v1/accounts/index.ts @@ -0,0 +1,13 @@ +import * as express from "express" +import * as accountsController from "../../../controllers/accounts" +import fungibleTokens from "./fungibleTokens" + +const router = express.Router() + +router.route("/").get(accountsController.getAccounts) + +router.route("/:address").get(accountsController.getAccount) + +router.use("/:address/fungible-tokens", fungibleTokens) + +export default router diff --git a/src/routes/v1/index.ts b/src/routes/v1/index.ts index 155cb3e1..102324a6 100644 --- a/src/routes/v1/index.ts +++ b/src/routes/v1/index.ts @@ -1,8 +1,8 @@ import * as express from "express" -import fungibleTokensRoute from "./fungibleTokens" +import accounts from "./accounts" const router = express.Router() -router.use("/fungible-tokens", fungibleTokensRoute) +router.use("/accounts", accounts) export default router diff --git a/src/services/accounts.ts b/src/services/accounts.ts new file mode 100644 index 00000000..1719e38b --- /dev/null +++ b/src/services/accounts.ts @@ -0,0 +1,67 @@ +import config from "../config" +import * as fcl from "@onflow/fcl" + +import getLeastRecentAdminSignerKey from "../database/getLeastRecentAdminSignerKey" +import getLeastRecentAccountKey from "../database/getLeastRecentAdminSignerKey" +import {AccountAuthorization, AccountAuthorizer, getAuthorization} from "../lib/flow" +import { HashAlgorithm, SignatureAlgorithm } from "../lib/flow/crypto" + +interface Account { + address: string + sigAlgo: SignatureAlgorithm + hashAlgo: HashAlgorithm +} + +// TODO: add support for user accounts +// +// The current API is hard-coded to only support the admin account +const adminAccount: Account = { + address: config.adminAddress, + sigAlgo: config.adminSigAlgo, + hashAlgo: config.adminHashAlgo, +} + +const allAccounts: Account[] = [adminAccount] + +export const queryAccounts = async (): Promise => { + return allAccounts +} + +export const getAccountByAddress = async (address: string): Promise => { + if (address === adminAccount.address) { + return adminAccount + } + + return null +} + +const getPrivateKeyByAddress = async (address: string): Promise => { + if (address === adminAccount.address) { + return config.adminPrivateKey + } + + return null +} + +export const getAccountAuthorization = async (address: string): Promise => { + + const account = await getAccountByAddress(address) + + const privateKey = await getPrivateKeyByAddress(address) + + const keyIndex = await getLeastRecentAdminSignerKey() + + console.log("Foo") + + const auth = getAuthorization( + account.address, + privateKey, + account.sigAlgo, + account.hashAlgo, + keyIndex, + ) + + console.log("Foo 1") + + return auth +} From 4c62119c221f0ed620b3b28fd319472bab095cd4 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 22 Apr 2021 01:23:30 -0700 Subject: [PATCH 032/112] Add transaction execution to README --- README.md | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ed0e3f38..aa30a08a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,12 @@ implements a simple custodial wallet service for the Flow blockchain. - [x] Single admin account (hot wallet) - [ ] [Create user accounts (using admin account)](https://github.com/onflow/flow-wallet-api-node-demo/issues/1) -### 2. Fungible Tokens +### 2. Transaction Execution + +- [ ] Send an arbitrary transaction from the admin account +- [ ] Send an arbitrary transaction from a user account + +### 3. Fungible Tokens - [x] Send fungible token withdrawals from admin account (FLOW, FUSD) - [ ] [Detect fungible token deposits to admin account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/2) @@ -22,7 +27,7 @@ implements a simple custodial wallet service for the Flow blockchain. - [ ] View the fungible token balance of the admin account - [ ] View the fungible token balance of a user account -### 3. Non-Fungible Tokens +### 4. Non-Fungible Tokens - [ ] Set up admin account with non-fungible token collections (`NFT.Collection`) - [ ] Send non-fungible token withdrawals from admin account @@ -137,6 +142,37 @@ curl --request POST \ --- +### Transaction Execution + +`POST /v1/accounts/{address}/transactions` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") + +Body (JSON) + +- `code`: The Cadence code to execute in the transaction + - The code must always specify exactly one authorizer (i.e. `prepare(auth: AuthAccount)`) + +Example + +```sh +curl --request POST \ + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/transactions \ + --header 'Content-Type: application/json' \ + --data '{ "code": "transaction { prepare(auth: AuthAccount) { log(\"Hello, World!\") } }" }' +``` + +```json +{ + "transactionId": "18647b584a03345f3b2d2c4d9ab2c4179ae1b124a7f62ef9f33910e5ca8b353c", + "error": null, +} +``` + +--- + ### Fungible Tokens Supported tokens: From e12bdfa3115df96dfe5e1b2e733adf6aa18af4d7 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 22 Apr 2021 01:25:12 -0700 Subject: [PATCH 033/112] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aa30a08a..f18d9da3 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ curl --request GET \ --- -### Get an account +#### Get an account > :warning: Not yet implemented @@ -121,7 +121,7 @@ curl --request GET \ --- -### Create an account +#### Create an account > :warning: Not yet implemented @@ -144,6 +144,10 @@ curl --request POST \ ### Transaction Execution +#### Execute a transaction + +> :warning: Not yet implemented + `POST /v1/accounts/{address}/transactions` Parameters From 1656eecb0304316a82da1585f92e569922f981be Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 22 Apr 2021 10:05:27 -0700 Subject: [PATCH 034/112] Update accounts.ts --- src/services/accounts.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/services/accounts.ts b/src/services/accounts.ts index 1719e38b..a2fac417 100644 --- a/src/services/accounts.ts +++ b/src/services/accounts.ts @@ -51,17 +51,11 @@ export const getAccountAuthorization = async (address: string): Promise Date: Sun, 25 Apr 2021 13:15:13 -0700 Subject: [PATCH 035/112] Refactor crypto library --- package-lock.json | 18 +++++ package.json | 2 + src/config.ts | 8 +- src/controllers/accounts.ts | 6 +- src/lib/crypto/elliptic.ts | 124 +++++++++++++++++++++++++++++++ src/lib/crypto/hash.ts | 48 ++++++++++++ src/lib/crypto/index.ts | 10 +++ src/lib/crypto/publicKey.ts | 4 + src/lib/crypto/sign.ts | 13 ++++ src/lib/flow/crypto.ts | 63 ---------------- src/lib/flow/getAuthorization.ts | 27 +++---- src/lib/flow/index.ts | 4 +- src/lib/flow/sendTransaction.ts | 10 +-- src/lib/fungibleTokens/index.ts | 2 +- src/services/accounts.ts | 46 ++++++------ 15 files changed, 267 insertions(+), 118 deletions(-) create mode 100644 src/lib/crypto/elliptic.ts create mode 100644 src/lib/crypto/hash.ts create mode 100644 src/lib/crypto/index.ts create mode 100644 src/lib/crypto/publicKey.ts create mode 100644 src/lib/crypto/sign.ts delete mode 100644 src/lib/flow/crypto.ts diff --git a/package-lock.json b/package-lock.json index fb0666f6..780cf725 100644 --- a/package-lock.json +++ b/package-lock.json @@ -626,6 +626,15 @@ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d.tgz", "integrity": "sha512-9/fE1gdPWmjbMjXUJjrTMt848TsgEnSjZCcJ1wu9OAcRlAKKJBLehftqC3gSEShDijvMYgeTdGU5snMpwmv4vg==" }, + "@types/bn.js": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", @@ -645,6 +654,15 @@ "@types/node": "*" } }, + "@types/elliptic": { + "version": "6.4.12", + "resolved": "https://registry.npmjs.org/@types/elliptic/-/elliptic-6.4.12.tgz", + "integrity": "sha512-gP1KsqoouLJGH6IJa28x7PXb3cRqh83X8HCLezd2dF+XcAIMKYv53KV+9Zn6QA561E120uOqZBQ+Jy/cl+fviw==", + "dev": true, + "requires": { + "@types/bn.js": "*" + } + }, "@types/express": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", diff --git a/package.json b/package.json index 69c40c9a..42e61fbd 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,8 @@ "winston": "^3.3.3" }, "devDependencies": { + "@types/bn.js": "^5.1.0", + "@types/elliptic": "^6.4.12", "@types/express": "^4.17.11", "@types/node": "^14.14.39", "@typescript-eslint/eslint-plugin": "^4.22.0", diff --git a/src/config.ts b/src/config.ts index 2821e674..17fac89c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,4 @@ -import { HashAlgorithm, SignatureAlgorithm } from "./lib/flow/crypto" +import {HashAlgorithm, SignatureAlgorithm} from "./lib/flow/crypto" const chainEmulator = "emulator" const chainTestnet = "testnet" @@ -34,9 +34,9 @@ const config = { accessApiHost: process.env.ACCESS_API_HOST || "http://localhost:8080", adminAddress: process.env.ADMIN_ADDRESS || "0xf8d6e0586b0a20c7", adminPrivateKey: process.env.ADMIN_PRIVATE_KEY, - adminSigAlgo: SignatureAlgorithm[(process.env.ADMIN_SIG_ALGO || "ECDSA_P256")], + adminSigAlgo: SignatureAlgorithm[process.env.ADMIN_SIG_ALGO || "ECDSA_P256"], adminHashAlgo: (process.env.ADMIN_HASH_ALGO || "SHA3_256") as HashAlgorithm, contracts: getContracts(process.env.CHAIN), -} +} -export default config; +export default config diff --git a/src/controllers/accounts.ts b/src/controllers/accounts.ts index 7c608fb5..d67c3f85 100644 --- a/src/controllers/accounts.ts +++ b/src/controllers/accounts.ts @@ -1,7 +1,6 @@ -import config from "../config" import catchAsync from "../errors/catchAsync" import NotFoundError from "../errors/NotFoundError" -import { getAccountByAddress, queryAccounts } from "../services/accounts" +import {getAccountByAddress, queryAccounts} from "../services/accounts" export const getAccounts = catchAsync(async (req, res) => { const accounts = await queryAccounts() @@ -20,6 +19,3 @@ export const getAccount = catchAsync(async (req, res) => { res.json(account) }) - -// TODO: implement create account function -export const createAccount = catchAsync(async (req, res) => {}) diff --git a/src/lib/crypto/elliptic.ts b/src/lib/crypto/elliptic.ts new file mode 100644 index 00000000..ce54e191 --- /dev/null +++ b/src/lib/crypto/elliptic.ts @@ -0,0 +1,124 @@ +import * as elliptic from "elliptic" +import {Endianness} from "bn.js" + +import {PublicKey} from "./publicKey" +import {Signer, Signature, SignatureAlgorithm} from "./sign" +import {Hasher, HashAlgorithm, getHasher} from "./hash" + +const ECDSA_P256 = new elliptic.ec("p256") +const ECDSA_secp256k1 = new elliptic.ec("secp256k1") + +const bufferEndianness: Endianness = "be" + +function getEC(sigAlgo: SignatureAlgorithm): elliptic.ec { + switch (sigAlgo) { + case SignatureAlgorithm.ECDSA_P256: + return ECDSA_P256 + case SignatureAlgorithm.ECDSA_secp256k1: + return ECDSA_secp256k1 + } +} + +class ECSignature implements Signature { + private static n = 32 + private ecSignature: elliptic.ec.Signature + + constructor(ecSignature: elliptic.ec.Signature) { + this.ecSignature = ecSignature + } + + toBuffer(): Buffer { + const r = this.ecSignature.r.toArrayLike( + Buffer, + bufferEndianness, + ECSignature.n + ) + const s = this.ecSignature.s.toArrayLike( + Buffer, + bufferEndianness, + ECSignature.n + ) + + return Buffer.concat([r, s]) + } + + toHex(): string { + return this.toBuffer().toString("hex") + } +} + +class ECPublicKey implements PublicKey { + private static size = 32 + private ecPublicKey: elliptic.curve.base.BasePoint + + constructor(ecPublicKey: elliptic.curve.base.BasePoint) { + this.ecPublicKey = ecPublicKey + } + + toBuffer(): Buffer { + const x = this.ecPublicKey + .getX() + .toArrayLike(Buffer, bufferEndianness, ECPublicKey.size) + const y = this.ecPublicKey + .getY() + .toArrayLike(Buffer, bufferEndianness, ECPublicKey.size) + + return Buffer.concat([x, y]) + } + + toHex(): string { + return this.toBuffer().toString("hex") + } +} + +export class InMemoryPrivateKey { + private ecKeyPair: elliptic.ec.KeyPair + private sigAlgo: SignatureAlgorithm + + constructor(privateKey: Buffer, sigAlgo: SignatureAlgorithm) { + const ec = getEC(sigAlgo) + this.ecKeyPair = ec.keyFromPrivate(privateKey) + this.sigAlgo = sigAlgo + } + + public static fromHex( + hex: string, + sigalgo: SignatureAlgorithm + ): InMemoryPrivateKey { + const buffer = Buffer.from(hex, "hex") + return new InMemoryPrivateKey(buffer, sigalgo) + } + + sign(digest: Buffer): Signature { + const ecSignature = this.ecKeyPair.sign(digest) + return new ECSignature(ecSignature) + } + + getPublicKey(): PublicKey { + const ecPublicKey = this.ecKeyPair.getPublic() + return new ECPublicKey(ecPublicKey) + } + + getSignatureAlgorithm(): SignatureAlgorithm { + return this.sigAlgo + } + + toHex(): string { + return this.ecKeyPair.getPrivate().toArrayLike(Buffer, "be").toString("hex") + } +} + +export class InMemorySigner implements Signer { + private privateKey: InMemoryPrivateKey + private hasher: Hasher + + constructor(privateKey: InMemoryPrivateKey, hashAlgo: HashAlgorithm) { + this.privateKey = privateKey + this.hasher = getHasher(hashAlgo) + } + + sign(message: Buffer): Signature { + const digest = this.hasher.hash(message) + return this.privateKey.sign(digest) + } +} diff --git a/src/lib/crypto/hash.ts b/src/lib/crypto/hash.ts new file mode 100644 index 00000000..39250556 --- /dev/null +++ b/src/lib/crypto/hash.ts @@ -0,0 +1,48 @@ +import {createHash, Hash} from "crypto" +import {SHA3} from "sha3" + +export enum HashAlgorithm { + SHA2_256 = "SHA2_256", + SHA3_256 = "SHA3_256", +} + +export interface Hasher { + hash(message: Buffer): Buffer +} + +export class SHA2_256Hasher { + private static shaType = "sha256" + private sha: Hash + + constructor() { + this.sha = createHash(SHA2_256Hasher.shaType) + } + + hash(message: Buffer): Buffer { + this.sha.update(message) + return this.sha.digest() + } +} + +export class SHA3_256Hasher { + private static size: 256 = 256 + private sha: SHA3 + + constructor() { + this.sha = new SHA3(SHA3_256Hasher.size) + } + + hash(message: Buffer): Buffer { + this.sha.update(message) + return this.sha.digest() + } +} + +export function getHasher(hashAlgo: HashAlgorithm): Hasher { + switch (hashAlgo) { + case HashAlgorithm.SHA2_256: + return new SHA2_256Hasher() + case HashAlgorithm.SHA3_256: + return new SHA3_256Hasher() + } +} diff --git a/src/lib/crypto/index.ts b/src/lib/crypto/index.ts new file mode 100644 index 00000000..858c56a1 --- /dev/null +++ b/src/lib/crypto/index.ts @@ -0,0 +1,10 @@ +export {Signer, Signature, SignatureAlgorithm} from "./sign" +export { + Hasher, + HashAlgorithm, + SHA2_256Hasher, + SHA3_256Hasher, + getHasher, +} from "./hash" +export {PublicKey} from "./publicKey" +export {InMemoryPrivateKey, InMemorySigner} from "./elliptic" diff --git a/src/lib/crypto/publicKey.ts b/src/lib/crypto/publicKey.ts new file mode 100644 index 00000000..4709bd5e --- /dev/null +++ b/src/lib/crypto/publicKey.ts @@ -0,0 +1,4 @@ +export interface PublicKey { + toBuffer(): Buffer + toHex(): string +} diff --git a/src/lib/crypto/sign.ts b/src/lib/crypto/sign.ts new file mode 100644 index 00000000..97e1791e --- /dev/null +++ b/src/lib/crypto/sign.ts @@ -0,0 +1,13 @@ +export enum SignatureAlgorithm { + ECDSA_P256 = "ECDSA_P256", + ECDSA_secp256k1 = "ECDSA_secp256k1", +} + +export interface Signature { + toBuffer(): Buffer + toHex(): string +} + +export interface Signer { + sign(message: Buffer): Signature +} diff --git a/src/lib/flow/crypto.ts b/src/lib/flow/crypto.ts deleted file mode 100644 index e566e04f..00000000 --- a/src/lib/flow/crypto.ts +++ /dev/null @@ -1,63 +0,0 @@ -import {createHash} from "crypto" -import {SHA3} from "sha3" -import {ec as EC} from "elliptic" - -export enum SignatureAlgorithm { - ECDSA_P256 = "ECDSA_P256", - ECDSA_secp256k1 = "ECDSA_secp256k1", -} - -export enum HashAlgorithm { - SHA2_256 = "SHA2_256", - SHA3_256 = "SHA3_256", -} - -const signers = { - ECDSA_P256: () => new EC("p256"), - ECDSA_secp256k1: () => new EC("secp256k1"), -} - -function hashSHA2(msg) { - const sha = createHash("sha256") - sha.update(Buffer.from(msg, "hex")) - return sha.digest() -} - -function hashSHA3(msg) { - const sha = new SHA3(256) - sha.update(Buffer.from(msg, "hex")) - return sha.digest() -} - -const hashers = { - SHA2_256: hashSHA2, - SHA3_256: hashSHA3, -} - -const getSigner = sigAlgo => signers[sigAlgo]() -const getHasher = hashAlgo => hashers[hashAlgo] - -function encodeSignature(rawSig): string { - const n = 32 - const r = rawSig.r.toArrayLike(Buffer, "be", n) - const s = rawSig.s.toArrayLike(Buffer, "be", n) - - return Buffer.concat([r, s]).toString("hex") -} - -export function signWithPrivateKey( - privateKey: string, - sigAlgo: SignatureAlgorithm, - hashAlgo: HashAlgorithm, - msg: string -): string { - const signer = getSigner(sigAlgo) - const hasher = getHasher(hashAlgo) - - const key = signer.keyFromPrivate(Buffer.from(privateKey, "hex")) - const digest = hasher(msg) - - const sig = key.sign(digest) - - return encodeSignature(sig) -} diff --git a/src/lib/flow/getAuthorization.ts b/src/lib/flow/getAuthorization.ts index 0012bd27..a1315960 100644 --- a/src/lib/flow/getAuthorization.ts +++ b/src/lib/flow/getAuthorization.ts @@ -1,30 +1,25 @@ import * as fcl from "@onflow/fcl" import {AccountAuthorizer} from "./index" -import {SignatureAlgorithm, HashAlgorithm, signWithPrivateKey} from "./crypto" +import {Signer} from "./crypto" + +const fromHex = (hex: string) => Buffer.from(hex, "hex") export default function getAuthorization( - signerAddress: string, - signerPrivateKey: string, - signerSigAlgo: SignatureAlgorithm, - signerHashAlgo: HashAlgorithm, - signerKeyIndex: number + address: string, + keyIndex: number, + signer: Signer ): AccountAuthorizer { return async (account = {}) => { return { ...account, tempId: "SIGNER", - addr: fcl.sansPrefix(signerAddress), - keyId: signerKeyIndex, + addr: fcl.sansPrefix(address), + keyId: keyIndex, signingFunction: data => ({ - addr: fcl.withPrefix(signerAddress), - keyId: signerKeyIndex, - signature: signWithPrivateKey( - signerPrivateKey, - signerSigAlgo, - signerHashAlgo, - data.message - ), + addr: fcl.withPrefix(address), + keyId: keyIndex, + signature: signer.sign(fromHex(data.message)).toHex(), }), } } diff --git a/src/lib/flow/index.ts b/src/lib/flow/index.ts index b8d0bc0b..1eda987e 100644 --- a/src/lib/flow/index.ts +++ b/src/lib/flow/index.ts @@ -17,6 +17,8 @@ export interface AccountAuthorization { signingFunction: (data: {message: string}) => AccountSignature } -export type AccountAuthorizer = (account?: Account) => Promise +export type AccountAuthorizer = ( + account?: Account +) => Promise export {getAuthorization} diff --git a/src/lib/flow/sendTransaction.ts b/src/lib/flow/sendTransaction.ts index b096543a..16e8b42b 100644 --- a/src/lib/flow/sendTransaction.ts +++ b/src/lib/flow/sendTransaction.ts @@ -4,14 +4,14 @@ import {AccountAuthorization} from "./index" interface Argument { value: string - xform: any + xform: any // eslint-disable-line } interface Transaction { - transaction: string, - args: Argument[], - proposer: string, - authorizations: AccountAuthorization[], + transaction: string + args: Argument[] + proposer: string + authorizations: AccountAuthorization[] payer: string } diff --git a/src/lib/fungibleTokens/index.ts b/src/lib/fungibleTokens/index.ts index 783a701b..10c1b248 100644 --- a/src/lib/fungibleTokens/index.ts +++ b/src/lib/fungibleTokens/index.ts @@ -10,7 +10,7 @@ const transferFuncs = { [tokenFUSD]: transferFUSD, } -export function isValidToken(token) { +export function isValidToken(token: string): boolean { return token in transferFuncs } diff --git a/src/services/accounts.ts b/src/services/accounts.ts index a2fac417..de4c166f 100644 --- a/src/services/accounts.ts +++ b/src/services/accounts.ts @@ -2,23 +2,18 @@ import config from "../config" import * as fcl from "@onflow/fcl" import getLeastRecentAdminSignerKey from "../database/getLeastRecentAdminSignerKey" -import getLeastRecentAccountKey from "../database/getLeastRecentAdminSignerKey" -import {AccountAuthorization, AccountAuthorizer, getAuthorization} from "../lib/flow" -import { HashAlgorithm, SignatureAlgorithm } from "../lib/flow/crypto" +import {AccountAuthorizer, getAuthorization} from "../lib/flow" +import * as Crypto from "../lib/flow/crypto" interface Account { address: string - sigAlgo: SignatureAlgorithm - hashAlgo: HashAlgorithm } // TODO: add support for user accounts // // The current API is hard-coded to only support the admin account -const adminAccount: Account = { +const adminAccount: Account = { address: config.adminAddress, - sigAlgo: config.adminSigAlgo, - hashAlgo: config.adminHashAlgo, } const allAccounts: Account[] = [adminAccount] @@ -27,7 +22,9 @@ export const queryAccounts = async (): Promise => { return allAccounts } -export const getAccountByAddress = async (address: string): Promise => { +export const getAccountByAddress = async ( + address: string +): Promise => { if (address === adminAccount.address) { return adminAccount } @@ -35,27 +32,30 @@ export const getAccountByAddress = async (address: string): Promise => { - if (address === adminAccount.address) { - return config.adminPrivateKey +const getSignerByAddress = async ( + address: string +): Promise => { + // TODO: add support for user accounts + if (address !== adminAccount.address) { + return null } - return null -} + const privateKey = Crypto.InMemoryPrivateKey.fromHex( + config.adminPrivateKey, + config.adminSigAlgo + ) -export const getAccountAuthorization = async (address: string): Promise => { + return new Crypto.InMemorySigner(privateKey, config.adminHashAlgo) +} +export const getAccountAuthorization = async ( + address: string +): Promise => { const account = await getAccountByAddress(address) - const privateKey = await getPrivateKeyByAddress(address) + const signer = await getSignerByAddress(address) const keyIndex = await getLeastRecentAdminSignerKey() - return getAuthorization( - account.address, - privateKey, - account.sigAlgo, - account.hashAlgo, - keyIndex, - ) + return getAuthorization(account.address, keyIndex, signer) } From a1e24979174f6230c11e7620a338de85d96e7081 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 25 Apr 2021 14:11:41 -0700 Subject: [PATCH 036/112] Fix crypto imports --- src/config.ts | 4 ++-- src/lib/crypto/elliptic.ts | 30 ++++++++++++++++++++++++------ src/lib/flow/getAuthorization.ts | 2 +- src/services/accounts.ts | 3 +-- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/config.ts b/src/config.ts index 17fac89c..ac7098d6 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,4 @@ -import {HashAlgorithm, SignatureAlgorithm} from "./lib/flow/crypto" +import {HashAlgorithm, SignatureAlgorithm} from "./lib/crypto" const chainEmulator = "emulator" const chainTestnet = "testnet" @@ -35,7 +35,7 @@ const config = { adminAddress: process.env.ADMIN_ADDRESS || "0xf8d6e0586b0a20c7", adminPrivateKey: process.env.ADMIN_PRIVATE_KEY, adminSigAlgo: SignatureAlgorithm[process.env.ADMIN_SIG_ALGO || "ECDSA_P256"], - adminHashAlgo: (process.env.ADMIN_HASH_ALGO || "SHA3_256") as HashAlgorithm, + adminHashAlgo: HashAlgorithm[process.env.ADMIN_HASH_ALGO || "SHA3_256"], contracts: getContracts(process.env.CHAIN), } diff --git a/src/lib/crypto/elliptic.ts b/src/lib/crypto/elliptic.ts index ce54e191..d1b25791 100644 --- a/src/lib/crypto/elliptic.ts +++ b/src/lib/crypto/elliptic.ts @@ -75,18 +75,32 @@ export class InMemoryPrivateKey { private ecKeyPair: elliptic.ec.KeyPair private sigAlgo: SignatureAlgorithm - constructor(privateKey: Buffer, sigAlgo: SignatureAlgorithm) { - const ec = getEC(sigAlgo) - this.ecKeyPair = ec.keyFromPrivate(privateKey) + constructor(ecKeyPair: elliptic.ec.KeyPair, sigAlgo: SignatureAlgorithm) { + this.ecKeyPair = ecKeyPair this.sigAlgo = sigAlgo } + public static generate(sigAlgo: SignatureAlgorithm): InMemoryPrivateKey { + const ec = getEC(sigAlgo) + const ecKeyPair = ec.genKeyPair() + return new InMemoryPrivateKey(ecKeyPair, sigAlgo) + } + + public static fromBuffer( + buffer: Buffer, + sigAlgo: SignatureAlgorithm + ): InMemoryPrivateKey { + const ec = getEC(sigAlgo) + const ecKeyPair = ec.keyFromPrivate(buffer) + return new InMemoryPrivateKey(ecKeyPair, sigAlgo) + } + public static fromHex( hex: string, - sigalgo: SignatureAlgorithm + sigAlgo: SignatureAlgorithm ): InMemoryPrivateKey { const buffer = Buffer.from(hex, "hex") - return new InMemoryPrivateKey(buffer, sigalgo) + return InMemoryPrivateKey.fromBuffer(buffer, sigAlgo) } sign(digest: Buffer): Signature { @@ -103,8 +117,12 @@ export class InMemoryPrivateKey { return this.sigAlgo } + toBuffer(): Buffer { + return this.ecKeyPair.getPrivate().toArrayLike(Buffer, bufferEndianness) + } + toHex(): string { - return this.ecKeyPair.getPrivate().toArrayLike(Buffer, "be").toString("hex") + return this.toBuffer().toString("hex") } } diff --git a/src/lib/flow/getAuthorization.ts b/src/lib/flow/getAuthorization.ts index a1315960..c76dc96f 100644 --- a/src/lib/flow/getAuthorization.ts +++ b/src/lib/flow/getAuthorization.ts @@ -1,7 +1,7 @@ import * as fcl from "@onflow/fcl" import {AccountAuthorizer} from "./index" -import {Signer} from "./crypto" +import {Signer} from "../crypto" const fromHex = (hex: string) => Buffer.from(hex, "hex") diff --git a/src/services/accounts.ts b/src/services/accounts.ts index de4c166f..06f4d7a9 100644 --- a/src/services/accounts.ts +++ b/src/services/accounts.ts @@ -1,9 +1,8 @@ import config from "../config" -import * as fcl from "@onflow/fcl" import getLeastRecentAdminSignerKey from "../database/getLeastRecentAdminSignerKey" import {AccountAuthorizer, getAuthorization} from "../lib/flow" -import * as Crypto from "../lib/flow/crypto" +import * as Crypto from "../lib/crypto" interface Account { address: string From 688b0fbb95ef97330950c38c11b1dd5cd5f7c3cb Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 25 Apr 2021 17:25:56 -0700 Subject: [PATCH 037/112] Refactor controllers and services --- package.json | 1 + src/app.ts | 21 +++- src/controllers/accounts.ts | 35 +++--- src/controllers/fungibleTokens.ts | 109 +++++++++--------- src/database/accounts.ts | 12 ++ src/database/getLeastRecentAdminSignerKey.ts | 6 +- .../migration.sql | 8 ++ src/database/models.ts | 3 + src/database/schema.prisma | 8 ++ src/lib/crypto/generate.ts | 3 + .../v1/accounts/fungibleTokens/index.ts | 27 +++-- .../v1/accounts/fungibleTokens/withdrawals.ts | 25 ++-- src/routes/v1/accounts/index.ts | 29 +++-- src/routes/v1/index.ts | 19 ++- src/services/accounts.ts | 74 ++++++------ src/services/fungibleTokens.ts | 49 ++++++++ src/services/service.ts | 9 ++ 17 files changed, 303 insertions(+), 135 deletions(-) create mode 100644 src/database/accounts.ts create mode 100644 src/database/migrations/20210425210753_create_accounts/migration.sql create mode 100644 src/database/models.ts create mode 100644 src/lib/crypto/generate.ts create mode 100644 src/services/fungibleTokens.ts create mode 100644 src/services/service.ts diff --git a/package.json b/package.json index 42e61fbd..7ee4c7da 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "deploy-contracts": "flow project deploy --network=emulator --update", "db-migrate-dev": "prisma migrate dev --preview-feature", "db-migrate-deploy": "prisma migrate deploy --preview-feature", + "db-generate-client": "prisma generate", "db-sync-admin-keys": "ts-node ./src/database/syncAdminSignerKeys.ts", "lint": "eslint .", "lint-fix": "eslint . --fix" diff --git a/src/app.ts b/src/app.ts index a38ecad1..9b048f2e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -6,7 +6,12 @@ import morganMiddleware from "./middleware/morgan" import errorsMiddleware from "./middleware/errors" import NotFoundError from "./errors/NotFoundError" -import routes from "./routes/v1" +import createRouter from "./routes/v1" +import AccountsController from "./controllers/accounts" +import AccountsService from "./services/accounts" +import {PrismaClient} from ".prisma/client" +import FungibleTokensService from "./services/fungibleTokens" +import FungibleTokensController from "./controllers/fungibleTokens" const app = express() @@ -14,7 +19,19 @@ app.use(morganMiddleware) app.use(express.json()) app.use(express.urlencoded({extended: false})) -app.use("/v1", routes) +const prisma = new PrismaClient() + +const accountsService = new AccountsService(prisma) +const accountsController = new AccountsController(accountsService) + +const fungiblTokensService = new FungibleTokensService(prisma, accountsService) +const fungiblTokensController = new FungibleTokensController( + fungiblTokensService +) + +const v1Router = createRouter(accountsController, fungiblTokensController) + +app.use("/v1", v1Router) // catch 404 and forward to error handler app.use((req, res, next) => { diff --git a/src/controllers/accounts.ts b/src/controllers/accounts.ts index d67c3f85..48ef26af 100644 --- a/src/controllers/accounts.ts +++ b/src/controllers/accounts.ts @@ -1,21 +1,28 @@ -import catchAsync from "../errors/catchAsync" import NotFoundError from "../errors/NotFoundError" -import {getAccountByAddress, queryAccounts} from "../services/accounts" +import AccountsService from "../services/accounts" -export const getAccounts = catchAsync(async (req, res) => { - const accounts = await queryAccounts() +export default class AccountsController { + private accounts: AccountsService - res.json(accounts) -}) - -export const getAccount = catchAsync(async (req, res) => { - const address = req.params.address + constructor(accounts: AccountsService) { + this.accounts = accounts + } - const account = await getAccountByAddress(address) + async getAccounts(req, res) { + const accounts = await this.accounts.query() - if (account === null) { - throw new NotFoundError() + res.json(accounts) } - res.json(account) -}) + async getAccount(req, res) { + const address = req.params.address + + const account = await this.accounts.getByAddress(address) + + if (account === null) { + throw new NotFoundError() + } + + res.json(account) + } +} diff --git a/src/controllers/fungibleTokens.ts b/src/controllers/fungibleTokens.ts index 255e8aa1..b13c15e7 100644 --- a/src/controllers/fungibleTokens.ts +++ b/src/controllers/fungibleTokens.ts @@ -1,73 +1,76 @@ import * as httpStatus from "http-status" -import config from "../config" -import {tokens, isValidToken, getTokenTransferFunc} from "../lib/fungibleTokens" -import {getAccountAuthorization} from "../services/accounts" -import catchAsync from "../errors/catchAsync" +import {isValidToken} from "../lib/fungibleTokens" +import FungibleTokensService from "../services/fungibleTokens" import ApiError from "../errors/ApiError" import InvalidFungibleTokenError from "../errors/InvalidFungibleTokenError" -const makeToken = tokenName => ({name: tokenName}) -const allTokens = tokens.map(tokenName => makeToken(tokenName)) +export default class FungibleTokensController { + private fungibleTokens: FungibleTokensService -export const getTokens = catchAsync(async (req, res) => res.json(allTokens)) - -export const getToken = catchAsync(async (req, res) => { - const tokenName = req.params.tokenName - - if (!isValidToken(tokenName)) { - throw new InvalidFungibleTokenError(tokenName) + constructor(fungibleTokens: FungibleTokensService) { + this.fungibleTokens = fungibleTokens } - const token = makeToken(tokenName) + async getTokens(req, res) { + const tokens = await this.fungibleTokens.query() - res.json(token) -}) + res.json(tokens) + } -// TODO: implement withdrawal getters -export const getWithdrawals = catchAsync(async (req, res) => { - res.send("TODO: implement me") -}) + async getToken(req, res) { + const tokenName = req.params.tokenName -// TODO: implement withdrawal getters -export const getWithdrawal = catchAsync(async (req, res) => { - res.send("TODO: implement me") -}) + if (!isValidToken(tokenName)) { + throw new InvalidFungibleTokenError(tokenName) + } -export const createWithdrawal = catchAsync(async (req, res) => { - const address = req.params.address - const tokenName = req.params.tokenName + const token = await this.fungibleTokens.getByName(tokenName) - if (!isValidToken(tokenName)) { - throw new InvalidFungibleTokenError(tokenName) + res.json(token) } - // TODO: validate recipient and amount - const {recipient, amount} = req.body - - const authorization = await getAccountAuthorization(address) + // TODO: implement withdrawal getters + async getWithdrawals(req, res) { + res.send("TODO: implement me") + } - const transfer = getTokenTransferFunc(tokenName) + // TODO: implement withdrawal getters + async getWithdrawal(req, res) { + res.send("TODO: implement me") + } - try { - const transactionId = await transfer( - recipient, - amount, - authorization, - config.contracts - ) + async createWithdrawal(req, res) { + const sender = req.params.address + const tokenName = req.params.tokenName - const response = { - transactionId, - recipient, - amount, + if (!isValidToken(tokenName)) { + throw new InvalidFungibleTokenError(tokenName) } - res.json(response) - } catch (e) { - console.log(e) - throw new ApiError( - httpStatus.INTERNAL_SERVER_ERROR, - "failed to complete withdrawal" - ) + // TODO: validate recipient and amount + const {recipient, amount} = req.body + + try { + const transactionId = await this.fungibleTokens.createWithdrawal( + sender, + recipient, + tokenName, + amount + ) + + const response = { + transactionId, + recipient, + amount, + } + + res.json(response) + } catch (e) { + console.log(e) + throw new ApiError( + httpStatus.INTERNAL_SERVER_ERROR, + "failed to complete withdrawal" + ) + } } -}) +} diff --git a/src/database/accounts.ts b/src/database/accounts.ts new file mode 100644 index 00000000..3004c3a9 --- /dev/null +++ b/src/database/accounts.ts @@ -0,0 +1,12 @@ +import {Prisma, PrismaClient} from "@prisma/client" + +export async function insertAccount( + prisma: PrismaClient, + account: Prisma.AccountCreateInput +) { + await prisma.account.create({ + data: { + address: account.address, + }, + }) +} diff --git a/src/database/getLeastRecentAdminSignerKey.ts b/src/database/getLeastRecentAdminSignerKey.ts index aac7e3bc..a6fc3748 100644 --- a/src/database/getLeastRecentAdminSignerKey.ts +++ b/src/database/getLeastRecentAdminSignerKey.ts @@ -1,7 +1,5 @@ import {PrismaClient} from "@prisma/client" -const prisma = new PrismaClient() - const getLeastRecentAccountKeySql = ` UPDATE admin_signer_keys SET updated_at = current_timestamp @@ -14,7 +12,9 @@ WHERE index = ( RETURNING index ` -export default async function getLeastRecentAdminSignerKey(): Promise { +export default async function getLeastRecentAdminSignerKey( + prisma: PrismaClient +): Promise { const results = await prisma.$queryRaw(getLeastRecentAccountKeySql) return results[0].index } diff --git a/src/database/migrations/20210425210753_create_accounts/migration.sql b/src/database/migrations/20210425210753_create_accounts/migration.sql new file mode 100644 index 00000000..6c29d982 --- /dev/null +++ b/src/database/migrations/20210425210753_create_accounts/migration.sql @@ -0,0 +1,8 @@ +-- CreateTable +CREATE TABLE "accounts" ( + "address" TEXT NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + PRIMARY KEY ("address") +); diff --git a/src/database/models.ts b/src/database/models.ts new file mode 100644 index 00000000..0d75a509 --- /dev/null +++ b/src/database/models.ts @@ -0,0 +1,3 @@ +import * as Prisma from "@prisma/client" + +export type Account = Prisma.Account diff --git a/src/database/schema.prisma b/src/database/schema.prisma index 473c5de9..5f0c6b2b 100644 --- a/src/database/schema.prisma +++ b/src/database/schema.prisma @@ -10,6 +10,14 @@ generator client { provider = "prisma-client-js" } +model Account { + @@map(name: "accounts") + + address String @id + created_at DateTime @default(now()) + updated_at DateTime @updatedAt +} + model AdminSignerKey { @@map(name: "admin_signer_keys") diff --git a/src/lib/crypto/generate.ts b/src/lib/crypto/generate.ts new file mode 100644 index 00000000..dfd97e28 --- /dev/null +++ b/src/lib/crypto/generate.ts @@ -0,0 +1,3 @@ +export interface KeyGenerator { + generatePrivateKey() +} diff --git a/src/routes/v1/accounts/fungibleTokens/index.ts b/src/routes/v1/accounts/fungibleTokens/index.ts index e086b0b2..f7d69fda 100644 --- a/src/routes/v1/accounts/fungibleTokens/index.ts +++ b/src/routes/v1/accounts/fungibleTokens/index.ts @@ -1,13 +1,26 @@ import * as express from "express" -import * as fungibleTokensController from "../../../../controllers/fungibleTokens" -import withdrawals from "./withdrawals" +import FungibleTokensController from "../../../../controllers/fungibleTokens" +import catchAsync from "../../../../errors/catchAsync" +import createWithdrawalsRouter from "./withdrawals" -const router = express.Router({mergeParams: true}) +function createRouter( + fungibleTokens: FungibleTokensController +): express.Router { + const router = express.Router({mergeParams: true}) -router.route("/").get(fungibleTokensController.getTokens) + const withdrawalsRouter = createWithdrawalsRouter(fungibleTokens) -router.route("/:tokenName").get(fungibleTokensController.getToken) + router + .route("/") + .get(catchAsync((req, res) => fungibleTokens.getTokens(req, res))) -router.use("/:tokenName/withdrawals", withdrawals) + router + .route("/:tokenName") + .get(catchAsync((req, res) => fungibleTokens.getToken(req, res))) -export default router + router.use("/:tokenName/withdrawals", withdrawalsRouter) + + return router +} + +export default createRouter diff --git a/src/routes/v1/accounts/fungibleTokens/withdrawals.ts b/src/routes/v1/accounts/fungibleTokens/withdrawals.ts index c76739c4..1bdb9695 100644 --- a/src/routes/v1/accounts/fungibleTokens/withdrawals.ts +++ b/src/routes/v1/accounts/fungibleTokens/withdrawals.ts @@ -1,13 +1,22 @@ import * as express from "express" -import * as fungibleTokensController from "../../../../controllers/fungibleTokens" +import FungibleTokensController from "../../../../controllers/fungibleTokens" +import catchAsync from "../../../../errors/catchAsync" -const router = express.Router({mergeParams: true}) +function createRouter( + fungibleTokens: FungibleTokensController +): express.Router { + const router = express.Router({mergeParams: true}) -router - .route("/") - .get(fungibleTokensController.getWithdrawals) - .post(fungibleTokensController.createWithdrawal) + router + .route("/") + .get(catchAsync((req, res) => fungibleTokens.getWithdrawals(req, res))) + .post(catchAsync((req, res) => fungibleTokens.createWithdrawal(req, res))) -router.route("/:transactionId").get(fungibleTokensController.getWithdrawal) + router + .route("/:transactionId") + .get(catchAsync((req, res) => fungibleTokens.getWithdrawal(req, res))) -export default router + return router +} + +export default createRouter diff --git a/src/routes/v1/accounts/index.ts b/src/routes/v1/accounts/index.ts index ce5f0dc8..761bc95f 100644 --- a/src/routes/v1/accounts/index.ts +++ b/src/routes/v1/accounts/index.ts @@ -1,13 +1,28 @@ import * as express from "express" -import * as accountsController from "../../../controllers/accounts" -import fungibleTokens from "./fungibleTokens" +import AccountsController from "../../../controllers/accounts" +import FungibleTokensController from "../../../controllers/fungibleTokens" +import catchAsync from "../../../errors/catchAsync" +import createFungibleTokensRouter from "./fungibleTokens" -const router = express.Router() +function createRouter( + accounts: AccountsController, + fungibleTokens: FungibleTokensController +): express.Router { + const router = express.Router() -router.route("/").get(accountsController.getAccounts) + const fungibleTokensRouter = createFungibleTokensRouter(fungibleTokens) -router.route("/:address").get(accountsController.getAccount) + router + .route("/") + .get(catchAsync((req, res) => accounts.getAccounts(req, res))) -router.use("/:address/fungible-tokens", fungibleTokens) + router + .route("/:address") + .get(catchAsync((req, res) => accounts.getAccount(req, res))) -export default router + router.use("/:address/fungible-tokens", fungibleTokensRouter) + + return router +} + +export default createRouter diff --git a/src/routes/v1/index.ts b/src/routes/v1/index.ts index 102324a6..ebe917f5 100644 --- a/src/routes/v1/index.ts +++ b/src/routes/v1/index.ts @@ -1,8 +1,19 @@ import * as express from "express" -import accounts from "./accounts" +import AccountsController from "../../controllers/accounts" +import FungibleTokensController from "../../controllers/fungibleTokens" +import createAccountsRouter from "./accounts" -const router = express.Router() +function createRouter( + accounts: AccountsController, + fungibleTokens: FungibleTokensController +): express.Router { + const router = express.Router() -router.use("/accounts", accounts) + const accountsRouter = createAccountsRouter(accounts, fungibleTokens) -export default router + router.use("/accounts", accountsRouter) + + return router +} + +export default createRouter diff --git a/src/services/accounts.ts b/src/services/accounts.ts index 06f4d7a9..6d10074c 100644 --- a/src/services/accounts.ts +++ b/src/services/accounts.ts @@ -1,60 +1,60 @@ import config from "../config" - -import getLeastRecentAdminSignerKey from "../database/getLeastRecentAdminSignerKey" import {AccountAuthorizer, getAuthorization} from "../lib/flow" import * as Crypto from "../lib/crypto" - -interface Account { - address: string -} +import Service from "./service" +import * as models from "../database/models" +import getLeastRecentAdminSignerKey from "../database/getLeastRecentAdminSignerKey" +import {insertAccount} from "../database/accounts" // TODO: add support for user accounts // // The current API is hard-coded to only support the admin account -const adminAccount: Account = { +const adminAccount: models.Account = { address: config.adminAddress, + created_at: null, + updated_at: null, } -const allAccounts: Account[] = [adminAccount] - -export const queryAccounts = async (): Promise => { - return allAccounts -} +export default class AccountsService extends Service { + async create(address: string) { + await insertAccount(this.prisma, {address: address}) + } -export const getAccountByAddress = async ( - address: string -): Promise => { - if (address === adminAccount.address) { - return adminAccount + async query(): Promise { + // TODO: add support for user accounts + return [adminAccount] } - return null -} + async getByAddress(address: string): Promise { + // TODO: add support for user accounts + if (address === adminAccount.address) { + return adminAccount + } -const getSignerByAddress = async ( - address: string -): Promise => { - // TODO: add support for user accounts - if (address !== adminAccount.address) { return null } - const privateKey = Crypto.InMemoryPrivateKey.fromHex( - config.adminPrivateKey, - config.adminSigAlgo - ) + async getSignerByAddress(address: string): Promise { + // TODO: add support for user accounts + if (address !== adminAccount.address) { + return null + } - return new Crypto.InMemorySigner(privateKey, config.adminHashAlgo) -} + const privateKey = Crypto.InMemoryPrivateKey.fromHex( + config.adminPrivateKey, + config.adminSigAlgo + ) -export const getAccountAuthorization = async ( - address: string -): Promise => { - const account = await getAccountByAddress(address) + return new Crypto.InMemorySigner(privateKey, config.adminHashAlgo) + } + + async getAuthorization(address: string): Promise { + const account = await this.getByAddress(address) - const signer = await getSignerByAddress(address) + const signer = await this.getSignerByAddress(address) - const keyIndex = await getLeastRecentAdminSignerKey() + const keyIndex = await getLeastRecentAdminSignerKey(this.prisma) - return getAuthorization(account.address, keyIndex, signer) + return getAuthorization(account.address, keyIndex, signer) + } } diff --git a/src/services/fungibleTokens.ts b/src/services/fungibleTokens.ts new file mode 100644 index 00000000..0d92c97f --- /dev/null +++ b/src/services/fungibleTokens.ts @@ -0,0 +1,49 @@ +import {PrismaClient} from ".prisma/client" +import config from "../config" +import {getTokenTransferFunc, tokens} from "../lib/fungibleTokens" +import Service from "./service" +import AccountsService from "./accounts" + +type FungibleToken = { + name: string +} + +const makeToken = tokenName => ({name: tokenName}) +const allTokens = tokens.map(tokenName => makeToken(tokenName)) + +export default class FungibleTokensService extends Service { + private accounts: AccountsService + + constructor(prisma: PrismaClient, accounts: AccountsService) { + super(prisma) + this.accounts = accounts + } + + async query(): Promise { + return allTokens + } + + async getByName(tokenName: string): Promise { + return makeToken(tokenName) + } + + async createWithdrawal( + sender: string, + recipient: string, + tokenName: string, + amount: string + ): Promise { + const authorization = await this.accounts.getAuthorization(sender) + + const transfer = getTokenTransferFunc(tokenName) + + const transactionId = await transfer( + recipient, + amount, + authorization, + config.contracts + ) + + return transactionId + } +} diff --git a/src/services/service.ts b/src/services/service.ts new file mode 100644 index 00000000..d2eabdb5 --- /dev/null +++ b/src/services/service.ts @@ -0,0 +1,9 @@ +import {PrismaClient} from "@prisma/client" + +export default class Service { + protected prisma: PrismaClient + + constructor(prisma: PrismaClient) { + this.prisma = prisma + } +} From 0aeff6c42e2cb6fa23489d73460ae494ca693de0 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 25 Apr 2021 18:42:56 -0700 Subject: [PATCH 038/112] Simple account creation --- package-lock.json | 9 +++++ package.json | 1 + src/controllers/accounts.ts | 6 +++ src/database/accounts.ts | 23 +++++++++-- src/database/schema.prisma | 8 ++-- src/lib/flow/createAccount.ts | 62 +++++++++++++++++++++++++++++ src/lib/flow/sendTransaction.ts | 31 ++++++++++----- src/routes/v1/accounts/index.ts | 1 + src/services/accounts.ts | 69 ++++++++++++++++++++++----------- 9 files changed, 171 insertions(+), 39 deletions(-) create mode 100644 src/lib/flow/createAccount.ts diff --git a/package-lock.json b/package-lock.json index 780cf725..52029296 100644 --- a/package-lock.json +++ b/package-lock.json @@ -592,6 +592,15 @@ "resolved": "https://registry.npmjs.org/@onflow/util-address/-/util-address-0.0.0.tgz", "integrity": "sha512-Lzbw/wV3O1fmfXYF2q6iGLgHz/7ATsLXOHceP5tzeEAKNf+srdtTNJv5jhNGhpFFAtQ6TcomXURVMhUg+/4YbA==" }, + "@onflow/util-encode-key": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@onflow/util-encode-key/-/util-encode-key-0.0.2.tgz", + "integrity": "sha512-gY9H4f1USijXbgk6IoNibAibF8VB6spZgq2fdkxA9PKgKay6UjolnsKTvIkA0k4/CIiODQIh5ziL069mrRjOJw==", + "requires": { + "@onflow/rlp": "0.0.3", + "@onflow/util-invariant": "0.0.0" + } + }, "@onflow/util-invariant": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/@onflow/util-invariant/-/util-invariant-0.0.0.tgz", diff --git a/package.json b/package.json index 7ee4c7da..b55b504e 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "dependencies": { "@onflow/fcl": "0.0.67", "@onflow/types": "0.0.4", + "@onflow/util-encode-key": "0.0.2", "@prisma/client": "^2.21.2", "dedent-js": "^1.0.1", "dotenv": "^8.2.0", diff --git a/src/controllers/accounts.ts b/src/controllers/accounts.ts index 48ef26af..b7b999ac 100644 --- a/src/controllers/accounts.ts +++ b/src/controllers/accounts.ts @@ -8,6 +8,12 @@ export default class AccountsController { this.accounts = accounts } + async createAccount(req, res) { + const account = await this.accounts.create() + + res.json(account) + } + async getAccounts(req, res) { const accounts = await this.accounts.query() diff --git a/src/database/accounts.ts b/src/database/accounts.ts index 3004c3a9..b92f6689 100644 --- a/src/database/accounts.ts +++ b/src/database/accounts.ts @@ -1,12 +1,27 @@ import {Prisma, PrismaClient} from "@prisma/client" +import {Account} from "./models" export async function insertAccount( prisma: PrismaClient, - account: Prisma.AccountCreateInput -) { - await prisma.account.create({ + address: string +): Promise { + return await prisma.account.create({ data: { - address: account.address, + address: address, }, }) } + +export async function listAccounts(prisma: PrismaClient): Promise { + // TODO: add pagination + return await prisma.account.findMany() +} + +export async function getAccount( + prisma: PrismaClient, + address: string +): Promise { + return await prisma.account.findFirst({ + where: {address}, + }) +} diff --git a/src/database/schema.prisma b/src/database/schema.prisma index 5f0c6b2b..14205079 100644 --- a/src/database/schema.prisma +++ b/src/database/schema.prisma @@ -14,14 +14,14 @@ model Account { @@map(name: "accounts") address String @id - created_at DateTime @default(now()) - updated_at DateTime @updatedAt + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") } model AdminSignerKey { @@map(name: "admin_signer_keys") index Int @id - created_at DateTime @default(now()) - updated_at DateTime @updatedAt + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") } diff --git a/src/lib/flow/createAccount.ts b/src/lib/flow/createAccount.ts new file mode 100644 index 00000000..2cc40bf7 --- /dev/null +++ b/src/lib/flow/createAccount.ts @@ -0,0 +1,62 @@ +import * as fcl from "@onflow/fcl" +import * as t from "@onflow/types" +import * as Crypto from "../crypto" + +import { + ECDSA_P256, + ECDSA_secp256k1, + SHA2_256, + SHA3_256, + encodeKey, +} from "@onflow/util-encode-key" + +import sendTransaction from "./sendTransaction" +import {AccountAuthorizer} from "./index" + +const sigAlgos = { + [Crypto.SignatureAlgorithm.ECDSA_P256]: ECDSA_P256, + [Crypto.SignatureAlgorithm.ECDSA_secp256k1]: ECDSA_secp256k1, +} + +const hashAlgos = { + [Crypto.HashAlgorithm.SHA2_256]: SHA2_256, + [Crypto.HashAlgorithm.SHA3_256]: SHA3_256, +} + +const accountKeyWeight = 1000 + +const txCreateAccount = ` +transaction(publicKey: String) { + + prepare(signer: AuthAccount) { + let account = AuthAccount(payer: signer) + account.addPublicKey(publicKey.decodeHex()) + } +} +` + +export async function createAccount( + publicKey: Crypto.PublicKey, + sigAlgo: Crypto.SignatureAlgorithm, + hashAlgo: Crypto.HashAlgorithm, + authorization: AccountAuthorizer +): Promise { + const encodedPublicKey = encodeKey( + publicKey.toHex(), + sigAlgos[sigAlgo], + hashAlgos[hashAlgo], + accountKeyWeight + ) + + const result = await sendTransaction({ + transaction: txCreateAccount, + args: [fcl.arg(encodedPublicKey, t.String)], + authorizations: [authorization], + payer: authorization, + proposer: authorization, + }) + + const accountCreatedEvent = result.events[0].data + + return accountCreatedEvent.address +} diff --git a/src/lib/flow/sendTransaction.ts b/src/lib/flow/sendTransaction.ts index 16e8b42b..0750b952 100644 --- a/src/lib/flow/sendTransaction.ts +++ b/src/lib/flow/sendTransaction.ts @@ -1,18 +1,28 @@ import * as fcl from "@onflow/fcl" -import {AccountAuthorization} from "./index" +import {AccountAuthorizer} from "./index" interface Argument { value: string xform: any // eslint-disable-line } -interface Transaction { +type Transaction = { transaction: string args: Argument[] - proposer: string - authorizations: AccountAuthorization[] - payer: string + proposer: AccountAuthorizer + authorizations: AccountAuthorizer[] + payer: AccountAuthorizer +} + +interface Event { + data: any // eslint-disable-line +} + +type TransactionResult = { + id: string + error: string + events: Event[] } export default async function sendTransaction({ @@ -21,7 +31,7 @@ export default async function sendTransaction({ proposer, authorizations, payer, -}: Transaction): Promise { +}: Transaction): Promise { const response = await fcl.send([ fcl.transaction(transaction), fcl.args(args), @@ -32,8 +42,11 @@ export default async function sendTransaction({ ]) const {transactionId} = response + const {error, events} = await fcl.tx(response).onceSealed() - await fcl.tx(response).onceSealed() - - return transactionId + return { + id: transactionId, + error: error, + events: events, + } } diff --git a/src/routes/v1/accounts/index.ts b/src/routes/v1/accounts/index.ts index 761bc95f..dc148783 100644 --- a/src/routes/v1/accounts/index.ts +++ b/src/routes/v1/accounts/index.ts @@ -15,6 +15,7 @@ function createRouter( router .route("/") .get(catchAsync((req, res) => accounts.getAccounts(req, res))) + .post(catchAsync((req, res) => accounts.createAccount(req, res))) router .route("/:address") diff --git a/src/services/accounts.ts b/src/services/accounts.ts index 6d10074c..c6f9a951 100644 --- a/src/services/accounts.ts +++ b/src/services/accounts.ts @@ -4,57 +4,82 @@ import * as Crypto from "../lib/crypto" import Service from "./service" import * as models from "../database/models" import getLeastRecentAdminSignerKey from "../database/getLeastRecentAdminSignerKey" -import {insertAccount} from "../database/accounts" +import {getAccount, insertAccount, listAccounts} from "../database/accounts" +import {createAccount} from "../lib/flow/createAccount" // TODO: add support for user accounts // // The current API is hard-coded to only support the admin account const adminAccount: models.Account = { address: config.adminAddress, - created_at: null, - updated_at: null, + createdAt: null, + updatedAt: null, } +const userSigAlgo = Crypto.SignatureAlgorithm.ECDSA_P256 +const userHashAlgo = Crypto.HashAlgorithm.SHA3_256 + export default class AccountsService extends Service { - async create(address: string) { - await insertAccount(this.prisma, {address: address}) + async create(): Promise { + const adminAuthorization = await this.getAdminAuthorization() + + const userPrivateKey = Crypto.InMemoryPrivateKey.generate(userSigAlgo) + const userPublicKey = userPrivateKey.getPublicKey() + + const address = await createAccount( + userPublicKey, + userSigAlgo, + userHashAlgo, + adminAuthorization + ) + + // TODO: save private key + return await insertAccount(this.prisma, address) } async query(): Promise { - // TODO: add support for user accounts - return [adminAccount] + // TODO: add pagination + + const accounts = await listAccounts(this.prisma) + + return [adminAccount, ...accounts] } async getByAddress(address: string): Promise { - // TODO: add support for user accounts if (address === adminAccount.address) { return adminAccount } - return null + return await getAccount(this.prisma, address) } async getSignerByAddress(address: string): Promise { // TODO: add support for user accounts - if (address !== adminAccount.address) { - return null - } + return null + } - const privateKey = Crypto.InMemoryPrivateKey.fromHex( - config.adminPrivateKey, - config.adminSigAlgo - ) + async getAuthorization(address: string): Promise { + if (address === config.adminAddress) { + return this.getAdminAuthorization() + } - return new Crypto.InMemorySigner(privateKey, config.adminHashAlgo) + // TODO: add support for user accounts + return null } - async getAuthorization(address: string): Promise { - const account = await this.getByAddress(address) + private async getAdminAuthorization(): Promise { + const adminKeyIndex = await getLeastRecentAdminSignerKey(this.prisma) - const signer = await this.getSignerByAddress(address) + const adminPrivateKey = Crypto.InMemoryPrivateKey.fromHex( + config.adminPrivateKey, + config.adminSigAlgo + ) - const keyIndex = await getLeastRecentAdminSignerKey(this.prisma) + const adminSigner = new Crypto.InMemorySigner( + adminPrivateKey, + config.adminHashAlgo + ) - return getAuthorization(account.address, keyIndex, signer) + return getAuthorization(config.adminAddress, adminKeyIndex, adminSigner) } } From b32c4667f55ae279b14d7ced6a670614faed1034 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 25 Apr 2021 21:34:58 -0700 Subject: [PATCH 039/112] User signing with encryption --- docker-compose.yml | 1 + env.example | 3 + src/admin.ts | 19 +++++ src/app.ts | 13 +++- src/config.ts | 37 ++++++++- src/controllers/fungibleTokens.ts | 1 - src/database/accounts.ts | 25 ++++++- .../migration.sql | 15 ++++ src/database/schema.prisma | 22 +++++- src/lib/flow/createAccount.ts | 43 ++++++++--- src/lib/flow/getAuthorization.ts | 4 +- src/lib/keys/encryption.ts | 43 +++++++++++ src/lib/keys/inMemory.ts | 75 +++++++++++++++++++ src/lib/keys/index.ts | 21 ++++++ src/services/accounts.ts | 74 ++++++++++-------- src/services/fungibleTokens.ts | 8 +- 16 files changed, 348 insertions(+), 56 deletions(-) create mode 100644 src/admin.ts create mode 100644 src/database/migrations/20210426021914_create_account_keys/migration.sql create mode 100644 src/lib/keys/encryption.ts create mode 100644 src/lib/keys/inMemory.ts create mode 100644 src/lib/keys/index.ts diff --git a/docker-compose.yml b/docker-compose.yml index f000385e..fab451e7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,3 +17,4 @@ services: - FLOW_SERVICEPRIVATEKEY=${ADMIN_PRIVATE_KEY} - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 - FLOW_SERVICEKEYHASHALGO=SHA3_256 + - FLOW_VERBOSE=true diff --git a/env.example b/env.example index 60bb70ff..613450ae 100644 --- a/env.example +++ b/env.example @@ -3,4 +3,7 @@ ACCESS_API_HOST=http://localhost:8080 DATABASE_URL="postgresql://wallet:wallet@localhost:5432/wallet?schema=public" ADMIN_ADDRESS=0xf8d6e0586b0a20c7 +ADMIN_KEY_TYPE="in-memory" ADMIN_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068 + +USER_ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8eaf6ec53b0143d32a02d84c54aa128bf2 diff --git a/src/admin.ts b/src/admin.ts new file mode 100644 index 00000000..3c4410ae --- /dev/null +++ b/src/admin.ts @@ -0,0 +1,19 @@ +import config from "./config" +import {KeyType, Key} from "./lib/keys" +import InMemoryKeyManager from "./lib/keys/inMemory" + +export function getAdminKey(): Key { + switch (config.adminKeyType) { + case KeyType.InMemory: + return getAdminInMemoryKey() + } +} + +function getAdminInMemoryKey(): Key { + const keyManager = new InMemoryKeyManager( + config.adminSigAlgo, + config.adminHashAlgo, + ) + + return keyManager.load(config.adminPrivateKey) +} diff --git a/src/app.ts b/src/app.ts index 9b048f2e..bd88f045 100644 --- a/src/app.ts +++ b/src/app.ts @@ -12,6 +12,9 @@ import AccountsService from "./services/accounts" import {PrismaClient} from ".prisma/client" import FungibleTokensService from "./services/fungibleTokens" import FungibleTokensController from "./controllers/fungibleTokens" +import InMemoryKeyManager from "./lib/keys/inMemory" +import config from "./config" +import {getAdminKey} from "./admin" const app = express() @@ -21,7 +24,15 @@ app.use(express.urlencoded({extended: false})) const prisma = new PrismaClient() -const accountsService = new AccountsService(prisma) +const userKeyManager = new InMemoryKeyManager( + config.userSigAlgo, + config.userHashAlgo, + config.userEncryptionKey, +) + +const adminKey = getAdminKey() + +const accountsService = new AccountsService(prisma, adminKey, userKeyManager) const accountsController = new AccountsController(accountsService) const fungiblTokensService = new FungibleTokensService(prisma, accountsService) diff --git a/src/config.ts b/src/config.ts index ac7098d6..a5b2d604 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,4 +1,5 @@ import {HashAlgorithm, SignatureAlgorithm} from "./lib/crypto" +import {KeyType} from "./lib/keys" const chainEmulator = "emulator" const chainTestnet = "testnet" @@ -28,14 +29,46 @@ function getContracts(chain) { throw `Invalid chain: ${chain}` } +const defaultSigAlgo = SignatureAlgorithm.ECDSA_P256 +const defaultHashAlgo = HashAlgorithm.SHA3_256 +const defaultkeyType = KeyType.InMemory + +function parseSigAlgo(sigAlgo: string): SignatureAlgorithm { + return SignatureAlgorithm[sigAlgo] || defaultSigAlgo +} + +function parseHashAlgo(hashAlgo: string): HashAlgorithm { + return HashAlgorithm[hashAlgo] || defaultHashAlgo +} + +function parseKeyType(keyType: string): KeyType { + return KeyType[keyType] || defaultkeyType +} + +function parseHex(hex: string): Buffer | null { + if (!hex) { + return null + } + + return Buffer.from(hex, "hex") +} + const config = { env: process.env.NODE_ENV, port: process.env.PORT || 3000, accessApiHost: process.env.ACCESS_API_HOST || "http://localhost:8080", + adminAddress: process.env.ADMIN_ADDRESS || "0xf8d6e0586b0a20c7", + adminKeyType: parseKeyType(process.env.ADMIN_KEY_TYPE), adminPrivateKey: process.env.ADMIN_PRIVATE_KEY, - adminSigAlgo: SignatureAlgorithm[process.env.ADMIN_SIG_ALGO || "ECDSA_P256"], - adminHashAlgo: HashAlgorithm[process.env.ADMIN_HASH_ALGO || "SHA3_256"], + adminSigAlgo: parseSigAlgo(process.env.ADMIN_SIG_ALGO), + adminHashAlgo: parseHashAlgo(process.env.ADMIN_HASH_ALGO), + + userKeyType: parseKeyType(process.env.USER_KEY_TYPE), + userSigAlgo: parseSigAlgo(process.env.USER_SIG_ALGO), + userHashAlgo: parseHashAlgo(process.env.USER_HASH_ALGO), + userEncryptionKey: parseHex(process.env.USER_ENCRYPTION_KEY), + contracts: getContracts(process.env.CHAIN), } diff --git a/src/controllers/fungibleTokens.ts b/src/controllers/fungibleTokens.ts index b13c15e7..b9525779 100644 --- a/src/controllers/fungibleTokens.ts +++ b/src/controllers/fungibleTokens.ts @@ -66,7 +66,6 @@ export default class FungibleTokensController { res.json(response) } catch (e) { - console.log(e) throw new ApiError( httpStatus.INTERNAL_SERVER_ERROR, "failed to complete withdrawal" diff --git a/src/database/accounts.ts b/src/database/accounts.ts index b92f6689..f1baed6b 100644 --- a/src/database/accounts.ts +++ b/src/database/accounts.ts @@ -1,13 +1,25 @@ -import {Prisma, PrismaClient} from "@prisma/client" +import {AccountKey, Prisma, PrismaClient} from "@prisma/client" +import {KeyType} from "../lib/keys" import {Account} from "./models" export async function insertAccount( prisma: PrismaClient, - address: string + address: string, + accountKeyType: KeyType, + accountKeyValue: string ): Promise { + const accountKeyIndex = 0 + return await prisma.account.create({ data: { address: address, + keys: { + create: { + index: accountKeyIndex, + type: accountKeyType, + value: accountKeyValue, + }, + }, }, }) } @@ -25,3 +37,12 @@ export async function getAccount( where: {address}, }) } + +export async function getAccountKey( + prisma: PrismaClient, + address: string +): Promise { + return await prisma.accountKey.findFirst({ + where: {accountAddress: address}, + }) +} diff --git a/src/database/migrations/20210426021914_create_account_keys/migration.sql b/src/database/migrations/20210426021914_create_account_keys/migration.sql new file mode 100644 index 00000000..50bd47b8 --- /dev/null +++ b/src/database/migrations/20210426021914_create_account_keys/migration.sql @@ -0,0 +1,15 @@ +-- CreateTable +CREATE TABLE "account_keys" ( + "account_address" TEXT NOT NULL, + "index" INTEGER NOT NULL, + "type" TEXT NOT NULL, + "value" TEXT NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "account_keys.account_address_index_unique" ON "account_keys"("account_address", "index"); + +-- AddForeignKey +ALTER TABLE "account_keys" ADD FOREIGN KEY ("account_address") REFERENCES "accounts"("address") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/src/database/schema.prisma b/src/database/schema.prisma index 14205079..1742b992 100644 --- a/src/database/schema.prisma +++ b/src/database/schema.prisma @@ -13,7 +13,24 @@ generator client { model Account { @@map(name: "accounts") - address String @id + address String @id + keys AccountKey[] + + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") +} + +model AccountKey { + @@map(name: "account_keys") + + @@unique([accountAddress, index]) + + account Account @relation(fields: [accountAddress], references: [address]) + accountAddress String @map("account_address") + index Int + type String + value String + createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") } @@ -21,7 +38,8 @@ model Account { model AdminSignerKey { @@map(name: "admin_signer_keys") - index Int @id + index Int @id + createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") } diff --git a/src/lib/flow/createAccount.ts b/src/lib/flow/createAccount.ts index 2cc40bf7..50638b70 100644 --- a/src/lib/flow/createAccount.ts +++ b/src/lib/flow/createAccount.ts @@ -1,6 +1,6 @@ import * as fcl from "@onflow/fcl" import * as t from "@onflow/types" -import * as Crypto from "../crypto" +import * as dedent from "dedent-js" import { ECDSA_P256, @@ -10,6 +10,7 @@ import { encodeKey, } from "@onflow/util-encode-key" +import * as Crypto from "../crypto" import sendTransaction from "./sendTransaction" import {AccountAuthorizer} from "./index" @@ -25,21 +26,45 @@ const hashAlgos = { const accountKeyWeight = 1000 -const txCreateAccount = ` -transaction(publicKey: String) { +function txCreateAccount(contracts) { + return dedent` + import FungibleToken from ${contracts.FungibleToken} + import FUSD from ${contracts.FUSD} + + transaction(publicKey: String) { + + let account: AuthAccount + + prepare(signer: AuthAccount) { + self.account = AuthAccount(payer: signer) + } + + execute { + self.account.addPublicKey(publicKey.decodeHex()) + + // Add FUSD vault + self.account.save(<-FUSD.createEmptyVault(), to: /storage/fusdVault) + + self.account.link<&FUSD.Vault{FungibleToken.Receiver}>( + /public/fusdReceiver, + target: /storage/fusdVault + ) - prepare(signer: AuthAccount) { - let account = AuthAccount(payer: signer) - account.addPublicKey(publicKey.decodeHex()) + self.account.link<&FUSD.Vault{FungibleToken.Balance}>( + /public/fusdBalance, + target: /storage/fusdVault + ) + } } + ` } -` export async function createAccount( publicKey: Crypto.PublicKey, sigAlgo: Crypto.SignatureAlgorithm, hashAlgo: Crypto.HashAlgorithm, - authorization: AccountAuthorizer + authorization: AccountAuthorizer, + contracts, ): Promise { const encodedPublicKey = encodeKey( publicKey.toHex(), @@ -49,7 +74,7 @@ export async function createAccount( ) const result = await sendTransaction({ - transaction: txCreateAccount, + transaction: txCreateAccount(contracts), args: [fcl.arg(encodedPublicKey, t.String)], authorizations: [authorization], payer: authorization, diff --git a/src/lib/flow/getAuthorization.ts b/src/lib/flow/getAuthorization.ts index c76dc96f..a7ecdca2 100644 --- a/src/lib/flow/getAuthorization.ts +++ b/src/lib/flow/getAuthorization.ts @@ -1,14 +1,14 @@ import * as fcl from "@onflow/fcl" import {AccountAuthorizer} from "./index" -import {Signer} from "../crypto" +import * as Crypto from "../crypto" const fromHex = (hex: string) => Buffer.from(hex, "hex") export default function getAuthorization( address: string, keyIndex: number, - signer: Signer + signer: Crypto.Signer ): AccountAuthorizer { return async (account = {}) => { return { diff --git a/src/lib/keys/encryption.ts b/src/lib/keys/encryption.ts new file mode 100644 index 00000000..b55aeef1 --- /dev/null +++ b/src/lib/keys/encryption.ts @@ -0,0 +1,43 @@ +import {createCipheriv, createDecipheriv, randomBytes} from "crypto" + +const encryptionAlgo = "aes-256-ctr" +const ivSize = 16 + +export function encrypt( + encryptionKey: Buffer, + value: string, +): string { + const iv = randomBytes(ivSize) + + const cipher = createCipheriv( + encryptionAlgo, + encryptionKey, + iv, + ) + + const encrypted = cipher.update(value, "hex", "hex") + + const ivHex = iv.toString("hex") + + return ivHex + encrypted + cipher.final("hex") +} + +export function decrypt( + encryptionKey: Buffer, + value: string, +): string { + const ivHex = value.slice(0, ivSize*2) + const ciphertextHex = value.slice(ivSize*2) + + const iv = Buffer.from(ivHex, "hex") + + const decipher = createDecipheriv( + encryptionAlgo, + encryptionKey, + iv, + ) + + const decrypted = decipher.update(ciphertextHex, "hex", "hex") + + return decrypted + decipher.final("hex") +} diff --git a/src/lib/keys/inMemory.ts b/src/lib/keys/inMemory.ts new file mode 100644 index 00000000..cffa7621 --- /dev/null +++ b/src/lib/keys/inMemory.ts @@ -0,0 +1,75 @@ +import * as Crypto from "../crypto" +import { decrypt, encrypt } from "./encryption" +import {Key, KeyManager, KeyType} from "./index" + +class InMemoryKey implements Key { + privateKey: Crypto.InMemoryPrivateKey + hashAlgo: Crypto.HashAlgorithm + + constructor( + privateKey: Crypto.InMemoryPrivateKey, + hashAlgo: Crypto.HashAlgorithm + ) { + this.privateKey = privateKey + this.hashAlgo = hashAlgo + } + + getPublicKey(): Crypto.PublicKey { + return this.privateKey.getPublicKey() + } + + getSignatureAlgorithm(): Crypto.SignatureAlgorithm { + return this.privateKey.getSignatureAlgorithm() + } + + getHashAlgorithm(): Crypto.HashAlgorithm { + return this.hashAlgo + } + + getSigner(): Crypto.Signer { + return new Crypto.InMemorySigner(this.privateKey, this.hashAlgo) + } +} + +export default class InMemoryKeyManager implements KeyManager { + keyType = KeyType.InMemory + sigAlgo: Crypto.SignatureAlgorithm + hashAlgo: Crypto.HashAlgorithm + encryptionKey: Buffer + + constructor( + sigAlgo: Crypto.SignatureAlgorithm, + hashAlgo: Crypto.HashAlgorithm, + encryptionKey?: Buffer, + ) { + this.sigAlgo = sigAlgo + this.hashAlgo = hashAlgo + this.encryptionKey = encryptionKey + } + + generate(): InMemoryKey { + const privateKey = Crypto.InMemoryPrivateKey.generate(this.sigAlgo) + return new InMemoryKey(privateKey, this.hashAlgo) + } + + save(key: InMemoryKey): string { + const hex = key.privateKey.toHex() + + if (this.encryptionKey) { + return encrypt(this.encryptionKey, hex) + } + + return hex + } + + load(value: string): InMemoryKey { + let hex = value + + if (this.encryptionKey) { + hex = decrypt(this.encryptionKey, hex) + } + + const privateKey = Crypto.InMemoryPrivateKey.fromHex(hex, this.sigAlgo) + return new InMemoryKey(privateKey, this.hashAlgo) + } +} diff --git a/src/lib/keys/index.ts b/src/lib/keys/index.ts new file mode 100644 index 00000000..2761b7a0 --- /dev/null +++ b/src/lib/keys/index.ts @@ -0,0 +1,21 @@ +import * as Crypto from "../crypto" + +export enum KeyType { + InMemory = "in-memory", + GoogleKMS = "google-kms", +} + +export interface Key { + getPublicKey(): Crypto.PublicKey + getSignatureAlgorithm(): Crypto.SignatureAlgorithm + getHashAlgorithm(): Crypto.HashAlgorithm + getSigner(): Crypto.Signer +} + +export interface KeyManager { + keyType: KeyType + + generate(): T + save(key: T): string + load(value: string): T +} diff --git a/src/services/accounts.ts b/src/services/accounts.ts index c6f9a951..cbf57b8b 100644 --- a/src/services/accounts.ts +++ b/src/services/accounts.ts @@ -4,37 +4,53 @@ import * as Crypto from "../lib/crypto" import Service from "./service" import * as models from "../database/models" import getLeastRecentAdminSignerKey from "../database/getLeastRecentAdminSignerKey" -import {getAccount, insertAccount, listAccounts} from "../database/accounts" +import { + getAccount, + getAccountKey, + insertAccount, + listAccounts, +} from "../database/accounts" import {createAccount} from "../lib/flow/createAccount" +import {Key, KeyManager} from "../lib/keys" +import {PrismaClient} from "@prisma/client" -// TODO: add support for user accounts -// -// The current API is hard-coded to only support the admin account const adminAccount: models.Account = { address: config.adminAddress, createdAt: null, updatedAt: null, } -const userSigAlgo = Crypto.SignatureAlgorithm.ECDSA_P256 -const userHashAlgo = Crypto.HashAlgorithm.SHA3_256 - export default class AccountsService extends Service { + private adminKey: Key + private userKeyManager: KeyManager + + constructor( + prisma: PrismaClient, + adminKey: Key, + userKeyManager: KeyManager + ) { + super(prisma) + this.adminKey = adminKey + this.userKeyManager = userKeyManager + } + async create(): Promise { const adminAuthorization = await this.getAdminAuthorization() - const userPrivateKey = Crypto.InMemoryPrivateKey.generate(userSigAlgo) - const userPublicKey = userPrivateKey.getPublicKey() + const userKey = this.userKeyManager.generate() const address = await createAccount( - userPublicKey, - userSigAlgo, - userHashAlgo, - adminAuthorization + userKey.getPublicKey(), + userKey.getSignatureAlgorithm(), + userKey.getHashAlgorithm(), + adminAuthorization, + config.contracts, ) - // TODO: save private key - return await insertAccount(this.prisma, address) + const userKeyType = this.userKeyManager.keyType + const userKeyValue = this.userKeyManager.save(userKey) + + return await insertAccount(this.prisma, address, userKeyType, userKeyValue) } async query(): Promise { @@ -53,33 +69,25 @@ export default class AccountsService extends Service { return await getAccount(this.prisma, address) } - async getSignerByAddress(address: string): Promise { - // TODO: add support for user accounts - return null - } - async getAuthorization(address: string): Promise { - if (address === config.adminAddress) { + if (address === adminAccount.address) { return this.getAdminAuthorization() } - // TODO: add support for user accounts - return null + const accountKey = await getAccountKey(this.prisma, address) + + const signer = this.userKeyManager.load(accountKey.value).getSigner() + + return getAuthorization(address, accountKey.index, signer) } private async getAdminAuthorization(): Promise { const adminKeyIndex = await getLeastRecentAdminSignerKey(this.prisma) - const adminPrivateKey = Crypto.InMemoryPrivateKey.fromHex( - config.adminPrivateKey, - config.adminSigAlgo + return getAuthorization( + adminAccount.address, + adminKeyIndex, + this.adminKey.getSigner() ) - - const adminSigner = new Crypto.InMemorySigner( - adminPrivateKey, - config.adminHashAlgo - ) - - return getAuthorization(config.adminAddress, adminKeyIndex, adminSigner) } } diff --git a/src/services/fungibleTokens.ts b/src/services/fungibleTokens.ts index 0d92c97f..bfbea1e9 100644 --- a/src/services/fungibleTokens.ts +++ b/src/services/fungibleTokens.ts @@ -33,17 +33,17 @@ export default class FungibleTokensService extends Service { tokenName: string, amount: string ): Promise { - const authorization = await this.accounts.getAuthorization(sender) + const userAuthorization = await this.accounts.getAuthorization(sender) const transfer = getTokenTransferFunc(tokenName) - const transactionId = await transfer( + const { id } = await transfer( recipient, amount, - authorization, + userAuthorization, config.contracts ) - return transactionId + return id } } From 35c7b77ea7405544bc2b970f18ff78d8942cbcb9 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 25 Apr 2021 21:37:21 -0700 Subject: [PATCH 040/112] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f18d9da3..d1b10cbf 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ implements a simple custodial wallet service for the Flow blockchain. ### 1. Admin - [x] Single admin account (hot wallet) -- [ ] [Create user accounts (using admin account)](https://github.com/onflow/flow-wallet-api-node-demo/issues/1) +- [x] [Create user accounts (using admin account)](https://github.com/onflow/flow-wallet-api-node-demo/issues/1) ### 2. Transaction Execution @@ -22,7 +22,7 @@ implements a simple custodial wallet service for the Flow blockchain. - [x] Send fungible token withdrawals from admin account (FLOW, FUSD) - [ ] [Detect fungible token deposits to admin account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/2) -- [ ] [Send fungible token withdrawals from a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/3) +- [x] [Send fungible token withdrawals from a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/3) - [ ] [Detect fungible token deposits to a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/4) - [ ] View the fungible token balance of the admin account - [ ] View the fungible token balance of a user account From c71cb6001a16691057b0d58f0aa5fc3bd71102ae Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 25 Apr 2021 21:54:54 -0700 Subject: [PATCH 041/112] Update README.md --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index d1b10cbf..64003274 100644 --- a/README.md +++ b/README.md @@ -72,8 +72,6 @@ npm run start #### List all accounts -> :warning: Not yet implemented - `GET /v1/accounts` Example @@ -98,8 +96,6 @@ curl --request GET \ #### Get an account -> :warning: Not yet implemented - `GET /v1/accounts/{address}` Parameters @@ -123,8 +119,6 @@ curl --request GET \ #### Create an account -> :warning: Not yet implemented - `POST /v1/accounts` Example From 1b88a85d2c4504d34c27d72320136a56f60b486e Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 26 Apr 2021 01:02:01 -0700 Subject: [PATCH 042/112] Code cleanup --- .eslintrc.js | 27 ++ package-lock.json | 411 ++++++++++++++++++ package.json | 4 +- src/admin.ts | 8 +- src/app.ts | 31 +- src/config.ts | 7 +- src/controllers/accounts.ts | 18 +- src/controllers/fungibleTokens.ts | 30 +- src/database/accounts.ts | 6 +- src/database/syncAdminSignerKeys.ts | 3 +- src/errors/InvalidFungibleTokenError.ts | 3 +- src/errors/NotFoundError.ts | 1 + src/index.ts | 6 +- src/lib/crypto/hash.ts | 1 + src/lib/flow/createAccount.ts | 5 +- src/lib/flow/getAuthorization.ts | 3 +- src/lib/flow/sendTransaction.ts | 8 +- src/lib/fungibleTokens/index.ts | 46 +- .../fungibleTokens/templates/transferFLOW.ts | 2 +- .../fungibleTokens/templates/transferFUSD.ts | 2 +- src/lib/fungibleTokens/transfer.ts | 50 --- src/lib/keys/encryption.ts | 28 +- src/lib/keys/inMemory.ts | 6 +- src/logger.ts | 3 +- src/middleware/errors.ts | 18 +- src/middleware/morgan.ts | 3 +- .../v1/accounts/fungibleTokens/index.ts | 6 +- .../v1/accounts/fungibleTokens/withdrawals.ts | 5 +- src/routes/v1/accounts/index.ts | 8 +- src/routes/v1/index.ts | 6 +- src/services/accounts.ts | 23 +- src/services/fungibleTokens.ts | 13 +- tsconfig.json | 4 + 33 files changed, 625 insertions(+), 170 deletions(-) delete mode 100644 src/lib/fungibleTokens/transfer.ts diff --git a/.eslintrc.js b/.eslintrc.js index 008514dc..cb417ac2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -7,5 +7,32 @@ module.exports = { "eslint:recommended", "plugin:prettier/recommended", "plugin:@typescript-eslint/recommended", + "plugin:import/errors", + "plugin:import/warnings", + "plugin:import/typescript", ], + rules: { + "import/no-unresolved": 0, + "import/order": [ + "error", + { + groups: [ + "builtin", + "external", + "internal", + "parent", + "sibling", + "index", + ], + pathGroups: [ + { + pattern: "src/**", + group: "internal", + }, + ], + pathGroupsExcludedImportTypes: ["builtin"], + "newlines-between": "always-and-inside-groups", + }, + ], + }, } diff --git a/package-lock.json b/package-lock.json index 52029296..576335bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -701,6 +701,12 @@ "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", "dev": true }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -979,12 +985,36 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.5" + } + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -1196,6 +1226,12 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -1277,6 +1313,15 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -1401,6 +1446,41 @@ } } }, + "es-abstract": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", + "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.2", + "is-string": "^1.0.5", + "object-inspect": "^1.9.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.0" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1559,6 +1639,116 @@ "integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==", "dev": true }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, "eslint-plugin-prettier": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz", @@ -1989,6 +2179,12 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2103,6 +2299,12 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "is-bigint": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", + "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==", + "dev": true + }, "is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2121,6 +2323,12 @@ "call-bind": "^1.0.0" } }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", + "dev": true + }, "is-core-module": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", @@ -2130,6 +2338,12 @@ "has": "^1.0.3" } }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2157,6 +2371,12 @@ "is-extglob": "^2.1.1" } }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2169,6 +2389,16 @@ "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", "dev": true }, + "is-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", + "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.1" + } + }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -2180,6 +2410,15 @@ "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", "dev": true }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -2231,6 +2470,15 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, "kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", @@ -2259,6 +2507,24 @@ "strip-bom": "^2.0.0" } }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -2491,6 +2757,42 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-inspect": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.2.tgz", + "integrity": "sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.values": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", + "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has": "^1.0.3" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -2535,6 +2837,30 @@ "word-wrap": "^1.2.3" } }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2628,6 +2954,26 @@ "pinkie": "^2.0.0" } }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + } + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3036,6 +3382,26 @@ "strip-ansi": "^6.0.0" } }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -3243,6 +3609,26 @@ } } }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -3288,6 +3674,18 @@ "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", "dev": true }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -3342,6 +3740,19 @@ "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, "winston": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", diff --git a/package.json b/package.json index b55b504e..07e517a0 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "start": "npm run deploy-contracts && npm run db-migrate-dev && npm run db-sync-admin-keys && npm run start-api", - "start-api": "NODE_ENV=development ts-node-dev --respawn ./src/index.ts", + "start-api": "NODE_ENV=development ts-node-dev -r tsconfig-paths/register --respawn ./src/index.ts", "deploy-contracts": "flow project deploy --network=emulator --update", "db-migrate-dev": "prisma migrate dev --preview-feature", "db-migrate-deploy": "prisma migrate deploy --preview-feature", @@ -40,11 +40,13 @@ "babel-eslint": "^10.1.0", "eslint": "^7.21.0", "eslint-config-prettier": "^8.1.0", + "eslint-plugin-import": "^2.22.1", "eslint-plugin-prettier": "^3.3.1", "prettier": "^2.2.1", "prisma": "^2.20.1", "ts-node": "^9.1.1", "ts-node-dev": "^1.1.6", + "tsconfig-paths": "^3.9.0", "typescript": "^4.2.4" } } diff --git a/src/admin.ts b/src/admin.ts index 3c4410ae..a683f1ab 100644 --- a/src/admin.ts +++ b/src/admin.ts @@ -1,6 +1,6 @@ -import config from "./config" -import {KeyType, Key} from "./lib/keys" -import InMemoryKeyManager from "./lib/keys/inMemory" +import config from "src/config" +import {KeyType, Key} from "src/lib/keys" +import InMemoryKeyManager from "src/lib/keys/inMemory" export function getAdminKey(): Key { switch (config.adminKeyType) { @@ -12,7 +12,7 @@ export function getAdminKey(): Key { function getAdminInMemoryKey(): Key { const keyManager = new InMemoryKeyManager( config.adminSigAlgo, - config.adminHashAlgo, + config.adminHashAlgo ) return keyManager.load(config.adminPrivateKey) diff --git a/src/app.ts b/src/app.ts index bd88f045..8e8c47b7 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,20 +1,17 @@ -import * as dotenv from "dotenv" -dotenv.config() - import * as express from "express" -import morganMiddleware from "./middleware/morgan" -import errorsMiddleware from "./middleware/errors" -import NotFoundError from "./errors/NotFoundError" - -import createRouter from "./routes/v1" -import AccountsController from "./controllers/accounts" -import AccountsService from "./services/accounts" -import {PrismaClient} from ".prisma/client" -import FungibleTokensService from "./services/fungibleTokens" -import FungibleTokensController from "./controllers/fungibleTokens" -import InMemoryKeyManager from "./lib/keys/inMemory" -import config from "./config" -import {getAdminKey} from "./admin" +import {PrismaClient} from "@prisma/client" + +import morganMiddleware from "src/middleware/morgan" +import errorsMiddleware from "src/middleware/errors" +import NotFoundError from "src/errors/NotFoundError" +import createRouter from "src/routes/v1" +import AccountsController from "src/controllers/accounts" +import AccountsService from "src/services/accounts" +import FungibleTokensService from "src/services/fungibleTokens" +import FungibleTokensController from "src/controllers/fungibleTokens" +import InMemoryKeyManager from "src/lib/keys/inMemory" +import config from "src/config" +import {getAdminKey} from "src/admin" const app = express() @@ -27,7 +24,7 @@ const prisma = new PrismaClient() const userKeyManager = new InMemoryKeyManager( config.userSigAlgo, config.userHashAlgo, - config.userEncryptionKey, + config.userEncryptionKey ) const adminKey = getAdminKey() diff --git a/src/config.ts b/src/config.ts index a5b2d604..aa5f153b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,5 +1,8 @@ -import {HashAlgorithm, SignatureAlgorithm} from "./lib/crypto" -import {KeyType} from "./lib/keys" +import * as dotenv from "dotenv" +dotenv.config() + +import {HashAlgorithm, SignatureAlgorithm} from "src/lib/crypto" +import {KeyType} from "src/lib/keys" const chainEmulator = "emulator" const chainTestnet = "testnet" diff --git a/src/controllers/accounts.ts b/src/controllers/accounts.ts index b7b999ac..6cb9df64 100644 --- a/src/controllers/accounts.ts +++ b/src/controllers/accounts.ts @@ -1,5 +1,7 @@ -import NotFoundError from "../errors/NotFoundError" -import AccountsService from "../services/accounts" +import * as express from "express" + +import NotFoundError from "src/errors/NotFoundError" +import AccountsService from "src/services/accounts" export default class AccountsController { private accounts: AccountsService @@ -8,19 +10,25 @@ export default class AccountsController { this.accounts = accounts } - async createAccount(req, res) { + async createAccount( + req: express.Request, + res: express.Response + ): Promise { const account = await this.accounts.create() res.json(account) } - async getAccounts(req, res) { + async getAccounts( + req: express.Request, + res: express.Response + ): Promise { const accounts = await this.accounts.query() res.json(accounts) } - async getAccount(req, res) { + async getAccount(req: express.Request, res: express.Response): Promise { const address = req.params.address const account = await this.accounts.getByAddress(address) diff --git a/src/controllers/fungibleTokens.ts b/src/controllers/fungibleTokens.ts index b9525779..727ced2e 100644 --- a/src/controllers/fungibleTokens.ts +++ b/src/controllers/fungibleTokens.ts @@ -1,8 +1,11 @@ import * as httpStatus from "http-status" -import {isValidToken} from "../lib/fungibleTokens" -import FungibleTokensService from "../services/fungibleTokens" -import ApiError from "../errors/ApiError" -import InvalidFungibleTokenError from "../errors/InvalidFungibleTokenError" + +import * as express from "express" + +import {isValidToken} from "src/lib/fungibleTokens" +import FungibleTokensService from "src/services/fungibleTokens" +import ApiError from "src/errors/ApiError" +import InvalidFungibleTokenError from "src/errors/InvalidFungibleTokenError" export default class FungibleTokensController { private fungibleTokens: FungibleTokensService @@ -11,13 +14,13 @@ export default class FungibleTokensController { this.fungibleTokens = fungibleTokens } - async getTokens(req, res) { + async getTokens(req: express.Request, res: express.Response): Promise { const tokens = await this.fungibleTokens.query() res.json(tokens) } - async getToken(req, res) { + async getToken(req: express.Request, res: express.Response): Promise { const tokenName = req.params.tokenName if (!isValidToken(tokenName)) { @@ -30,16 +33,25 @@ export default class FungibleTokensController { } // TODO: implement withdrawal getters - async getWithdrawals(req, res) { + async getWithdrawals( + req: express.Request, + res: express.Response + ): Promise { res.send("TODO: implement me") } // TODO: implement withdrawal getters - async getWithdrawal(req, res) { + async getWithdrawal( + req: express.Request, + res: express.Response + ): Promise { res.send("TODO: implement me") } - async createWithdrawal(req, res) { + async createWithdrawal( + req: express.Request, + res: express.Response + ): Promise { const sender = req.params.address const tokenName = req.params.tokenName diff --git a/src/database/accounts.ts b/src/database/accounts.ts index f1baed6b..535555e2 100644 --- a/src/database/accounts.ts +++ b/src/database/accounts.ts @@ -1,5 +1,7 @@ -import {AccountKey, Prisma, PrismaClient} from "@prisma/client" -import {KeyType} from "../lib/keys" +import {AccountKey, PrismaClient} from "@prisma/client" + +import {KeyType} from "src/lib/keys" + import {Account} from "./models" export async function insertAccount( diff --git a/src/database/syncAdminSignerKeys.ts b/src/database/syncAdminSignerKeys.ts index 5d950ff8..6f6a686b 100644 --- a/src/database/syncAdminSignerKeys.ts +++ b/src/database/syncAdminSignerKeys.ts @@ -1,10 +1,9 @@ import * as fcl from "@onflow/fcl" import * as dotenv from "dotenv" +import {PrismaClient} from "@prisma/client" dotenv.config() -import {PrismaClient} from "@prisma/client" - const prisma = new PrismaClient() const adminAddress = process.env.ADMIN_ADDRESS diff --git a/src/errors/InvalidFungibleTokenError.ts b/src/errors/InvalidFungibleTokenError.ts index faeeaea6..1e5047a0 100644 --- a/src/errors/InvalidFungibleTokenError.ts +++ b/src/errors/InvalidFungibleTokenError.ts @@ -1,10 +1,11 @@ import * as httpStatus from "http-status" + import ApiError from "./ApiError" export default class InvalidFungibleTokenError extends ApiError { constructor(token: string) { const statusCode = httpStatus.BAD_REQUEST - const message = `${token} is not valid fungible token` + const message = `${token} is not a valid fungible token` super(statusCode, message) } diff --git a/src/errors/NotFoundError.ts b/src/errors/NotFoundError.ts index 9ace0cf1..c05250f3 100644 --- a/src/errors/NotFoundError.ts +++ b/src/errors/NotFoundError.ts @@ -1,4 +1,5 @@ import * as httpStatus from "http-status" + import ApiError from "./ApiError" export default class NotFoundError extends ApiError { diff --git a/src/index.ts b/src/index.ts index 6e52c998..36b6c798 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ -import app from "./app" -import config from "./config" -import logger from "./logger" +import config from "src/config" +import logger from "src/logger" +import app from "src/app" const server = app.listen(config.port, () => { logger.info(`Listening on port ${config.port}`) diff --git a/src/lib/crypto/hash.ts b/src/lib/crypto/hash.ts index 39250556..25b1addb 100644 --- a/src/lib/crypto/hash.ts +++ b/src/lib/crypto/hash.ts @@ -1,4 +1,5 @@ import {createHash, Hash} from "crypto" + import {SHA3} from "sha3" export enum HashAlgorithm { diff --git a/src/lib/flow/createAccount.ts b/src/lib/flow/createAccount.ts index 50638b70..9217c092 100644 --- a/src/lib/flow/createAccount.ts +++ b/src/lib/flow/createAccount.ts @@ -1,7 +1,6 @@ import * as fcl from "@onflow/fcl" import * as t from "@onflow/types" import * as dedent from "dedent-js" - import { ECDSA_P256, ECDSA_secp256k1, @@ -11,7 +10,9 @@ import { } from "@onflow/util-encode-key" import * as Crypto from "../crypto" + import sendTransaction from "./sendTransaction" + import {AccountAuthorizer} from "./index" const sigAlgos = { @@ -64,7 +65,7 @@ export async function createAccount( sigAlgo: Crypto.SignatureAlgorithm, hashAlgo: Crypto.HashAlgorithm, authorization: AccountAuthorizer, - contracts, + contracts: {[key: string]: string} ): Promise { const encodedPublicKey = encodeKey( publicKey.toHex(), diff --git a/src/lib/flow/getAuthorization.ts b/src/lib/flow/getAuthorization.ts index a7ecdca2..789e6f0c 100644 --- a/src/lib/flow/getAuthorization.ts +++ b/src/lib/flow/getAuthorization.ts @@ -1,8 +1,9 @@ import * as fcl from "@onflow/fcl" -import {AccountAuthorizer} from "./index" import * as Crypto from "../crypto" +import {AccountAuthorizer} from "./index" + const fromHex = (hex: string) => Buffer.from(hex, "hex") export default function getAuthorization( diff --git a/src/lib/flow/sendTransaction.ts b/src/lib/flow/sendTransaction.ts index 0750b952..526a2c17 100644 --- a/src/lib/flow/sendTransaction.ts +++ b/src/lib/flow/sendTransaction.ts @@ -2,12 +2,12 @@ import * as fcl from "@onflow/fcl" import {AccountAuthorizer} from "./index" -interface Argument { +export interface Argument { value: string xform: any // eslint-disable-line } -type Transaction = { +export type Transaction = { transaction: string args: Argument[] proposer: AccountAuthorizer @@ -15,11 +15,11 @@ type Transaction = { payer: AccountAuthorizer } -interface Event { +export interface Event { data: any // eslint-disable-line } -type TransactionResult = { +export type TransactionResult = { id: string error: string events: Event[] diff --git a/src/lib/fungibleTokens/index.ts b/src/lib/fungibleTokens/index.ts index 10c1b248..6eefba90 100644 --- a/src/lib/fungibleTokens/index.ts +++ b/src/lib/fungibleTokens/index.ts @@ -1,19 +1,49 @@ -import {transferFLOW, transferFUSD} from "./transfer" +import * as fcl from "@onflow/fcl" +import * as t from "@onflow/types" + +import {AccountAuthorizer} from "../flow" +import sendTransaction, {TransactionResult} from "../flow/sendTransaction" + +import transferFLOWTemplate from "./templates/transferFLOW" +import transferFUSDTemplate from "./templates/transferFUSD" const tokenFLOW = "flow" const tokenFUSD = "fusd" export const tokens = [tokenFLOW, tokenFUSD] -const transferFuncs = { - [tokenFLOW]: transferFLOW, - [tokenFUSD]: transferFUSD, +type TransactionTemplate = (contracts: {[key: string]: string}) => string + +export function isValidToken(tokenName: string): boolean { + return tokenName == tokenFLOW || tokenName == tokenFUSD } -export function isValidToken(token: string): boolean { - return token in transferFuncs +function getTransferTemplate(tokenName: string): TransactionTemplate { + switch (tokenName) { + case tokenFLOW: + return transferFLOWTemplate + case tokenFUSD: + return transferFUSDTemplate + } } -export function getTokenTransferFunc(token) { - return transferFuncs[token] +export async function transfer( + tokenName: string, + recipient: string, + amount: string, + authorization: AccountAuthorizer, + contracts: {[key: string]: string} +): Promise { + const template = getTransferTemplate(tokenName) + + return await sendTransaction({ + transaction: template(contracts), + args: [ + fcl.arg(fcl.withPrefix(recipient), t.Address), + fcl.arg(amount, t.UFix64), + ], + authorizations: [authorization], + payer: authorization, + proposer: authorization, + }) } diff --git a/src/lib/fungibleTokens/templates/transferFLOW.ts b/src/lib/fungibleTokens/templates/transferFLOW.ts index 92261ea3..45ff39bd 100644 --- a/src/lib/fungibleTokens/templates/transferFLOW.ts +++ b/src/lib/fungibleTokens/templates/transferFLOW.ts @@ -1,6 +1,6 @@ import * as dedent from "dedent-js" -export default function template(contracts) { +export default function template(contracts: {[key: string]: string}): string { return dedent` import FungibleToken from ${contracts.FungibleToken} import FlowToken from ${contracts.FlowToken} diff --git a/src/lib/fungibleTokens/templates/transferFUSD.ts b/src/lib/fungibleTokens/templates/transferFUSD.ts index 761cc81d..03f79d86 100644 --- a/src/lib/fungibleTokens/templates/transferFUSD.ts +++ b/src/lib/fungibleTokens/templates/transferFUSD.ts @@ -1,6 +1,6 @@ import * as dedent from "dedent-js" -export default function template(contracts) { +export default function template(contracts: {[key: string]: string}): string { return dedent` import FungibleToken from ${contracts.FungibleToken} import FUSD from ${contracts.FUSD} diff --git a/src/lib/fungibleTokens/transfer.ts b/src/lib/fungibleTokens/transfer.ts deleted file mode 100644 index 6f63942d..00000000 --- a/src/lib/fungibleTokens/transfer.ts +++ /dev/null @@ -1,50 +0,0 @@ -import * as fcl from "@onflow/fcl" -import * as t from "@onflow/types" - -import sendTransaction from "../flow/sendTransaction" - -import transferFLOWTemplate from "./templates/transferFLOW" -import transferFUSDTemplate from "./templates/transferFUSD" - -async function transfer(template, recipient, amount, authorization, contracts) { - return await sendTransaction({ - transaction: template(contracts), - args: [ - fcl.arg(fcl.withPrefix(recipient), t.Address), - fcl.arg(amount, t.UFix64), - ], - authorizations: [authorization], - payer: authorization, - proposer: authorization, - }) -} - -export async function transferFLOW( - recipient, - amount, - authorization, - contracts -) { - return transfer( - transferFLOWTemplate, - recipient, - amount, - authorization, - contracts - ) -} - -export async function transferFUSD( - recipient, - amount, - authorization, - contracts -) { - return transfer( - transferFUSDTemplate, - recipient, - amount, - authorization, - contracts - ) -} diff --git a/src/lib/keys/encryption.ts b/src/lib/keys/encryption.ts index b55aeef1..a528cc9b 100644 --- a/src/lib/keys/encryption.ts +++ b/src/lib/keys/encryption.ts @@ -3,17 +3,10 @@ import {createCipheriv, createDecipheriv, randomBytes} from "crypto" const encryptionAlgo = "aes-256-ctr" const ivSize = 16 -export function encrypt( - encryptionKey: Buffer, - value: string, -): string { +export function encrypt(encryptionKey: Buffer, value: string): string { const iv = randomBytes(ivSize) - const cipher = createCipheriv( - encryptionAlgo, - encryptionKey, - iv, - ) + const cipher = createCipheriv(encryptionAlgo, encryptionKey, iv) const encrypted = cipher.update(value, "hex", "hex") @@ -21,21 +14,14 @@ export function encrypt( return ivHex + encrypted + cipher.final("hex") } - -export function decrypt( - encryptionKey: Buffer, - value: string, -): string { - const ivHex = value.slice(0, ivSize*2) - const ciphertextHex = value.slice(ivSize*2) + +export function decrypt(encryptionKey: Buffer, value: string): string { + const ivHex = value.slice(0, ivSize * 2) + const ciphertextHex = value.slice(ivSize * 2) const iv = Buffer.from(ivHex, "hex") - const decipher = createDecipheriv( - encryptionAlgo, - encryptionKey, - iv, - ) + const decipher = createDecipheriv(encryptionAlgo, encryptionKey, iv) const decrypted = decipher.update(ciphertextHex, "hex", "hex") diff --git a/src/lib/keys/inMemory.ts b/src/lib/keys/inMemory.ts index cffa7621..384da3a3 100644 --- a/src/lib/keys/inMemory.ts +++ b/src/lib/keys/inMemory.ts @@ -1,5 +1,7 @@ import * as Crypto from "../crypto" -import { decrypt, encrypt } from "./encryption" + +import {decrypt, encrypt} from "./encryption" + import {Key, KeyManager, KeyType} from "./index" class InMemoryKey implements Key { @@ -40,7 +42,7 @@ export default class InMemoryKeyManager implements KeyManager { constructor( sigAlgo: Crypto.SignatureAlgorithm, hashAlgo: Crypto.HashAlgorithm, - encryptionKey?: Buffer, + encryptionKey?: Buffer ) { this.sigAlgo = sigAlgo this.hashAlgo = hashAlgo diff --git a/src/logger.ts b/src/logger.ts index 3db0f4cf..a22b2d6d 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,5 +1,6 @@ import * as winston from "winston" -import config from "./config" + +import config from "src/config" const enumerateErrorFormat = winston.format(info => { if (info instanceof Error) { diff --git a/src/middleware/errors.ts b/src/middleware/errors.ts index 21ab2b81..7c8d4973 100644 --- a/src/middleware/errors.ts +++ b/src/middleware/errors.ts @@ -1,19 +1,25 @@ import * as httpStatus from "http-status" -import config from "../config" -import logger from "../logger" -import ApiError from "../errors/ApiError" +import * as express from "express" -const errorsMiddleware = (err, req, res, next) => { +import config from "src/config" +import logger from "src/logger" +import ApiError from "src/errors/ApiError" + +function errorsMiddleware( + err: ApiError, + req: express.Request, + res: express.Response +): void { let {statusCode, message} = err if (!(err instanceof ApiError)) { statusCode = httpStatus.INTERNAL_SERVER_ERROR - message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR] + message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR] as string } else if ( config.env === "production" && statusCode === httpStatus.INTERNAL_SERVER_ERROR ) { - message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR] + message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR] as string } const response = { diff --git a/src/middleware/morgan.ts b/src/middleware/morgan.ts index ac1c7161..280cec22 100644 --- a/src/middleware/morgan.ts +++ b/src/middleware/morgan.ts @@ -1,5 +1,6 @@ import * as morgan from "morgan" -import logger from "../logger" + +import logger from "src/logger" const stream = { write: message => logger.info(message), diff --git a/src/routes/v1/accounts/fungibleTokens/index.ts b/src/routes/v1/accounts/fungibleTokens/index.ts index f7d69fda..55a4eb27 100644 --- a/src/routes/v1/accounts/fungibleTokens/index.ts +++ b/src/routes/v1/accounts/fungibleTokens/index.ts @@ -1,6 +1,8 @@ import * as express from "express" -import FungibleTokensController from "../../../../controllers/fungibleTokens" -import catchAsync from "../../../../errors/catchAsync" + +import FungibleTokensController from "src/controllers/fungibleTokens" +import catchAsync from "src/errors/catchAsync" + import createWithdrawalsRouter from "./withdrawals" function createRouter( diff --git a/src/routes/v1/accounts/fungibleTokens/withdrawals.ts b/src/routes/v1/accounts/fungibleTokens/withdrawals.ts index 1bdb9695..0cc104aa 100644 --- a/src/routes/v1/accounts/fungibleTokens/withdrawals.ts +++ b/src/routes/v1/accounts/fungibleTokens/withdrawals.ts @@ -1,6 +1,7 @@ import * as express from "express" -import FungibleTokensController from "../../../../controllers/fungibleTokens" -import catchAsync from "../../../../errors/catchAsync" + +import FungibleTokensController from "src/controllers/fungibleTokens" +import catchAsync from "src/errors/catchAsync" function createRouter( fungibleTokens: FungibleTokensController diff --git a/src/routes/v1/accounts/index.ts b/src/routes/v1/accounts/index.ts index dc148783..42334f7b 100644 --- a/src/routes/v1/accounts/index.ts +++ b/src/routes/v1/accounts/index.ts @@ -1,7 +1,9 @@ import * as express from "express" -import AccountsController from "../../../controllers/accounts" -import FungibleTokensController from "../../../controllers/fungibleTokens" -import catchAsync from "../../../errors/catchAsync" + +import AccountsController from "src/controllers/accounts" +import FungibleTokensController from "src/controllers/fungibleTokens" +import catchAsync from "src/errors/catchAsync" + import createFungibleTokensRouter from "./fungibleTokens" function createRouter( diff --git a/src/routes/v1/index.ts b/src/routes/v1/index.ts index ebe917f5..7296900c 100644 --- a/src/routes/v1/index.ts +++ b/src/routes/v1/index.ts @@ -1,6 +1,8 @@ import * as express from "express" -import AccountsController from "../../controllers/accounts" -import FungibleTokensController from "../../controllers/fungibleTokens" + +import AccountsController from "src/controllers/accounts" +import FungibleTokensController from "src/controllers/fungibleTokens" + import createAccountsRouter from "./accounts" function createRouter( diff --git a/src/services/accounts.ts b/src/services/accounts.ts index cbf57b8b..f6b8466a 100644 --- a/src/services/accounts.ts +++ b/src/services/accounts.ts @@ -1,18 +1,19 @@ -import config from "../config" -import {AccountAuthorizer, getAuthorization} from "../lib/flow" -import * as Crypto from "../lib/crypto" -import Service from "./service" -import * as models from "../database/models" -import getLeastRecentAdminSignerKey from "../database/getLeastRecentAdminSignerKey" +import {PrismaClient} from "@prisma/client" + +import {AccountAuthorizer, getAuthorization} from "src/lib/flow" +import getLeastRecentAdminSignerKey from "src/database/getLeastRecentAdminSignerKey" +import * as models from "src/database/models" import { getAccount, getAccountKey, insertAccount, listAccounts, -} from "../database/accounts" -import {createAccount} from "../lib/flow/createAccount" -import {Key, KeyManager} from "../lib/keys" -import {PrismaClient} from "@prisma/client" +} from "src/database/accounts" +import {createAccount} from "src/lib/flow/createAccount" +import {Key, KeyManager} from "src/lib/keys" +import config from "src/config" + +import Service from "./service" const adminAccount: models.Account = { address: config.adminAddress, @@ -44,7 +45,7 @@ export default class AccountsService extends Service { userKey.getSignatureAlgorithm(), userKey.getHashAlgorithm(), adminAuthorization, - config.contracts, + config.contracts ) const userKeyType = this.userKeyManager.keyType diff --git a/src/services/fungibleTokens.ts b/src/services/fungibleTokens.ts index bfbea1e9..0c12ca7b 100644 --- a/src/services/fungibleTokens.ts +++ b/src/services/fungibleTokens.ts @@ -1,6 +1,8 @@ -import {PrismaClient} from ".prisma/client" -import config from "../config" -import {getTokenTransferFunc, tokens} from "../lib/fungibleTokens" +import {PrismaClient} from "@prisma/client" + +import config from "src/config" +import {transfer, tokens} from "src/lib/fungibleTokens" + import Service from "./service" import AccountsService from "./accounts" @@ -35,9 +37,8 @@ export default class FungibleTokensService extends Service { ): Promise { const userAuthorization = await this.accounts.getAuthorization(sender) - const transfer = getTokenTransferFunc(tokenName) - - const { id } = await transfer( + const {id} = await transfer( + tokenName, recipient, amount, userAuthorization, diff --git a/tsconfig.json b/tsconfig.json index f1fa2cee..0ae5bc31 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,10 @@ "outDir": "./built", "target": "es2020", "moduleResolution": "node", + "baseUrl": ".", + "paths": { + "src": ["./src"] + } }, "include": ["./src/**/*"] } From ef3e5c3c491eb1b09efef6b15edeaa2d16ea9ca7 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 26 Apr 2021 01:15:16 -0700 Subject: [PATCH 043/112] Rename Crypto to lowercase crypto --- src/lib/flow/createAccount.ts | 16 ++++++++-------- src/lib/flow/getAuthorization.ts | 4 ++-- src/lib/keys/inMemory.ts | 32 ++++++++++++++++---------------- src/lib/keys/index.ts | 10 +++++----- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/lib/flow/createAccount.ts b/src/lib/flow/createAccount.ts index 9217c092..cdb659fb 100644 --- a/src/lib/flow/createAccount.ts +++ b/src/lib/flow/createAccount.ts @@ -9,20 +9,20 @@ import { encodeKey, } from "@onflow/util-encode-key" -import * as Crypto from "../crypto" +import * as crypto from "../crypto" import sendTransaction from "./sendTransaction" import {AccountAuthorizer} from "./index" const sigAlgos = { - [Crypto.SignatureAlgorithm.ECDSA_P256]: ECDSA_P256, - [Crypto.SignatureAlgorithm.ECDSA_secp256k1]: ECDSA_secp256k1, + [crypto.SignatureAlgorithm.ECDSA_P256]: ECDSA_P256, + [crypto.SignatureAlgorithm.ECDSA_secp256k1]: ECDSA_secp256k1, } const hashAlgos = { - [Crypto.HashAlgorithm.SHA2_256]: SHA2_256, - [Crypto.HashAlgorithm.SHA3_256]: SHA3_256, + [crypto.HashAlgorithm.SHA2_256]: SHA2_256, + [crypto.HashAlgorithm.SHA3_256]: SHA3_256, } const accountKeyWeight = 1000 @@ -61,9 +61,9 @@ function txCreateAccount(contracts) { } export async function createAccount( - publicKey: Crypto.PublicKey, - sigAlgo: Crypto.SignatureAlgorithm, - hashAlgo: Crypto.HashAlgorithm, + publicKey: crypto.PublicKey, + sigAlgo: crypto.SignatureAlgorithm, + hashAlgo: crypto.HashAlgorithm, authorization: AccountAuthorizer, contracts: {[key: string]: string} ): Promise { diff --git a/src/lib/flow/getAuthorization.ts b/src/lib/flow/getAuthorization.ts index 789e6f0c..895608f4 100644 --- a/src/lib/flow/getAuthorization.ts +++ b/src/lib/flow/getAuthorization.ts @@ -1,6 +1,6 @@ import * as fcl from "@onflow/fcl" -import * as Crypto from "../crypto" +import * as crypto from "../crypto" import {AccountAuthorizer} from "./index" @@ -9,7 +9,7 @@ const fromHex = (hex: string) => Buffer.from(hex, "hex") export default function getAuthorization( address: string, keyIndex: number, - signer: Crypto.Signer + signer: crypto.Signer ): AccountAuthorizer { return async (account = {}) => { return { diff --git a/src/lib/keys/inMemory.ts b/src/lib/keys/inMemory.ts index 384da3a3..3fd1a47d 100644 --- a/src/lib/keys/inMemory.ts +++ b/src/lib/keys/inMemory.ts @@ -1,47 +1,47 @@ -import * as Crypto from "../crypto" +import * as crypto from "../crypto" import {decrypt, encrypt} from "./encryption" import {Key, KeyManager, KeyType} from "./index" class InMemoryKey implements Key { - privateKey: Crypto.InMemoryPrivateKey - hashAlgo: Crypto.HashAlgorithm + privateKey: crypto.InMemoryPrivateKey + hashAlgo: crypto.HashAlgorithm constructor( - privateKey: Crypto.InMemoryPrivateKey, - hashAlgo: Crypto.HashAlgorithm + privateKey: crypto.InMemoryPrivateKey, + hashAlgo: crypto.HashAlgorithm ) { this.privateKey = privateKey this.hashAlgo = hashAlgo } - getPublicKey(): Crypto.PublicKey { + getPublicKey(): crypto.PublicKey { return this.privateKey.getPublicKey() } - getSignatureAlgorithm(): Crypto.SignatureAlgorithm { + getSignatureAlgorithm(): crypto.SignatureAlgorithm { return this.privateKey.getSignatureAlgorithm() } - getHashAlgorithm(): Crypto.HashAlgorithm { + getHashAlgorithm(): crypto.HashAlgorithm { return this.hashAlgo } - getSigner(): Crypto.Signer { - return new Crypto.InMemorySigner(this.privateKey, this.hashAlgo) + getSigner(): crypto.Signer { + return new crypto.InMemorySigner(this.privateKey, this.hashAlgo) } } export default class InMemoryKeyManager implements KeyManager { keyType = KeyType.InMemory - sigAlgo: Crypto.SignatureAlgorithm - hashAlgo: Crypto.HashAlgorithm + sigAlgo: crypto.SignatureAlgorithm + hashAlgo: crypto.HashAlgorithm encryptionKey: Buffer constructor( - sigAlgo: Crypto.SignatureAlgorithm, - hashAlgo: Crypto.HashAlgorithm, + sigAlgo: crypto.SignatureAlgorithm, + hashAlgo: crypto.HashAlgorithm, encryptionKey?: Buffer ) { this.sigAlgo = sigAlgo @@ -50,7 +50,7 @@ export default class InMemoryKeyManager implements KeyManager { } generate(): InMemoryKey { - const privateKey = Crypto.InMemoryPrivateKey.generate(this.sigAlgo) + const privateKey = crypto.InMemoryPrivateKey.generate(this.sigAlgo) return new InMemoryKey(privateKey, this.hashAlgo) } @@ -71,7 +71,7 @@ export default class InMemoryKeyManager implements KeyManager { hex = decrypt(this.encryptionKey, hex) } - const privateKey = Crypto.InMemoryPrivateKey.fromHex(hex, this.sigAlgo) + const privateKey = crypto.InMemoryPrivateKey.fromHex(hex, this.sigAlgo) return new InMemoryKey(privateKey, this.hashAlgo) } } diff --git a/src/lib/keys/index.ts b/src/lib/keys/index.ts index 2761b7a0..d95bf9ac 100644 --- a/src/lib/keys/index.ts +++ b/src/lib/keys/index.ts @@ -1,4 +1,4 @@ -import * as Crypto from "../crypto" +import * as crypto from "../crypto" export enum KeyType { InMemory = "in-memory", @@ -6,10 +6,10 @@ export enum KeyType { } export interface Key { - getPublicKey(): Crypto.PublicKey - getSignatureAlgorithm(): Crypto.SignatureAlgorithm - getHashAlgorithm(): Crypto.HashAlgorithm - getSigner(): Crypto.Signer + getPublicKey(): crypto.PublicKey + getSignatureAlgorithm(): crypto.SignatureAlgorithm + getHashAlgorithm(): crypto.HashAlgorithm + getSigner(): crypto.Signer } export interface KeyManager { From 57e04ee730e8d5b0dc3654b97b48c0e7a50e8ab9 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 26 Apr 2021 22:59:36 -0700 Subject: [PATCH 044/112] Create LICENSE --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 51e12b698cc308b15bbf00677a24578c4c4a97f1 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 26 Apr 2021 23:01:40 -0700 Subject: [PATCH 045/112] Add simple transaction execution --- src/app.ts | 17 ++++++-- src/controllers/transactions.ts | 59 ++++++++++++++++++++++++++ src/lib/flow/createAccount.ts | 11 +++-- src/lib/flow/sendTransaction.ts | 13 +++--- src/lib/fungibleTokens/index.ts | 13 ++++-- src/routes/v1/accounts/index.ts | 5 +++ src/routes/v1/accounts/transactions.ts | 21 +++++++++ src/routes/v1/index.ts | 8 +++- src/services/transactions.ts | 33 ++++++++++++++ 9 files changed, 161 insertions(+), 19 deletions(-) create mode 100644 src/controllers/transactions.ts create mode 100644 src/routes/v1/accounts/transactions.ts create mode 100644 src/services/transactions.ts diff --git a/src/app.ts b/src/app.ts index 8e8c47b7..ba0ad773 100644 --- a/src/app.ts +++ b/src/app.ts @@ -9,6 +9,8 @@ import AccountsController from "src/controllers/accounts" import AccountsService from "src/services/accounts" import FungibleTokensService from "src/services/fungibleTokens" import FungibleTokensController from "src/controllers/fungibleTokens" +import TransactionsController from "src/controllers/transactions" +import TransactionsService from "src/services/transactions" import InMemoryKeyManager from "src/lib/keys/inMemory" import config from "src/config" import {getAdminKey} from "src/admin" @@ -32,12 +34,19 @@ const adminKey = getAdminKey() const accountsService = new AccountsService(prisma, adminKey, userKeyManager) const accountsController = new AccountsController(accountsService) -const fungiblTokensService = new FungibleTokensService(prisma, accountsService) -const fungiblTokensController = new FungibleTokensController( - fungiblTokensService +const transactionsService = new TransactionsService(prisma, accountsService) +const transactionsController = new TransactionsController(transactionsService) + +const fungibleTokensService = new FungibleTokensService(prisma, accountsService) +const fungibleTokensController = new FungibleTokensController( + fungibleTokensService ) -const v1Router = createRouter(accountsController, fungiblTokensController) +const v1Router = createRouter( + accountsController, + transactionsController, + fungibleTokensController +) app.use("/v1", v1Router) diff --git a/src/controllers/transactions.ts b/src/controllers/transactions.ts new file mode 100644 index 00000000..de312f6e --- /dev/null +++ b/src/controllers/transactions.ts @@ -0,0 +1,59 @@ +import * as httpStatus from "http-status" + +import * as express from "express" + +import TransactionsService from "src/services/transactions" +import ApiError from "src/errors/ApiError" + +export default class TransactionsController { + private transactions: TransactionsService + + constructor(transactions: TransactionsService) { + this.transactions = transactions + } + + // TODO: implement transaction getters + async getTransactions( + req: express.Request, + res: express.Response + ): Promise { + res.send("TODO: implement me") + } + + // TODO: implement transaction getters + async getTransaction( + req: express.Request, + res: express.Response + ): Promise { + res.send("TODO: implement me") + } + + async createTransaction( + req: express.Request, + res: express.Response + ): Promise { + const sender = req.params.address + + // TODO: validate code and arguments + const {code, arguments: args} = req.body + + try { + const transactionId = await this.transactions.createTransaction( + sender, + code, + args + ) + + const response = { + transactionId, + } + + res.json(response) + } catch (e) { + throw new ApiError( + httpStatus.INTERNAL_SERVER_ERROR, + "failed to complete transaction" + ) + } + } +} diff --git a/src/lib/flow/createAccount.ts b/src/lib/flow/createAccount.ts index cdb659fb..6999432c 100644 --- a/src/lib/flow/createAccount.ts +++ b/src/lib/flow/createAccount.ts @@ -1,5 +1,3 @@ -import * as fcl from "@onflow/fcl" -import * as t from "@onflow/types" import * as dedent from "dedent-js" import { ECDSA_P256, @@ -75,8 +73,13 @@ export async function createAccount( ) const result = await sendTransaction({ - transaction: txCreateAccount(contracts), - args: [fcl.arg(encodedPublicKey, t.String)], + code: txCreateAccount(contracts), + args: [ + { + type: "String", + value: encodedPublicKey, + }, + ], authorizations: [authorization], payer: authorization, proposer: authorization, diff --git a/src/lib/flow/sendTransaction.ts b/src/lib/flow/sendTransaction.ts index 526a2c17..be486c38 100644 --- a/src/lib/flow/sendTransaction.ts +++ b/src/lib/flow/sendTransaction.ts @@ -1,14 +1,15 @@ import * as fcl from "@onflow/fcl" +import * as types from "@onflow/types" import {AccountAuthorizer} from "./index" -export interface Argument { +export type Argument = { + type: string value: string - xform: any // eslint-disable-line } export type Transaction = { - transaction: string + code: string args: Argument[] proposer: AccountAuthorizer authorizations: AccountAuthorizer[] @@ -26,15 +27,15 @@ export type TransactionResult = { } export default async function sendTransaction({ - transaction, + code, args, proposer, authorizations, payer, }: Transaction): Promise { const response = await fcl.send([ - fcl.transaction(transaction), - fcl.args(args), + fcl.transaction(code), + fcl.args(args.map(arg => fcl.arg(arg.value, types[arg.type]))), fcl.proposer(proposer), fcl.authorizations(authorizations), fcl.payer(payer), diff --git a/src/lib/fungibleTokens/index.ts b/src/lib/fungibleTokens/index.ts index 6eefba90..3ef3623f 100644 --- a/src/lib/fungibleTokens/index.ts +++ b/src/lib/fungibleTokens/index.ts @@ -1,5 +1,4 @@ import * as fcl from "@onflow/fcl" -import * as t from "@onflow/types" import {AccountAuthorizer} from "../flow" import sendTransaction, {TransactionResult} from "../flow/sendTransaction" @@ -37,10 +36,16 @@ export async function transfer( const template = getTransferTemplate(tokenName) return await sendTransaction({ - transaction: template(contracts), + code: template(contracts), args: [ - fcl.arg(fcl.withPrefix(recipient), t.Address), - fcl.arg(amount, t.UFix64), + { + type: "Address", + value: fcl.withPrefix(recipient), + }, + { + type: "UFix64", + value: amount, + }, ], authorizations: [authorization], payer: authorization, diff --git a/src/routes/v1/accounts/index.ts b/src/routes/v1/accounts/index.ts index 42334f7b..38a09286 100644 --- a/src/routes/v1/accounts/index.ts +++ b/src/routes/v1/accounts/index.ts @@ -2,16 +2,20 @@ import * as express from "express" import AccountsController from "src/controllers/accounts" import FungibleTokensController from "src/controllers/fungibleTokens" +import TransactionsController from "src/controllers/transactions" import catchAsync from "src/errors/catchAsync" import createFungibleTokensRouter from "./fungibleTokens" +import createTransactionsRouter from "./transactions" function createRouter( accounts: AccountsController, + transactions: TransactionsController, fungibleTokens: FungibleTokensController ): express.Router { const router = express.Router() + const transactionsRouter = createTransactionsRouter(transactions) const fungibleTokensRouter = createFungibleTokensRouter(fungibleTokens) router @@ -23,6 +27,7 @@ function createRouter( .route("/:address") .get(catchAsync((req, res) => accounts.getAccount(req, res))) + router.use("/:address/transactions", transactionsRouter) router.use("/:address/fungible-tokens", fungibleTokensRouter) return router diff --git a/src/routes/v1/accounts/transactions.ts b/src/routes/v1/accounts/transactions.ts new file mode 100644 index 00000000..7101ede4 --- /dev/null +++ b/src/routes/v1/accounts/transactions.ts @@ -0,0 +1,21 @@ +import * as express from "express" + +import TransactionsController from "src/controllers/transactions" +import catchAsync from "src/errors/catchAsync" + +function createRouter(transactions: TransactionsController): express.Router { + const router = express.Router({mergeParams: true}) + + router + .route("/") + .get(catchAsync((req, res) => transactions.getTransactions(req, res))) + .post(catchAsync((req, res) => transactions.createTransaction(req, res))) + + router + .route("/:transactionId") + .get(catchAsync((req, res) => transactions.getTransaction(req, res))) + + return router +} + +export default createRouter diff --git a/src/routes/v1/index.ts b/src/routes/v1/index.ts index 7296900c..0bef210e 100644 --- a/src/routes/v1/index.ts +++ b/src/routes/v1/index.ts @@ -2,16 +2,22 @@ import * as express from "express" import AccountsController from "src/controllers/accounts" import FungibleTokensController from "src/controllers/fungibleTokens" +import TransactionsController from "src/controllers/transactions" import createAccountsRouter from "./accounts" function createRouter( accounts: AccountsController, + transactions: TransactionsController, fungibleTokens: FungibleTokensController ): express.Router { const router = express.Router() - const accountsRouter = createAccountsRouter(accounts, fungibleTokens) + const accountsRouter = createAccountsRouter( + accounts, + transactions, + fungibleTokens + ) router.use("/accounts", accountsRouter) diff --git a/src/services/transactions.ts b/src/services/transactions.ts new file mode 100644 index 00000000..8aab5024 --- /dev/null +++ b/src/services/transactions.ts @@ -0,0 +1,33 @@ +import {PrismaClient} from "@prisma/client" + +import sendTransaction, {Argument} from "src/lib/flow/sendTransaction" + +import Service from "./service" +import AccountsService from "./accounts" + +export default class TransactionsService extends Service { + private accounts: AccountsService + + constructor(prisma: PrismaClient, accounts: AccountsService) { + super(prisma) + this.accounts = accounts + } + + async createTransaction( + signer: string, + code: string, + args: Argument[] + ): Promise { + const userAuthorization = await this.accounts.getAuthorization(signer) + + const {id} = await sendTransaction({ + code, + args, + proposer: userAuthorization, + authorizations: [userAuthorization], + payer: userAuthorization, + }) + + return id + } +} From 9c856a832afdd540192c03a77d4775bce4372a18 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 26 Apr 2021 23:02:11 -0700 Subject: [PATCH 046/112] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 64003274..00e8a21f 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ implements a simple custodial wallet service for the Flow blockchain. ### 2. Transaction Execution -- [ ] Send an arbitrary transaction from the admin account -- [ ] Send an arbitrary transaction from a user account +- [x] Send an arbitrary transaction from the admin account +- [x] Send an arbitrary transaction from a user account ### 3. Fungible Tokens From 6295b5cd8d4ee6166e76c04f80a9f641a71897e9 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 26 Apr 2021 23:03:03 -0700 Subject: [PATCH 047/112] Delete generate.ts --- src/lib/crypto/generate.ts | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/lib/crypto/generate.ts diff --git a/src/lib/crypto/generate.ts b/src/lib/crypto/generate.ts deleted file mode 100644 index dfd97e28..00000000 --- a/src/lib/crypto/generate.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface KeyGenerator { - generatePrivateKey() -} From 35c5a218388105dd0189935c413e600e6ea15e96 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 26 Apr 2021 23:06:45 -0700 Subject: [PATCH 048/112] Update KeyManager interface --- src/lib/keys/inMemory.ts | 7 ++++++- src/lib/keys/index.ts | 3 +-- src/services/accounts.ts | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/lib/keys/inMemory.ts b/src/lib/keys/inMemory.ts index 3fd1a47d..41f38b83 100644 --- a/src/lib/keys/inMemory.ts +++ b/src/lib/keys/inMemory.ts @@ -34,7 +34,8 @@ class InMemoryKey implements Key { } export default class InMemoryKeyManager implements KeyManager { - keyType = KeyType.InMemory + static keyType = KeyType.InMemory + sigAlgo: crypto.SignatureAlgorithm hashAlgo: crypto.HashAlgorithm encryptionKey: Buffer @@ -49,6 +50,10 @@ export default class InMemoryKeyManager implements KeyManager { this.encryptionKey = encryptionKey } + getKeyType(): KeyType { + return InMemoryKeyManager.keyType + } + generate(): InMemoryKey { const privateKey = crypto.InMemoryPrivateKey.generate(this.sigAlgo) return new InMemoryKey(privateKey, this.hashAlgo) diff --git a/src/lib/keys/index.ts b/src/lib/keys/index.ts index d95bf9ac..b9a400f2 100644 --- a/src/lib/keys/index.ts +++ b/src/lib/keys/index.ts @@ -13,8 +13,7 @@ export interface Key { } export interface KeyManager { - keyType: KeyType - + getKeyType(): KeyType generate(): T save(key: T): string load(value: string): T diff --git a/src/services/accounts.ts b/src/services/accounts.ts index f6b8466a..d7fa1e82 100644 --- a/src/services/accounts.ts +++ b/src/services/accounts.ts @@ -48,7 +48,7 @@ export default class AccountsService extends Service { config.contracts ) - const userKeyType = this.userKeyManager.keyType + const userKeyType = this.userKeyManager.getKeyType() const userKeyValue = this.userKeyManager.save(userKey) return await insertAccount(this.prisma, address, userKeyType, userKeyValue) From 814d2421cb4b7810499441caa7c1982b0af3b7aa Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 3 May 2021 00:05:47 -0700 Subject: [PATCH 049/112] Add OpenAPI 3 spec --- openapi.yml | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 openapi.yml diff --git a/openapi.yml b/openapi.yml new file mode 100644 index 00000000..6a8aff36 --- /dev/null +++ b/openapi.yml @@ -0,0 +1,255 @@ +openapi: 3.0.3 +info: + title: Flow Custodial Wallet API + version: 0.0.1 +servers: + - url: /v1 +components: + schemas: + account: + type: object + properties: + address: + type: string + example: "0xf8d6e0586b0a20c7" + createdAt: + type: string + example: "2021-04-27T05:49:53.211Z" + updatedAt: + type: string + example: "2021-04-27T05:49:53.211Z" + transaction: + type: object + properties: + transactionId: + type: string + example: "9613c9689a50a5ed9198dc43839cd90ef39203dfdd7ab54f0fc5ca12f256eef0" + createdAt: + type: string + example: "2021-04-27T05:49:53.211Z" + fungibleToken: + type: object + properties: + name: + type: string + example: FLOW + enum: [FLOW, FUSD] + balance: + type: string + example: "42.0" + fungibleTokenWithdrawal: + type: object + properties: + transactionId: + type: string + example: "9613c9689a50a5ed9198dc43839cd90ef39203dfdd7ab54f0fc5ca12f256eef0" + recipient: + type: string + example: "0xf8d6e0586b0a20c7" + amount: + type: string + example: "42.0" + createdAt: + type: string + example: "2021-04-27T05:49:53.211Z" + parameters: + address: + name: address + in: path + required: true + schema: + type: string + example: "0xf8d6e0586b0a20c7" + tokenName: + name: tokenName + in: path + required: true + schema: + type: string + enum: [FLOW, FUSD] + example: FLOW + transactionId: + name: transactionId + in: path + required: true + schema: + type: string + example: "9613c9689a50a5ed9198dc43839cd90ef39203dfdd7ab54f0fc5ca12f256eef0" +paths: + /accounts: + get: + summary: List all accounts + description: Get a list of all accounts manged by the wallet service. + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/account" + post: + summary: Create an account + description: Create a new account that will be managed by the wallet service. + responses: + "201": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/account" + /accounts/{address}: + get: + summary: Get an account + description: Get the details of a specific account. + parameters: + - $ref: "#/components/parameters/address" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/account" + /accounts/{address}/transactions: + get: + summary: List all account transactions + description: Get a list of all transactions sent by an account. + parameters: + - $ref: "#/components/parameters/address" + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/transaction" + post: + summary: Create a transaction + description: Send a transaction from an account. + parameters: + - $ref: "#/components/parameters/address" + requestBody: + content: + application/json: + schema: + type: object + properties: + code: + type: string + arguments: + type: array + items: + type: object + properties: + type: + type: string + value: + type: string + responses: + "201": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/transaction" + /accounts/{address}/transactions/{transactionId}: + get: + summary: Get a transaction + description: Get the details of a transaction sent by an account. + parameters: + - $ref: "#/components/parameters/address" + - $ref: "#/components/parameters/transactionId" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/transaction" + /accounts/{address}/fungible-tokens: + get: + summary: List account fungible tokens + description: Get a list of all fungible tokens held by an account. + parameters: + - $ref: "#/components/parameters/address" + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/fungibleToken" + /accounts/{address}/fungible-tokens/{tokenName}: + get: + summary: Get an account fungible token + description: Get the details of a fungible token held by an account. + parameters: + - $ref: "#/components/parameters/address" + - $ref: "#/components/parameters/tokenName" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/fungibleToken" + /accounts/{address}/fungible-tokens/{tokenName}/withdrawals: + get: + summary: List account fungible token withdrawals + description: Get a list of all fungible token withdrawals sent by an account. + parameters: + - $ref: "#/components/parameters/address" + - $ref: "#/components/parameters/tokenName" + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/fungibleTokenWithdrawal" + post: + summary: Create a fungible token withdrawal + description: Send a fungible token withdrawal transaction from an account. + parameters: + - $ref: "#/components/parameters/address" + - $ref: "#/components/parameters/tokenName" + requestBody: + content: + application/json: + schema: + type: object + properties: + recipient: + type: string + amount: + type: string + responses: + "201": + description: Created + content: + application/json: + schema: + $ref: "#/components/schemas/fungibleTokenWithdrawal" + /accounts/{address}/fungible-tokens/{tokenName}/withdrawals/{transactionId}: + get: + summary: Get an account fungible token withdrawal + description: Get the details of a fungible token withdrawal sent by an account. + parameters: + - $ref: "#/components/parameters/address" + - $ref: "#/components/parameters/tokenName" + - $ref: "#/components/parameters/transactionId" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/fungibleTokenWithdrawal" From 7f8bf6690b0e3a3c469ee24fe4e27cb9946ca778 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 4 May 2021 20:08:45 -0700 Subject: [PATCH 050/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 00e8a21f..6bcc67d0 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,7 @@ curl --request GET \ > :warning: Not yet implemented -`GET /v1/accounts/{v1/accounts/{address}/fungible-tokens}/fungible-tokens/{tokenName}/withdrawals` +`GET /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals` Parameters From 6607ad905e840c4ba2ca7e4e5fd0db5b13fb012e Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 13 May 2021 22:57:26 -0700 Subject: [PATCH 051/112] Dockerize API --- .dockerignore | 4 + .env.emulator.example | 6 ++ env.example => .env.example | 3 +- .env.testnet.example | 6 ++ Dockerfile | 16 ++++ README.md | 80 +++++++++++++++++++- docker-compose.yml => docker-compose.dev.yml | 1 + docker-compose.emulator.yml | 36 +++++++++ docker-compose.testnet.yml | 36 +++++++++ flow.json | 7 ++ package-lock.json | 5 ++ package.json | 15 +++- src/app.ts | 3 + src/index.ts | 2 + tsconfig.json | 9 ++- 15 files changed, 217 insertions(+), 12 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.emulator.example rename env.example => .env.example (99%) create mode 100644 .env.testnet.example create mode 100644 Dockerfile rename docker-compose.yml => docker-compose.dev.yml (99%) create mode 100644 docker-compose.emulator.yml create mode 100644 docker-compose.testnet.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..25ab276c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +node_modules +npm-debug.log +dist +.env diff --git a/.env.emulator.example b/.env.emulator.example new file mode 100644 index 00000000..748d0cef --- /dev/null +++ b/.env.emulator.example @@ -0,0 +1,6 @@ +ADMIN_ADDRESS=0xf8d6e0586b0a20c7 +ADMIN_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068 + +ADMIN_KEY_TYPE="in-memory" + +USER_ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8eaf6ec53b0143d32a02d84c54aa128bf2 diff --git a/env.example b/.env.example similarity index 99% rename from env.example rename to .env.example index 613450ae..137aa80d 100644 --- a/env.example +++ b/.env.example @@ -3,7 +3,8 @@ ACCESS_API_HOST=http://localhost:8080 DATABASE_URL="postgresql://wallet:wallet@localhost:5432/wallet?schema=public" ADMIN_ADDRESS=0xf8d6e0586b0a20c7 -ADMIN_KEY_TYPE="in-memory" ADMIN_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068 +ADMIN_KEY_TYPE="in-memory" + USER_ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8eaf6ec53b0143d32a02d84c54aa128bf2 diff --git a/.env.testnet.example b/.env.testnet.example new file mode 100644 index 00000000..1df338af --- /dev/null +++ b/.env.testnet.example @@ -0,0 +1,6 @@ +ADMIN_ADDRESS= +ADMIN_PRIVATE_KEY= + +ADMIN_KEY_TYPE="in-memory" + +USER_ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8eaf6ec53b0143d32a02d84c54aa128bf2 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..966c835e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM node:14-alpine3.13 + +WORKDIR /app + +COPY package.json . +COPY src/database/schema.prisma src/database/schema.prisma + +RUN npm install + +ADD . /app + +RUN npm run build + +CMD [ "npm", "start" ] + +EXPOSE 3000 diff --git a/README.md b/README.md index 00e8a21f..47904cf6 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,11 @@ implements a simple custodial wallet service for the Flow blockchain. - [ ] View the non-fungible tokens owned by the admin account - [ ] View the non-fungible tokens owned by a user account -## Getting Started +## Local Development + +> This local development environment uses the +> [Flow Emulator](https://docs.onflow.org/emulator) to +> simulate the real Flow network. ### Install the Flow CLI @@ -49,7 +53,7 @@ First, install the [Flow CLI](https://docs.onflow.org/flow-cli/install/). ```sh npm install -cp env.example .env +cp .env.example .env ``` ### Start the database and emulator @@ -57,13 +61,81 @@ cp env.example .env Use Docker Compose to launch Postgres and the [Flow Emulator](https://docs.onflow.org/emulator): ```sh -docker-compose up -d +npm run docker-local-network ``` ### Start the server ```sh -npm run start +npm run dev +``` + +## Deploy with Docker + +To deploy this API as a Docker container in your infrastructure, +either build from source or use the pre-built image: TODO + +The Docker Compose sample configurations +in this repository show how to configure this application when +running as a Docker container. + +### Emulator + +> This example shows how to connect the Docker container +> to an instance of the [Flow Emulator](https://docs.onflow.org/emulator). + +```sh +cp .env.emulator.example .env + +docker-compose -f docker-compose.emulator.yml up +``` + +Once the emulator is running, +you will need to deploy the FUSD contract: + +```sh +npm run dev-deploy-contracts +``` + +### Testnet + +> This example shows how to connect the Docker container +> to Flow Testnet. + +First you'll need a Testnet account. Here's how to make one: + +#### Generate a key pair + +Generate a new key pair with the Flow CLI: + +```sh +flow keys generate +``` + +_⚠️ Make sure to save these keys in a safe place, you'll need them later._ + +#### Create your account + +Go to the [Flow Testnet Faucet](https://testnet-faucet.onflow.org/) to create a new account. Use the **public key** from the previous step. + +#### Save your keys + +After your account has been created, save the address and private key in the `.env` file: + +```sh +cp .env.testnet.example .env +``` + +```sh +# Replace these values with your own! +FLOW_ADDRESS=0xabcdef12345689 +FLOW_PRIVATE_KEY=aaaaaa...aaaaaa +``` + +### Start the Docker containers + +```sh +docker-compose -f docker-compose.testnet.yml up ``` ## API Routes diff --git a/docker-compose.yml b/docker-compose.dev.yml similarity index 99% rename from docker-compose.yml rename to docker-compose.dev.yml index fab451e7..d135a18f 100644 --- a/docker-compose.yml +++ b/docker-compose.dev.yml @@ -8,6 +8,7 @@ services: - POSTGRES_DB=wallet - POSTGRES_USER=wallet - POSTGRES_PASSWORD=wallet + emulator: image: gcr.io/flow-container-registry/emulator:v0.16.1 ports: diff --git a/docker-compose.emulator.yml b/docker-compose.emulator.yml new file mode 100644 index 00000000..3c85daca --- /dev/null +++ b/docker-compose.emulator.yml @@ -0,0 +1,36 @@ +version: "3" +services: + web: + image: gcr.io/flow-container-registry/flow-wallet-api-demo:latest + ports: + - "3000:3000" + depends_on: + - db + - emulator + environment: + - ACCESS_API_HOST=http://emulator:8080 + - DATABASE_URL=postgresql://wallet:wallet@db:5432/wallet?schema=public + - ADMIN_ADDRESS=${ADMIN_ADDRESS} + - ADMIN_KEY_TYPE=${ADMIN_KEY_TYPE} + - ADMIN_PRIVATE_KEY=${ADMIN_PRIVATE_KEY} + - USER_ENCRYPTION_KEY=${USER_ENCRYPTION_KEY} + + db: + image: postgres + ports: + - "5432:5432" + environment: + - POSTGRES_DB=wallet + - POSTGRES_USER=wallet + - POSTGRES_PASSWORD=wallet + + emulator: + image: gcr.io/flow-container-registry/emulator:v0.16.1 + ports: + - "8080:8080" + - "3569:3569" + environment: + - FLOW_SERVICEPRIVATEKEY=${ADMIN_PRIVATE_KEY} + - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 + - FLOW_SERVICEKEYHASHALGO=SHA3_256 + - FLOW_VERBOSE=true diff --git a/docker-compose.testnet.yml b/docker-compose.testnet.yml new file mode 100644 index 00000000..618e314b --- /dev/null +++ b/docker-compose.testnet.yml @@ -0,0 +1,36 @@ +version: "3" +services: + web: + image: gcr.io/flow-container-registry/flow-wallet-api-demo:latest + ports: + - "3000:3000" + depends_on: + - db + environment: + - ACCESS_API_HOST=https://access-testnet.onflow.org + - CHAIN=testnet + - DATABASE_URL=postgresql://wallet:wallet@db:5432/wallet?schema=public + - ADMIN_ADDRESS=${ADMIN_ADDRESS} + - ADMIN_KEY_TYPE=${ADMIN_KEY_TYPE} + - ADMIN_PRIVATE_KEY=${ADMIN_PRIVATE_KEY} + - USER_ENCRYPTION_KEY=${USER_ENCRYPTION_KEY} + + db: + image: postgres + ports: + - "5432:5432" + environment: + - POSTGRES_DB=wallet + - POSTGRES_USER=wallet + - POSTGRES_PASSWORD=wallet + + emulator: + image: gcr.io/flow-container-registry/emulator:v0.16.1 + ports: + - "8080:8080" + - "3569:3569" + environment: + - FLOW_SERVICEPRIVATEKEY=${ADMIN_PRIVATE_KEY} + - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 + - FLOW_SERVICEKEYHASHALGO=SHA3_256 + - FLOW_VERBOSE=true diff --git a/flow.json b/flow.json index d427dfb6..72a8cb0e 100644 --- a/flow.json +++ b/flow.json @@ -30,6 +30,9 @@ "deployments": { "emulator": { "emulator-account": [ "FUSD" ] + }, + "emulator-docker": { + "emulator-account": [ "FUSD" ] } }, "networks": { @@ -37,6 +40,10 @@ "host": "127.0.0.1:3569", "chain": "flow-emulator" }, + "emulator-docker": { + "host": "emulator:3569", + "chain": "flow-emulator" + }, "mainnet": { "host": "access.mainnet.nodes.onflow.org:9000", "chain": "flow-mainnet" diff --git a/package-lock.json b/package-lock.json index 576335bb..4a439aa3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2697,6 +2697,11 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, + "module-alias": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", + "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" + }, "morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", diff --git a/package.json b/package.json index 07e517a0..1eff9e23 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,14 @@ "version": "0.0.0", "private": true, "scripts": { - "start": "npm run deploy-contracts && npm run db-migrate-dev && npm run db-sync-admin-keys && npm run start-api", - "start-api": "NODE_ENV=development ts-node-dev -r tsconfig-paths/register --respawn ./src/index.ts", - "deploy-contracts": "flow project deploy --network=emulator --update", + "dev": "npm run dev-prepare && npm run dev-api", + "dev-api": "NODE_ENV=development ts-node-dev -r tsconfig-paths/register --respawn ./src/index.ts", + "dev-prepare": "npm run dev-deploy-contracts && npm run db-migrate-dev && npm run db-sync-admin-keys", + "dev-deploy-contracts": "flow project deploy --network=emulator --update", + "build": "tsc -p tsconfig.json", + "start": "npm run start-prepare && npm run start-api", + "start-api": "node -r tsconfig-paths/register dist/index.js", + "start-prepare": "npm run db-migrate-deploy && npm run db-sync-admin-keys", "db-migrate-dev": "prisma migrate dev --preview-feature", "db-migrate-deploy": "prisma migrate deploy --preview-feature", "db-generate-client": "prisma generate", @@ -26,6 +31,7 @@ "elliptic": "^6.5.4", "express": "~4.16.1", "http-status": "^1.5.0", + "module-alias": "^2.2.2", "morgan": "~1.9.1", "sha3": "^2.1.4", "winston": "^3.3.3" @@ -48,5 +54,8 @@ "ts-node-dev": "^1.1.6", "tsconfig-paths": "^3.9.0", "typescript": "^4.2.4" + }, + "_moduleAliases": { + "src": "dist" } } diff --git a/src/app.ts b/src/app.ts index ba0ad773..4930ab6b 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,4 +1,5 @@ import * as express from "express" +import * as fcl from "@onflow/fcl" import {PrismaClient} from "@prisma/client" import morganMiddleware from "src/middleware/morgan" @@ -21,6 +22,8 @@ app.use(morganMiddleware) app.use(express.json()) app.use(express.urlencoded({extended: false})) +fcl.config().put("accessNode.api", config.accessApiHost) + const prisma = new PrismaClient() const userKeyManager = new InMemoryKeyManager( diff --git a/src/index.ts b/src/index.ts index 36b6c798..c7ac40c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,5 @@ +require("module-alias/register") + import config from "src/config" import logger from "src/logger" import app from "src/app" diff --git a/tsconfig.json b/tsconfig.json index 0ae5bc31..f5fb6e14 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,12 @@ { "compilerOptions": { - "outDir": "./built", - "target": "es2020", + "module": "commonjs", "moduleResolution": "node", - "baseUrl": ".", + "outDir": "./dist", + "target": "es6", + "baseUrl": "src", "paths": { - "src": ["./src"] + "src/*": ["*"] } }, "include": ["./src/**/*"] From 94894fe97d62ad17ba2e35793372c191b72bd032 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 13 May 2021 22:58:33 -0700 Subject: [PATCH 052/112] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d8778a6..3c90d1fc 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,11 @@ npm run dev ## Deploy with Docker To deploy this API as a Docker container in your infrastructure, -either build from source or use the pre-built image: TODO +either build from source or use the pre-built image: + +```sh +docker pull gcr.io/flow-container-registry/flow-wallet-api-demo:latest +``` The Docker Compose sample configurations in this repository show how to configure this application when From 101e03fd0b256e83b844f5d64262aa8c57b47473 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 13 May 2021 23:00:39 -0700 Subject: [PATCH 053/112] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3c90d1fc..404038ab 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,8 @@ running as a Docker container. > This example shows how to connect the Docker container > to an instance of the [Flow Emulator](https://docs.onflow.org/emulator). +[docker-compose.emulator.yml](docker-compose.emulator.yml) + ```sh cp .env.emulator.example .env @@ -138,6 +140,8 @@ FLOW_PRIVATE_KEY=aaaaaa...aaaaaa ### Start the Docker containers +[docker-compose.testnet.yml](docker-compose.testnet.yml) + ```sh docker-compose -f docker-compose.testnet.yml up ``` From 393d785ff32cb71efcf4d27cb929cf98bf732fdb Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 13 May 2021 23:01:20 -0700 Subject: [PATCH 054/112] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 404038ab..e1f1dfb6 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ running as a Docker container. > This example shows how to connect the Docker container > to an instance of the [Flow Emulator](https://docs.onflow.org/emulator). -[docker-compose.emulator.yml](docker-compose.emulator.yml) +Configuration: s[docker-compose.emulator.yml](docker-compose.emulator.yml) ```sh cp .env.emulator.example .env @@ -140,7 +140,7 @@ FLOW_PRIVATE_KEY=aaaaaa...aaaaaa ### Start the Docker containers -[docker-compose.testnet.yml](docker-compose.testnet.yml) +Configuration: [docker-compose.testnet.yml](docker-compose.testnet.yml) ```sh docker-compose -f docker-compose.testnet.yml up From f2d8fd381169c3f830db05e2b0503314f753d63c Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 13 May 2021 23:01:40 -0700 Subject: [PATCH 055/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e1f1dfb6..a1514000 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ running as a Docker container. > This example shows how to connect the Docker container > to an instance of the [Flow Emulator](https://docs.onflow.org/emulator). -Configuration: s[docker-compose.emulator.yml](docker-compose.emulator.yml) +Configuration: [docker-compose.emulator.yml](docker-compose.emulator.yml) ```sh cp .env.emulator.example .env From 7ab1e6c3f65ba11e3b8396f18753b191cd77588f Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Wed, 9 Jun 2021 23:43:11 -0700 Subject: [PATCH 056/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a1514000..88703e63 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ The Docker Compose sample configurations in this repository show how to configure this application when running as a Docker container. -### Emulator +### Emulator (Local Development) > This example shows how to connect the Docker container > to an instance of the [Flow Emulator](https://docs.onflow.org/emulator). From 9ec1a45751b27203e479022246400903ca80fd7a Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Wed, 9 Jun 2021 23:46:24 -0700 Subject: [PATCH 057/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88703e63..a1514000 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ The Docker Compose sample configurations in this repository show how to configure this application when running as a Docker container. -### Emulator (Local Development) +### Emulator > This example shows how to connect the Docker container > to an instance of the [Flow Emulator](https://docs.onflow.org/emulator). From f7b7850a04888a7500f2c2510094fa0dbd4ffa7a Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 18 Jun 2021 12:06:53 -0700 Subject: [PATCH 058/112] Update README.md --- README.md | 53 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a1514000..485635f9 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,46 @@ -# Flow Wallet API Demo (Node.js + express) +# Flow Wallet API -> :warning: This demo is a work in progress. +> :warning: This software is a work in progress and is not yet intended for production use. +The Flow Wallet API is an HTTP service that allows a developer to integrate wallet functionality into a larger Flow application infrastructure. +This service can be used by an application that needs to manage Flow user accounts and the assets inside them. -This is a demonstration of a RESTful API that -implements a simple custodial wallet service for the Flow blockchain. +## Features + +- Create new Flow accounts +- Securely store account private keys +- Send a transaction from an account +- Transfer fungible tokens (e.g. FLOW, FUSD) +- Detect fungible token deposits +- Transfer NFTs (e.g. FLOW, FUSD) +- Detect NFT deposits + +[View full list of supported functionality](#functionality). + +## Background + +Some application developers may wish to manage Flow accounts in a fully-custodial fashion, +but without taking on the complexity of building an account management system. + +An application may need to support custody of fungible tokens (FLOW, FUSD), non-fungible tokens, or both. + +For security and/or legal reasons, +some developers need to use a custody service running on-premises as part of their existing infrastructure, +rather than a hosted 3rd-party solution. + +### Example use cases + +- **Custodial NFT Dapp** — an NFT dapp where each user receives a Flow account that is fully managed by the dapp admin. This application requires that each user account can store and transfer NFTs, but does not need to support fungible token custody. +- **FLOW/FUSD Hot Wallet** — an application that allows users to convert fiat currency to FLOW or FUSD. A single admin account would be used as a hot wallet for outgoing payments, and additional deposit accounts would be created to accept incoming payments. +- **Exchange** — a cryptocurrency exchange that is listing FLOW and/or FUSD. Similar to the case above, one or more admin accounts may be used as a hot wallet for outgoing payments, and additional deposit accounts would be created to accept incoming payments. +- **Web Wallet** — a user-facing wallet application that is compatible with Flow dapps. Each user account would be created and managed by the wallet service. ## Functionality ### 1. Admin - [x] Single admin account (hot wallet) -- [x] [Create user accounts (using admin account)](https://github.com/onflow/flow-wallet-api-node-demo/issues/1) +- [x] Create user accounts (using admin account) ### 2. Transaction Execution @@ -21,9 +50,9 @@ implements a simple custodial wallet service for the Flow blockchain. ### 3. Fungible Tokens - [x] Send fungible token withdrawals from admin account (FLOW, FUSD) -- [ ] [Detect fungible token deposits to admin account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/2) -- [x] [Send fungible token withdrawals from a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/3) -- [ ] [Detect fungible token deposits to a user account (FLOW, FUSD)](https://github.com/onflow/flow-wallet-api-node-demo/issues/4) +- [ ] Detect fungible token deposits to admin account (FLOW, FUSD) +- [x] Send fungible token withdrawals from a user account (FLOW, FUSD) +- [ ] Detect fungible token deposits to a user account (FLOW, FUSD) - [ ] View the fungible token balance of the admin account - [ ] View the fungible token balance of a user account @@ -38,6 +67,14 @@ implements a simple custodial wallet service for the Flow blockchain. - [ ] View the non-fungible tokens owned by the admin account - [ ] View the non-fungible tokens owned by a user account +## Installation + +## API Specification + +[View the full Wallet API specification](API.md). + +--- + ## Local Development > This local development environment uses the From 3d2b360efcc29e729692b0b410615f5beb9ae75f Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 18 Jun 2021 12:08:59 -0700 Subject: [PATCH 059/112] Update README.md --- README.md | 305 +----------------------------------------------------- 1 file changed, 1 insertion(+), 304 deletions(-) diff --git a/README.md b/README.md index 485635f9..be22042f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > :warning: This software is a work in progress and is not yet intended for production use. -The Flow Wallet API is an HTTP service that allows a developer to integrate wallet functionality into a larger Flow application infrastructure. +The Flow Wallet API is a REST HTTP service that allows a developer to integrate wallet functionality into a larger Flow application infrastructure. This service can be used by an application that needs to manage Flow user accounts and the assets inside them. ## Features @@ -182,306 +182,3 @@ Configuration: [docker-compose.testnet.yml](docker-compose.testnet.yml) ```sh docker-compose -f docker-compose.testnet.yml up ``` - -## API Routes - -### Accounts - -#### List all accounts - -`GET /v1/accounts` - -Example - -```sh -curl --request GET \ - --url http://localhost:3000/v1/accounts -``` - -```json -[ - { - "address": "0xf8d6e0586b0a20c7" - }, - { - "address": "0xe467b9dd11fa00df" - } -] -``` - ---- - -#### Get an account - -`GET /v1/accounts/{address}` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - -Example - -```sh -curl --request GET \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7 -``` - -```json -{ - "address": "0xf8d6e0586b0a20c7" -} -``` - ---- - -#### Create an account - -`POST /v1/accounts` - -Example - -```sh -curl --request POST \ - --url http://localhost:3000/v1/accounts -``` - -```json -{ - "address": "0xe467b9dd11fa00df" -} -``` - ---- - -### Transaction Execution - -#### Execute a transaction - -> :warning: Not yet implemented - -`POST /v1/accounts/{address}/transactions` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - -Body (JSON) - -- `code`: The Cadence code to execute in the transaction - - The code must always specify exactly one authorizer (i.e. `prepare(auth: AuthAccount)`) - -Example - -```sh -curl --request POST \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/transactions \ - --header 'Content-Type: application/json' \ - --data '{ "code": "transaction { prepare(auth: AuthAccount) { log(\"Hello, World!\") } }" }' -``` - -```json -{ - "transactionId": "18647b584a03345f3b2d2c4d9ab2c4179ae1b124a7f62ef9f33910e5ca8b353c", - "error": null, -} -``` - ---- - -### Fungible Tokens - -Supported tokens: -- `FLOW` -- `FUSD` - -#### List all tokens - -`GET /v1/accounts/{address}/fungible-tokens` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - -Example - -```sh -curl --request GET \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens -``` - -```json -[ - { - "name": "flow" - }, - { - "name": "fusd" - } -] -``` - ---- - -#### Get details of a token type - -`GET /v1/accounts/{address}/fungible-tokens/{tokenName}` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the fungible token (e.g. "flow") - -Example - -```sh -curl --request GET \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens/flow -``` - -```json -{ - "name": "flow", - "balance": "42.0" -} -``` - ---- - -#### List all withdrawals of a token type - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the fungible token (e.g. "flow") - ---- - -#### Get details of a token withdrawal - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals/{transactionId}` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the fungible token (e.g. "flow") -- `transactionId`: The Flow transaction ID for the withdrawal - ---- - -#### Create a token withdrawal - -`POST /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the fungible token (e.g. "flow") - -Body (JSON) - -- `amount`: The number of tokens to transfer (e.g. "123.456") - - Must be a fixed-point number with a maximum of 8 decimal places -- `recipient`: The Flow address of the recipient (e.g. "0xf8d6e0586b0a20c7") - -Example - -```sh -curl --request POST \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens/fusd/withdrawls \ - --header 'Content-Type: application/json' \ - --data '{ "recipient": "0xe467b9dd11fa00df", "amount": "123.456" }' -``` - -```json -{ - "transactionId": "18647b584a03345f3b2d2c4d9ab2c4179ae1b124a7f62ef9f33910e5ca8b353c", - "recipient": "0xe467b9dd11fa00df", - "amount": "123.456" -} -``` - ---- - -### Non-Fungible Tokens - -> :warning: Not yet implemented - -#### List all tokens - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/non-fungible-tokens` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - -Example - -```sh -curl --request GET \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/non-fungible-tokens -``` - ---- - -#### Get details of a token - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") - ---- - -#### List all withdrawals of a token type - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") - ---- - -#### Get details of a token withdrawal - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals/{transactionId}` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") -- `transactionId`: The Flow transaction ID for the withdrawal - ---- - -#### Create a token withdrawal - -> :warning: Not yet implemented - -`POST /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") - -Body (JSON) - -- `recipient`: The Flow address of the recipient (e.g. "0xf8d6e0586b0a20c7") From fea37184136e0d3f42be25e043f05519039774f0 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 18 Jun 2021 12:12:24 -0700 Subject: [PATCH 060/112] Create API.md --- API.md | 306 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 API.md diff --git a/API.md b/API.md new file mode 100644 index 00000000..9c0fde86 --- /dev/null +++ b/API.md @@ -0,0 +1,306 @@ +# Wallet API REST HTTP Routes + +The documents contains the REST routes provided by the Flow Wallet API. + +These routes are also defined in the [OpenAPI specification for this service](openapi.yml). + +## Accounts + +### List all accounts + +`GET /v1/accounts` + +Example + +```sh +curl --request GET \ + --url http://localhost:3000/v1/accounts +``` + +```json +[ + { + "address": "0xf8d6e0586b0a20c7" + }, + { + "address": "0xe467b9dd11fa00df" + } +] +``` + +--- + +### Get an account + +`GET /v1/accounts/{address}` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") + +Example + +```sh +curl --request GET \ + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7 +``` + +```json +{ + "address": "0xf8d6e0586b0a20c7" +} +``` + +--- + +### Create an account + +`POST /v1/accounts` + +Example + +```sh +curl --request POST \ + --url http://localhost:3000/v1/accounts +``` + +```json +{ + "address": "0xe467b9dd11fa00df" +} +``` + +--- + +## Transaction Execution + +### Execute a transaction + +> :warning: Not yet implemented + +`POST /v1/accounts/{address}/transactions` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") + +Body (JSON) + +- `code`: The Cadence code to execute in the transaction + - The code must always specify exactly one authorizer (i.e. `prepare(auth: AuthAccount)`) + +Example + +```sh +curl --request POST \ + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/transactions \ + --header 'Content-Type: application/json' \ + --data '{ "code": "transaction { prepare(auth: AuthAccount) { log(\"Hello, World!\") } }" }' +``` + +```json +{ + "transactionId": "18647b584a03345f3b2d2c4d9ab2c4179ae1b124a7f62ef9f33910e5ca8b353c", + "error": null, +} +``` + +--- + +## Fungible Tokens + +Supported tokens: +- `FLOW` +- `FUSD` + +### List all tokens + +`GET /v1/accounts/{address}/fungible-tokens` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") + +Example + +```sh +curl --request GET \ + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens +``` + +```json +[ + { + "name": "flow" + }, + { + "name": "fusd" + } +] +``` + +--- + +### Get details of a token type + +`GET /v1/accounts/{address}/fungible-tokens/{tokenName}` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") +- `tokenName`: The name of the fungible token (e.g. "flow") + +Example + +```sh +curl --request GET \ + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens/flow +``` + +```json +{ + "name": "flow", + "balance": "42.0" +} +``` + +--- + +### List all withdrawals of a token type + +> :warning: Not yet implemented + +`GET /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") +- `tokenName`: The name of the fungible token (e.g. "flow") + +--- + +### Get details of a token withdrawal + +> :warning: Not yet implemented + +`GET /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals/{transactionId}` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") +- `tokenName`: The name of the fungible token (e.g. "flow") +- `transactionId`: The Flow transaction ID for the withdrawal + +--- + +### Create a token withdrawal + +`POST /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") +- `tokenName`: The name of the fungible token (e.g. "flow") + +Body (JSON) + +- `amount`: The number of tokens to transfer (e.g. "123.456") + - Must be a fixed-point number with a maximum of 8 decimal places +- `recipient`: The Flow address of the recipient (e.g. "0xf8d6e0586b0a20c7") + +Example + +```sh +curl --request POST \ + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens/fusd/withdrawls \ + --header 'Content-Type: application/json' \ + --data '{ "recipient": "0xe467b9dd11fa00df", "amount": "123.456" }' +``` + +```json +{ + "transactionId": "18647b584a03345f3b2d2c4d9ab2c4179ae1b124a7f62ef9f33910e5ca8b353c", + "recipient": "0xe467b9dd11fa00df", + "amount": "123.456" +} +``` + +--- + +## Non-Fungible Tokens + +> :warning: Not yet implemented + +### List all tokens + +> :warning: Not yet implemented + +`GET /v1/accounts/{address}/non-fungible-tokens` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") + +Example + +```sh +curl --request GET \ + --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/non-fungible-tokens +``` + +--- + +### Get details of a token + +> :warning: Not yet implemented + +`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") +- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") + +--- + +### List all withdrawals of a token type + +> :warning: Not yet implemented + +`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") +- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") + +--- + +### Get details of a token withdrawal + +> :warning: Not yet implemented + +`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals/{transactionId}` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") +- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") +- `transactionId`: The Flow transaction ID for the withdrawal + +--- + +#### Create a token withdrawal + +> :warning: Not yet implemented + +`POST /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals` + +Parameters + +- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") +- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") + +Body (JSON) + +- `recipient`: The Flow address of the recipient (e.g. "0xf8d6e0586b0a20c7") From 5abf440705a5e8a61b34fb7b8ca350bd2d784db3 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 18 Jun 2021 12:14:52 -0700 Subject: [PATCH 061/112] Update README.md --- README.md | 112 +----------------------------------------------------- 1 file changed, 2 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index be22042f..d564d6a8 100644 --- a/README.md +++ b/README.md @@ -69,116 +69,8 @@ rather than a hosted 3rd-party solution. ## Installation +TODO + ## API Specification [View the full Wallet API specification](API.md). - ---- - -## Local Development - -> This local development environment uses the -> [Flow Emulator](https://docs.onflow.org/emulator) to -> simulate the real Flow network. - -### Install the Flow CLI - -First, install the [Flow CLI](https://docs.onflow.org/flow-cli/install/). - -### Install dependencies and configure environment - -```sh -npm install - -cp .env.example .env -``` - -### Start the database and emulator - -Use Docker Compose to launch Postgres and the [Flow Emulator](https://docs.onflow.org/emulator): - -```sh -npm run docker-local-network -``` - -### Start the server - -```sh -npm run dev -``` - -## Deploy with Docker - -To deploy this API as a Docker container in your infrastructure, -either build from source or use the pre-built image: - -```sh -docker pull gcr.io/flow-container-registry/flow-wallet-api-demo:latest -``` - -The Docker Compose sample configurations -in this repository show how to configure this application when -running as a Docker container. - -### Emulator - -> This example shows how to connect the Docker container -> to an instance of the [Flow Emulator](https://docs.onflow.org/emulator). - -Configuration: [docker-compose.emulator.yml](docker-compose.emulator.yml) - -```sh -cp .env.emulator.example .env - -docker-compose -f docker-compose.emulator.yml up -``` - -Once the emulator is running, -you will need to deploy the FUSD contract: - -```sh -npm run dev-deploy-contracts -``` - -### Testnet - -> This example shows how to connect the Docker container -> to Flow Testnet. - -First you'll need a Testnet account. Here's how to make one: - -#### Generate a key pair - -Generate a new key pair with the Flow CLI: - -```sh -flow keys generate -``` - -_⚠️ Make sure to save these keys in a safe place, you'll need them later._ - -#### Create your account - -Go to the [Flow Testnet Faucet](https://testnet-faucet.onflow.org/) to create a new account. Use the **public key** from the previous step. - -#### Save your keys - -After your account has been created, save the address and private key in the `.env` file: - -```sh -cp .env.testnet.example .env -``` - -```sh -# Replace these values with your own! -FLOW_ADDRESS=0xabcdef12345689 -FLOW_PRIVATE_KEY=aaaaaa...aaaaaa -``` - -### Start the Docker containers - -Configuration: [docker-compose.testnet.yml](docker-compose.testnet.yml) - -```sh -docker-compose -f docker-compose.testnet.yml up -``` From d62a563f989e237371a091c03e4945dfe524ea14 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 22 Jun 2021 10:27:32 -0700 Subject: [PATCH 062/112] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d564d6a8..17b637b2 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ This service can be used by an application that needs to manage Flow user accoun - Send a transaction from an account - Transfer fungible tokens (e.g. FLOW, FUSD) - Detect fungible token deposits -- Transfer NFTs (e.g. FLOW, FUSD) -- Detect NFT deposits +- _Transfer NFTs (e.g. FLOW, FUSD) (coming soon)_ +- _Detect NFT deposits (coming soon)_ [View full list of supported functionality](#functionality). @@ -50,11 +50,11 @@ rather than a hosted 3rd-party solution. ### 3. Fungible Tokens - [x] Send fungible token withdrawals from admin account (FLOW, FUSD) -- [ ] Detect fungible token deposits to admin account (FLOW, FUSD) +- [x] Detect fungible token deposits to admin account (FLOW, FUSD) - [x] Send fungible token withdrawals from a user account (FLOW, FUSD) -- [ ] Detect fungible token deposits to a user account (FLOW, FUSD) -- [ ] View the fungible token balance of the admin account -- [ ] View the fungible token balance of a user account +- [x] Detect fungible token deposits to a user account (FLOW, FUSD) +- [x] View the fungible token balance of the admin account +- [x] View the fungible token balance of a user account ### 4. Non-Fungible Tokens From c94e57c7b65623d5399aba349f05759523e11ef2 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 22 Jun 2021 11:01:13 -0700 Subject: [PATCH 063/112] Restructure README --- API.md | 34 +++++++++++++ README.md | 139 ++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 147 insertions(+), 26 deletions(-) diff --git a/API.md b/API.md index 9c0fde86..c2a4d09e 100644 --- a/API.md +++ b/API.md @@ -4,6 +4,40 @@ The documents contains the REST routes provided by the Flow Wallet API. These routes are also defined in the [OpenAPI specification for this service](openapi.yml). +## Functionality + +### 1. Admin + +- [x] Single admin account (hot wallet) +- [x] Create user accounts (using admin account) + +### 2. Transaction Execution + +- [x] Send an arbitrary transaction from the admin account +- [x] Send an arbitrary transaction from a user account + +### 3. Fungible Tokens + +- [x] Send fungible token withdrawals from admin account (FLOW, FUSD) +- [x] Detect fungible token deposits to admin account (FLOW, FUSD) +- [x] Send fungible token withdrawals from a user account (FLOW, FUSD) +- [x] Detect fungible token deposits to a user account (FLOW, FUSD) +- [x] View the fungible token balance of the admin account +- [x] View the fungible token balance of a user account + +### 4. Non-Fungible Tokens + +- [ ] Set up admin account with non-fungible token collections (`NFT.Collection`) +- [ ] Send non-fungible token withdrawals from admin account +- [ ] Detect non-fungible token deposits to admin account +- [ ] Set up a user account with non-fungible token collections (`NFT.Collection`) +- [ ] Send non-fungible token withdrawals from a user account +- [ ] Detect non-fungible token deposits to a user account +- [ ] View the non-fungible tokens owned by the admin account +- [ ] View the non-fungible tokens owned by a user account + +--- + ## Accounts ### List all accounts diff --git a/README.md b/README.md index 17b637b2..fff8f938 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ This service can be used by an application that needs to manage Flow user accoun - _Transfer NFTs (e.g. FLOW, FUSD) (coming soon)_ - _Detect NFT deposits (coming soon)_ -[View full list of supported functionality](#functionality). +View full list of functionality in the [API specification](API.md). ## Background @@ -35,41 +35,128 @@ rather than a hosted 3rd-party solution. - **Exchange** — a cryptocurrency exchange that is listing FLOW and/or FUSD. Similar to the case above, one or more admin accounts may be used as a hot wallet for outgoing payments, and additional deposit accounts would be created to accept incoming payments. - **Web Wallet** — a user-facing wallet application that is compatible with Flow dapps. Each user account would be created and managed by the wallet service. -## Functionality +## Installation -### 1. Admin +The Wallet API is provided as a Docker image. -- [x] Single admin account (hot wallet) -- [x] Create user accounts (using admin account) +## Basic example setup (Testnet) -### 2. Transaction Execution +`.env` file: -- [x] Send an arbitrary transaction from the admin account -- [x] Send an arbitrary transaction from a user account +```sh +ACCESS_API_HOST=https://access-testnet.onflow.org +CHAIN_ID=flow-testnet +DATABASE_DSN=postgresql://postgres:postgres@db:5432/postgres # replace this +DATABASE_TYPE=psql -### 3. Fungible Tokens +ADMIN_ADDRESS= +ADMIN_PRIVATE_KEY= +DEFAULT_KEY_TYPE=local # Will store keys in your database, use "google_kms" if you have that setup +ENCRYPTION_KEY=passphrasewhichneedstobe32bytes! # replace this with something that is 32 bytes -- [x] Send fungible token withdrawals from admin account (FLOW, FUSD) -- [x] Detect fungible token deposits to admin account (FLOW, FUSD) -- [x] Send fungible token withdrawals from a user account (FLOW, FUSD) -- [x] Detect fungible token deposits to a user account (FLOW, FUSD) -- [x] View the fungible token balance of the admin account -- [x] View the fungible token balance of a user account +ENABLED_TOKENS=FlowToken:0x7e60df042a9c0868 +``` -### 4. Non-Fungible Tokens +Running: -- [ ] Set up admin account with non-fungible token collections (`NFT.Collection`) -- [ ] Send non-fungible token withdrawals from admin account -- [ ] Detect non-fungible token deposits to admin account -- [ ] Set up a user account with non-fungible token collections (`NFT.Collection`) -- [ ] Send non-fungible token withdrawals from a user account -- [ ] Detect non-fungible token deposits to a user account -- [ ] View the non-fungible tokens owned by the admin account -- [ ] View the non-fungible tokens owned by a user account +```sh +docker run -d --name flow-wallet-api --env-file .env gcr.io/flow-container-registry/wallet-api:v0.0.3 +``` -## Installation +## Configuration + +### Enabled fungible tokens + +A comma separated list of fungible tokens and their corresponding addresses enabled for this instance. Make sure to name each token exactly as it is in the corresponding cadence code (FlowToken, FUSD etc.). Include at least FlowToken as functionality without it is undetermined. + +Examples: + + ENABLED_TOKENS=FlowToken:0x0ae53cb6e3f42a79 + ENABLED_TOKENS=FlowToken:0x0ae53cb6e3f42a79,FUSD:0xf8d6e0586b0a20c7 + +### Database + +| Config variable | Environment variable | Description | Default | Examples | +| --------------- | :------------------- | ------------------------------------------------------------------------------------------------ | ----------- | ------------------------- | +| DatabaseType | `DATABASE_TYPE` | Type of database driver | `sqlite` | `sqlite`, `psql`, `mysql` | +| DatabaseDSN | `DATABASE_DSN` | Data source name ([DSN](https://en.wikipedia.org/wiki/Data_source_name)) for database connection | `wallet.db` | See below | + +Examples of Database DSN + + mysql://john:pass@localhost:3306/my_db + + postgresql://postgres:postgres@localhost:5432/postgres + + user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local + + host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai + +For more: https://gorm.io/docs/connecting_to_the_database.html + +### Google KMS setup + +Note: In order to use Google KMS for remote key management you'll need a Google Cloud Platform account. + +Pre-requisites: + +1. Create a new Project if you don't have one already. You'll need the Project ID later. +2. Enable Cloud Key Management Service (KMS) API for the project, Security -> [Cryptographic Keys](https://console.cloud.google.com/security/kms). +3. Create a new Key Ring for your wallet (or use an existing Key Ring), Security -> Cryptographic Keys -> [Create Key Ring](https://console.cloud.google.com/security/kms/keyring/create), you'll need the Location ID (or _Location_) and Key Ring ID (or _Name_) later. + +Using a Service Account to access the KMS API (see [official docs](https://cloud.google.com/docs/authentication/getting-started) for more); + +1. Create a new Service Account, IAM & Admin -> Service Accounts -> [Create Service Account](https://console.cloud.google.com/iam-admin/serviceaccounts/create) +2. Use the roles `Cloud KMS Admin` & `Cloud KMS Signer/Verifier` or grant the required permissions through a custom role (NOTE: deletion not supported yet): + - `cloudkms.cryptoKeyVersions.useToSign` + - `cloudkms.cryptoKeyVersions.viewPublicKey` + - `cloudkms.cryptoKeys.create` +3. After creating the Service Account, select Manage Keys from the Actions menu in the Service Account listing. +4. Create a new key, Add Key -> Create New key, and select JSON as the key type. +5. Save the JSON file. + +Configure the Google KMS client library by setting the environment variable `GOOGLE_APPLICATION_CREDENTIALS`: + +``` +export GOOGLE_APPLICATION_CREDENTIALS="/home/example/path/to/service-account-file.json" +``` + +Configure Google KMS as the key storage for `flow-wallet-service` and set the necessary environment variables: + +| Config variable | Environment variable | Description | Default | Examples | +| --------------- | ------------------------ | ---------------- | ------- | --------------------------- | +| DefaultKeyType | `DEFAULT_KEY_TYPE` | Default key type | `local` | `local`, `google_kms` | +| ProjectID | `GOOGLE_KMS_PROJECT_ID` | GCP Project ID | - | `flow-wallet-example` | +| LocationID | `GOOGLE_KMS_LOCATION_ID` | GCP Location ID | - | `europe-north1`, `us-west1` | +| KeyRingID | `GOOGLE_KMS_KEYRING_ID` | GCP Key Ring ID | - | `example-wallet-keyring` | + +### All possible environment variables + +``` +HOST= +PORT=3000 +ACCESS_API_HOST=localhost:3569 + +ENABLED_TOKENS=FlowToken:0x0ae53cb6e3f42a79 + +DATABASE_DSN=wallet.db +DATABASE_TYPE=sqlite + +ADMIN_ADDRESS= +ADMIN_KEY_INDEX=0 +ADMIN_KEY_TYPE=local +ADMIN_PRIVATE_KEY= +CHAIN_ID=flow-emulator +DEFAULT_KEY_TYPE=local +DEFAULT_KEY_INDEX=0 +DEFAULT_KEY_WEIGHT=-1 +DEFAULT_SIGN_ALGO=ECDSA_P256 +DEFAULT_HASH_ALGO=SHA3_256 +ENCRYPTION_KEY= -TODO +GOOGLE_APPLICATION_CREDENTIALS= +GOOGLE_KMS_PROJECT_ID= +GOOGLE_KMS_LOCATION_ID= +``` ## API Specification From fa6ef0021d490a14305d626758e4a92a514d5700 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 22 Jun 2021 11:01:27 -0700 Subject: [PATCH 064/112] Add Equilibrium credit --- README.md | 6 ++++++ equilibrium.svg | 1 + 2 files changed, 7 insertions(+) create mode 100644 equilibrium.svg diff --git a/README.md b/README.md index fff8f938..4e6dbfb4 100644 --- a/README.md +++ b/README.md @@ -161,3 +161,9 @@ GOOGLE_KMS_LOCATION_ID= ## API Specification [View the full Wallet API specification](API.md). + +## Credit + +The Flow Wallet API is developed and maintained by [Equilibrium](https://equilibrium.co/). + +[![Equilibrium](equilibrium.svg)](https://equilibrium.co/) diff --git a/equilibrium.svg b/equilibrium.svg new file mode 100644 index 00000000..84ac57f1 --- /dev/null +++ b/equilibrium.svg @@ -0,0 +1 @@ + From 9b2735dbb370cb910be5603cddfc2ef9b0a78fbb Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 22 Jun 2021 11:03:37 -0700 Subject: [PATCH 065/112] Update Equilibrium image --- README.md | 2 +- equilibrium.png | Bin 0 -> 435 bytes equilibrium.svg | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 equilibrium.png delete mode 100644 equilibrium.svg diff --git a/README.md b/README.md index 4e6dbfb4..de04244c 100644 --- a/README.md +++ b/README.md @@ -166,4 +166,4 @@ GOOGLE_KMS_LOCATION_ID= The Flow Wallet API is developed and maintained by [Equilibrium](https://equilibrium.co/). -[![Equilibrium](equilibrium.svg)](https://equilibrium.co/) +[![Equilibrium](equilibrium.png)](https://equilibrium.co/) diff --git a/equilibrium.png b/equilibrium.png new file mode 100644 index 0000000000000000000000000000000000000000..1e0956d93f3f352fbd136bf86881ec479dbb4794 GIT binary patch literal 435 zcmeAS@N?(olHy`uVBq!ia0vp^TR@nJ8Av)Tezy=vv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpaf@tPl)U5*RQ!ac>n+Z z->$UU1t`dv21sKV@MTm(!8`6`=?ea`AL=49U3n_VPkr1_c2Y zM}aTx4gaDKPEv4Cu$wPge)5(!D?AYQ0b63dS&dVy@WOeTHl~>WcGmloziu${s zjlm<2(ezH@hYK<@1cBzMmbgZgq$HN4S|t~y0x1R~10zFS0~1{X^AICrD`NvI6BBI% zBP#;~f&AM`Q8eV{r(~v8;?|I8lq?C<5MC7$Q4*9`u24{vpO%@Es!&o{kgAYbP?F5R zP%-E6CmxQ%Fb$1U{-@7)J`G}ER_4}A<`z~K_MR-lEUe(tU~)KxS$T7a!s#1VP8>ON jMCJ(l=?0GlUV03##05(}IhjrcTEXDy>gTe~DWM4fTc?L` literal 0 HcmV?d00001 diff --git a/equilibrium.svg b/equilibrium.svg deleted file mode 100644 index 84ac57f1..00000000 --- a/equilibrium.svg +++ /dev/null @@ -1 +0,0 @@ - From b4deb6955e45d266fda42f984f03ce3e7ddb1834 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 22 Jun 2021 11:05:12 -0700 Subject: [PATCH 066/112] try new image --- README.md | 4 +++- equilibrium.svg | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 equilibrium.svg diff --git a/README.md b/README.md index de04244c..fd2dbbfb 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,8 @@ GOOGLE_KMS_LOCATION_ID= ## Credit +Equilibrium + The Flow Wallet API is developed and maintained by [Equilibrium](https://equilibrium.co/). -[![Equilibrium](equilibrium.png)](https://equilibrium.co/) + diff --git a/equilibrium.svg b/equilibrium.svg new file mode 100644 index 00000000..84ac57f1 --- /dev/null +++ b/equilibrium.svg @@ -0,0 +1 @@ + From ab7b5e9e800f34ff12110a0919e0d4c6b2d8649c Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 22 Jun 2021 11:06:46 -0700 Subject: [PATCH 067/112] Change to full logo --- README.md | 3 +-- equilibrium.svg | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fd2dbbfb..388bad4a 100644 --- a/README.md +++ b/README.md @@ -164,8 +164,7 @@ GOOGLE_KMS_LOCATION_ID= ## Credit -Equilibrium The Flow Wallet API is developed and maintained by [Equilibrium](https://equilibrium.co/). - +Equilibrium diff --git a/equilibrium.svg b/equilibrium.svg index 84ac57f1..01b28d7e 100644 --- a/equilibrium.svg +++ b/equilibrium.svg @@ -1 +1 @@ - + From 6b1cb7e83a0f62baa3aaebda2ea2f65f8dcb357d Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 22 Jun 2021 11:07:46 -0700 Subject: [PATCH 068/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 388bad4a..267da72b 100644 --- a/README.md +++ b/README.md @@ -167,4 +167,4 @@ GOOGLE_KMS_LOCATION_ID= The Flow Wallet API is developed and maintained by [Equilibrium](https://equilibrium.co/). -Equilibrium +Equilibrium From a5d9c9c068372d1c962169068324c9bf53f50f1e Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 22:32:08 -0700 Subject: [PATCH 069/112] Update README.md --- README.md | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 267da72b..7d59801f 100644 --- a/README.md +++ b/README.md @@ -37,32 +37,40 @@ rather than a hosted 3rd-party solution. ## Installation -The Wallet API is provided as a Docker image. +The Wallet API is provided as a Docker image: -## Basic example setup (Testnet) +```sh +docker pull gcr.io/flow-container-registry/wallet-api:v0.0.4 +``` + +### Basic example usage -`.env` file: +> This setup requires [Docker](https://docs.docker.com/engine/install/) and the [Flow CLI](https://docs.onflow.org/flow-cli/install/). + +Create a configuration file: ```sh -ACCESS_API_HOST=https://access-testnet.onflow.org -CHAIN_ID=flow-testnet -DATABASE_DSN=postgresql://postgres:postgres@db:5432/postgres # replace this -DATABASE_TYPE=psql +cp .env.example .env +``` -ADMIN_ADDRESS= -ADMIN_PRIVATE_KEY= -DEFAULT_KEY_TYPE=local # Will store keys in your database, use "google_kms" if you have that setup -ENCRYPTION_KEY=passphrasewhichneedstobe32bytes! # replace this with something that is 32 bytes +Start the Wallet API, Flow Emulator and Postgres: -ENABLED_TOKENS=FlowToken:0x7e60df042a9c0868 +```sh +docker-compose up ``` -Running: +Deploy the FUSD contract to the emulator: ```sh -docker run -d --name flow-wallet-api --env-file .env gcr.io/flow-container-registry/wallet-api:v0.0.3 +flow project deploy -n emulator ``` +You can now access the API at http://localhost:3000. + +Next, see the [FUSD sample app](/examples/nextjs-fusd-provider) +for an example of how to use this configuration as part of +a complete application. + ## Configuration ### Enabled fungible tokens @@ -71,8 +79,10 @@ A comma separated list of fungible tokens and their corresponding addresses enab Examples: - ENABLED_TOKENS=FlowToken:0x0ae53cb6e3f42a79 - ENABLED_TOKENS=FlowToken:0x0ae53cb6e3f42a79,FUSD:0xf8d6e0586b0a20c7 +```sh +ENABLED_TOKENS=FlowToken:0x0ae53cb6e3f42a79 +ENABLED_TOKENS=FlowToken:0x0ae53cb6e3f42a79,FUSD:0xf8d6e0586b0a20c7 +``` ### Database @@ -164,7 +174,6 @@ GOOGLE_KMS_LOCATION_ID= ## Credit - The Flow Wallet API is developed and maintained by [Equilibrium](https://equilibrium.co/). Equilibrium From ea02d9142d57011e4359613f47f3279f90a69d39 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 22:37:52 -0700 Subject: [PATCH 070/112] Add FUSD example --- examples/nextjs-fusd-provider/.eslintrc | 3 + examples/nextjs-fusd-provider/.gitignore | 34 + examples/nextjs-fusd-provider/README.md | 45 + .../components/AccountList.js | 77 + .../components/PurchaseForm.js | 59 + examples/nextjs-fusd-provider/lib/actions.js | 32 + examples/nextjs-fusd-provider/lib/config.js | 3 + .../nextjs-fusd-provider/lib/walletApi.js | 109 + examples/nextjs-fusd-provider/next.config.js | 3 + .../nextjs-fusd-provider/package-lock.json | 4253 +++++++++++++++++ examples/nextjs-fusd-provider/package.json | 22 + examples/nextjs-fusd-provider/pages/_app.js | 15 + .../pages/api/accounts.js | 46 + .../pages/api/purchase.js | 33 + examples/nextjs-fusd-provider/pages/index.js | 53 + 15 files changed, 4787 insertions(+) create mode 100644 examples/nextjs-fusd-provider/.eslintrc create mode 100644 examples/nextjs-fusd-provider/.gitignore create mode 100644 examples/nextjs-fusd-provider/README.md create mode 100644 examples/nextjs-fusd-provider/components/AccountList.js create mode 100644 examples/nextjs-fusd-provider/components/PurchaseForm.js create mode 100644 examples/nextjs-fusd-provider/lib/actions.js create mode 100644 examples/nextjs-fusd-provider/lib/config.js create mode 100644 examples/nextjs-fusd-provider/lib/walletApi.js create mode 100644 examples/nextjs-fusd-provider/next.config.js create mode 100644 examples/nextjs-fusd-provider/package-lock.json create mode 100644 examples/nextjs-fusd-provider/package.json create mode 100644 examples/nextjs-fusd-provider/pages/_app.js create mode 100644 examples/nextjs-fusd-provider/pages/api/accounts.js create mode 100644 examples/nextjs-fusd-provider/pages/api/purchase.js create mode 100644 examples/nextjs-fusd-provider/pages/index.js diff --git a/examples/nextjs-fusd-provider/.eslintrc b/examples/nextjs-fusd-provider/.eslintrc new file mode 100644 index 00000000..97a2bb84 --- /dev/null +++ b/examples/nextjs-fusd-provider/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": ["next", "next/core-web-vitals"] +} diff --git a/examples/nextjs-fusd-provider/.gitignore b/examples/nextjs-fusd-provider/.gitignore new file mode 100644 index 00000000..1437c53f --- /dev/null +++ b/examples/nextjs-fusd-provider/.gitignore @@ -0,0 +1,34 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/examples/nextjs-fusd-provider/README.md b/examples/nextjs-fusd-provider/README.md new file mode 100644 index 00000000..65b58716 --- /dev/null +++ b/examples/nextjs-fusd-provider/README.md @@ -0,0 +1,45 @@ +# Next.js FUSD Provider Example + +This example application demonstrates how to use the Flow Wallet API +to distribute the FUSD stablecoin. + +## Usage + +### Start the API + +This example uses the sample emulator configuration +from the root of this repository. + +Change to the repository root: + +```ls +cd ../../ +``` + +Create a configuration file: + +```sh +cp .env.example .env +``` + +Start the Wallet API, Flow Emulator and Postgres: + +```sh +docker-compose up +``` + +Deploy the FUSD contract to the emulator: + +```sh +flow project deploy -n emulator +``` + +### Start the app + +In this directory, install and run the Next.js app: + +```bash +npm run dev +``` + +Try the application at http://localhost:3001! diff --git a/examples/nextjs-fusd-provider/components/AccountList.js b/examples/nextjs-fusd-provider/components/AccountList.js new file mode 100644 index 00000000..9b627bfc --- /dev/null +++ b/examples/nextjs-fusd-provider/components/AccountList.js @@ -0,0 +1,77 @@ +import { useState } from 'react' +import { + Table, + Button, + Spacer, + Tooltip, + Badge, +} from "@geist-ui/react" +import { UserPlus } from '@geist-ui/react-icons' +import { createAccount } from '../lib/actions' + +const tooltipMessage = "This is the account that supplies FUSD for purchase." + +export default function AccountList({ accounts, onCreate }) { + + const [isLoading, setIsLoading] = useState(false) + + const create = async event => { + event.preventDefault() + + setIsLoading(true) + + const account = await createAccount() + + onCreate(account) + + setIsLoading(false) + } + + return ( + <> + + + +
+ + + + + + ) +} + +function formatAccounts(accounts) { + return accounts.map(account => { + if (account.isAdmin) { + return formatAdminAccount(account) + } + + return account + }) +} + +function formatAdminAccount(account) { + return { + address: ( + <> + {account.address} + + + Supplier + + + + ), + balance: account.balance, + } +} diff --git a/examples/nextjs-fusd-provider/components/PurchaseForm.js b/examples/nextjs-fusd-provider/components/PurchaseForm.js new file mode 100644 index 00000000..1d7fc2aa --- /dev/null +++ b/examples/nextjs-fusd-provider/components/PurchaseForm.js @@ -0,0 +1,59 @@ +import { useState } from 'react' +import { Input, Button, Spacer, useToasts } from "@geist-ui/react" +import { ShoppingCart } from '@geist-ui/react-icons' +import { purchaseFusd } from '../lib/actions' + +export default function PurchaseForm({ onPurchase }) { + + const [isLoading, setIsLoading] = useState(false) + + const [, setToast] = useToasts() + + const purchase = async event => { + event.preventDefault() + + setIsLoading(true) + + const { recipient, amount } = await purchaseFusd( + event.target.address.value, + event.target.amount.value, + ) + + await onPurchase() + + setToast({ + text: `${amount} FUSD delivered to ${recipient}.`, + type: "success", + delay: 5000, + }) + + setIsLoading(false) + } + + return ( +
+
+ + + + + + + +
+ + + +
+ ) +} diff --git a/examples/nextjs-fusd-provider/lib/actions.js b/examples/nextjs-fusd-provider/lib/actions.js new file mode 100644 index 00000000..a0ff11cb --- /dev/null +++ b/examples/nextjs-fusd-provider/lib/actions.js @@ -0,0 +1,32 @@ +export async function purchaseFusd(recipient, amount) { + return post('/api/purchase', { recipient, amount }) +} + +export async function createAccount() { + return post('/api/accounts') +} + +export async function listAccounts() { + return get('/api/accounts') +} + +const contentType = "application/json" + +async function get(url) { + return fetch(url, { + method: 'GET', + headers: { + 'Content-Type': contentType + } + }).then(res => res.json()) +} + +async function post(url, body) { + return fetch(url, { + method: 'POST', + headers: { + 'Content-Type': contentType + }, + body: JSON.stringify(body), + }).then(res => res.json()) +} diff --git a/examples/nextjs-fusd-provider/lib/config.js b/examples/nextjs-fusd-provider/lib/config.js new file mode 100644 index 00000000..eac7e080 --- /dev/null +++ b/examples/nextjs-fusd-provider/lib/config.js @@ -0,0 +1,3 @@ +export const baseUrl = "http://localhost:3000" +export const adminAddress = "0xf8d6e0586b0a20c7" +export const fusdTokenName = "FUSD" diff --git a/examples/nextjs-fusd-provider/lib/walletApi.js b/examples/nextjs-fusd-provider/lib/walletApi.js new file mode 100644 index 00000000..513f6592 --- /dev/null +++ b/examples/nextjs-fusd-provider/lib/walletApi.js @@ -0,0 +1,109 @@ +const jobPollInterval = 1000 +const jobPollAttempts = 40 + +const jobStatusComplete = "Complete" +const jobStatusError = "Error" + +const contentType = "application/json" + +export default class WalletApiClient { + + constructor(baseUrl) { + this.baseUrl = baseUrl + } + + async getAccounts() { + return this.get("/v1/accounts") + } + + async createAccount() { + const result = await this.post("/v1/accounts") + + const { jobId } = result; + + const address = await this.pollJobUntilComplete(jobId) + + return address + } + + async initFungibleToken(address, token) { + return this.post( + `/v1/accounts/${address}/fungible-tokens/${token}` + ) + } + + async getFungibleToken(address, token) { + return this.get( + `/v1/accounts/${address}/fungible-tokens/${token}` + ) + } + + async createFungibleTokenWithdrawal(from, to, token, amount) { + const result = await this.post( + `/v1/accounts/${from}/fungible-tokens/${token}/withdrawals`, + { + amount, + recipient: to, + }, + ) + + const { jobId } = result; + + const txId = await this.pollJobUntilComplete(jobId) + + const withdrawal = await this.getFungibleTokenWithdrawal(from, token, txId) + + return withdrawal + } + + async getFungibleTokenWithdrawal(from, token, id) { + return this.get( + `/v1/accounts/${from}/fungible-tokens/${token}/withdrawals/${id}` + ) + } + + async getJob(id) { + return this.get(`/v1/jobs/${id}`) + } + + async pollJobUntilComplete(id) { + for (let i = 0; i < jobPollAttempts; i++) { + const job = await this.getJob(id) + + if (job.status === jobStatusError) { + throw "failed job" + } + + if (job.status === jobStatusComplete) { + return job.result + } + + await sleep(jobPollInterval); + } + + return null + } + + async post(endpoint, body) { + return fetch(this.baseUrl + endpoint, { + method: 'POST', + headers: { + 'Content-Type': contentType, + }, + body: JSON.stringify(body), + }).then(res => res.json()) + } + + async get(endpoint) { + return fetch(this.baseUrl + endpoint, { + method: 'GET', + headers: { + 'Content-Type': contentType, + }, + }).then(res => res.json()) + } +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/examples/nextjs-fusd-provider/next.config.js b/examples/nextjs-fusd-provider/next.config.js new file mode 100644 index 00000000..0d607100 --- /dev/null +++ b/examples/nextjs-fusd-provider/next.config.js @@ -0,0 +1,3 @@ +module.exports = { + reactStrictMode: true, +} diff --git a/examples/nextjs-fusd-provider/package-lock.json b/examples/nextjs-fusd-provider/package-lock.json new file mode 100644 index 00000000..eec41d5c --- /dev/null +++ b/examples/nextjs-fusd-provider/package-lock.json @@ -0,0 +1,4253 @@ +{ + "name": "nextjs-fusd-provider", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-module-imports": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", + "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", + "requires": { + "@babel/types": "^7.14.5" + }, + "dependencies": { + "@babel/types": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz", + "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==", + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-plugin-utils": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==" + }, + "@babel/helper-validator-identifier": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", + "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==" + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz", + "integrity": "sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==", + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/runtime": { + "version": "7.14.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz", + "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz", + "integrity": "sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA==", + "dev": true, + "requires": { + "core-js-pure": "^3.15.0", + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/types": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", + "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@emotion/babel-plugin": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.3.0.tgz", + "integrity": "sha512-UZKwBV2rADuhRp+ZOGgNWg2eYgbzKzQXfQPtJbu/PLy8onurxlNCLvxMQEvlr1/GudguPI5IU9qIY1+2z1M5bA==", + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/runtime": "^7.13.10", + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.5", + "@emotion/serialize": "^1.0.2", + "babel-plugin-macros": "^2.6.1", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "^4.0.3" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "stylis": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz", + "integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==" + } + } + }, + "@emotion/cache": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.4.0.tgz", + "integrity": "sha512-Zx70bjE7LErRO9OaZrhf22Qye1y4F7iDl+ITjet0J+i+B88PrAOBkKvaAWhxsZf72tDLajwCgfCjJ2dvH77C3g==", + "requires": { + "@emotion/memoize": "^0.7.4", + "@emotion/sheet": "^1.0.0", + "@emotion/utils": "^1.0.0", + "@emotion/weak-memoize": "^0.2.5", + "stylis": "^4.0.3" + }, + "dependencies": { + "stylis": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz", + "integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==" + } + } + }, + "@emotion/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + }, + "@emotion/is-prop-valid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.0.tgz", + "integrity": "sha512-9RkilvXAufQHsSsjQ3PIzSns+pxuX4EW8EbGeSPjZMHuMx6z/MOzb9LpqNieQX4F3mre3NWS2+X3JNRHTQztUQ==", + "requires": { + "@emotion/memoize": "^0.7.4" + } + }, + "@emotion/memoize": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", + "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" + }, + "@emotion/react": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.4.0.tgz", + "integrity": "sha512-4XklWsl9BdtatLoJpSjusXhpKv9YVteYKh9hPKP1Sxl+mswEFoUe0WtmtWjxEjkA51DQ2QRMCNOvKcSlCQ7ivg==", + "requires": { + "@babel/runtime": "^7.13.10", + "@emotion/cache": "^11.4.0", + "@emotion/serialize": "^1.0.2", + "@emotion/sheet": "^1.0.1", + "@emotion/utils": "^1.0.0", + "@emotion/weak-memoize": "^0.2.5", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz", + "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==", + "requires": { + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.4", + "@emotion/unitless": "^0.7.5", + "@emotion/utils": "^1.0.0", + "csstype": "^3.0.2" + } + }, + "@emotion/sheet": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.0.1.tgz", + "integrity": "sha512-GbIvVMe4U+Zc+929N1V7nW6YYJtidj31lidSmdYcWozwoBIObXBnaJkKNDjZrLm9Nc0BR+ZyHNaRZxqNZbof5g==" + }, + "@emotion/styled": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.3.0.tgz", + "integrity": "sha512-fUoLcN3BfMiLlRhJ8CuPUMEyKkLEoM+n+UyAbnqGEsCd5IzKQ7VQFLtzpJOaCD2/VR2+1hXQTnSZXVJeiTNltA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@emotion/babel-plugin": "^11.3.0", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/serialize": "^1.0.2", + "@emotion/utils": "^1.0.0" + } + }, + "@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "@emotion/utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz", + "integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==" + }, + "@emotion/weak-memoize": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", + "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" + }, + "@eslint/eslintrc": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", + "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + } + }, + "@geist-ui/react": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@geist-ui/react/-/react-2.1.5.tgz", + "integrity": "sha512-vHn3yOAyXon88m6UnaU9e16iC5U0hFwYbwlroD05dbx4GbH/f5uRhmQ5QI4nkTvVi3/m0++cLGR2ek9U8WY6hg==", + "requires": { + "styled-jsx": "3.3.0" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + }, + "styled-jsx": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-3.3.0.tgz", + "integrity": "sha512-sh8BI5eGKyJlwL4kNXHjb27/a/GJV8wP4ElRIkRXrGW3sHKOsY9Pa1VZRNxyvf3+lisdPwizD9JDkzVO9uGwZw==", + "requires": { + "@babel/types": "7.8.3", + "babel-plugin-syntax-jsx": "6.18.0", + "convert-source-map": "1.7.0", + "loader-utils": "1.2.3", + "source-map": "0.7.3", + "string-hash": "1.1.3", + "stylis": "3.5.4", + "stylis-rule-sheet": "0.0.10" + } + } + } + }, + "@geist-ui/react-icons": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@geist-ui/react-icons/-/react-icons-1.0.1.tgz", + "integrity": "sha512-/phre1NwXT0Mrxcq3yb+KksJ16/ykptgxI7+jh5pmDZfxnIMPlBWFBGDK0yw96oi752Wuo24X6+tkWjVe/g5Bg==" + }, + "@hackclub/theme": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@hackclub/theme/-/theme-0.3.1.tgz", + "integrity": "sha512-Kl1emuTu+LQdo+g1RqOwUr8Q3eV/A9Xn6hcwwhiKnsI2hFnSVxjvP1ZKTzdUdQjhSqgl2FAMg84AAn7ZjZRJWg==" + }, + "@hapi/accept": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@hapi/accept/-/accept-5.0.2.tgz", + "integrity": "sha512-CmzBx/bXUR8451fnZRuZAJRlzgm0Jgu5dltTX/bszmR2lheb9BpyN47Q1RbaGTsvFzn0PXAEs+lXDKfshccYZw==", + "requires": { + "@hapi/boom": "9.x.x", + "@hapi/hoek": "9.x.x" + } + }, + "@hapi/boom": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.2.tgz", + "integrity": "sha512-uJEJtiNHzKw80JpngDGBCGAmWjBtzxDCz17A9NO2zCi8LLBlb5Frpq4pXwyN+2JQMod4pKz5BALwyneCgDg89Q==", + "requires": { + "@hapi/hoek": "9.x.x" + } + }, + "@hapi/hoek": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz", + "integrity": "sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==" + }, + "@mdx-js/react": { + "version": "1.6.22", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", + "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==" + }, + "@next/env": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-11.0.1.tgz", + "integrity": "sha512-yZfKh2U6R9tEYyNUrs2V3SBvCMufkJ07xMH5uWy8wqcl5gAXoEw6A/1LDqwX3j7pUutF9d1ZxpdGDA3Uag+aQQ==" + }, + "@next/eslint-plugin-next": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-11.0.1.tgz", + "integrity": "sha512-UzdX3y6XSrj9YuASUb/p4sRvfjP2klj2YgIOfMwrWoLTTPJQMh00hREB9Ftr7m7RIxjVSAaaLXIRLdxvq948GA==", + "dev": true + }, + "@next/polyfill-module": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@next/polyfill-module/-/polyfill-module-11.0.1.tgz", + "integrity": "sha512-Cjs7rrKCg4CF4Jhri8PCKlBXhszTfOQNl9AjzdNy4K5jXFyxyoSzuX2rK4IuoyE+yGp5A3XJCBEmOQ4xbUp9Mg==" + }, + "@next/react-dev-overlay": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@next/react-dev-overlay/-/react-dev-overlay-11.0.1.tgz", + "integrity": "sha512-lvUjMVpLsgzADs9Q8wtC5LNqvfdN+M0BDMSrqr04EDWAyyX0vURHC9hkvLbyEYWyh+WW32pwjKBXdkMnJhoqMg==", + "requires": { + "@babel/code-frame": "7.12.11", + "anser": "1.4.9", + "chalk": "4.0.0", + "classnames": "2.2.6", + "css.escape": "1.5.1", + "data-uri-to-buffer": "3.0.1", + "platform": "1.3.6", + "shell-quote": "1.7.2", + "source-map": "0.8.0-beta.0", + "stacktrace-parser": "0.1.10", + "strip-ansi": "6.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", + "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "@next/react-refresh-utils": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@next/react-refresh-utils/-/react-refresh-utils-11.0.1.tgz", + "integrity": "sha512-K347DM6Z7gBSE+TfUaTTceWvbj0B6iNAsFZXbFZOlfg3uyz2sbKpzPYYFocCc27yjLaS8OfR8DEdS2mZXi8Saw==" + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", + "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@rushstack/eslint-patch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.0.6.tgz", + "integrity": "sha512-Myxw//kzromB9yWgS8qYGuGVf91oBUUJpNvy5eM50sqvmKLbKjwLxohJnkWGTeeI9v9IBMtPLxz5Gc60FIfvCA==", + "dev": true + }, + "@styled-system/background": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@styled-system/background/-/background-5.1.2.tgz", + "integrity": "sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==", + "requires": { + "@styled-system/core": "^5.1.2" + } + }, + "@styled-system/border": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@styled-system/border/-/border-5.1.5.tgz", + "integrity": "sha512-JvddhNrnhGigtzWRCVuAHepniyVi6hBlimxWDVAdcTuk7aRn9BYJUwfHslURtwYFsF5FoEs8Zmr1oZq2M1AP0A==", + "requires": { + "@styled-system/core": "^5.1.2" + } + }, + "@styled-system/color": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@styled-system/color/-/color-5.1.2.tgz", + "integrity": "sha512-1kCkeKDZkt4GYkuFNKc7vJQMcOmTl3bJY3YBUs7fCNM6mMYJeT1pViQ2LwBSBJytj3AB0o4IdLBoepgSgGl5MA==", + "requires": { + "@styled-system/core": "^5.1.2" + } + }, + "@styled-system/core": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@styled-system/core/-/core-5.1.2.tgz", + "integrity": "sha512-XclBDdNIy7OPOsN4HBsawG2eiWfCcuFt6gxKn1x4QfMIgeO6TOlA2pZZ5GWZtIhCUqEPTgIBta6JXsGyCkLBYw==", + "requires": { + "object-assign": "^4.1.1" + } + }, + "@styled-system/css": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@styled-system/css/-/css-5.1.5.tgz", + "integrity": "sha512-XkORZdS5kypzcBotAMPBoeckDs9aSZVkvrAlq5K3xP8IMAUek+x2O4NtwoSgkYkWWzVBu6DGdFZLR790QWGG+A==" + }, + "@styled-system/flexbox": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@styled-system/flexbox/-/flexbox-5.1.2.tgz", + "integrity": "sha512-6hHV52+eUk654Y1J2v77B8iLeBNtc+SA3R4necsu2VVinSD7+XY5PCCEzBFaWs42dtOEDIa2lMrgL0YBC01mDQ==", + "requires": { + "@styled-system/core": "^5.1.2" + } + }, + "@styled-system/grid": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@styled-system/grid/-/grid-5.1.2.tgz", + "integrity": "sha512-K3YiV1KyHHzgdNuNlaw8oW2ktMuGga99o1e/NAfTEi5Zsa7JXxzwEnVSDSBdJC+z6R8WYTCYRQC6bkVFcvdTeg==", + "requires": { + "@styled-system/core": "^5.1.2" + } + }, + "@styled-system/layout": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@styled-system/layout/-/layout-5.1.2.tgz", + "integrity": "sha512-wUhkMBqSeacPFhoE9S6UF3fsMEKFv91gF4AdDWp0Aym1yeMPpqz9l9qS/6vjSsDPF7zOb5cOKC3tcKKOMuDCPw==", + "requires": { + "@styled-system/core": "^5.1.2" + } + }, + "@styled-system/position": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@styled-system/position/-/position-5.1.2.tgz", + "integrity": "sha512-60IZfMXEOOZe3l1mCu6sj/2NAyUmES2kR9Kzp7s2D3P4qKsZWxD1Se1+wJvevb+1TP+ZMkGPEYYXRyU8M1aF5A==", + "requires": { + "@styled-system/core": "^5.1.2" + } + }, + "@styled-system/shadow": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@styled-system/shadow/-/shadow-5.1.2.tgz", + "integrity": "sha512-wqniqYb7XuZM7K7C0d1Euxc4eGtqEe/lvM0WjuAFsQVImiq6KGT7s7is+0bNI8O4Dwg27jyu4Lfqo/oIQXNzAg==", + "requires": { + "@styled-system/core": "^5.1.2" + } + }, + "@styled-system/should-forward-prop": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@styled-system/should-forward-prop/-/should-forward-prop-5.1.5.tgz", + "integrity": "sha512-+rPRomgCGYnUIaFabDoOgpSDc4UUJ1KsmlnzcEp0tu5lFrBQKgZclSo18Z1URhaZm7a6agGtS5Xif7tuC2s52Q==", + "requires": { + "@emotion/is-prop-valid": "^0.8.1", + "@emotion/memoize": "^0.7.1", + "styled-system": "^5.1.5" + }, + "dependencies": { + "@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "requires": { + "@emotion/memoize": "0.7.4" + }, + "dependencies": { + "@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" + } + } + } + } + }, + "@styled-system/space": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@styled-system/space/-/space-5.1.2.tgz", + "integrity": "sha512-+zzYpR8uvfhcAbaPXhH8QgDAV//flxqxSjHiS9cDFQQUSznXMQmxJegbhcdEF7/eNnJgHeIXv1jmny78kipgBA==", + "requires": { + "@styled-system/core": "^5.1.2" + } + }, + "@styled-system/typography": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@styled-system/typography/-/typography-5.1.2.tgz", + "integrity": "sha512-BxbVUnN8N7hJ4aaPOd7wEsudeT7CxarR+2hns8XCX1zp0DFfbWw4xYa/olA0oQaqx7F1hzDg+eRaGzAJbF+jOg==", + "requires": { + "@styled-system/core": "^5.1.2" + } + }, + "@styled-system/variant": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@styled-system/variant/-/variant-5.1.5.tgz", + "integrity": "sha512-Yn8hXAFoWIro8+Q5J8YJd/mP85Teiut3fsGVR9CAxwgNfIAiqlYxsk5iHU7VHJks/0KjL4ATSjmbtCDC/4l1qw==", + "requires": { + "@styled-system/core": "^5.1.2", + "@styled-system/css": "^5.1.5" + } + }, + "@theme-ui/color-modes": { + "version": "0.6.0-alpha.8", + "resolved": "https://registry.npmjs.org/@theme-ui/color-modes/-/color-modes-0.6.0-alpha.8.tgz", + "integrity": "sha512-40NJmJHrSQ5O46wz0A4aXwqRtBLvQw2Ugv3ChTrq9fqmGPeCXwlmHIAt4MBLD5mBZ0GaS7Nua5n+t+74OZEDdw==", + "requires": { + "@emotion/react": "^11.1.1", + "@theme-ui/core": "0.6.0-alpha.8", + "@theme-ui/css": "0.6.0-alpha.8", + "deepmerge": "^4.2.2" + } + }, + "@theme-ui/components": { + "version": "0.6.0-alpha.8", + "resolved": "https://registry.npmjs.org/@theme-ui/components/-/components-0.6.0-alpha.8.tgz", + "integrity": "sha512-uY33Kvvj4e+vZGH63p1s9fuGQA8odZeGrSPUqwod3JxS6akDppIugkFLUMrhEgohfKcpRgblYx1gpcC9tlY3eQ==", + "requires": { + "@emotion/react": "^11.1.1", + "@emotion/styled": "^11.0.0", + "@styled-system/color": "^5.1.2", + "@styled-system/should-forward-prop": "^5.1.2", + "@styled-system/space": "^5.1.2", + "@theme-ui/css": "0.6.0-alpha.8", + "@types/styled-system": "^5.1.10" + } + }, + "@theme-ui/core": { + "version": "0.6.0-alpha.8", + "resolved": "https://registry.npmjs.org/@theme-ui/core/-/core-0.6.0-alpha.8.tgz", + "integrity": "sha512-2NH1UG2f29nD+Ri1xLFQJeX4SWU5hOCaV/8UHQ0E0bkOhUCPe7ct2gd/jfNmNG/3+8Q9fDMphG1ZPz1va2G1uA==", + "requires": { + "@emotion/react": "^11.1.1", + "@theme-ui/css": "0.6.0-alpha.8", + "@theme-ui/parse-props": "0.6.0-alpha.8", + "deepmerge": "^4.2.2" + } + }, + "@theme-ui/css": { + "version": "0.6.0-alpha.8", + "resolved": "https://registry.npmjs.org/@theme-ui/css/-/css-0.6.0-alpha.8.tgz", + "integrity": "sha512-Gk8onu12Sz29LwPsYbfHo/KqZtTE/D2+Qxa1XidaGa2xGom5SdgJpTVZ3t0uYuuOtFXUJ/Bp/pQLe6CIBu1Jfw==", + "requires": { + "@emotion/react": "^11.1.1", + "csstype": "^3.0.5" + } + }, + "@theme-ui/mdx": { + "version": "0.6.0-alpha.8", + "resolved": "https://registry.npmjs.org/@theme-ui/mdx/-/mdx-0.6.0-alpha.8.tgz", + "integrity": "sha512-cq7XHavSBdP7wi44JItwDTkt/rLX0X3YmutXBvjqAsj2Exitc2XT3jfk/qJoN/+lD70HqxSVTSB3grhcSl61Ug==", + "requires": { + "@emotion/react": "^11.1.1", + "@emotion/styled": "^11.0.0", + "@mdx-js/react": "^1.6.22", + "@theme-ui/core": "0.6.0-alpha.8", + "@theme-ui/css": "0.6.0-alpha.8" + } + }, + "@theme-ui/parse-props": { + "version": "0.6.0-alpha.8", + "resolved": "https://registry.npmjs.org/@theme-ui/parse-props/-/parse-props-0.6.0-alpha.8.tgz", + "integrity": "sha512-nrZr/Vnda19lfCVL+kq+4CCTOBFR8gxuD3HThO+CjVNyOn+rSUFEj6dKO40SYTbVH6BApGCE/0J87NJZQ3Cy2Q==", + "requires": { + "@emotion/react": "^11.1.1", + "@theme-ui/css": "0.6.0-alpha.8" + } + }, + "@theme-ui/theme-provider": { + "version": "0.6.0-alpha.8", + "resolved": "https://registry.npmjs.org/@theme-ui/theme-provider/-/theme-provider-0.6.0-alpha.8.tgz", + "integrity": "sha512-RdHJKeCfkNS46lcs6MV3tRnx8efzI7OI6reMJKGnqx4n0D7NLFZMZJZgdiqaiFcev5Lli8cfRePMZYbU6UZ8oA==", + "requires": { + "@emotion/react": "^11.1.1", + "@theme-ui/color-modes": "0.6.0-alpha.8", + "@theme-ui/core": "0.6.0-alpha.8", + "@theme-ui/mdx": "0.6.0-alpha.8" + } + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/node": { + "version": "15.12.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.5.tgz", + "integrity": "sha512-se3yX7UHv5Bscf8f1ERKvQOD6sTyycH3hdaoozvaLxgUiY5lIGEeH37AD0G0Qi9kPqihPn0HOfd2yaIEN9VwEg==" + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "@types/styled-system": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@types/styled-system/-/styled-system-5.1.11.tgz", + "integrity": "sha512-R+JxEZYa5T0HD2urViR/mdklVaGhwbNOtDoWWGQ1+z1CGs/gF1UAKCaS//YwsUwterEKpyaKxgaXyFNKF04GCA==", + "requires": { + "csstype": "^3.0.2" + } + }, + "@typescript-eslint/parser": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.0.tgz", + "integrity": "sha512-7x4D22oPY8fDaOCvkuXtYYTQ6mTMmkivwEzS+7iml9F9VkHGbbZ3x4fHRwxAb5KeuSkLqfnYjs46tGx2Nour4A==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.28.0", + "@typescript-eslint/types": "4.28.0", + "@typescript-eslint/typescript-estree": "4.28.0", + "debug": "^4.3.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.0.tgz", + "integrity": "sha512-eCALCeScs5P/EYjwo6se9bdjtrh8ByWjtHzOkC4Tia6QQWtQr3PHovxh3TdYTuFcurkYI4rmFsRFpucADIkseg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.0", + "@typescript-eslint/visitor-keys": "4.28.0" + } + }, + "@typescript-eslint/types": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.0.tgz", + "integrity": "sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.0.tgz", + "integrity": "sha512-m19UQTRtxMzKAm8QxfKpvh6OwQSXaW1CdZPoCaQuLwAq7VZMNuhJmZR4g5281s2ECt658sldnJfdpSZZaxUGMQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.0", + "@typescript-eslint/visitor-keys": "4.28.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz", + "integrity": "sha512-PjJyTWwrlrvM5jazxYF5ZPs/nl0kHDZMVbuIcbpawVXaDPelp3+S9zpOz5RmVUfS/fD5l5+ZXNKnWhNYjPzCvw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.28.0", + "eslint-visitor-keys": "^2.0.0" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "anser": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.9.tgz", + "integrity": "sha512-AI+BjTeGt2+WFk4eWcqbQ7snZpDBt8SaLlj0RT2h5xfdWaiy51OjYvqwMrNzJLGy8iOAL6nKDITWO+rd4MkYEA==" + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "array-includes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", + "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.5" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "array.prototype.flatmap": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz", + "integrity": "sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "function-bind": "^1.1.1" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "requires": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "ast-types": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.2.tgz", + "integrity": "sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA==" + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz", + "integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==" + }, + "axe-core": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.2.3.tgz", + "integrity": "sha512-pXnVMfJKSIWU2Ml4JHP7pZEPIrgBO1Fd3WGx+fPBsS+KRGhE4vxooD8XBGWbQOIVSZsVK7pUDBBkCicNu80yzQ==", + "dev": true + }, + "axobject-query": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", + "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", + "dev": true + }, + "babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "requires": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + } + }, + "babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "caniuse-lite": { + "version": "1.0.30001240", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001240.tgz", + "integrity": "sha512-nb8mDzfMdxBDN7ZKx8chWafAdBp5DAAlpWvNyUGe5tcDWd838zpzDN3Rah9cjCqhfOKkrvx40G2SDtP0qiWX/w==" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "requires": { + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "core-js-pure": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.15.1.tgz", + "integrity": "sha512-OZuWHDlYcIda8sJLY4Ec6nWq2hRjlyCqCZ+jCflyleMkVt3tPedDVErvHslyS2nbO+SlBFMSBJYvtLMwxnrzjA==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "dependencies": { + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + } + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "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" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=" + }, + "cssnano-preset-simple": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssnano-preset-simple/-/cssnano-preset-simple-2.0.0.tgz", + "integrity": "sha512-HkufSLkaBJbKBFx/7aj5HmCK9Ni/JedRQm0mT2qBzMG/dEuJOLnMt2lK6K1rwOOyV4j9aSY+knbW9WoS7BYpzg==", + "requires": { + "caniuse-lite": "^1.0.30001202" + } + }, + "cssnano-simple": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssnano-simple/-/cssnano-simple-2.0.0.tgz", + "integrity": "sha512-0G3TXaFxlh/szPEG/o3VcmCwl0N3E60XNb9YZZijew5eIs6fLjJuOPxQd9yEBaX2p/YfJtt49i4vYi38iH6/6w==", + "requires": { + "cssnano-preset-simple": "^2.0.0" + } + }, + "csstype": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", + "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" + }, + "damerau-levenshtein": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz", + "integrity": "sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==", + "dev": true + }, + "data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==" + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "domain-browser": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.19.0.tgz", + "integrity": "sha512-fRA+BaAWOR/yr/t7T9E9GJztHPeFjj8U35ajyAjCDtAAnTn1Rc1f6W6VGPJrO1tkQv9zWu+JRof7z6oQtiYVFQ==" + }, + "electron-to-chromium": { + "version": "1.3.759", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.759.tgz", + "integrity": "sha512-nM76xH0t2FBH5iMEZDVc3S/qbdKjGH7TThezxC8k1Q7w7WHvIAyJh8lAe2UamGfdRqBTjHfPDn82LJ0ksCiB9g==" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", + "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.10.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "eslint": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.29.0.tgz", + "integrity": "sha512-82G/JToB9qIy/ArBzIWG9xvvwL3R86AlCjtGw+A29OMZDqhTybz/MByORSukGxeI+YPCR4coYyITKk8BFH9nDA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + } + } + }, + "eslint-config-next": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-11.0.1.tgz", + "integrity": "sha512-yy63K4Bmy8amE6VMb26CZK6G99cfVX3JaMTvuvmq/LL8/b8vKHcauUZREBTAQ+2DrIvlH4YrFXrkQ1vpYDL9Eg==", + "dev": true, + "requires": { + "@next/eslint-plugin-next": "11.0.1", + "@rushstack/eslint-patch": "^1.0.6", + "@typescript-eslint/parser": "^4.20.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-import-resolver-typescript": "^2.4.0", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-jsx-a11y": "^6.4.1", + "eslint-plugin-react": "^7.23.1", + "eslint-plugin-react-hooks": "^4.2.0" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-import-resolver-typescript": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz", + "integrity": "sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + } + }, + "eslint-module-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", + "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.23.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", + "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.1", + "find-up": "^2.0.0", + "has": "^1.0.3", + "is-core-module": "^2.4.0", + "minimatch": "^3.0.4", + "object.values": "^1.1.3", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz", + "integrity": "sha512-0rGPJBbwHoGNPU73/QCLP/vveMlM1b1Z9PponxO87jfr6tuH5ligXbDT6nHSSzBC8ovX2Z+BQu7Bk5D/Xgq9zg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.11.2", + "aria-query": "^4.2.2", + "array-includes": "^3.1.1", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.0.2", + "axobject-query": "^2.2.0", + "damerau-levenshtein": "^1.0.6", + "emoji-regex": "^9.0.0", + "has": "^1.0.3", + "jsx-ast-utils": "^3.1.0", + "language-tags": "^1.0.5" + } + }, + "eslint-plugin-react": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz", + "integrity": "sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q==", + "dev": true, + "requires": { + "array-includes": "^3.1.3", + "array.prototype.flatmap": "^1.2.4", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.0.4", + "object.entries": "^1.1.4", + "object.fromentries": "^2.0.4", + "object.values": "^1.1.4", + "prop-types": "^15.7.2", + "resolve": "^2.0.0-next.3", + "string.prototype.matchall": "^4.0.5" + }, + "dependencies": { + "resolve": { + "version": "2.0.0-next.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", + "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz", + "integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.6.tgz", + "integrity": "sha512-GnLuqj/pvQ7pX8/L4J84nijv6sAnlwvSDpMkJi9i7nPmPxGtRPkBSStfvDW5l6nMdX9VWe+pkKWFTgD+vF2QSQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-cache-dir": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz", + "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-orientation": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-orientation/-/get-orientation-1.1.2.tgz", + "integrity": "sha512-/pViTfifW+gBbh/RnlFYHINvELT9Znt+SYyDKAUL6uV6By019AK/s+i9XP4jSwq7lwP38Fd8HVeTxym3+hkwmQ==", + "requires": { + "stream-parser": "^0.3.1" + } + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "globals": { + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", + "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "image-size": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.0.tgz", + "integrity": "sha512-JLJ6OwBfO1KcA+TvJT+v8gbE6iWbj24LyDNFgFEN0lzegn6cC6a/p3NIDaepMsJjQjlUWqIC7wJv8lBFxPNjcw==", + "requires": { + "queue": "6.0.2" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "is-arguments": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", + "integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==", + "requires": { + "call-bind": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-callable": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==" + }, + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-generator-function": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.9.tgz", + "integrity": "sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A==" + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==" + }, + "is-regex": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "requires": { + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + } + }, + "is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==" + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz", + "integrity": "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==", + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.0-next.2", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "jest-worker": { + "version": "27.0.0-next.5", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.0.0-next.5.tgz", + "integrity": "sha512-mk0umAQ5lT+CaOJ+Qp01N6kz48sJG2kr2n1rX0koqKf6FIygQV0qLOdN9SCYID4IVeSigDOcPeGLozdMLYfb5g==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "jsx-ast-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz", + "integrity": "sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==", + "dev": true, + "requires": { + "array-includes": "^3.1.2", + "object.assign": "^4.1.2" + } + }, + "language-subtag-registry": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz", + "integrity": "sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==", + "dev": true + }, + "language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=", + "dev": true, + "requires": { + "language-subtag-registry": "~0.3.2" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" + }, + "native-url": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/native-url/-/native-url-0.3.4.tgz", + "integrity": "sha512-6iM8R99ze45ivyH8vybJ7X0yekIcPf5GgLV5K0ENCbmRcaRIDoj37BC8iLEmaaBfqqb8enuZ5p0uhY+lVAbAcA==", + "requires": { + "querystring": "^0.2.0" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "next": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/next/-/next-11.0.1.tgz", + "integrity": "sha512-yR7be7asNbvpVNpi6xxEg28wZ7Gqmj1nOt0sABH9qORmF3+pms2KZ7Cng33oK5nqPIzEEFJD0pp2PCe3/ueMIg==", + "requires": { + "@babel/runtime": "7.12.5", + "@hapi/accept": "5.0.2", + "@next/env": "11.0.1", + "@next/polyfill-module": "11.0.1", + "@next/react-dev-overlay": "11.0.1", + "@next/react-refresh-utils": "11.0.1", + "assert": "2.0.0", + "ast-types": "0.13.2", + "browserify-zlib": "0.2.0", + "browserslist": "4.16.6", + "buffer": "5.6.0", + "caniuse-lite": "^1.0.30001228", + "chalk": "2.4.2", + "chokidar": "3.5.1", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "cssnano-simple": "2.0.0", + "domain-browser": "4.19.0", + "encoding": "0.1.13", + "etag": "1.8.1", + "find-cache-dir": "3.3.1", + "get-orientation": "1.1.2", + "https-browserify": "1.0.0", + "image-size": "1.0.0", + "jest-worker": "27.0.0-next.5", + "native-url": "0.3.4", + "node-fetch": "2.6.1", + "node-html-parser": "1.4.9", + "node-libs-browser": "^2.2.1", + "os-browserify": "0.3.0", + "p-limit": "3.1.0", + "path-browserify": "1.0.1", + "pnp-webpack-plugin": "1.6.4", + "postcss": "8.2.13", + "process": "0.11.10", + "prop-types": "15.7.2", + "querystring-es3": "0.2.1", + "raw-body": "2.4.1", + "react-is": "17.0.2", + "react-refresh": "0.8.3", + "stream-browserify": "3.0.0", + "stream-http": "3.1.1", + "string_decoder": "1.3.0", + "styled-jsx": "3.3.2", + "timers-browserify": "2.0.12", + "tty-browserify": "0.0.1", + "use-subscription": "1.5.1", + "util": "0.12.3", + "vm-browserify": "1.1.2", + "watchpack": "2.1.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } + } + }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, + "node-html-parser": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-1.4.9.tgz", + "integrity": "sha512-UVcirFD1Bn0O+TSmloHeHqZZCxHjvtIeGdVdGMhyZ8/PWlEiZaZ5iJzR189yKZr8p0FXN58BUeC7RHRkf/KYGw==", + "requires": { + "he": "1.2.0" + } + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + } + } + }, + "node-releases": { + "version": "1.1.73", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.73.tgz", + "integrity": "sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg==" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.3.tgz", + "integrity": "sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==" + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.4.tgz", + "integrity": "sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + } + }, + "object.fromentries": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz", + "integrity": "sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.2", + "has": "^1.0.3" + } + }, + "object.values": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.4.tgz", + "integrity": "sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + }, + "dependencies": { + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + } + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + } + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==" + }, + "pnp-webpack-plugin": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", + "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==", + "requires": { + "ts-pnp": "^1.1.6" + } + }, + "postcss": { + "version": "8.2.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.13.tgz", + "integrity": "sha512-FCE5xLH+hjbzRdpbRb1IMCvPv9yZx2QnDarBEYSN0N0HYk+TcXsEhwdFcFb+SRWOKzKGErhIEbBK2ogyLdTtfQ==", + "requires": { + "colorette": "^1.2.2", + "nanoid": "^3.1.22", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + }, + "dependencies": { + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "querystring": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", + "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "requires": { + "inherits": "~2.0.3" + } + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "raw-body": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", + "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.3", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "react-dom": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", + "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "scheduler": "^0.20.2" + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "react-refresh": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", + "integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "dependencies": { + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + } + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "regexp.prototype.flags": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz", + "integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "requires": { + "whatwg-url": "^7.0.0" + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz", + "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "requires": { + "type-fest": "^0.7.1" + }, + "dependencies": { + "type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==" + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "requires": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "stream-http": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.1.1.tgz", + "integrity": "sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "stream-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz", + "integrity": "sha1-FhhUhpRCACGhGC/wrxkRwSl2F3M=", + "requires": { + "debug": "2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "string-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz", + "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=" + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + } + } + }, + "string.prototype.matchall": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz", + "integrity": "sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.2", + "get-intrinsic": "^1.1.1", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.3.1", + "side-channel": "^1.0.4" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "styled-jsx": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-3.3.2.tgz", + "integrity": "sha512-daAkGd5mqhbBhLd6jYAjYBa9LpxYCzsgo/f6qzPdFxVB8yoGbhxvzQgkC0pfmCVvW3JuAEBn0UzFLBfkHVZG1g==", + "requires": { + "@babel/types": "7.8.3", + "babel-plugin-syntax-jsx": "6.18.0", + "convert-source-map": "1.7.0", + "loader-utils": "1.2.3", + "source-map": "0.7.3", + "string-hash": "1.1.3", + "stylis": "3.5.4", + "stylis-rule-sheet": "0.0.10" + }, + "dependencies": { + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + } + } + }, + "styled-system": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/styled-system/-/styled-system-5.1.5.tgz", + "integrity": "sha512-7VoD0o2R3RKzOzPK0jYrVnS8iJdfkKsQJNiLRDjikOpQVqQHns/DXWaPZOH4tIKkhAT7I6wIsy9FWTWh2X3q+A==", + "requires": { + "@styled-system/background": "^5.1.2", + "@styled-system/border": "^5.1.5", + "@styled-system/color": "^5.1.2", + "@styled-system/core": "^5.1.2", + "@styled-system/flexbox": "^5.1.2", + "@styled-system/grid": "^5.1.2", + "@styled-system/layout": "^5.1.2", + "@styled-system/position": "^5.1.2", + "@styled-system/shadow": "^5.1.2", + "@styled-system/space": "^5.1.2", + "@styled-system/typography": "^5.1.2", + "@styled-system/variant": "^5.1.5", + "object-assign": "^4.1.1" + } + }, + "stylis": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz", + "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==" + }, + "stylis-rule-sheet": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz", + "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", + "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "theme-ui": { + "version": "0.6.0-alpha.8", + "resolved": "https://registry.npmjs.org/theme-ui/-/theme-ui-0.6.0-alpha.8.tgz", + "integrity": "sha512-I7d1OOSVR+byk3he8m++eS3TeMUXPtY/gQwdUPUhvvvJRjW6qMyBy+V0KH98kpYypevoR3ir+D2xZLe9CUpRZA==", + "requires": { + "@theme-ui/color-modes": "0.6.0-alpha.8", + "@theme-ui/components": "0.6.0-alpha.8", + "@theme-ui/core": "0.6.0-alpha.8", + "@theme-ui/css": "0.6.0-alpha.8", + "@theme-ui/mdx": "0.6.0-alpha.8", + "@theme-ui/theme-provider": "0.6.0-alpha.8" + } + }, + "theme-ui-preset-geist": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/theme-ui-preset-geist/-/theme-ui-preset-geist-0.0.2.tgz", + "integrity": "sha512-G2CK8gDM2XOSEZq4RipA/0oiVs6hUl87EbzdkdCY96fU4VRiAQLyTJxdXN/wjOddr4uyPkzGqX/iNZReJ3KBuQ==" + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "requires": { + "punycode": "^2.1.0" + } + }, + "ts-pnp": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz", + "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==" + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==" + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + } + } + }, + "use-subscription": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.5.1.tgz", + "integrity": "sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==", + "requires": { + "object-assign": "^4.1.1" + } + }, + "util": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", + "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "watchpack": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.1.1.tgz", + "integrity": "sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==", + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-typed-array": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "requires": { + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + } + } +} diff --git a/examples/nextjs-fusd-provider/package.json b/examples/nextjs-fusd-provider/package.json new file mode 100644 index 00000000..fbe13029 --- /dev/null +++ b/examples/nextjs-fusd-provider/package.json @@ -0,0 +1,22 @@ +{ + "name": "nextjs-fusd-provider", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev -p 3001", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@geist-ui/react": "^2.1.5", + "@geist-ui/react-icons": "^1.0.1", + "next": "11.0.1", + "react": "17.0.2", + "react-dom": "17.0.2" + }, + "devDependencies": { + "eslint": "7.29.0", + "eslint-config-next": "11.0.1" + } +} diff --git a/examples/nextjs-fusd-provider/pages/_app.js b/examples/nextjs-fusd-provider/pages/_app.js new file mode 100644 index 00000000..dffc8bf7 --- /dev/null +++ b/examples/nextjs-fusd-provider/pages/_app.js @@ -0,0 +1,15 @@ +import React from 'react' +import NextApp from 'next/app' +import { GeistProvider, CssBaseline } from '@geist-ui/react' + +export default class App extends NextApp { + render() { + const { Component, pageProps } = this.props + return ( + + + + + ) + } +} diff --git a/examples/nextjs-fusd-provider/pages/api/accounts.js b/examples/nextjs-fusd-provider/pages/api/accounts.js new file mode 100644 index 00000000..95634a02 --- /dev/null +++ b/examples/nextjs-fusd-provider/pages/api/accounts.js @@ -0,0 +1,46 @@ +import WalletApiClient from "../../lib/walletApi"; +import {adminAddress, baseUrl, fusdTokenName} from "../../lib/config" + +const walletApi = new WalletApiClient(baseUrl) + +export default async function handler(req, res) { + if (req.method === "GET") { + return get(req, res) + } + + if (req.method === "POST") { + return post(req, res) + } +} + +async function get(req, res) { + const accounts = await walletApi.getAccounts() + + const result = await Promise.all(accounts.map(async account => ({ + address: account.address, + balance: await getFusdBalance(account.address), + isAdmin: account.address === adminAddress, + }))) + + res.status(200).json(result) +} + +const emptyBalance = "0.00000000" + +async function post(req, res) { + const address = await walletApi.createAccount() + + await walletApi.initFungibleToken(address, fusdTokenName) + + const result = { + address, + balance: emptyBalance + } + + res.status(201).json(result) +} + +async function getFusdBalance(address) { + const result = await walletApi.getFungibleToken(address, fusdTokenName) + return result.balance +} diff --git a/examples/nextjs-fusd-provider/pages/api/purchase.js b/examples/nextjs-fusd-provider/pages/api/purchase.js new file mode 100644 index 00000000..2666c49d --- /dev/null +++ b/examples/nextjs-fusd-provider/pages/api/purchase.js @@ -0,0 +1,33 @@ +import WalletApiClient from "../../lib/walletApi"; +import {adminAddress, baseUrl, fusdTokenName} from "../../lib/config" + +const walletApi = new WalletApiClient(baseUrl) + +export default async function handler(req, res) { + if (req.method === "POST") { + return post(req, res) + } + + res.status(405).send() +} + +async function post(req, res) { + const { recipient, amount } = req.body + + await walletApi.createFungibleTokenWithdrawal( + adminAddress, + recipient, + fusdTokenName, + sanitizeAmount(amount), + ) + + res.status(200).json({ recipient, amount }) +} + +function sanitizeAmount(amount) { + if (amount.includes(".")) { + return amount + } + + return amount + ".0" +} diff --git a/examples/nextjs-fusd-provider/pages/index.js b/examples/nextjs-fusd-provider/pages/index.js new file mode 100644 index 00000000..7574b139 --- /dev/null +++ b/examples/nextjs-fusd-provider/pages/index.js @@ -0,0 +1,53 @@ +import Head from 'next/head' +import { useEffect, useState } from 'react' +import { Text, Divider } from "@geist-ui/react" +import PurchaseForm from '../components/PurchaseForm' +import AccountList from '../components/AccountList' +import { listAccounts } from '../lib/actions' + +export default function Home() { + const [accounts, setAccounts] = useState([]) + + const syncAccounts = async () => { + const accounts = await listAccounts() + setAccounts(accounts) + } + + const addAccount = account => { + setAccounts([ + account, + ...accounts, + ]) + } + + useEffect(() => { syncAccounts() }, []) + + return ( +
+ + FUSD Wallet API Demo + + +
+ Purchase FUSD + + This example application demonstrates how to use the Flow Wallet API + to distribute the FUSD stablecoin. + +
+ + + + + + +
+ ) +} From 818ab9880b2a90c982e7319cca26fb3e76c3ac26 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 22:38:02 -0700 Subject: [PATCH 071/112] Update flow.json --- flow.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/flow.json b/flow.json index 72a8cb0e..88a6c1ae 100644 --- a/flow.json +++ b/flow.json @@ -40,10 +40,6 @@ "host": "127.0.0.1:3569", "chain": "flow-emulator" }, - "emulator-docker": { - "host": "emulator:3569", - "chain": "flow-emulator" - }, "mainnet": { "host": "access.mainnet.nodes.onflow.org:9000", "chain": "flow-mainnet" From 896c612fd18f87be4c4598220218b91afab30f51 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 22:38:09 -0700 Subject: [PATCH 072/112] Update openapi.yml --- openapi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openapi.yml b/openapi.yml index 6a8aff36..1f6ba810 100644 --- a/openapi.yml +++ b/openapi.yml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - title: Flow Custodial Wallet API + title: Flow Wallet API version: 0.0.1 servers: - url: /v1 From dce4331834ce647f4c55b3e533162148b7eceee6 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 22:38:12 -0700 Subject: [PATCH 073/112] Create docker-compose.yml --- docker-compose.yml | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..c89c6514 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +version: "3.3" +services: + wallet: + image: gcr.io/flow-container-registry/wallet-api:v0.0.4 + ports: + - "3000:3000" + environment: + - DATABASE_TYPE=psql + - DATABASE_DSN=postgresql://wallet:wallet@db:5432/wallet + - ACCESS_API_HOST=emulator:3569 + - CHAIN_ID=flow-emulator + - ADMIN_ADDRESS=${ADMIN_ADDRESS} + - ADMIN_PRIVATE_KEY=${ADMIN_PRIVATE_KEY} + - DEFAULT_KEY_TYPE=local + - ENCRYPTION_KEY=${ENCRYPTION_KEY} + - ENABLED_TOKENS=FUSD:${ADMIN_ADDRESS}:fusd,FlowToken:0x0ae53cb6e3f42a79:flow + depends_on: + - db + - emulator + + emulator: + image: gcr.io/flow-container-registry/emulator:latest + ports: + - "3569:3569" + - "8080:8080" + environment: + - FLOW_SERVICEPRIVATEKEY=${ADMIN_PRIVATE_KEY} + - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 + - FLOW_SERVICEKEYHASHALGO=SHA3_256 + - FLOW_VERBOSE=true + + db: + image: postgres:13-alpine + ports: + - "5432:5432" + environment: + - POSTGRES_DB=wallet + - POSTGRES_USER=wallet + - POSTGRES_PASSWORD=wallet From a9433cc7b3f3ad0321d1a4e168a216b911cad55c Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 22:38:23 -0700 Subject: [PATCH 074/112] Update .env.example --- .env.example | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 137aa80d..343c4be7 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,16 @@ -ACCESS_API_HOST=http://localhost:8080 - -DATABASE_URL="postgresql://wallet:wallet@localhost:5432/wallet?schema=public" - ADMIN_ADDRESS=0xf8d6e0586b0a20c7 ADMIN_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068 -ADMIN_KEY_TYPE="in-memory" +DATABASE_TYPE=psql +DATABASE_DSN=postgresql://wallet:wallet@localhost:5432/wallet + +ACCESS_API_HOST=emulator:3569 +CHAIN_ID=flow-emulator + +# When set to "local", private keys are generated by the API +# and stored as encrypted text in the database. +DEFAULT_KEY_TYPE=local -USER_ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8eaf6ec53b0143d32a02d84c54aa128bf2 +# This symmetrical key is used to encrypt private keys +# that are stored in the database. +ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8e From 595857b4820180f57d4b79e8ff23c033bdfe4618 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 22:38:31 -0700 Subject: [PATCH 075/112] Delete old files --- .dockerignore | 4 - .env.emulator.example | 6 - .env.testnet.example | 6 - .eslintrc.js | 38 - .prettierrc | 6 - Dockerfile | 16 - docker-compose.dev.yml | 21 - docker-compose.emulator.yml | 36 - docker-compose.testnet.yml | 36 - package-lock.json | 3841 ----------------- package.json | 61 - src/admin.ts | 19 - src/app.ts | 64 - src/config.ts | 78 - src/controllers/accounts.ts | 42 - src/controllers/fungibleTokens.ts | 87 - src/controllers/transactions.ts | 59 - src/database/accounts.ts | 50 - src/database/getLeastRecentAdminSignerKey.ts | 20 - .../migration.sql | 7 - .../migration.sql | 17 - .../migration.sql | 8 - .../migration.sql | 15 - src/database/migrations/migration_lock.toml | 3 - src/database/models.ts | 3 - src/database/schema.prisma | 45 - src/database/syncAdminSignerKeys.ts | 50 - src/errors/ApiError.ts | 11 - src/errors/InvalidFungibleTokenError.ts | 12 - src/errors/NotFoundError.ts | 12 - src/errors/catchAsync.ts | 11 - src/index.ts | 35 - src/lib/crypto/elliptic.ts | 142 - src/lib/crypto/hash.ts | 49 - src/lib/crypto/index.ts | 10 - src/lib/crypto/publicKey.ts | 4 - src/lib/crypto/sign.ts | 13 - src/lib/flow/createAccount.ts | 91 - src/lib/flow/getAuthorization.ts | 27 - src/lib/flow/index.ts | 24 - src/lib/flow/sendTransaction.ts | 53 - src/lib/fungibleTokens/index.ts | 54 - .../fungibleTokens/templates/transferFLOW.ts | 28 - .../fungibleTokens/templates/transferFUSD.ts | 28 - src/lib/keys/encryption.ts | 29 - src/lib/keys/inMemory.ts | 82 - src/lib/keys/index.ts | 20 - src/logger.ts | 29 - src/middleware/errors.ts | 38 - src/middleware/morgan.ts | 19 - .../v1/accounts/fungibleTokens/index.ts | 28 - .../v1/accounts/fungibleTokens/withdrawals.ts | 23 - src/routes/v1/accounts/index.ts | 36 - src/routes/v1/accounts/transactions.ts | 21 - src/routes/v1/index.ts | 27 - src/services/accounts.ts | 94 - src/services/fungibleTokens.ts | 50 - src/services/service.ts | 9 - src/services/transactions.ts | 33 - tsconfig.json | 13 - 60 files changed, 5793 deletions(-) delete mode 100644 .dockerignore delete mode 100644 .env.emulator.example delete mode 100644 .env.testnet.example delete mode 100644 .eslintrc.js delete mode 100644 .prettierrc delete mode 100644 Dockerfile delete mode 100644 docker-compose.dev.yml delete mode 100644 docker-compose.emulator.yml delete mode 100644 docker-compose.testnet.yml delete mode 100644 package-lock.json delete mode 100644 package.json delete mode 100644 src/admin.ts delete mode 100644 src/app.ts delete mode 100644 src/config.ts delete mode 100644 src/controllers/accounts.ts delete mode 100644 src/controllers/fungibleTokens.ts delete mode 100644 src/controllers/transactions.ts delete mode 100644 src/database/accounts.ts delete mode 100644 src/database/getLeastRecentAdminSignerKey.ts delete mode 100644 src/database/migrations/20210412042610_create_account_keys/migration.sql delete mode 100644 src/database/migrations/20210421075756_admin_signer_keys/migration.sql delete mode 100644 src/database/migrations/20210425210753_create_accounts/migration.sql delete mode 100644 src/database/migrations/20210426021914_create_account_keys/migration.sql delete mode 100644 src/database/migrations/migration_lock.toml delete mode 100644 src/database/models.ts delete mode 100644 src/database/schema.prisma delete mode 100644 src/database/syncAdminSignerKeys.ts delete mode 100644 src/errors/ApiError.ts delete mode 100644 src/errors/InvalidFungibleTokenError.ts delete mode 100644 src/errors/NotFoundError.ts delete mode 100644 src/errors/catchAsync.ts delete mode 100644 src/index.ts delete mode 100644 src/lib/crypto/elliptic.ts delete mode 100644 src/lib/crypto/hash.ts delete mode 100644 src/lib/crypto/index.ts delete mode 100644 src/lib/crypto/publicKey.ts delete mode 100644 src/lib/crypto/sign.ts delete mode 100644 src/lib/flow/createAccount.ts delete mode 100644 src/lib/flow/getAuthorization.ts delete mode 100644 src/lib/flow/index.ts delete mode 100644 src/lib/flow/sendTransaction.ts delete mode 100644 src/lib/fungibleTokens/index.ts delete mode 100644 src/lib/fungibleTokens/templates/transferFLOW.ts delete mode 100644 src/lib/fungibleTokens/templates/transferFUSD.ts delete mode 100644 src/lib/keys/encryption.ts delete mode 100644 src/lib/keys/inMemory.ts delete mode 100644 src/lib/keys/index.ts delete mode 100644 src/logger.ts delete mode 100644 src/middleware/errors.ts delete mode 100644 src/middleware/morgan.ts delete mode 100644 src/routes/v1/accounts/fungibleTokens/index.ts delete mode 100644 src/routes/v1/accounts/fungibleTokens/withdrawals.ts delete mode 100644 src/routes/v1/accounts/index.ts delete mode 100644 src/routes/v1/accounts/transactions.ts delete mode 100644 src/routes/v1/index.ts delete mode 100644 src/services/accounts.ts delete mode 100644 src/services/fungibleTokens.ts delete mode 100644 src/services/service.ts delete mode 100644 src/services/transactions.ts delete mode 100644 tsconfig.json diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 25ab276c..00000000 --- a/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -npm-debug.log -dist -.env diff --git a/.env.emulator.example b/.env.emulator.example deleted file mode 100644 index 748d0cef..00000000 --- a/.env.emulator.example +++ /dev/null @@ -1,6 +0,0 @@ -ADMIN_ADDRESS=0xf8d6e0586b0a20c7 -ADMIN_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068 - -ADMIN_KEY_TYPE="in-memory" - -USER_ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8eaf6ec53b0143d32a02d84c54aa128bf2 diff --git a/.env.testnet.example b/.env.testnet.example deleted file mode 100644 index 1df338af..00000000 --- a/.env.testnet.example +++ /dev/null @@ -1,6 +0,0 @@ -ADMIN_ADDRESS= -ADMIN_PRIVATE_KEY= - -ADMIN_KEY_TYPE="in-memory" - -USER_ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8eaf6ec53b0143d32a02d84c54aa128bf2 diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index cb417ac2..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,38 +0,0 @@ -/* eslint-env node */ -module.exports = { - root: true, - parser: "@typescript-eslint/parser", - plugins: ["@typescript-eslint", "prettier"], - extends: [ - "eslint:recommended", - "plugin:prettier/recommended", - "plugin:@typescript-eslint/recommended", - "plugin:import/errors", - "plugin:import/warnings", - "plugin:import/typescript", - ], - rules: { - "import/no-unresolved": 0, - "import/order": [ - "error", - { - groups: [ - "builtin", - "external", - "internal", - "parent", - "sibling", - "index", - ], - pathGroups: [ - { - pattern: "src/**", - group: "internal", - }, - ], - pathGroupsExcludedImportTypes: ["builtin"], - "newlines-between": "always-and-inside-groups", - }, - ], - }, -} diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 7082e437..00000000 --- a/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "semi": false, - "trailingComma": "es5", - "bracketSpacing": false, - "arrowParens": "avoid" -} diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 966c835e..00000000 --- a/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM node:14-alpine3.13 - -WORKDIR /app - -COPY package.json . -COPY src/database/schema.prisma src/database/schema.prisma - -RUN npm install - -ADD . /app - -RUN npm run build - -CMD [ "npm", "start" ] - -EXPOSE 3000 diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml deleted file mode 100644 index d135a18f..00000000 --- a/docker-compose.dev.yml +++ /dev/null @@ -1,21 +0,0 @@ -version: "3" -services: - db: - image: postgres - ports: - - "5432:5432" - environment: - - POSTGRES_DB=wallet - - POSTGRES_USER=wallet - - POSTGRES_PASSWORD=wallet - - emulator: - image: gcr.io/flow-container-registry/emulator:v0.16.1 - ports: - - "8080:8080" - - "3569:3569" - environment: - - FLOW_SERVICEPRIVATEKEY=${ADMIN_PRIVATE_KEY} - - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 - - FLOW_SERVICEKEYHASHALGO=SHA3_256 - - FLOW_VERBOSE=true diff --git a/docker-compose.emulator.yml b/docker-compose.emulator.yml deleted file mode 100644 index 3c85daca..00000000 --- a/docker-compose.emulator.yml +++ /dev/null @@ -1,36 +0,0 @@ -version: "3" -services: - web: - image: gcr.io/flow-container-registry/flow-wallet-api-demo:latest - ports: - - "3000:3000" - depends_on: - - db - - emulator - environment: - - ACCESS_API_HOST=http://emulator:8080 - - DATABASE_URL=postgresql://wallet:wallet@db:5432/wallet?schema=public - - ADMIN_ADDRESS=${ADMIN_ADDRESS} - - ADMIN_KEY_TYPE=${ADMIN_KEY_TYPE} - - ADMIN_PRIVATE_KEY=${ADMIN_PRIVATE_KEY} - - USER_ENCRYPTION_KEY=${USER_ENCRYPTION_KEY} - - db: - image: postgres - ports: - - "5432:5432" - environment: - - POSTGRES_DB=wallet - - POSTGRES_USER=wallet - - POSTGRES_PASSWORD=wallet - - emulator: - image: gcr.io/flow-container-registry/emulator:v0.16.1 - ports: - - "8080:8080" - - "3569:3569" - environment: - - FLOW_SERVICEPRIVATEKEY=${ADMIN_PRIVATE_KEY} - - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 - - FLOW_SERVICEKEYHASHALGO=SHA3_256 - - FLOW_VERBOSE=true diff --git a/docker-compose.testnet.yml b/docker-compose.testnet.yml deleted file mode 100644 index 618e314b..00000000 --- a/docker-compose.testnet.yml +++ /dev/null @@ -1,36 +0,0 @@ -version: "3" -services: - web: - image: gcr.io/flow-container-registry/flow-wallet-api-demo:latest - ports: - - "3000:3000" - depends_on: - - db - environment: - - ACCESS_API_HOST=https://access-testnet.onflow.org - - CHAIN=testnet - - DATABASE_URL=postgresql://wallet:wallet@db:5432/wallet?schema=public - - ADMIN_ADDRESS=${ADMIN_ADDRESS} - - ADMIN_KEY_TYPE=${ADMIN_KEY_TYPE} - - ADMIN_PRIVATE_KEY=${ADMIN_PRIVATE_KEY} - - USER_ENCRYPTION_KEY=${USER_ENCRYPTION_KEY} - - db: - image: postgres - ports: - - "5432:5432" - environment: - - POSTGRES_DB=wallet - - POSTGRES_USER=wallet - - POSTGRES_PASSWORD=wallet - - emulator: - image: gcr.io/flow-container-registry/emulator:v0.16.1 - ports: - - "8080:8080" - - "3569:3569" - environment: - - FLOW_SERVICEPRIVATEKEY=${ADMIN_PRIVATE_KEY} - - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 - - FLOW_SERVICEKEYHASHALGO=SHA3_256 - - FLOW_VERBOSE=true diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 4a439aa3..00000000 --- a/package-lock.json +++ /dev/null @@ -1,3841 +0,0 @@ -{ - "name": "flow-wallet-api-node-demo", - "version": "0.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/generator": { - "version": "7.13.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz", - "integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==", - "dev": true, - "requires": { - "@babel/types": "^7.13.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz", - "integrity": "sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.13.10.tgz", - "integrity": "sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", - "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", - "dev": true - }, - "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/traverse": { - "version": "7.13.15", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", - "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.13.9", - "@babel/helper-function-name": "^7.12.13", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.15", - "@babel/types": "^7.13.14", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.13.14", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", - "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.12.11", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "@dabh/diagnostics": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", - "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", - "requires": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@improbable-eng/grpc-web": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.12.0.tgz", - "integrity": "sha512-uJjgMPngreRTYPBuo6gswMj1gK39Wbqre/RgE0XnSDXJRg6ST7ZhuS53dFE6Vc2CX4jxgl+cO+0B3op8LA4Q0Q==", - "requires": { - "browser-headers": "^0.4.0" - } - }, - "@improbable-eng/grpc-web-node-http-transport": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web-node-http-transport/-/grpc-web-node-http-transport-0.12.0.tgz", - "integrity": "sha512-+Kjz+Dktfz5LKTZA9ZW/Vlww6HF9KaKz4x2mVe1O8CJdOP2WfzC+KY8L6EWMqVLrV4MvdBuQdSgDmvSJz+OGuA==" - }, - "@nodelib/fs.scandir": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", - "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.4", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", - "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", - "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.4", - "fastq": "^1.6.0" - } - }, - "@onflow/config": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@onflow/config/-/config-0.0.2.tgz", - "integrity": "sha512-H/+yrAalzEnMWkubiWsDdWytKSzd+OfRCddTlaRUelxfXhcfw2QWegH9N8EzeKfKXcQ6PLzvu9vQwhFxCZTE8Q==", - "requires": { - "@onflow/util-actor": "0.0.2" - } - }, - "@onflow/decode": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@onflow/decode/-/decode-0.0.10.tgz", - "integrity": "sha512-+eInaMNzMtoA2Fkt27HW/GYJdqt7GjIW0gYDpkK6B5AYoRLj1NZTAIX0wV5BUoLCecMGzS1OVML79FxIwLPowQ==" - }, - "@onflow/encode": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@onflow/encode/-/encode-0.0.8.tgz", - "integrity": "sha512-I7p+/KNzsNCpA+AfIOKRKDnkc7kSW4Nr6Hapamh4ak8SDcNfcO4j8Dx+nTsRwhBuo/R/4XpxMpodC6V7+Vmw0w==", - "requires": { - "@onflow/rlp": "0.0.3" - } - }, - "@onflow/fcl": { - "version": "0.0.67", - "resolved": "https://registry.npmjs.org/@onflow/fcl/-/fcl-0.0.67.tgz", - "integrity": "sha512-A/1YD+eDx6+mjqENR0bvWSPPww99LYDlLM9jzFjxIyju58paHPCMA7Rav9nQVHfxWMCRAnkbK0yPanUPEyeaww==", - "requires": { - "@onflow/config": "0.0.2", - "@onflow/interaction": "0.0.10", - "@onflow/rlp": "0.0.3", - "@onflow/sdk-account": "0.0.8", - "@onflow/sdk-build-arguments": "0.0.0", - "@onflow/sdk-build-authorizations": "0.0.1", - "@onflow/sdk-build-get-account": "0.0.1", - "@onflow/sdk-build-get-block-by-height": "0.0.0", - "@onflow/sdk-build-get-block-by-id": "0.0.0", - "@onflow/sdk-build-get-events": "0.0.0", - "@onflow/sdk-build-get-latest-block": "0.0.0", - "@onflow/sdk-build-invariant": "0.0.0", - "@onflow/sdk-build-limit": "0.0.0", - "@onflow/sdk-build-params": "0.0.0", - "@onflow/sdk-build-payer": "0.0.0", - "@onflow/sdk-build-ping": "0.0.0", - "@onflow/sdk-build-proposer": "0.0.0", - "@onflow/sdk-build-ref": "0.0.0", - "@onflow/sdk-build-script": "0.0.0", - "@onflow/sdk-build-transaction": "0.0.0", - "@onflow/sdk-build-transaction-status": "0.0.0", - "@onflow/sdk-build-validator": "0.0.0", - "@onflow/sdk-decode": "0.0.2", - "@onflow/sdk-latest-block": "0.0.8", - "@onflow/sdk-resolve": "0.0.10", - "@onflow/sdk-send": "0.0.10", - "@onflow/util-actor": "0.0.2", - "@onflow/util-address": "0.0.0", - "@onflow/util-invariant": "0.0.0", - "@onflow/util-template": "0.0.1", - "@onflow/util-uid": "0.0.1" - } - }, - "@onflow/interaction": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@onflow/interaction/-/interaction-0.0.10.tgz", - "integrity": "sha512-3+Nhjn8zZjAXpxH+MuHpl4ZR0s9eQl4aZqmjat2sekDTNNl1dYhaWs+RT5CSxzehGzJ2W+aL7JxFn6exFoA0zA==" - }, - "@onflow/protobuf": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@onflow/protobuf/-/protobuf-0.1.8.tgz", - "integrity": "sha512-Taww31RrpJMr3fkWgF2HR2TcRbAoPbMjwRYt9IlYknyRX5YTzpaU35petdqDRfxwmfY9mRxHbiInujX4aV02Hw==", - "requires": { - "@improbable-eng/grpc-web": "^0.12.0", - "google-protobuf": "^3.11.4" - } - }, - "@onflow/response": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@onflow/response/-/response-0.0.2.tgz", - "integrity": "sha512-k5UD4nv9F6YAkDn0Gs0F6R2Izr6jekX9sGu6aPbTQCsfyv8kbG8GRhnfcugtwbGfEKmYO4Dj91QtWy246V9sUQ==" - }, - "@onflow/rlp": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@onflow/rlp/-/rlp-0.0.3.tgz", - "integrity": "sha512-oAf0VEiMjX8eC6Vd66j1BdGYTHOM6UBaS/sLSScnc7bKX5gICqe2gtEsCeJVE9rUzRk3GD3JqXRnPAW6YFWd/Q==" - }, - "@onflow/sdk-account": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@onflow/sdk-account/-/sdk-account-0.0.8.tgz", - "integrity": "sha512-fa0zJkb7xasvfCWyo1WqZNomAV4vKulhp9br0+E08cwdSJWcH2CrgWz75Q67YFpdnflhP0n3RzrAREcXt6Y54Q==", - "requires": { - "@onflow/sdk-build-get-account": "0.0.1", - "@onflow/sdk-decode": "0.0.2", - "@onflow/sdk-send": "0.0.10" - } - }, - "@onflow/sdk-build-arguments": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-arguments/-/sdk-build-arguments-0.0.0.tgz", - "integrity": "sha512-CAUboKBIIfpVpoZFGIHa7vGtTXuAeE/YRyyDGtdlvQcu+7AqumJ0Sz4MohUY+DyvcSTC9FdNs/d5XicS3EIo2A==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-authorizations": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-authorizations/-/sdk-build-authorizations-0.0.1.tgz", - "integrity": "sha512-N46d9Hv4Jtrb159+HPq9R8RCkao4DnUmbZYIxxM8rkuTUdRluDC52SKS1to9l0WnhJm5+div8E+dSVIg1MBfnw==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-get-account": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-get-account/-/sdk-build-get-account-0.0.1.tgz", - "integrity": "sha512-BOTLsAqhXwwRj9TYDmV40yBKHRWBToFY1WvJKUOI05oZBOBqDb2KB0L5ej3uKpA/XdmIH4HDmRMmxWU01Xy+FA==", - "requires": { - "@onflow/interaction": "0.0.10", - "@onflow/util-address": "0.0.0" - } - }, - "@onflow/sdk-build-get-block-by-height": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-get-block-by-height/-/sdk-build-get-block-by-height-0.0.0.tgz", - "integrity": "sha512-Efvnzj+eWRTp76RHH3z25G0R+3N8b5oK/addfyjsh5hK61Mk9DAN9JgHQdeglTWgNaOJ0dKrzBZ/SJP2Uz9RHA==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-get-block-by-id": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-get-block-by-id/-/sdk-build-get-block-by-id-0.0.0.tgz", - "integrity": "sha512-Zw5Gt9NNVUn9CrSUE+SpJO80HL3yUnFhZZcQHw3WvaTb3hDYpDlGs9ldPjYI+SrNxMJAg3KD5OzFnKHMEnmQLw==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-get-events": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-get-events/-/sdk-build-get-events-0.0.0.tgz", - "integrity": "sha512-xABLsjs2BftKezH2mc34k6GVIXgmUjdv5bY2MFQI3btkVXn/vVbg/WEB/JNOmXl4dIvZXFMD9ANncS6n0PvcSw==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-get-latest-block": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-get-latest-block/-/sdk-build-get-latest-block-0.0.0.tgz", - "integrity": "sha512-RnUkH3gqStboZ6pQXTeGq7CrMwaqD2p+z4rHqBlcbLF8pAiJQgFLDigQ2vUxrwkMJmnNqi3lJfMzX58dgDa2JA==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-invariant": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-invariant/-/sdk-build-invariant-0.0.0.tgz", - "integrity": "sha512-eB1Jf6NZQ7wKQMudc2a6T03/SFH3OyVm67mKzU+gWXOUBZwh2GZfaW8ujchrYwy0tFyyqzTP1nqEFws8cQyvJg==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-limit": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-limit/-/sdk-build-limit-0.0.0.tgz", - "integrity": "sha512-HDl6UD7M6QgkII8tMlVgYeHO8WxkjzeQq/d16ZBeux5IGA0jd3mdV0sZSB01n6dio82Ap+vW79uIgM3e1urs5A==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-params": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-params/-/sdk-build-params-0.0.0.tgz", - "integrity": "sha512-Y6TT0MbkzsemAOuwDoCkhUF286krqndGS9bzQ+ipaykGz6ibKDguXGzPCq+eRLA8+uVMsEWcUJ/UE6YxzuwUeg==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-payer": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-payer/-/sdk-build-payer-0.0.0.tgz", - "integrity": "sha512-siVQV8XE1l94Bqotx2thB59rTWkfTBuvyVOY7oj1hibYMBcyg+xQozZRJEZEV8H5aQOXkSmP7JXlutfqqFqt0Q==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-ping": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-ping/-/sdk-build-ping-0.0.0.tgz", - "integrity": "sha512-NRcg5drVzBkviK339t3KQL6yIN5JuO9xsTlALgAgPduABv1RfxdNCRSxWtxfqozNSz8c11ki+KTdZHJIwmBZAg==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-proposer": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-proposer/-/sdk-build-proposer-0.0.0.tgz", - "integrity": "sha512-voEjh9ioG/R4EKFx9QWOEplhYOfB5ddwuoYQq5/0R6hLKnylnnTFvnphtWIMlcwhOJWM17ViKyozvucS0MhlyQ==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-ref": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-ref/-/sdk-build-ref-0.0.0.tgz", - "integrity": "sha512-+5bPNaOWnNeB+6vSBgBW9ojg6pkK8nvPwqMWvJQ9nVKPhsCXDO8Y28PtFjX0/vWbt+lRR4vfgdbG8fGfLV7Nzg==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-script": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-script/-/sdk-build-script-0.0.0.tgz", - "integrity": "sha512-r7CG+Q9svfTUykH61t4epo4D290NYvKlryQ+Csjot5FGTsvssXEwELRqiUOkONW7KOH+GpDG7LRQlikTgnnREQ==", - "requires": { - "@onflow/interaction": "0.0.10", - "@onflow/util-template": "0.0.1" - } - }, - "@onflow/sdk-build-transaction": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-transaction/-/sdk-build-transaction-0.0.0.tgz", - "integrity": "sha512-19S6QB2LcFxOm5o91A8Q4Pcfdrk1bdvnasyNdG1Cel/SwKLPUoHmCoelFCHbZkIYlwnEdLQZm11plQIfo70o6w==", - "requires": { - "@onflow/interaction": "0.0.10", - "@onflow/util-template": "0.0.1" - } - }, - "@onflow/sdk-build-transaction-status": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-transaction-status/-/sdk-build-transaction-status-0.0.0.tgz", - "integrity": "sha512-hj1AJgJsK/PVmY5s7gzsqoekzswFudI7VWj1oee99NKqM+BRPGH7HBTuEIYP1PZZ7XCW1KsDy54QbqPfMmeIUQ==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-build-validator": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-build-validator/-/sdk-build-validator-0.0.0.tgz", - "integrity": "sha512-ZgYappRaqmDXKOP9By1ZODa3iQu6xlHN0I53UZ5HgLtQ8MXsVCvpsNR7Zj9rYeWdlfJDglQeWORmhluw7zssSw==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-decode": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@onflow/sdk-decode/-/sdk-decode-0.0.2.tgz", - "integrity": "sha512-qqz29UaGEVVmfols5VRUODL3m73rURs/VAEDbCFjlrGn2kPRCUOcYofazge88jYY/9rwYuDvueDEVDg4iLV2+A==", - "requires": { - "@onflow/config": "0.0.2", - "@onflow/decode": "0.0.10" - } - }, - "@onflow/sdk-latest-block": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/@onflow/sdk-latest-block/-/sdk-latest-block-0.0.8.tgz", - "integrity": "sha512-zBuETIqF+1KIi9C4BOUp1zKmrUdLHdYHSnVeV5rgS+90GNy5f3nH3xsWXKg74KRs3ZD0QgoJRkf3zTODuHpjmw==", - "requires": { - "@onflow/sdk-build-get-latest-block": "0.0.0", - "@onflow/sdk-decode": "0.0.2", - "@onflow/sdk-send": "0.0.10" - } - }, - "@onflow/sdk-resolve": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve/-/sdk-resolve-0.0.10.tgz", - "integrity": "sha512-SnlAZfenZWoWasLSQepnMljTEe7T9GWYUwR1VgF0/tXxnjDtnbhH9hH2WDp1Ioyev8DA7V3Jqd4qygj+fOsnMg==", - "requires": { - "@onflow/interaction": "0.0.10", - "@onflow/sdk-resolve-accounts": "0.0.0", - "@onflow/sdk-resolve-arguments": "0.0.1", - "@onflow/sdk-resolve-cadence": "0.0.1", - "@onflow/sdk-resolve-ref-block-id": "0.0.7", - "@onflow/sdk-resolve-signatures": "0.0.5", - "@onflow/sdk-resolve-validators": "0.0.0" - } - }, - "@onflow/sdk-resolve-accounts": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-accounts/-/sdk-resolve-accounts-0.0.0.tgz", - "integrity": "sha512-IrYWNC8Am/b8dznsbuh+/LLTkaXrkjsFMnRDc7ANFC7wZoukWv3Wwf2U/5ybtaTAJax5SF/SNgLeS4Inx60uOA==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-resolve-arguments": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-arguments/-/sdk-resolve-arguments-0.0.1.tgz", - "integrity": "sha512-CbGEZo28l8uaHt8oA5O7yVOOdakjTFXICQCtBDnT+z8tEvDaqIR29rgE9uzXbDnPSCSxHSsxttO1nxQ8DzDEOQ==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-resolve-cadence": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-cadence/-/sdk-resolve-cadence-0.0.1.tgz", - "integrity": "sha512-nZf5JwwjCcG55GaB325Y5PwyhYq6PRo1eVLz3rxFP8RB3LTxaPJwuPK8QD5wfeGumM/7UvtbvuEBZwEqN9eKtQ==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-resolve-ref-block-id": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-ref-block-id/-/sdk-resolve-ref-block-id-0.0.7.tgz", - "integrity": "sha512-Q+eD27TgpIiMu8FXZ0tBCZCDRwWmtc85dKNuJZlqLHl8aZ/5H7BnNpTaj2O6TWzJAXakeBtDIdHz8Zeb1to5oA==", - "requires": { - "@onflow/decode": "0.0.10", - "@onflow/interaction": "0.0.10", - "@onflow/sdk-build-get-latest-block": "0.0.0", - "@onflow/send": "0.0.35" - } - }, - "@onflow/sdk-resolve-signatures": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-signatures/-/sdk-resolve-signatures-0.0.5.tgz", - "integrity": "sha512-rh/8ZVS3O3bMQSwVYfxewSLVVf5zUmcaCGR9AufPxZJYCH7YCrjbYzkEpsTZKUNxn4uzxp3X4gsPLvzKyZuiMQ==", - "requires": { - "@onflow/encode": "0.0.8", - "@onflow/interaction": "0.0.10", - "@onflow/util-address": "0.0.0" - } - }, - "@onflow/sdk-resolve-validators": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/sdk-resolve-validators/-/sdk-resolve-validators-0.0.0.tgz", - "integrity": "sha512-OiC6o8VgzZ2mn1qD3Bi12zbXk9AS8ALoLg4X8XnYJqe1HwCBVaAAy+dFL8jI7SVvgqOINTGxcz+6cF4UvfZUPw==", - "requires": { - "@onflow/interaction": "0.0.10" - } - }, - "@onflow/sdk-send": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@onflow/sdk-send/-/sdk-send-0.0.10.tgz", - "integrity": "sha512-jzJuLsEKnW6lsP2gxcgF5LPW2HVjnClSKGo5Y+w8VQdgGvWVWLkMqUMJF3Pf/X6pQ1oWXfFu9UNioRKQnFW2ZQ==", - "requires": { - "@onflow/config": "0.0.2", - "@onflow/interaction": "0.0.10", - "@onflow/sdk-resolve": "0.0.10", - "@onflow/send": "0.0.35" - } - }, - "@onflow/send": { - "version": "0.0.35", - "resolved": "https://registry.npmjs.org/@onflow/send/-/send-0.0.35.tgz", - "integrity": "sha512-yJbpC8tF1SOaBpNWi2CEoGQSSO/sH8+9VBWwbxolc4xUwmyl8F+1WpYdXesOSDQFCzSP5womt3xKYWG4LF7FGw==", - "requires": { - "@improbable-eng/grpc-web": "0.12.0", - "@improbable-eng/grpc-web-node-http-transport": "0.12.0", - "@onflow/config": "0.0.2", - "@onflow/interaction": "0.0.10", - "@onflow/protobuf": "0.1.8", - "@onflow/response": "0.0.2", - "@onflow/util-address": "^0.0.0" - } - }, - "@onflow/types": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@onflow/types/-/types-0.0.4.tgz", - "integrity": "sha512-47r0qTM45mR7FSvyEE0njGljkpgy3MCf/2W82pfZ/PTFGWr1nsGV/4j2bUfK9Sry4Ymz2W20iRZVP3xpqOhqGw==" - }, - "@onflow/util-actor": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@onflow/util-actor/-/util-actor-0.0.2.tgz", - "integrity": "sha512-NV3zPXQue3FqVgcIIMo6ifJOiP3hVSQTaR4ZrWLFU5iAZ/L73cTtBMbCB4BUFOe20ALtF2c9PFmpNVowCYV+nw==", - "requires": { - "queue-microtask": "1.1.2" - } - }, - "@onflow/util-address": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/util-address/-/util-address-0.0.0.tgz", - "integrity": "sha512-Lzbw/wV3O1fmfXYF2q6iGLgHz/7ATsLXOHceP5tzeEAKNf+srdtTNJv5jhNGhpFFAtQ6TcomXURVMhUg+/4YbA==" - }, - "@onflow/util-encode-key": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@onflow/util-encode-key/-/util-encode-key-0.0.2.tgz", - "integrity": "sha512-gY9H4f1USijXbgk6IoNibAibF8VB6spZgq2fdkxA9PKgKay6UjolnsKTvIkA0k4/CIiODQIh5ziL069mrRjOJw==", - "requires": { - "@onflow/rlp": "0.0.3", - "@onflow/util-invariant": "0.0.0" - } - }, - "@onflow/util-invariant": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@onflow/util-invariant/-/util-invariant-0.0.0.tgz", - "integrity": "sha512-ZCt+NqLdeHt9tZhb0DGxo6iSIS9oNUpLkd0PEMzUYUHr4UwrUO7VruV1AUW3PaF9V78DZ13fNZUiQEzdF76O/w==" - }, - "@onflow/util-template": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@onflow/util-template/-/util-template-0.0.1.tgz", - "integrity": "sha512-qlJ0oq+QujnZRCOGYaw5OKSDpiDIOpwQMYlMe4Mbz//Wn+LOmUghoKZGmRP+YCgp7BJ4aB6AWW/7kL83NDy50A==" - }, - "@onflow/util-uid": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@onflow/util-uid/-/util-uid-0.0.1.tgz", - "integrity": "sha512-SzBscBdyn1Zoks0Wo/w7J/Ds9IZ/T+KM/wyWMwWla4PnxwBFviy1BytEQY+sM5q1UNOvaGWgGEoRmH/oOCcglA==" - }, - "@prisma/client": { - "version": "2.21.2", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-2.21.2.tgz", - "integrity": "sha512-UjkOXYpxLuHyoMDsP2m0LTcxhrjQa1dEOLFe3aDrO/BLrs/2yUxyPdtwSKxizRXFzuXSGkKIK225vcjZRuMpAg==", - "requires": { - "@prisma/engines-version": "2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d" - } - }, - "@prisma/engines": { - "version": "2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e.tgz", - "integrity": "sha512-zOWETm7DTRvlwf/CekPNSeJe6EC5bn2IFexd74wM9zgBXCZo+1sMDuNGtCqIt4Rzv8CcimEgyzrEFVq0LPV8qg==", - "dev": true - }, - "@prisma/engines-version": { - "version": "2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d.tgz", - "integrity": "sha512-9/fE1gdPWmjbMjXUJjrTMt848TsgEnSjZCcJ1wu9OAcRlAKKJBLehftqC3gSEShDijvMYgeTdGU5snMpwmv4vg==" - }, - "@types/bn.js": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", - "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/body-parser": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", - "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.34", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", - "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/elliptic": { - "version": "6.4.12", - "resolved": "https://registry.npmjs.org/@types/elliptic/-/elliptic-6.4.12.tgz", - "integrity": "sha512-gP1KsqoouLJGH6IJa28x7PXb3cRqh83X8HCLezd2dF+XcAIMKYv53KV+9Zn6QA561E120uOqZBQ+Jy/cl+fviw==", - "dev": true, - "requires": { - "@types/bn.js": "*" - } - }, - "@types/express": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", - "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", - "dev": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", - "integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true - }, - "@types/node": { - "version": "14.14.39", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.39.tgz", - "integrity": "sha512-Qipn7rfTxGEDqZiezH+wxqWYR8vcXq5LRpZrETD19Gs4o8LbklbmqotSUsMU+s5G3PJwMRDfNEYoxrcBwIxOuw==", - "dev": true - }, - "@types/qs": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", - "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==", - "dev": true - }, - "@types/range-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", - "dev": true - }, - "@types/serve-static": { - "version": "1.13.9", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", - "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", - "dev": true, - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", - "dev": true - }, - "@types/strip-json-comments": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", - "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.0.tgz", - "integrity": "sha512-U8SP9VOs275iDXaL08Ln1Fa/wLXfj5aTr/1c0t0j6CdbOnxh+TruXu1p4I0NAvdPBQgoPjHsgKn28mOi0FzfoA==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "4.22.0", - "@typescript-eslint/scope-manager": "4.22.0", - "debug": "^4.1.1", - "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.15", - "regexpp": "^3.0.0", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@typescript-eslint/experimental-utils": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.0.tgz", - "integrity": "sha512-xJXHHl6TuAxB5AWiVrGhvbGL8/hbiCQ8FiWwObO3r0fnvBdrbWEDy1hlvGQOAWc6qsCWuWMKdVWlLAEMpxnddg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.22.0", - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/typescript-estree": "4.22.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.22.0.tgz", - "integrity": "sha512-z/bGdBJJZJN76nvAY9DkJANYgK3nlRstRRi74WHm3jjgf2I8AglrSY+6l7ogxOmn55YJ6oKZCLLy+6PW70z15Q==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "4.22.0", - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/typescript-estree": "4.22.0", - "debug": "^4.1.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@typescript-eslint/scope-manager": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.22.0.tgz", - "integrity": "sha512-OcCO7LTdk6ukawUM40wo61WdeoA7NM/zaoq1/2cs13M7GyiF+T4rxuA4xM+6LeHWjWbss7hkGXjFDRcKD4O04Q==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/visitor-keys": "4.22.0" - } - }, - "@typescript-eslint/types": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.22.0.tgz", - "integrity": "sha512-sW/BiXmmyMqDPO2kpOhSy2Py5w6KvRRsKZnV0c4+0nr4GIcedJwXAq+RHNK4lLVEZAJYFltnnk1tJSlbeS9lYA==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.0.tgz", - "integrity": "sha512-TkIFeu5JEeSs5ze/4NID+PIcVjgoU3cUQUIZnH3Sb1cEn1lBo7StSV5bwPuJQuoxKXlzAObjYTilOEKRuhR5yg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.22.0", - "@typescript-eslint/visitor-keys": "4.22.0", - "debug": "^4.1.1", - "globby": "^11.0.1", - "is-glob": "^4.0.1", - "semver": "^7.3.2", - "tsutils": "^3.17.1" - }, - "dependencies": { - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.0.tgz", - "integrity": "sha512-nnMu4F+s4o0sll6cBSsTeVsT4cwxB7zECK3dFxzEjPBii9xLpq4yqqsy/FU5zMfan6G60DKZSCXAa3sHJZrcYw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.22.0", - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - } - } - }, - "accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "requires": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" - } - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "array-includes": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.3.tgz", - "integrity": "sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "get-intrinsic": "^1.1.1", - "is-string": "^1.0.5" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "async": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", - "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" - }, - "babel-eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", - "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0", - "eslint-visitor-keys": "^1.0.0", - "resolve": "^1.12.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" - }, - "browser-headers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/browser-headers/-/browser-headers-0.4.1.tgz", - "integrity": "sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==" - }, - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - } - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" - } - }, - "color": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", - "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", - "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "color-string": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", - "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" - }, - "colorspace": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", - "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", - "requires": { - "color": "3.0.x", - "text-hex": "1.0.x" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "^1.0.1" - } - }, - "dateformat": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", - "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "dedent-js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz", - "integrity": "sha1-vuX7fJ5yfYXf+iRZDRDsGrElUwU=" - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - }, - "dependencies": { - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - } - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dotenv": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", - "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" - }, - "dynamic-dedupe": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", - "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", - "dev": true, - "requires": { - "xtend": "^4.0.0" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - } - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - } - } - }, - "es-abstract": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz", - "integrity": "sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.2", - "is-callable": "^1.2.3", - "is-negative-zero": "^2.0.1", - "is-regex": "^1.1.2", - "is-string": "^1.0.5", - "object-inspect": "^1.9.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.0" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.24.0.tgz", - "integrity": "sha512-k9gaHeHiFmGCDQ2rEfvULlSLruz6tgfA8DEn+rY9/oYPFFTlz55mM/Q/Rij1b2Y42jwZiK3lXvNTw6w6TXzcKQ==", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.21", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - }, - "globals": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", - "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "eslint-config-prettier": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz", - "integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - } - }, - "eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" - } - }, - "eslint-plugin-import": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", - "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", - "dev": true, - "requires": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" - }, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "eslint-plugin-prettier": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz", - "integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", - "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", - "requires": { - "accepts": "~1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" - } - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - } - } - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" - }, - "fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fecha": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz", - "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==" - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - } - } - }, - "google-protobuf": { - "version": "3.15.7", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.15.7.tgz", - "integrity": "sha512-S/kTHcT98AV2FxEwtT5lvgffeS87BB6hloZm+pYKkpzwtySwNiKcqXZbxpq/Odh3Wib1RdOe/oY2EHdi17YrlQ==" - }, - "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - } - }, - "http-status": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/http-status/-/http-status-1.5.0.tgz", - "integrity": "sha512-wcGvY31MpFNHIkUcXHHnvrE4IKYlpvitJw5P/1u892gMBAM46muQ+RH7UN1d+Ntnfx5apnOnVY6vcLmrWHOLwg==" - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "is-bigint": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", - "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", - "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", - "dev": true, - "requires": { - "call-bind": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", - "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", - "dev": true - }, - "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true - }, - "is-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", - "integrity": "sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-symbols": "^1.0.1" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "logform": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", - "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", - "requires": { - "colors": "^1.2.1", - "fast-safe-stringify": "^2.0.4", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" - }, - "mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" - }, - "mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", - "requires": { - "mime-db": "1.47.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "module-alias": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", - "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" - }, - "morgan": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", - "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", - "requires": { - "basic-auth": "~2.0.0", - "debug": "2.6.9", - "depd": "~1.1.2", - "on-finished": "~2.3.0", - "on-headers": "~1.0.1" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-inspect": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.10.2.tgz", - "integrity": "sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "object.values": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz", - "integrity": "sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.2", - "has": "^1.0.3" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "requires": { - "fn.name": "1.x.x" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "prisma": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-2.20.1.tgz", - "integrity": "sha512-zyPvJSUfJrmciP2D/4aUrsyIefiH8AIJUeuq1a0X1df1AFw9QQ+ata/7VQdoP+RIQHnCb6Kln9kqfUw/fieljw==", - "dev": true, - "requires": { - "@prisma/engines": "2.20.0-26.60ba6551f29b17d7d6ce479e5733c70d9c00860e" - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "proxy-addr": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", - "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.9.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "queue-microtask": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.1.2.tgz", - "integrity": "sha512-F9wwNePtXrzZenAB3ax0Y8TSKGvuB7Qw16J30hspEUTbfUM+H827XyN3rlpwhVmtm5wuZtbKIHjOnwDn7MUxWQ==" - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - } - }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "^1.0.0" - } - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dev": true, - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - }, - "dependencies": { - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" - } - }, - "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" - } - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "sha3": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", - "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", - "requires": { - "buffer": "6.0.3" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "requires": { - "is-arrayish": "^0.3.1" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - } - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.9.tgz", - "integrity": "sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "lodash.clonedeep": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "ajv": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.1.0.tgz", - "integrity": "sha512-B/Sk2Ix7A36fs/ZkuGLIR86EdjbgR6fsAcbx9lOP/QBSXujDNbVmIS/U4Itz5k8fPFDeVZl/zQ/gJW4Jrq6XjQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, - "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" - }, - "ts-node": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", - "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", - "dev": true, - "requires": { - "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" - } - }, - "ts-node-dev": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.6.tgz", - "integrity": "sha512-RTUi7mHMNQospArGz07KiraQcdgUVNXKsgO2HAi7FoiyPMdTDqdniB6K1dqyaIxT7c9v/VpSbfBZPS6uVpaFLQ==", - "dev": true, - "requires": { - "chokidar": "^3.5.1", - "dateformat": "~1.0.4-1.2.3", - "dynamic-dedupe": "^0.3.0", - "minimist": "^1.2.5", - "mkdirp": "^1.0.4", - "resolve": "^1.0.0", - "rimraf": "^2.6.1", - "source-map-support": "^0.5.12", - "tree-kill": "^1.2.2", - "ts-node": "^9.0.0", - "tsconfig": "^7.0.0" - }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "tsconfig": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", - "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", - "dev": true, - "requires": { - "@types/strip-bom": "^3.0.0", - "@types/strip-json-comments": "0.0.30", - "strip-bom": "^3.0.0", - "strip-json-comments": "^2.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - } - } - }, - "tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typescript": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", - "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "winston": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", - "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", - "requires": { - "@dabh/diagnostics": "^2.0.2", - "async": "^3.1.0", - "is-stream": "^2.0.0", - "logform": "^2.2.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.4.0" - } - }, - "winston-transport": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", - "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", - "requires": { - "readable-stream": "^2.3.7", - "triple-beam": "^1.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 1eff9e23..00000000 --- a/package.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "flow-wallet-api-node-demo", - "version": "0.0.0", - "private": true, - "scripts": { - "dev": "npm run dev-prepare && npm run dev-api", - "dev-api": "NODE_ENV=development ts-node-dev -r tsconfig-paths/register --respawn ./src/index.ts", - "dev-prepare": "npm run dev-deploy-contracts && npm run db-migrate-dev && npm run db-sync-admin-keys", - "dev-deploy-contracts": "flow project deploy --network=emulator --update", - "build": "tsc -p tsconfig.json", - "start": "npm run start-prepare && npm run start-api", - "start-api": "node -r tsconfig-paths/register dist/index.js", - "start-prepare": "npm run db-migrate-deploy && npm run db-sync-admin-keys", - "db-migrate-dev": "prisma migrate dev --preview-feature", - "db-migrate-deploy": "prisma migrate deploy --preview-feature", - "db-generate-client": "prisma generate", - "db-sync-admin-keys": "ts-node ./src/database/syncAdminSignerKeys.ts", - "lint": "eslint .", - "lint-fix": "eslint . --fix" - }, - "prisma": { - "schema": "src/database/schema.prisma" - }, - "dependencies": { - "@onflow/fcl": "0.0.67", - "@onflow/types": "0.0.4", - "@onflow/util-encode-key": "0.0.2", - "@prisma/client": "^2.21.2", - "dedent-js": "^1.0.1", - "dotenv": "^8.2.0", - "elliptic": "^6.5.4", - "express": "~4.16.1", - "http-status": "^1.5.0", - "module-alias": "^2.2.2", - "morgan": "~1.9.1", - "sha3": "^2.1.4", - "winston": "^3.3.3" - }, - "devDependencies": { - "@types/bn.js": "^5.1.0", - "@types/elliptic": "^6.4.12", - "@types/express": "^4.17.11", - "@types/node": "^14.14.39", - "@typescript-eslint/eslint-plugin": "^4.22.0", - "@typescript-eslint/parser": "^4.22.0", - "babel-eslint": "^10.1.0", - "eslint": "^7.21.0", - "eslint-config-prettier": "^8.1.0", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-prettier": "^3.3.1", - "prettier": "^2.2.1", - "prisma": "^2.20.1", - "ts-node": "^9.1.1", - "ts-node-dev": "^1.1.6", - "tsconfig-paths": "^3.9.0", - "typescript": "^4.2.4" - }, - "_moduleAliases": { - "src": "dist" - } -} diff --git a/src/admin.ts b/src/admin.ts deleted file mode 100644 index a683f1ab..00000000 --- a/src/admin.ts +++ /dev/null @@ -1,19 +0,0 @@ -import config from "src/config" -import {KeyType, Key} from "src/lib/keys" -import InMemoryKeyManager from "src/lib/keys/inMemory" - -export function getAdminKey(): Key { - switch (config.adminKeyType) { - case KeyType.InMemory: - return getAdminInMemoryKey() - } -} - -function getAdminInMemoryKey(): Key { - const keyManager = new InMemoryKeyManager( - config.adminSigAlgo, - config.adminHashAlgo - ) - - return keyManager.load(config.adminPrivateKey) -} diff --git a/src/app.ts b/src/app.ts deleted file mode 100644 index 4930ab6b..00000000 --- a/src/app.ts +++ /dev/null @@ -1,64 +0,0 @@ -import * as express from "express" -import * as fcl from "@onflow/fcl" -import {PrismaClient} from "@prisma/client" - -import morganMiddleware from "src/middleware/morgan" -import errorsMiddleware from "src/middleware/errors" -import NotFoundError from "src/errors/NotFoundError" -import createRouter from "src/routes/v1" -import AccountsController from "src/controllers/accounts" -import AccountsService from "src/services/accounts" -import FungibleTokensService from "src/services/fungibleTokens" -import FungibleTokensController from "src/controllers/fungibleTokens" -import TransactionsController from "src/controllers/transactions" -import TransactionsService from "src/services/transactions" -import InMemoryKeyManager from "src/lib/keys/inMemory" -import config from "src/config" -import {getAdminKey} from "src/admin" - -const app = express() - -app.use(morganMiddleware) -app.use(express.json()) -app.use(express.urlencoded({extended: false})) - -fcl.config().put("accessNode.api", config.accessApiHost) - -const prisma = new PrismaClient() - -const userKeyManager = new InMemoryKeyManager( - config.userSigAlgo, - config.userHashAlgo, - config.userEncryptionKey -) - -const adminKey = getAdminKey() - -const accountsService = new AccountsService(prisma, adminKey, userKeyManager) -const accountsController = new AccountsController(accountsService) - -const transactionsService = new TransactionsService(prisma, accountsService) -const transactionsController = new TransactionsController(transactionsService) - -const fungibleTokensService = new FungibleTokensService(prisma, accountsService) -const fungibleTokensController = new FungibleTokensController( - fungibleTokensService -) - -const v1Router = createRouter( - accountsController, - transactionsController, - fungibleTokensController -) - -app.use("/v1", v1Router) - -// catch 404 and forward to error handler -app.use((req, res, next) => { - next(new NotFoundError()) -}) - -// error handler -app.use(errorsMiddleware) - -export default app diff --git a/src/config.ts b/src/config.ts deleted file mode 100644 index aa5f153b..00000000 --- a/src/config.ts +++ /dev/null @@ -1,78 +0,0 @@ -import * as dotenv from "dotenv" -dotenv.config() - -import {HashAlgorithm, SignatureAlgorithm} from "src/lib/crypto" -import {KeyType} from "src/lib/keys" - -const chainEmulator = "emulator" -const chainTestnet = "testnet" - -const contractsEmulator = { - FlowToken: "0x0ae53cb6e3f42a79", - FungibleToken: "0xee82856bf20e2aa6", - FUSD: "0xf8d6e0586b0a20c7", -} - -const contractsTestnet = { - FlowToken: "0x7e60df042a9c0868", - FungibleToken: "0x9a0766d93b6608b7", - FUSD: "0xe223d8a629e49c68", -} - -function getContracts(chain) { - chain = chain || chainEmulator - - switch (chain) { - case chainEmulator: - return contractsEmulator - case chainTestnet: - return contractsTestnet - } - - throw `Invalid chain: ${chain}` -} - -const defaultSigAlgo = SignatureAlgorithm.ECDSA_P256 -const defaultHashAlgo = HashAlgorithm.SHA3_256 -const defaultkeyType = KeyType.InMemory - -function parseSigAlgo(sigAlgo: string): SignatureAlgorithm { - return SignatureAlgorithm[sigAlgo] || defaultSigAlgo -} - -function parseHashAlgo(hashAlgo: string): HashAlgorithm { - return HashAlgorithm[hashAlgo] || defaultHashAlgo -} - -function parseKeyType(keyType: string): KeyType { - return KeyType[keyType] || defaultkeyType -} - -function parseHex(hex: string): Buffer | null { - if (!hex) { - return null - } - - return Buffer.from(hex, "hex") -} - -const config = { - env: process.env.NODE_ENV, - port: process.env.PORT || 3000, - accessApiHost: process.env.ACCESS_API_HOST || "http://localhost:8080", - - adminAddress: process.env.ADMIN_ADDRESS || "0xf8d6e0586b0a20c7", - adminKeyType: parseKeyType(process.env.ADMIN_KEY_TYPE), - adminPrivateKey: process.env.ADMIN_PRIVATE_KEY, - adminSigAlgo: parseSigAlgo(process.env.ADMIN_SIG_ALGO), - adminHashAlgo: parseHashAlgo(process.env.ADMIN_HASH_ALGO), - - userKeyType: parseKeyType(process.env.USER_KEY_TYPE), - userSigAlgo: parseSigAlgo(process.env.USER_SIG_ALGO), - userHashAlgo: parseHashAlgo(process.env.USER_HASH_ALGO), - userEncryptionKey: parseHex(process.env.USER_ENCRYPTION_KEY), - - contracts: getContracts(process.env.CHAIN), -} - -export default config diff --git a/src/controllers/accounts.ts b/src/controllers/accounts.ts deleted file mode 100644 index 6cb9df64..00000000 --- a/src/controllers/accounts.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as express from "express" - -import NotFoundError from "src/errors/NotFoundError" -import AccountsService from "src/services/accounts" - -export default class AccountsController { - private accounts: AccountsService - - constructor(accounts: AccountsService) { - this.accounts = accounts - } - - async createAccount( - req: express.Request, - res: express.Response - ): Promise { - const account = await this.accounts.create() - - res.json(account) - } - - async getAccounts( - req: express.Request, - res: express.Response - ): Promise { - const accounts = await this.accounts.query() - - res.json(accounts) - } - - async getAccount(req: express.Request, res: express.Response): Promise { - const address = req.params.address - - const account = await this.accounts.getByAddress(address) - - if (account === null) { - throw new NotFoundError() - } - - res.json(account) - } -} diff --git a/src/controllers/fungibleTokens.ts b/src/controllers/fungibleTokens.ts deleted file mode 100644 index 727ced2e..00000000 --- a/src/controllers/fungibleTokens.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as httpStatus from "http-status" - -import * as express from "express" - -import {isValidToken} from "src/lib/fungibleTokens" -import FungibleTokensService from "src/services/fungibleTokens" -import ApiError from "src/errors/ApiError" -import InvalidFungibleTokenError from "src/errors/InvalidFungibleTokenError" - -export default class FungibleTokensController { - private fungibleTokens: FungibleTokensService - - constructor(fungibleTokens: FungibleTokensService) { - this.fungibleTokens = fungibleTokens - } - - async getTokens(req: express.Request, res: express.Response): Promise { - const tokens = await this.fungibleTokens.query() - - res.json(tokens) - } - - async getToken(req: express.Request, res: express.Response): Promise { - const tokenName = req.params.tokenName - - if (!isValidToken(tokenName)) { - throw new InvalidFungibleTokenError(tokenName) - } - - const token = await this.fungibleTokens.getByName(tokenName) - - res.json(token) - } - - // TODO: implement withdrawal getters - async getWithdrawals( - req: express.Request, - res: express.Response - ): Promise { - res.send("TODO: implement me") - } - - // TODO: implement withdrawal getters - async getWithdrawal( - req: express.Request, - res: express.Response - ): Promise { - res.send("TODO: implement me") - } - - async createWithdrawal( - req: express.Request, - res: express.Response - ): Promise { - const sender = req.params.address - const tokenName = req.params.tokenName - - if (!isValidToken(tokenName)) { - throw new InvalidFungibleTokenError(tokenName) - } - - // TODO: validate recipient and amount - const {recipient, amount} = req.body - - try { - const transactionId = await this.fungibleTokens.createWithdrawal( - sender, - recipient, - tokenName, - amount - ) - - const response = { - transactionId, - recipient, - amount, - } - - res.json(response) - } catch (e) { - throw new ApiError( - httpStatus.INTERNAL_SERVER_ERROR, - "failed to complete withdrawal" - ) - } - } -} diff --git a/src/controllers/transactions.ts b/src/controllers/transactions.ts deleted file mode 100644 index de312f6e..00000000 --- a/src/controllers/transactions.ts +++ /dev/null @@ -1,59 +0,0 @@ -import * as httpStatus from "http-status" - -import * as express from "express" - -import TransactionsService from "src/services/transactions" -import ApiError from "src/errors/ApiError" - -export default class TransactionsController { - private transactions: TransactionsService - - constructor(transactions: TransactionsService) { - this.transactions = transactions - } - - // TODO: implement transaction getters - async getTransactions( - req: express.Request, - res: express.Response - ): Promise { - res.send("TODO: implement me") - } - - // TODO: implement transaction getters - async getTransaction( - req: express.Request, - res: express.Response - ): Promise { - res.send("TODO: implement me") - } - - async createTransaction( - req: express.Request, - res: express.Response - ): Promise { - const sender = req.params.address - - // TODO: validate code and arguments - const {code, arguments: args} = req.body - - try { - const transactionId = await this.transactions.createTransaction( - sender, - code, - args - ) - - const response = { - transactionId, - } - - res.json(response) - } catch (e) { - throw new ApiError( - httpStatus.INTERNAL_SERVER_ERROR, - "failed to complete transaction" - ) - } - } -} diff --git a/src/database/accounts.ts b/src/database/accounts.ts deleted file mode 100644 index 535555e2..00000000 --- a/src/database/accounts.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {AccountKey, PrismaClient} from "@prisma/client" - -import {KeyType} from "src/lib/keys" - -import {Account} from "./models" - -export async function insertAccount( - prisma: PrismaClient, - address: string, - accountKeyType: KeyType, - accountKeyValue: string -): Promise { - const accountKeyIndex = 0 - - return await prisma.account.create({ - data: { - address: address, - keys: { - create: { - index: accountKeyIndex, - type: accountKeyType, - value: accountKeyValue, - }, - }, - }, - }) -} - -export async function listAccounts(prisma: PrismaClient): Promise { - // TODO: add pagination - return await prisma.account.findMany() -} - -export async function getAccount( - prisma: PrismaClient, - address: string -): Promise { - return await prisma.account.findFirst({ - where: {address}, - }) -} - -export async function getAccountKey( - prisma: PrismaClient, - address: string -): Promise { - return await prisma.accountKey.findFirst({ - where: {accountAddress: address}, - }) -} diff --git a/src/database/getLeastRecentAdminSignerKey.ts b/src/database/getLeastRecentAdminSignerKey.ts deleted file mode 100644 index a6fc3748..00000000 --- a/src/database/getLeastRecentAdminSignerKey.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {PrismaClient} from "@prisma/client" - -const getLeastRecentAccountKeySql = ` -UPDATE admin_signer_keys -SET updated_at = current_timestamp -WHERE index = ( - SELECT index - FROM admin_signer_keys - ORDER BY updated_at - LIMIT 1 -) -RETURNING index -` - -export default async function getLeastRecentAdminSignerKey( - prisma: PrismaClient -): Promise { - const results = await prisma.$queryRaw(getLeastRecentAccountKeySql) - return results[0].index -} diff --git a/src/database/migrations/20210412042610_create_account_keys/migration.sql b/src/database/migrations/20210412042610_create_account_keys/migration.sql deleted file mode 100644 index cf8e12d0..00000000 --- a/src/database/migrations/20210412042610_create_account_keys/migration.sql +++ /dev/null @@ -1,7 +0,0 @@ --- CreateTable -CREATE TABLE "account_keys" ( - "index" INTEGER NOT NULL, - "last_used_at" TIMESTAMP(3) NOT NULL, - - PRIMARY KEY ("index") -); diff --git a/src/database/migrations/20210421075756_admin_signer_keys/migration.sql b/src/database/migrations/20210421075756_admin_signer_keys/migration.sql deleted file mode 100644 index 29f9c10d..00000000 --- a/src/database/migrations/20210421075756_admin_signer_keys/migration.sql +++ /dev/null @@ -1,17 +0,0 @@ -/* - Warnings: - - - You are about to drop the `account_keys` table. If the table is not empty, all the data it contains will be lost. - -*/ --- DropTable -DROP TABLE "account_keys"; - --- CreateTable -CREATE TABLE "admin_signer_keys" ( - "index" INTEGER NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - PRIMARY KEY ("index") -); diff --git a/src/database/migrations/20210425210753_create_accounts/migration.sql b/src/database/migrations/20210425210753_create_accounts/migration.sql deleted file mode 100644 index 6c29d982..00000000 --- a/src/database/migrations/20210425210753_create_accounts/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ --- CreateTable -CREATE TABLE "accounts" ( - "address" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - PRIMARY KEY ("address") -); diff --git a/src/database/migrations/20210426021914_create_account_keys/migration.sql b/src/database/migrations/20210426021914_create_account_keys/migration.sql deleted file mode 100644 index 50bd47b8..00000000 --- a/src/database/migrations/20210426021914_create_account_keys/migration.sql +++ /dev/null @@ -1,15 +0,0 @@ --- CreateTable -CREATE TABLE "account_keys" ( - "account_address" TEXT NOT NULL, - "index" INTEGER NOT NULL, - "type" TEXT NOT NULL, - "value" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL -); - --- CreateIndex -CREATE UNIQUE INDEX "account_keys.account_address_index_unique" ON "account_keys"("account_address", "index"); - --- AddForeignKey -ALTER TABLE "account_keys" ADD FOREIGN KEY ("account_address") REFERENCES "accounts"("address") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/src/database/migrations/migration_lock.toml b/src/database/migrations/migration_lock.toml deleted file mode 100644 index fbffa92c..00000000 --- a/src/database/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) -provider = "postgresql" \ No newline at end of file diff --git a/src/database/models.ts b/src/database/models.ts deleted file mode 100644 index 0d75a509..00000000 --- a/src/database/models.ts +++ /dev/null @@ -1,3 +0,0 @@ -import * as Prisma from "@prisma/client" - -export type Account = Prisma.Account diff --git a/src/database/schema.prisma b/src/database/schema.prisma deleted file mode 100644 index 1742b992..00000000 --- a/src/database/schema.prisma +++ /dev/null @@ -1,45 +0,0 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -generator client { - provider = "prisma-client-js" -} - -model Account { - @@map(name: "accounts") - - address String @id - keys AccountKey[] - - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") -} - -model AccountKey { - @@map(name: "account_keys") - - @@unique([accountAddress, index]) - - account Account @relation(fields: [accountAddress], references: [address]) - accountAddress String @map("account_address") - index Int - type String - value String - - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") -} - -model AdminSignerKey { - @@map(name: "admin_signer_keys") - - index Int @id - - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") -} diff --git a/src/database/syncAdminSignerKeys.ts b/src/database/syncAdminSignerKeys.ts deleted file mode 100644 index 6f6a686b..00000000 --- a/src/database/syncAdminSignerKeys.ts +++ /dev/null @@ -1,50 +0,0 @@ -import * as fcl from "@onflow/fcl" -import * as dotenv from "dotenv" -import {PrismaClient} from "@prisma/client" - -dotenv.config() - -const prisma = new PrismaClient() - -const adminAddress = process.env.ADMIN_ADDRESS -const accessAPIHost = process.env.ACCESS_API_HOST - -fcl.config().put("accessNode.api", accessAPIHost) - -async function getAccount(address) { - const {account} = await fcl.send([fcl.getAccount(address)]) - return account -} - -async function main() { - const account = await getAccount(adminAddress) - - console.log( - `Fetched admin account information for ${adminAddress} from ${accessAPIHost}\n` - ) - - // truncate existing keys - const {count} = await prisma.adminSignerKey.deleteMany({where: {}}) - - console.log(`Removed ${count} existing admin key(s) from DB\n`) - - await Promise.all( - account.keys.map(async key => { - console.log(`- Inserting key ${key.index}`) - - await prisma.adminSignerKey.create({ - data: {index: key.index}, - }) - }) - ) - - console.log(`\nInserted ${account.keys.length} admin key(s) into DB`) -} - -main() - .catch(e => { - throw e - }) - .finally(async () => { - await prisma.$disconnect() - }) diff --git a/src/errors/ApiError.ts b/src/errors/ApiError.ts deleted file mode 100644 index ee0b4599..00000000 --- a/src/errors/ApiError.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default class ApiError extends Error { - statusCode: number - - constructor(statusCode: number, message: string) { - super(message) - - this.statusCode = statusCode - - Error.captureStackTrace(this, this.constructor) - } -} diff --git a/src/errors/InvalidFungibleTokenError.ts b/src/errors/InvalidFungibleTokenError.ts deleted file mode 100644 index 1e5047a0..00000000 --- a/src/errors/InvalidFungibleTokenError.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as httpStatus from "http-status" - -import ApiError from "./ApiError" - -export default class InvalidFungibleTokenError extends ApiError { - constructor(token: string) { - const statusCode = httpStatus.BAD_REQUEST - const message = `${token} is not a valid fungible token` - - super(statusCode, message) - } -} diff --git a/src/errors/NotFoundError.ts b/src/errors/NotFoundError.ts deleted file mode 100644 index c05250f3..00000000 --- a/src/errors/NotFoundError.ts +++ /dev/null @@ -1,12 +0,0 @@ -import * as httpStatus from "http-status" - -import ApiError from "./ApiError" - -export default class NotFoundError extends ApiError { - constructor() { - const statusCode = httpStatus.NOT_FOUND - const message = "not found" - - super(statusCode, message) - } -} diff --git a/src/errors/catchAsync.ts b/src/errors/catchAsync.ts deleted file mode 100644 index f5533908..00000000 --- a/src/errors/catchAsync.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as express from "express" - -const catchAsync = (fn: express.RequestHandler) => ( - req: express.Request, - res: express.Response, - next: express.NextFunction -): void => { - Promise.resolve(fn(req, res, next)).catch(err => next(err)) -} - -export default catchAsync diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index c7ac40c1..00000000 --- a/src/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -require("module-alias/register") - -import config from "src/config" -import logger from "src/logger" -import app from "src/app" - -const server = app.listen(config.port, () => { - logger.info(`Listening on port ${config.port}`) -}) - -const exitHandler = () => { - if (server) { - server.close(() => { - logger.info("Server closed") - process.exit(1) - }) - } else { - process.exit(1) - } -} - -const unexpectedErrorHandler = error => { - logger.error(error) - exitHandler() -} - -process.on("uncaughtException", unexpectedErrorHandler) -process.on("unhandledRejection", unexpectedErrorHandler) - -process.on("SIGTERM", () => { - logger.info("SIGTERM received") - if (server) { - server.close() - } -}) diff --git a/src/lib/crypto/elliptic.ts b/src/lib/crypto/elliptic.ts deleted file mode 100644 index d1b25791..00000000 --- a/src/lib/crypto/elliptic.ts +++ /dev/null @@ -1,142 +0,0 @@ -import * as elliptic from "elliptic" -import {Endianness} from "bn.js" - -import {PublicKey} from "./publicKey" -import {Signer, Signature, SignatureAlgorithm} from "./sign" -import {Hasher, HashAlgorithm, getHasher} from "./hash" - -const ECDSA_P256 = new elliptic.ec("p256") -const ECDSA_secp256k1 = new elliptic.ec("secp256k1") - -const bufferEndianness: Endianness = "be" - -function getEC(sigAlgo: SignatureAlgorithm): elliptic.ec { - switch (sigAlgo) { - case SignatureAlgorithm.ECDSA_P256: - return ECDSA_P256 - case SignatureAlgorithm.ECDSA_secp256k1: - return ECDSA_secp256k1 - } -} - -class ECSignature implements Signature { - private static n = 32 - private ecSignature: elliptic.ec.Signature - - constructor(ecSignature: elliptic.ec.Signature) { - this.ecSignature = ecSignature - } - - toBuffer(): Buffer { - const r = this.ecSignature.r.toArrayLike( - Buffer, - bufferEndianness, - ECSignature.n - ) - const s = this.ecSignature.s.toArrayLike( - Buffer, - bufferEndianness, - ECSignature.n - ) - - return Buffer.concat([r, s]) - } - - toHex(): string { - return this.toBuffer().toString("hex") - } -} - -class ECPublicKey implements PublicKey { - private static size = 32 - private ecPublicKey: elliptic.curve.base.BasePoint - - constructor(ecPublicKey: elliptic.curve.base.BasePoint) { - this.ecPublicKey = ecPublicKey - } - - toBuffer(): Buffer { - const x = this.ecPublicKey - .getX() - .toArrayLike(Buffer, bufferEndianness, ECPublicKey.size) - const y = this.ecPublicKey - .getY() - .toArrayLike(Buffer, bufferEndianness, ECPublicKey.size) - - return Buffer.concat([x, y]) - } - - toHex(): string { - return this.toBuffer().toString("hex") - } -} - -export class InMemoryPrivateKey { - private ecKeyPair: elliptic.ec.KeyPair - private sigAlgo: SignatureAlgorithm - - constructor(ecKeyPair: elliptic.ec.KeyPair, sigAlgo: SignatureAlgorithm) { - this.ecKeyPair = ecKeyPair - this.sigAlgo = sigAlgo - } - - public static generate(sigAlgo: SignatureAlgorithm): InMemoryPrivateKey { - const ec = getEC(sigAlgo) - const ecKeyPair = ec.genKeyPair() - return new InMemoryPrivateKey(ecKeyPair, sigAlgo) - } - - public static fromBuffer( - buffer: Buffer, - sigAlgo: SignatureAlgorithm - ): InMemoryPrivateKey { - const ec = getEC(sigAlgo) - const ecKeyPair = ec.keyFromPrivate(buffer) - return new InMemoryPrivateKey(ecKeyPair, sigAlgo) - } - - public static fromHex( - hex: string, - sigAlgo: SignatureAlgorithm - ): InMemoryPrivateKey { - const buffer = Buffer.from(hex, "hex") - return InMemoryPrivateKey.fromBuffer(buffer, sigAlgo) - } - - sign(digest: Buffer): Signature { - const ecSignature = this.ecKeyPair.sign(digest) - return new ECSignature(ecSignature) - } - - getPublicKey(): PublicKey { - const ecPublicKey = this.ecKeyPair.getPublic() - return new ECPublicKey(ecPublicKey) - } - - getSignatureAlgorithm(): SignatureAlgorithm { - return this.sigAlgo - } - - toBuffer(): Buffer { - return this.ecKeyPair.getPrivate().toArrayLike(Buffer, bufferEndianness) - } - - toHex(): string { - return this.toBuffer().toString("hex") - } -} - -export class InMemorySigner implements Signer { - private privateKey: InMemoryPrivateKey - private hasher: Hasher - - constructor(privateKey: InMemoryPrivateKey, hashAlgo: HashAlgorithm) { - this.privateKey = privateKey - this.hasher = getHasher(hashAlgo) - } - - sign(message: Buffer): Signature { - const digest = this.hasher.hash(message) - return this.privateKey.sign(digest) - } -} diff --git a/src/lib/crypto/hash.ts b/src/lib/crypto/hash.ts deleted file mode 100644 index 25b1addb..00000000 --- a/src/lib/crypto/hash.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {createHash, Hash} from "crypto" - -import {SHA3} from "sha3" - -export enum HashAlgorithm { - SHA2_256 = "SHA2_256", - SHA3_256 = "SHA3_256", -} - -export interface Hasher { - hash(message: Buffer): Buffer -} - -export class SHA2_256Hasher { - private static shaType = "sha256" - private sha: Hash - - constructor() { - this.sha = createHash(SHA2_256Hasher.shaType) - } - - hash(message: Buffer): Buffer { - this.sha.update(message) - return this.sha.digest() - } -} - -export class SHA3_256Hasher { - private static size: 256 = 256 - private sha: SHA3 - - constructor() { - this.sha = new SHA3(SHA3_256Hasher.size) - } - - hash(message: Buffer): Buffer { - this.sha.update(message) - return this.sha.digest() - } -} - -export function getHasher(hashAlgo: HashAlgorithm): Hasher { - switch (hashAlgo) { - case HashAlgorithm.SHA2_256: - return new SHA2_256Hasher() - case HashAlgorithm.SHA3_256: - return new SHA3_256Hasher() - } -} diff --git a/src/lib/crypto/index.ts b/src/lib/crypto/index.ts deleted file mode 100644 index 858c56a1..00000000 --- a/src/lib/crypto/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export {Signer, Signature, SignatureAlgorithm} from "./sign" -export { - Hasher, - HashAlgorithm, - SHA2_256Hasher, - SHA3_256Hasher, - getHasher, -} from "./hash" -export {PublicKey} from "./publicKey" -export {InMemoryPrivateKey, InMemorySigner} from "./elliptic" diff --git a/src/lib/crypto/publicKey.ts b/src/lib/crypto/publicKey.ts deleted file mode 100644 index 4709bd5e..00000000 --- a/src/lib/crypto/publicKey.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface PublicKey { - toBuffer(): Buffer - toHex(): string -} diff --git a/src/lib/crypto/sign.ts b/src/lib/crypto/sign.ts deleted file mode 100644 index 97e1791e..00000000 --- a/src/lib/crypto/sign.ts +++ /dev/null @@ -1,13 +0,0 @@ -export enum SignatureAlgorithm { - ECDSA_P256 = "ECDSA_P256", - ECDSA_secp256k1 = "ECDSA_secp256k1", -} - -export interface Signature { - toBuffer(): Buffer - toHex(): string -} - -export interface Signer { - sign(message: Buffer): Signature -} diff --git a/src/lib/flow/createAccount.ts b/src/lib/flow/createAccount.ts deleted file mode 100644 index 6999432c..00000000 --- a/src/lib/flow/createAccount.ts +++ /dev/null @@ -1,91 +0,0 @@ -import * as dedent from "dedent-js" -import { - ECDSA_P256, - ECDSA_secp256k1, - SHA2_256, - SHA3_256, - encodeKey, -} from "@onflow/util-encode-key" - -import * as crypto from "../crypto" - -import sendTransaction from "./sendTransaction" - -import {AccountAuthorizer} from "./index" - -const sigAlgos = { - [crypto.SignatureAlgorithm.ECDSA_P256]: ECDSA_P256, - [crypto.SignatureAlgorithm.ECDSA_secp256k1]: ECDSA_secp256k1, -} - -const hashAlgos = { - [crypto.HashAlgorithm.SHA2_256]: SHA2_256, - [crypto.HashAlgorithm.SHA3_256]: SHA3_256, -} - -const accountKeyWeight = 1000 - -function txCreateAccount(contracts) { - return dedent` - import FungibleToken from ${contracts.FungibleToken} - import FUSD from ${contracts.FUSD} - - transaction(publicKey: String) { - - let account: AuthAccount - - prepare(signer: AuthAccount) { - self.account = AuthAccount(payer: signer) - } - - execute { - self.account.addPublicKey(publicKey.decodeHex()) - - // Add FUSD vault - self.account.save(<-FUSD.createEmptyVault(), to: /storage/fusdVault) - - self.account.link<&FUSD.Vault{FungibleToken.Receiver}>( - /public/fusdReceiver, - target: /storage/fusdVault - ) - - self.account.link<&FUSD.Vault{FungibleToken.Balance}>( - /public/fusdBalance, - target: /storage/fusdVault - ) - } - } - ` -} - -export async function createAccount( - publicKey: crypto.PublicKey, - sigAlgo: crypto.SignatureAlgorithm, - hashAlgo: crypto.HashAlgorithm, - authorization: AccountAuthorizer, - contracts: {[key: string]: string} -): Promise { - const encodedPublicKey = encodeKey( - publicKey.toHex(), - sigAlgos[sigAlgo], - hashAlgos[hashAlgo], - accountKeyWeight - ) - - const result = await sendTransaction({ - code: txCreateAccount(contracts), - args: [ - { - type: "String", - value: encodedPublicKey, - }, - ], - authorizations: [authorization], - payer: authorization, - proposer: authorization, - }) - - const accountCreatedEvent = result.events[0].data - - return accountCreatedEvent.address -} diff --git a/src/lib/flow/getAuthorization.ts b/src/lib/flow/getAuthorization.ts deleted file mode 100644 index 895608f4..00000000 --- a/src/lib/flow/getAuthorization.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as fcl from "@onflow/fcl" - -import * as crypto from "../crypto" - -import {AccountAuthorizer} from "./index" - -const fromHex = (hex: string) => Buffer.from(hex, "hex") - -export default function getAuthorization( - address: string, - keyIndex: number, - signer: crypto.Signer -): AccountAuthorizer { - return async (account = {}) => { - return { - ...account, - tempId: "SIGNER", - addr: fcl.sansPrefix(address), - keyId: keyIndex, - signingFunction: data => ({ - addr: fcl.withPrefix(address), - keyId: keyIndex, - signature: signer.sign(fromHex(data.message)).toHex(), - }), - } - } -} diff --git a/src/lib/flow/index.ts b/src/lib/flow/index.ts deleted file mode 100644 index 1eda987e..00000000 --- a/src/lib/flow/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import getAuthorization from "./getAuthorization" - -export interface Account { - addr?: string -} - -export interface AccountSignature { - addr: string - keyId: number - signature: string -} - -export interface AccountAuthorization { - tempId: string - addr: string - keyId: number - signingFunction: (data: {message: string}) => AccountSignature -} - -export type AccountAuthorizer = ( - account?: Account -) => Promise - -export {getAuthorization} diff --git a/src/lib/flow/sendTransaction.ts b/src/lib/flow/sendTransaction.ts deleted file mode 100644 index be486c38..00000000 --- a/src/lib/flow/sendTransaction.ts +++ /dev/null @@ -1,53 +0,0 @@ -import * as fcl from "@onflow/fcl" -import * as types from "@onflow/types" - -import {AccountAuthorizer} from "./index" - -export type Argument = { - type: string - value: string -} - -export type Transaction = { - code: string - args: Argument[] - proposer: AccountAuthorizer - authorizations: AccountAuthorizer[] - payer: AccountAuthorizer -} - -export interface Event { - data: any // eslint-disable-line -} - -export type TransactionResult = { - id: string - error: string - events: Event[] -} - -export default async function sendTransaction({ - code, - args, - proposer, - authorizations, - payer, -}: Transaction): Promise { - const response = await fcl.send([ - fcl.transaction(code), - fcl.args(args.map(arg => fcl.arg(arg.value, types[arg.type]))), - fcl.proposer(proposer), - fcl.authorizations(authorizations), - fcl.payer(payer), - fcl.limit(1000), - ]) - - const {transactionId} = response - const {error, events} = await fcl.tx(response).onceSealed() - - return { - id: transactionId, - error: error, - events: events, - } -} diff --git a/src/lib/fungibleTokens/index.ts b/src/lib/fungibleTokens/index.ts deleted file mode 100644 index 3ef3623f..00000000 --- a/src/lib/fungibleTokens/index.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as fcl from "@onflow/fcl" - -import {AccountAuthorizer} from "../flow" -import sendTransaction, {TransactionResult} from "../flow/sendTransaction" - -import transferFLOWTemplate from "./templates/transferFLOW" -import transferFUSDTemplate from "./templates/transferFUSD" - -const tokenFLOW = "flow" -const tokenFUSD = "fusd" - -export const tokens = [tokenFLOW, tokenFUSD] - -type TransactionTemplate = (contracts: {[key: string]: string}) => string - -export function isValidToken(tokenName: string): boolean { - return tokenName == tokenFLOW || tokenName == tokenFUSD -} - -function getTransferTemplate(tokenName: string): TransactionTemplate { - switch (tokenName) { - case tokenFLOW: - return transferFLOWTemplate - case tokenFUSD: - return transferFUSDTemplate - } -} - -export async function transfer( - tokenName: string, - recipient: string, - amount: string, - authorization: AccountAuthorizer, - contracts: {[key: string]: string} -): Promise { - const template = getTransferTemplate(tokenName) - - return await sendTransaction({ - code: template(contracts), - args: [ - { - type: "Address", - value: fcl.withPrefix(recipient), - }, - { - type: "UFix64", - value: amount, - }, - ], - authorizations: [authorization], - payer: authorization, - proposer: authorization, - }) -} diff --git a/src/lib/fungibleTokens/templates/transferFLOW.ts b/src/lib/fungibleTokens/templates/transferFLOW.ts deleted file mode 100644 index 45ff39bd..00000000 --- a/src/lib/fungibleTokens/templates/transferFLOW.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as dedent from "dedent-js" - -export default function template(contracts: {[key: string]: string}): string { - return dedent` - import FungibleToken from ${contracts.FungibleToken} - import FlowToken from ${contracts.FlowToken} - - transaction(recipient: Address, amount: UFix64) { - - let transferVault: @FungibleToken.Vault - - prepare(signer: AuthAccount) { - let vaultRef = signer - .borrow<&FlowToken.Vault>(from: /storage/flowTokenVault)! - - self.transferVault <- vaultRef.withdraw(amount: amount) - } - - execute { - let receiverRef = getAccount(recipient) - .getCapability(/public/flowTokenReceiver) - .borrow<&{FungibleToken.Receiver}>()! - - receiverRef.deposit(from: <-self.transferVault) - } - } - ` -} diff --git a/src/lib/fungibleTokens/templates/transferFUSD.ts b/src/lib/fungibleTokens/templates/transferFUSD.ts deleted file mode 100644 index 03f79d86..00000000 --- a/src/lib/fungibleTokens/templates/transferFUSD.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as dedent from "dedent-js" - -export default function template(contracts: {[key: string]: string}): string { - return dedent` - import FungibleToken from ${contracts.FungibleToken} - import FUSD from ${contracts.FUSD} - - transaction(recipient: Address, amount: UFix64) { - - let transferVault: @FungibleToken.Vault - - prepare(signer: AuthAccount) { - let vaultRef = signer - .borrow<&FUSD.Vault>(from: /storage/fusdVault)! - - self.transferVault <- vaultRef.withdraw(amount: amount) - } - - execute { - let receiverRef = getAccount(recipient) - .getCapability(/public/fusdReceiver) - .borrow<&{FungibleToken.Receiver}>()! - - receiverRef.deposit(from: <-self.transferVault) - } - } - ` -} diff --git a/src/lib/keys/encryption.ts b/src/lib/keys/encryption.ts deleted file mode 100644 index a528cc9b..00000000 --- a/src/lib/keys/encryption.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {createCipheriv, createDecipheriv, randomBytes} from "crypto" - -const encryptionAlgo = "aes-256-ctr" -const ivSize = 16 - -export function encrypt(encryptionKey: Buffer, value: string): string { - const iv = randomBytes(ivSize) - - const cipher = createCipheriv(encryptionAlgo, encryptionKey, iv) - - const encrypted = cipher.update(value, "hex", "hex") - - const ivHex = iv.toString("hex") - - return ivHex + encrypted + cipher.final("hex") -} - -export function decrypt(encryptionKey: Buffer, value: string): string { - const ivHex = value.slice(0, ivSize * 2) - const ciphertextHex = value.slice(ivSize * 2) - - const iv = Buffer.from(ivHex, "hex") - - const decipher = createDecipheriv(encryptionAlgo, encryptionKey, iv) - - const decrypted = decipher.update(ciphertextHex, "hex", "hex") - - return decrypted + decipher.final("hex") -} diff --git a/src/lib/keys/inMemory.ts b/src/lib/keys/inMemory.ts deleted file mode 100644 index 41f38b83..00000000 --- a/src/lib/keys/inMemory.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as crypto from "../crypto" - -import {decrypt, encrypt} from "./encryption" - -import {Key, KeyManager, KeyType} from "./index" - -class InMemoryKey implements Key { - privateKey: crypto.InMemoryPrivateKey - hashAlgo: crypto.HashAlgorithm - - constructor( - privateKey: crypto.InMemoryPrivateKey, - hashAlgo: crypto.HashAlgorithm - ) { - this.privateKey = privateKey - this.hashAlgo = hashAlgo - } - - getPublicKey(): crypto.PublicKey { - return this.privateKey.getPublicKey() - } - - getSignatureAlgorithm(): crypto.SignatureAlgorithm { - return this.privateKey.getSignatureAlgorithm() - } - - getHashAlgorithm(): crypto.HashAlgorithm { - return this.hashAlgo - } - - getSigner(): crypto.Signer { - return new crypto.InMemorySigner(this.privateKey, this.hashAlgo) - } -} - -export default class InMemoryKeyManager implements KeyManager { - static keyType = KeyType.InMemory - - sigAlgo: crypto.SignatureAlgorithm - hashAlgo: crypto.HashAlgorithm - encryptionKey: Buffer - - constructor( - sigAlgo: crypto.SignatureAlgorithm, - hashAlgo: crypto.HashAlgorithm, - encryptionKey?: Buffer - ) { - this.sigAlgo = sigAlgo - this.hashAlgo = hashAlgo - this.encryptionKey = encryptionKey - } - - getKeyType(): KeyType { - return InMemoryKeyManager.keyType - } - - generate(): InMemoryKey { - const privateKey = crypto.InMemoryPrivateKey.generate(this.sigAlgo) - return new InMemoryKey(privateKey, this.hashAlgo) - } - - save(key: InMemoryKey): string { - const hex = key.privateKey.toHex() - - if (this.encryptionKey) { - return encrypt(this.encryptionKey, hex) - } - - return hex - } - - load(value: string): InMemoryKey { - let hex = value - - if (this.encryptionKey) { - hex = decrypt(this.encryptionKey, hex) - } - - const privateKey = crypto.InMemoryPrivateKey.fromHex(hex, this.sigAlgo) - return new InMemoryKey(privateKey, this.hashAlgo) - } -} diff --git a/src/lib/keys/index.ts b/src/lib/keys/index.ts deleted file mode 100644 index b9a400f2..00000000 --- a/src/lib/keys/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as crypto from "../crypto" - -export enum KeyType { - InMemory = "in-memory", - GoogleKMS = "google-kms", -} - -export interface Key { - getPublicKey(): crypto.PublicKey - getSignatureAlgorithm(): crypto.SignatureAlgorithm - getHashAlgorithm(): crypto.HashAlgorithm - getSigner(): crypto.Signer -} - -export interface KeyManager { - getKeyType(): KeyType - generate(): T - save(key: T): string - load(value: string): T -} diff --git a/src/logger.ts b/src/logger.ts deleted file mode 100644 index a22b2d6d..00000000 --- a/src/logger.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as winston from "winston" - -import config from "src/config" - -const enumerateErrorFormat = winston.format(info => { - if (info instanceof Error) { - Object.assign(info, {message: info.stack}) - } - return info -}) - -const logger = winston.createLogger({ - level: config.env === "development" ? "debug" : "info", - format: winston.format.combine( - enumerateErrorFormat(), - config.env === "development" - ? winston.format.colorize() - : winston.format.uncolorize(), - winston.format.splat(), - winston.format.printf(({level, message}) => `${level}: ${message}`) - ), - transports: [ - new winston.transports.Console({ - stderrLevels: ["error"], - }), - ], -}) - -export default logger diff --git a/src/middleware/errors.ts b/src/middleware/errors.ts deleted file mode 100644 index 7c8d4973..00000000 --- a/src/middleware/errors.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as httpStatus from "http-status" -import * as express from "express" - -import config from "src/config" -import logger from "src/logger" -import ApiError from "src/errors/ApiError" - -function errorsMiddleware( - err: ApiError, - req: express.Request, - res: express.Response -): void { - let {statusCode, message} = err - - if (!(err instanceof ApiError)) { - statusCode = httpStatus.INTERNAL_SERVER_ERROR - message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR] as string - } else if ( - config.env === "production" && - statusCode === httpStatus.INTERNAL_SERVER_ERROR - ) { - message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR] as string - } - - const response = { - code: statusCode, - message, - ...(config.env === "development" && {stack: err.stack}), - } - - if (config.env === "development") { - logger.error(err) - } - - res.status(statusCode).send(response) -} - -export default errorsMiddleware diff --git a/src/middleware/morgan.ts b/src/middleware/morgan.ts deleted file mode 100644 index 280cec22..00000000 --- a/src/middleware/morgan.ts +++ /dev/null @@ -1,19 +0,0 @@ -import * as morgan from "morgan" - -import logger from "src/logger" - -const stream = { - write: message => logger.info(message), -} - -const skip = () => { - const env = process.env.NODE_ENV || "development" - return env !== "development" -} - -const morganMiddleware = morgan( - ":method :url :status :res[content-length] - :response-time ms", - {stream, skip} -) - -export default morganMiddleware diff --git a/src/routes/v1/accounts/fungibleTokens/index.ts b/src/routes/v1/accounts/fungibleTokens/index.ts deleted file mode 100644 index 55a4eb27..00000000 --- a/src/routes/v1/accounts/fungibleTokens/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import * as express from "express" - -import FungibleTokensController from "src/controllers/fungibleTokens" -import catchAsync from "src/errors/catchAsync" - -import createWithdrawalsRouter from "./withdrawals" - -function createRouter( - fungibleTokens: FungibleTokensController -): express.Router { - const router = express.Router({mergeParams: true}) - - const withdrawalsRouter = createWithdrawalsRouter(fungibleTokens) - - router - .route("/") - .get(catchAsync((req, res) => fungibleTokens.getTokens(req, res))) - - router - .route("/:tokenName") - .get(catchAsync((req, res) => fungibleTokens.getToken(req, res))) - - router.use("/:tokenName/withdrawals", withdrawalsRouter) - - return router -} - -export default createRouter diff --git a/src/routes/v1/accounts/fungibleTokens/withdrawals.ts b/src/routes/v1/accounts/fungibleTokens/withdrawals.ts deleted file mode 100644 index 0cc104aa..00000000 --- a/src/routes/v1/accounts/fungibleTokens/withdrawals.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as express from "express" - -import FungibleTokensController from "src/controllers/fungibleTokens" -import catchAsync from "src/errors/catchAsync" - -function createRouter( - fungibleTokens: FungibleTokensController -): express.Router { - const router = express.Router({mergeParams: true}) - - router - .route("/") - .get(catchAsync((req, res) => fungibleTokens.getWithdrawals(req, res))) - .post(catchAsync((req, res) => fungibleTokens.createWithdrawal(req, res))) - - router - .route("/:transactionId") - .get(catchAsync((req, res) => fungibleTokens.getWithdrawal(req, res))) - - return router -} - -export default createRouter diff --git a/src/routes/v1/accounts/index.ts b/src/routes/v1/accounts/index.ts deleted file mode 100644 index 38a09286..00000000 --- a/src/routes/v1/accounts/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as express from "express" - -import AccountsController from "src/controllers/accounts" -import FungibleTokensController from "src/controllers/fungibleTokens" -import TransactionsController from "src/controllers/transactions" -import catchAsync from "src/errors/catchAsync" - -import createFungibleTokensRouter from "./fungibleTokens" -import createTransactionsRouter from "./transactions" - -function createRouter( - accounts: AccountsController, - transactions: TransactionsController, - fungibleTokens: FungibleTokensController -): express.Router { - const router = express.Router() - - const transactionsRouter = createTransactionsRouter(transactions) - const fungibleTokensRouter = createFungibleTokensRouter(fungibleTokens) - - router - .route("/") - .get(catchAsync((req, res) => accounts.getAccounts(req, res))) - .post(catchAsync((req, res) => accounts.createAccount(req, res))) - - router - .route("/:address") - .get(catchAsync((req, res) => accounts.getAccount(req, res))) - - router.use("/:address/transactions", transactionsRouter) - router.use("/:address/fungible-tokens", fungibleTokensRouter) - - return router -} - -export default createRouter diff --git a/src/routes/v1/accounts/transactions.ts b/src/routes/v1/accounts/transactions.ts deleted file mode 100644 index 7101ede4..00000000 --- a/src/routes/v1/accounts/transactions.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as express from "express" - -import TransactionsController from "src/controllers/transactions" -import catchAsync from "src/errors/catchAsync" - -function createRouter(transactions: TransactionsController): express.Router { - const router = express.Router({mergeParams: true}) - - router - .route("/") - .get(catchAsync((req, res) => transactions.getTransactions(req, res))) - .post(catchAsync((req, res) => transactions.createTransaction(req, res))) - - router - .route("/:transactionId") - .get(catchAsync((req, res) => transactions.getTransaction(req, res))) - - return router -} - -export default createRouter diff --git a/src/routes/v1/index.ts b/src/routes/v1/index.ts deleted file mode 100644 index 0bef210e..00000000 --- a/src/routes/v1/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as express from "express" - -import AccountsController from "src/controllers/accounts" -import FungibleTokensController from "src/controllers/fungibleTokens" -import TransactionsController from "src/controllers/transactions" - -import createAccountsRouter from "./accounts" - -function createRouter( - accounts: AccountsController, - transactions: TransactionsController, - fungibleTokens: FungibleTokensController -): express.Router { - const router = express.Router() - - const accountsRouter = createAccountsRouter( - accounts, - transactions, - fungibleTokens - ) - - router.use("/accounts", accountsRouter) - - return router -} - -export default createRouter diff --git a/src/services/accounts.ts b/src/services/accounts.ts deleted file mode 100644 index d7fa1e82..00000000 --- a/src/services/accounts.ts +++ /dev/null @@ -1,94 +0,0 @@ -import {PrismaClient} from "@prisma/client" - -import {AccountAuthorizer, getAuthorization} from "src/lib/flow" -import getLeastRecentAdminSignerKey from "src/database/getLeastRecentAdminSignerKey" -import * as models from "src/database/models" -import { - getAccount, - getAccountKey, - insertAccount, - listAccounts, -} from "src/database/accounts" -import {createAccount} from "src/lib/flow/createAccount" -import {Key, KeyManager} from "src/lib/keys" -import config from "src/config" - -import Service from "./service" - -const adminAccount: models.Account = { - address: config.adminAddress, - createdAt: null, - updatedAt: null, -} - -export default class AccountsService extends Service { - private adminKey: Key - private userKeyManager: KeyManager - - constructor( - prisma: PrismaClient, - adminKey: Key, - userKeyManager: KeyManager - ) { - super(prisma) - this.adminKey = adminKey - this.userKeyManager = userKeyManager - } - - async create(): Promise { - const adminAuthorization = await this.getAdminAuthorization() - - const userKey = this.userKeyManager.generate() - - const address = await createAccount( - userKey.getPublicKey(), - userKey.getSignatureAlgorithm(), - userKey.getHashAlgorithm(), - adminAuthorization, - config.contracts - ) - - const userKeyType = this.userKeyManager.getKeyType() - const userKeyValue = this.userKeyManager.save(userKey) - - return await insertAccount(this.prisma, address, userKeyType, userKeyValue) - } - - async query(): Promise { - // TODO: add pagination - - const accounts = await listAccounts(this.prisma) - - return [adminAccount, ...accounts] - } - - async getByAddress(address: string): Promise { - if (address === adminAccount.address) { - return adminAccount - } - - return await getAccount(this.prisma, address) - } - - async getAuthorization(address: string): Promise { - if (address === adminAccount.address) { - return this.getAdminAuthorization() - } - - const accountKey = await getAccountKey(this.prisma, address) - - const signer = this.userKeyManager.load(accountKey.value).getSigner() - - return getAuthorization(address, accountKey.index, signer) - } - - private async getAdminAuthorization(): Promise { - const adminKeyIndex = await getLeastRecentAdminSignerKey(this.prisma) - - return getAuthorization( - adminAccount.address, - adminKeyIndex, - this.adminKey.getSigner() - ) - } -} diff --git a/src/services/fungibleTokens.ts b/src/services/fungibleTokens.ts deleted file mode 100644 index 0c12ca7b..00000000 --- a/src/services/fungibleTokens.ts +++ /dev/null @@ -1,50 +0,0 @@ -import {PrismaClient} from "@prisma/client" - -import config from "src/config" -import {transfer, tokens} from "src/lib/fungibleTokens" - -import Service from "./service" -import AccountsService from "./accounts" - -type FungibleToken = { - name: string -} - -const makeToken = tokenName => ({name: tokenName}) -const allTokens = tokens.map(tokenName => makeToken(tokenName)) - -export default class FungibleTokensService extends Service { - private accounts: AccountsService - - constructor(prisma: PrismaClient, accounts: AccountsService) { - super(prisma) - this.accounts = accounts - } - - async query(): Promise { - return allTokens - } - - async getByName(tokenName: string): Promise { - return makeToken(tokenName) - } - - async createWithdrawal( - sender: string, - recipient: string, - tokenName: string, - amount: string - ): Promise { - const userAuthorization = await this.accounts.getAuthorization(sender) - - const {id} = await transfer( - tokenName, - recipient, - amount, - userAuthorization, - config.contracts - ) - - return id - } -} diff --git a/src/services/service.ts b/src/services/service.ts deleted file mode 100644 index d2eabdb5..00000000 --- a/src/services/service.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {PrismaClient} from "@prisma/client" - -export default class Service { - protected prisma: PrismaClient - - constructor(prisma: PrismaClient) { - this.prisma = prisma - } -} diff --git a/src/services/transactions.ts b/src/services/transactions.ts deleted file mode 100644 index 8aab5024..00000000 --- a/src/services/transactions.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {PrismaClient} from "@prisma/client" - -import sendTransaction, {Argument} from "src/lib/flow/sendTransaction" - -import Service from "./service" -import AccountsService from "./accounts" - -export default class TransactionsService extends Service { - private accounts: AccountsService - - constructor(prisma: PrismaClient, accounts: AccountsService) { - super(prisma) - this.accounts = accounts - } - - async createTransaction( - signer: string, - code: string, - args: Argument[] - ): Promise { - const userAuthorization = await this.accounts.getAuthorization(signer) - - const {id} = await sendTransaction({ - code, - args, - proposer: userAuthorization, - authorizations: [userAuthorization], - payer: userAuthorization, - }) - - return id - } -} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index f5fb6e14..00000000 --- a/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "moduleResolution": "node", - "outDir": "./dist", - "target": "es6", - "baseUrl": "src", - "paths": { - "src/*": ["*"] - } - }, - "include": ["./src/**/*"] -} From ffdf36f760f9bbba9da82adde41dd07561b683f4 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 22:40:49 -0700 Subject: [PATCH 076/112] Pin emulator version --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index c89c6514..1d304962 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,7 @@ services: - emulator emulator: - image: gcr.io/flow-container-registry/emulator:latest + image: gcr.io/flow-container-registry/emulator:v0.22.0 ports: - "3569:3569" - "8080:8080" From f5c2c71fdf658aa39a06c49dcf1645a308ab7da4 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 22:53:38 -0700 Subject: [PATCH 077/112] Update example --- examples/nextjs-fusd-provider/README.md | 2 +- .../nextjs-fusd-provider/package-lock.json | 531 +----------------- 2 files changed, 17 insertions(+), 516 deletions(-) diff --git a/examples/nextjs-fusd-provider/README.md b/examples/nextjs-fusd-provider/README.md index 65b58716..148c4968 100644 --- a/examples/nextjs-fusd-provider/README.md +++ b/examples/nextjs-fusd-provider/README.md @@ -25,7 +25,7 @@ cp .env.example .env Start the Wallet API, Flow Emulator and Postgres: ```sh -docker-compose up +docker-compose up -d ``` Deploy the FUSD contract to the emulator: diff --git a/examples/nextjs-fusd-provider/package-lock.json b/examples/nextjs-fusd-provider/package-lock.json index eec41d5c..b255f657 100644 --- a/examples/nextjs-fusd-provider/package-lock.json +++ b/examples/nextjs-fusd-provider/package-lock.json @@ -12,30 +12,6 @@ "@babel/highlight": "^7.10.4" } }, - "@babel/helper-module-imports": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz", - "integrity": "sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==", - "requires": { - "@babel/types": "^7.14.5" - }, - "dependencies": { - "@babel/types": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.5.tgz", - "integrity": "sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg==", - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-plugin-utils": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", - "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==" - }, "@babel/helper-validator-identifier": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", @@ -51,18 +27,11 @@ "js-tokens": "^4.0.0" } }, - "@babel/plugin-syntax-jsx": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz", - "integrity": "sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, "@babel/runtime": { "version": "7.14.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz", "integrity": "sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==", + "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } @@ -87,137 +56,6 @@ "to-fast-properties": "^2.0.0" } }, - "@emotion/babel-plugin": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.3.0.tgz", - "integrity": "sha512-UZKwBV2rADuhRp+ZOGgNWg2eYgbzKzQXfQPtJbu/PLy8onurxlNCLvxMQEvlr1/GudguPI5IU9qIY1+2z1M5bA==", - "requires": { - "@babel/helper-module-imports": "^7.12.13", - "@babel/plugin-syntax-jsx": "^7.12.13", - "@babel/runtime": "^7.13.10", - "@emotion/hash": "^0.8.0", - "@emotion/memoize": "^0.7.5", - "@emotion/serialize": "^1.0.2", - "babel-plugin-macros": "^2.6.1", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "^4.0.3" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" - }, - "stylis": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz", - "integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==" - } - } - }, - "@emotion/cache": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.4.0.tgz", - "integrity": "sha512-Zx70bjE7LErRO9OaZrhf22Qye1y4F7iDl+ITjet0J+i+B88PrAOBkKvaAWhxsZf72tDLajwCgfCjJ2dvH77C3g==", - "requires": { - "@emotion/memoize": "^0.7.4", - "@emotion/sheet": "^1.0.0", - "@emotion/utils": "^1.0.0", - "@emotion/weak-memoize": "^0.2.5", - "stylis": "^4.0.3" - }, - "dependencies": { - "stylis": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz", - "integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==" - } - } - }, - "@emotion/hash": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" - }, - "@emotion/is-prop-valid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.0.tgz", - "integrity": "sha512-9RkilvXAufQHsSsjQ3PIzSns+pxuX4EW8EbGeSPjZMHuMx6z/MOzb9LpqNieQX4F3mre3NWS2+X3JNRHTQztUQ==", - "requires": { - "@emotion/memoize": "^0.7.4" - } - }, - "@emotion/memoize": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz", - "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==" - }, - "@emotion/react": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.4.0.tgz", - "integrity": "sha512-4XklWsl9BdtatLoJpSjusXhpKv9YVteYKh9hPKP1Sxl+mswEFoUe0WtmtWjxEjkA51DQ2QRMCNOvKcSlCQ7ivg==", - "requires": { - "@babel/runtime": "^7.13.10", - "@emotion/cache": "^11.4.0", - "@emotion/serialize": "^1.0.2", - "@emotion/sheet": "^1.0.1", - "@emotion/utils": "^1.0.0", - "@emotion/weak-memoize": "^0.2.5", - "hoist-non-react-statics": "^3.3.1" - } - }, - "@emotion/serialize": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz", - "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==", - "requires": { - "@emotion/hash": "^0.8.0", - "@emotion/memoize": "^0.7.4", - "@emotion/unitless": "^0.7.5", - "@emotion/utils": "^1.0.0", - "csstype": "^3.0.2" - } - }, - "@emotion/sheet": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.0.1.tgz", - "integrity": "sha512-GbIvVMe4U+Zc+929N1V7nW6YYJtidj31lidSmdYcWozwoBIObXBnaJkKNDjZrLm9Nc0BR+ZyHNaRZxqNZbof5g==" - }, - "@emotion/styled": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.3.0.tgz", - "integrity": "sha512-fUoLcN3BfMiLlRhJ8CuPUMEyKkLEoM+n+UyAbnqGEsCd5IzKQ7VQFLtzpJOaCD2/VR2+1hXQTnSZXVJeiTNltA==", - "requires": { - "@babel/runtime": "^7.13.10", - "@emotion/babel-plugin": "^11.3.0", - "@emotion/is-prop-valid": "^1.1.0", - "@emotion/serialize": "^1.0.2", - "@emotion/utils": "^1.0.0" - } - }, - "@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" - }, - "@emotion/utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz", - "integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA==" - }, - "@emotion/weak-memoize": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", - "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" - }, "@eslint/eslintrc": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", @@ -270,11 +108,6 @@ "resolved": "https://registry.npmjs.org/@geist-ui/react-icons/-/react-icons-1.0.1.tgz", "integrity": "sha512-/phre1NwXT0Mrxcq3yb+KksJ16/ykptgxI7+jh5pmDZfxnIMPlBWFBGDK0yw96oi752Wuo24X6+tkWjVe/g5Bg==" }, - "@hackclub/theme": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@hackclub/theme/-/theme-0.3.1.tgz", - "integrity": "sha512-Kl1emuTu+LQdo+g1RqOwUr8Q3eV/A9Xn6hcwwhiKnsI2hFnSVxjvP1ZKTzdUdQjhSqgl2FAMg84AAn7ZjZRJWg==" - }, "@hapi/accept": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@hapi/accept/-/accept-5.0.2.tgz", @@ -297,11 +130,6 @@ "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.0.tgz", "integrity": "sha512-sqKVVVOe5ivCaXDWivIJYVSaEgdQK9ul7a4Kity5Iw7u9+wBAPbX1RMSnLLmp7O4Vzj0WOWwMAJsTL00xwaNug==" }, - "@mdx-js/react": { - "version": "1.6.22", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", - "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==" - }, "@next/env": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-11.0.1.tgz", @@ -384,212 +212,6 @@ "integrity": "sha512-Myxw//kzromB9yWgS8qYGuGVf91oBUUJpNvy5eM50sqvmKLbKjwLxohJnkWGTeeI9v9IBMtPLxz5Gc60FIfvCA==", "dev": true }, - "@styled-system/background": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/background/-/background-5.1.2.tgz", - "integrity": "sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==", - "requires": { - "@styled-system/core": "^5.1.2" - } - }, - "@styled-system/border": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@styled-system/border/-/border-5.1.5.tgz", - "integrity": "sha512-JvddhNrnhGigtzWRCVuAHepniyVi6hBlimxWDVAdcTuk7aRn9BYJUwfHslURtwYFsF5FoEs8Zmr1oZq2M1AP0A==", - "requires": { - "@styled-system/core": "^5.1.2" - } - }, - "@styled-system/color": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/color/-/color-5.1.2.tgz", - "integrity": "sha512-1kCkeKDZkt4GYkuFNKc7vJQMcOmTl3bJY3YBUs7fCNM6mMYJeT1pViQ2LwBSBJytj3AB0o4IdLBoepgSgGl5MA==", - "requires": { - "@styled-system/core": "^5.1.2" - } - }, - "@styled-system/core": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/core/-/core-5.1.2.tgz", - "integrity": "sha512-XclBDdNIy7OPOsN4HBsawG2eiWfCcuFt6gxKn1x4QfMIgeO6TOlA2pZZ5GWZtIhCUqEPTgIBta6JXsGyCkLBYw==", - "requires": { - "object-assign": "^4.1.1" - } - }, - "@styled-system/css": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@styled-system/css/-/css-5.1.5.tgz", - "integrity": "sha512-XkORZdS5kypzcBotAMPBoeckDs9aSZVkvrAlq5K3xP8IMAUek+x2O4NtwoSgkYkWWzVBu6DGdFZLR790QWGG+A==" - }, - "@styled-system/flexbox": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/flexbox/-/flexbox-5.1.2.tgz", - "integrity": "sha512-6hHV52+eUk654Y1J2v77B8iLeBNtc+SA3R4necsu2VVinSD7+XY5PCCEzBFaWs42dtOEDIa2lMrgL0YBC01mDQ==", - "requires": { - "@styled-system/core": "^5.1.2" - } - }, - "@styled-system/grid": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/grid/-/grid-5.1.2.tgz", - "integrity": "sha512-K3YiV1KyHHzgdNuNlaw8oW2ktMuGga99o1e/NAfTEi5Zsa7JXxzwEnVSDSBdJC+z6R8WYTCYRQC6bkVFcvdTeg==", - "requires": { - "@styled-system/core": "^5.1.2" - } - }, - "@styled-system/layout": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/layout/-/layout-5.1.2.tgz", - "integrity": "sha512-wUhkMBqSeacPFhoE9S6UF3fsMEKFv91gF4AdDWp0Aym1yeMPpqz9l9qS/6vjSsDPF7zOb5cOKC3tcKKOMuDCPw==", - "requires": { - "@styled-system/core": "^5.1.2" - } - }, - "@styled-system/position": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/position/-/position-5.1.2.tgz", - "integrity": "sha512-60IZfMXEOOZe3l1mCu6sj/2NAyUmES2kR9Kzp7s2D3P4qKsZWxD1Se1+wJvevb+1TP+ZMkGPEYYXRyU8M1aF5A==", - "requires": { - "@styled-system/core": "^5.1.2" - } - }, - "@styled-system/shadow": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/shadow/-/shadow-5.1.2.tgz", - "integrity": "sha512-wqniqYb7XuZM7K7C0d1Euxc4eGtqEe/lvM0WjuAFsQVImiq6KGT7s7is+0bNI8O4Dwg27jyu4Lfqo/oIQXNzAg==", - "requires": { - "@styled-system/core": "^5.1.2" - } - }, - "@styled-system/should-forward-prop": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@styled-system/should-forward-prop/-/should-forward-prop-5.1.5.tgz", - "integrity": "sha512-+rPRomgCGYnUIaFabDoOgpSDc4UUJ1KsmlnzcEp0tu5lFrBQKgZclSo18Z1URhaZm7a6agGtS5Xif7tuC2s52Q==", - "requires": { - "@emotion/is-prop-valid": "^0.8.1", - "@emotion/memoize": "^0.7.1", - "styled-system": "^5.1.5" - }, - "dependencies": { - "@emotion/is-prop-valid": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", - "requires": { - "@emotion/memoize": "0.7.4" - }, - "dependencies": { - "@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==" - } - } - } - } - }, - "@styled-system/space": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/space/-/space-5.1.2.tgz", - "integrity": "sha512-+zzYpR8uvfhcAbaPXhH8QgDAV//flxqxSjHiS9cDFQQUSznXMQmxJegbhcdEF7/eNnJgHeIXv1jmny78kipgBA==", - "requires": { - "@styled-system/core": "^5.1.2" - } - }, - "@styled-system/typography": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@styled-system/typography/-/typography-5.1.2.tgz", - "integrity": "sha512-BxbVUnN8N7hJ4aaPOd7wEsudeT7CxarR+2hns8XCX1zp0DFfbWw4xYa/olA0oQaqx7F1hzDg+eRaGzAJbF+jOg==", - "requires": { - "@styled-system/core": "^5.1.2" - } - }, - "@styled-system/variant": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@styled-system/variant/-/variant-5.1.5.tgz", - "integrity": "sha512-Yn8hXAFoWIro8+Q5J8YJd/mP85Teiut3fsGVR9CAxwgNfIAiqlYxsk5iHU7VHJks/0KjL4ATSjmbtCDC/4l1qw==", - "requires": { - "@styled-system/core": "^5.1.2", - "@styled-system/css": "^5.1.5" - } - }, - "@theme-ui/color-modes": { - "version": "0.6.0-alpha.8", - "resolved": "https://registry.npmjs.org/@theme-ui/color-modes/-/color-modes-0.6.0-alpha.8.tgz", - "integrity": "sha512-40NJmJHrSQ5O46wz0A4aXwqRtBLvQw2Ugv3ChTrq9fqmGPeCXwlmHIAt4MBLD5mBZ0GaS7Nua5n+t+74OZEDdw==", - "requires": { - "@emotion/react": "^11.1.1", - "@theme-ui/core": "0.6.0-alpha.8", - "@theme-ui/css": "0.6.0-alpha.8", - "deepmerge": "^4.2.2" - } - }, - "@theme-ui/components": { - "version": "0.6.0-alpha.8", - "resolved": "https://registry.npmjs.org/@theme-ui/components/-/components-0.6.0-alpha.8.tgz", - "integrity": "sha512-uY33Kvvj4e+vZGH63p1s9fuGQA8odZeGrSPUqwod3JxS6akDppIugkFLUMrhEgohfKcpRgblYx1gpcC9tlY3eQ==", - "requires": { - "@emotion/react": "^11.1.1", - "@emotion/styled": "^11.0.0", - "@styled-system/color": "^5.1.2", - "@styled-system/should-forward-prop": "^5.1.2", - "@styled-system/space": "^5.1.2", - "@theme-ui/css": "0.6.0-alpha.8", - "@types/styled-system": "^5.1.10" - } - }, - "@theme-ui/core": { - "version": "0.6.0-alpha.8", - "resolved": "https://registry.npmjs.org/@theme-ui/core/-/core-0.6.0-alpha.8.tgz", - "integrity": "sha512-2NH1UG2f29nD+Ri1xLFQJeX4SWU5hOCaV/8UHQ0E0bkOhUCPe7ct2gd/jfNmNG/3+8Q9fDMphG1ZPz1va2G1uA==", - "requires": { - "@emotion/react": "^11.1.1", - "@theme-ui/css": "0.6.0-alpha.8", - "@theme-ui/parse-props": "0.6.0-alpha.8", - "deepmerge": "^4.2.2" - } - }, - "@theme-ui/css": { - "version": "0.6.0-alpha.8", - "resolved": "https://registry.npmjs.org/@theme-ui/css/-/css-0.6.0-alpha.8.tgz", - "integrity": "sha512-Gk8onu12Sz29LwPsYbfHo/KqZtTE/D2+Qxa1XidaGa2xGom5SdgJpTVZ3t0uYuuOtFXUJ/Bp/pQLe6CIBu1Jfw==", - "requires": { - "@emotion/react": "^11.1.1", - "csstype": "^3.0.5" - } - }, - "@theme-ui/mdx": { - "version": "0.6.0-alpha.8", - "resolved": "https://registry.npmjs.org/@theme-ui/mdx/-/mdx-0.6.0-alpha.8.tgz", - "integrity": "sha512-cq7XHavSBdP7wi44JItwDTkt/rLX0X3YmutXBvjqAsj2Exitc2XT3jfk/qJoN/+lD70HqxSVTSB3grhcSl61Ug==", - "requires": { - "@emotion/react": "^11.1.1", - "@emotion/styled": "^11.0.0", - "@mdx-js/react": "^1.6.22", - "@theme-ui/core": "0.6.0-alpha.8", - "@theme-ui/css": "0.6.0-alpha.8" - } - }, - "@theme-ui/parse-props": { - "version": "0.6.0-alpha.8", - "resolved": "https://registry.npmjs.org/@theme-ui/parse-props/-/parse-props-0.6.0-alpha.8.tgz", - "integrity": "sha512-nrZr/Vnda19lfCVL+kq+4CCTOBFR8gxuD3HThO+CjVNyOn+rSUFEj6dKO40SYTbVH6BApGCE/0J87NJZQ3Cy2Q==", - "requires": { - "@emotion/react": "^11.1.1", - "@theme-ui/css": "0.6.0-alpha.8" - } - }, - "@theme-ui/theme-provider": { - "version": "0.6.0-alpha.8", - "resolved": "https://registry.npmjs.org/@theme-ui/theme-provider/-/theme-provider-0.6.0-alpha.8.tgz", - "integrity": "sha512-RdHJKeCfkNS46lcs6MV3tRnx8efzI7OI6reMJKGnqx4n0D7NLFZMZJZgdiqaiFcev5Lli8cfRePMZYbU6UZ8oA==", - "requires": { - "@emotion/react": "^11.1.1", - "@theme-ui/color-modes": "0.6.0-alpha.8", - "@theme-ui/core": "0.6.0-alpha.8", - "@theme-ui/mdx": "0.6.0-alpha.8" - } - }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -601,19 +223,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.5.tgz", "integrity": "sha512-se3yX7UHv5Bscf8f1ERKvQOD6sTyycH3hdaoozvaLxgUiY5lIGEeH37AD0G0Qi9kPqihPn0HOfd2yaIEN9VwEg==" }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "@types/styled-system": { - "version": "5.1.11", - "resolved": "https://registry.npmjs.org/@types/styled-system/-/styled-system-5.1.11.tgz", - "integrity": "sha512-R+JxEZYa5T0HD2urViR/mdklVaGhwbNOtDoWWGQ1+z1CGs/gF1UAKCaS//YwsUwterEKpyaKxgaXyFNKF04GCA==", - "requires": { - "csstype": "^3.0.2" - } - }, "@typescript-eslint/parser": { "version": "4.28.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.0.tgz", @@ -848,16 +457,6 @@ "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", "dev": true }, - "babel-plugin-macros": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", - "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", - "requires": { - "@babel/runtime": "^7.7.2", - "cosmiconfig": "^6.0.0", - "resolve": "^1.12.0" - } - }, "babel-plugin-syntax-jsx": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", @@ -1027,7 +626,8 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true }, "caniuse-lite": { "version": "1.0.30001240", @@ -1174,31 +774,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - }, - "dependencies": { - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - } - } - }, "create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -1290,11 +865,6 @@ "cssnano-preset-simple": "^2.0.0" } }, - "csstype": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", - "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" - }, "damerau-levenshtein": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz", @@ -1321,11 +891,6 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -1446,6 +1011,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -1965,11 +1531,6 @@ "pkg-dir": "^4.1.0" } }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -2161,21 +1722,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "requires": { - "react-is": "^16.7.0" - }, - "dependencies": { - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } - } - }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -2230,6 +1776,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2278,7 +1825,8 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true }, "is-bigint": { "version": "1.0.2", @@ -2310,6 +1858,7 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, "requires": { "has": "^1.0.3" } @@ -2453,11 +2002,6 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2513,11 +2057,6 @@ "type-check": "~0.4.0" } }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" - }, "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -3112,6 +2651,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "requires": { "callsites": "^3.0.0" } @@ -3164,12 +2704,14 @@ "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true }, "pbkdf2": { "version": "3.1.2", @@ -3522,6 +3064,7 @@ "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, "requires": { "is-core-module": "^2.2.0", "path-parse": "^1.0.6" @@ -3530,7 +3073,8 @@ "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true }, "reusify": { "version": "1.0.4", @@ -3877,26 +3421,6 @@ } } }, - "styled-system": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/styled-system/-/styled-system-5.1.5.tgz", - "integrity": "sha512-7VoD0o2R3RKzOzPK0jYrVnS8iJdfkKsQJNiLRDjikOpQVqQHns/DXWaPZOH4tIKkhAT7I6wIsy9FWTWh2X3q+A==", - "requires": { - "@styled-system/background": "^5.1.2", - "@styled-system/border": "^5.1.5", - "@styled-system/color": "^5.1.2", - "@styled-system/core": "^5.1.2", - "@styled-system/flexbox": "^5.1.2", - "@styled-system/grid": "^5.1.2", - "@styled-system/layout": "^5.1.2", - "@styled-system/position": "^5.1.2", - "@styled-system/shadow": "^5.1.2", - "@styled-system/space": "^5.1.2", - "@styled-system/typography": "^5.1.2", - "@styled-system/variant": "^5.1.5", - "object-assign": "^4.1.1" - } - }, "stylis": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz", @@ -3955,24 +3479,6 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, - "theme-ui": { - "version": "0.6.0-alpha.8", - "resolved": "https://registry.npmjs.org/theme-ui/-/theme-ui-0.6.0-alpha.8.tgz", - "integrity": "sha512-I7d1OOSVR+byk3he8m++eS3TeMUXPtY/gQwdUPUhvvvJRjW6qMyBy+V0KH98kpYypevoR3ir+D2xZLe9CUpRZA==", - "requires": { - "@theme-ui/color-modes": "0.6.0-alpha.8", - "@theme-ui/components": "0.6.0-alpha.8", - "@theme-ui/core": "0.6.0-alpha.8", - "@theme-ui/css": "0.6.0-alpha.8", - "@theme-ui/mdx": "0.6.0-alpha.8", - "@theme-ui/theme-provider": "0.6.0-alpha.8" - } - }, - "theme-ui-preset-geist": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/theme-ui-preset-geist/-/theme-ui-preset-geist-0.0.2.tgz", - "integrity": "sha512-G2CK8gDM2XOSEZq4RipA/0oiVs6hUl87EbzdkdCY96fU4VRiAQLyTJxdXN/wjOddr4uyPkzGqX/iNZReJ3KBuQ==" - }, "timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -4239,11 +3745,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" - }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", From 7106158002154a559ee40520eef70b3b818693d2 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 22:53:43 -0700 Subject: [PATCH 078/112] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d59801f..81ca66a1 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ cp .env.example .env Start the Wallet API, Flow Emulator and Postgres: ```sh -docker-compose up +docker-compose up -d ``` Deploy the FUSD contract to the emulator: @@ -71,6 +71,12 @@ Next, see the [FUSD sample app](/examples/nextjs-fusd-provider) for an example of how to use this configuration as part of a complete application. +Once you're finished, run this to stop the containers: + +```sh +docker-compose down +``` + ## Configuration ### Enabled fungible tokens From bd5c2df48790c5a1e6b4049298b7adfc7a735464 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 22:53:46 -0700 Subject: [PATCH 079/112] Update API.md --- API.md | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/API.md b/API.md index c2a4d09e..111850aa 100644 --- a/API.md +++ b/API.md @@ -6,35 +6,28 @@ These routes are also defined in the [OpenAPI specification for this service](op ## Functionality -### 1. Admin +### Accounts -- [x] Single admin account (hot wallet) -- [x] Create user accounts (using admin account) +- [x] Initialize the API with a root admin account +- [x] Create a new Flow account -### 2. Transaction Execution +### Transactions -- [x] Send an arbitrary transaction from the admin account -- [x] Send an arbitrary transaction from a user account +- [x] Send a transaction signed by an account +- [x] Get the transaction history for an account ### 3. Fungible Tokens -- [x] Send fungible token withdrawals from admin account (FLOW, FUSD) -- [x] Detect fungible token deposits to admin account (FLOW, FUSD) -- [x] Send fungible token withdrawals from a user account (FLOW, FUSD) -- [x] Detect fungible token deposits to a user account (FLOW, FUSD) -- [x] View the fungible token balance of the admin account -- [x] View the fungible token balance of a user account +- [x] Send fungible token withdrawals from an account (FLOW, FUSD) +- [x] Detect fungible token deposits to an account (FLOW, FUSD) +- [x] View the fungible token balance of an account ### 4. Non-Fungible Tokens -- [ ] Set up admin account with non-fungible token collections (`NFT.Collection`) -- [ ] Send non-fungible token withdrawals from admin account -- [ ] Detect non-fungible token deposits to admin account -- [ ] Set up a user account with non-fungible token collections (`NFT.Collection`) -- [ ] Send non-fungible token withdrawals from a user account -- [ ] Detect non-fungible token deposits to a user account -- [ ] View the non-fungible tokens owned by the admin account -- [ ] View the non-fungible tokens owned by a user account +- [ ] Set up an account with non-fungible token collections (`NFT.Collection`) +- [ ] Send non-fungible token withdrawals from an account +- [ ] Detect non-fungible token deposits to an account +- [ ] View the non-fungible tokens owned by an account --- From 8e3075cca7a901e7cdb50b08014598cdd6282342 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Sun, 27 Jun 2021 23:05:16 -0700 Subject: [PATCH 080/112] Update API.md --- API.md | 310 +-------------------------------------------------------- 1 file changed, 4 insertions(+), 306 deletions(-) diff --git a/API.md b/API.md index 111850aa..2afa7bc4 100644 --- a/API.md +++ b/API.md @@ -1,6 +1,7 @@ # Wallet API REST HTTP Routes -The documents contains the REST routes provided by the Flow Wallet API. +TODO: this document will contain the REST +routes provided by the Flow Wallet API. These routes are also defined in the [OpenAPI specification for this service](openapi.yml). @@ -18,316 +19,13 @@ These routes are also defined in the [OpenAPI specification for this service](op ### 3. Fungible Tokens -- [x] Send fungible token withdrawals from an account (FLOW, FUSD) +- [x] Send a fungible token withdrawal from an account (FLOW, FUSD) - [x] Detect fungible token deposits to an account (FLOW, FUSD) - [x] View the fungible token balance of an account ### 4. Non-Fungible Tokens - [ ] Set up an account with non-fungible token collections (`NFT.Collection`) -- [ ] Send non-fungible token withdrawals from an account +- [ ] Send a non-fungible token withdrawal from an account - [ ] Detect non-fungible token deposits to an account - [ ] View the non-fungible tokens owned by an account - ---- - -## Accounts - -### List all accounts - -`GET /v1/accounts` - -Example - -```sh -curl --request GET \ - --url http://localhost:3000/v1/accounts -``` - -```json -[ - { - "address": "0xf8d6e0586b0a20c7" - }, - { - "address": "0xe467b9dd11fa00df" - } -] -``` - ---- - -### Get an account - -`GET /v1/accounts/{address}` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - -Example - -```sh -curl --request GET \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7 -``` - -```json -{ - "address": "0xf8d6e0586b0a20c7" -} -``` - ---- - -### Create an account - -`POST /v1/accounts` - -Example - -```sh -curl --request POST \ - --url http://localhost:3000/v1/accounts -``` - -```json -{ - "address": "0xe467b9dd11fa00df" -} -``` - ---- - -## Transaction Execution - -### Execute a transaction - -> :warning: Not yet implemented - -`POST /v1/accounts/{address}/transactions` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - -Body (JSON) - -- `code`: The Cadence code to execute in the transaction - - The code must always specify exactly one authorizer (i.e. `prepare(auth: AuthAccount)`) - -Example - -```sh -curl --request POST \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/transactions \ - --header 'Content-Type: application/json' \ - --data '{ "code": "transaction { prepare(auth: AuthAccount) { log(\"Hello, World!\") } }" }' -``` - -```json -{ - "transactionId": "18647b584a03345f3b2d2c4d9ab2c4179ae1b124a7f62ef9f33910e5ca8b353c", - "error": null, -} -``` - ---- - -## Fungible Tokens - -Supported tokens: -- `FLOW` -- `FUSD` - -### List all tokens - -`GET /v1/accounts/{address}/fungible-tokens` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - -Example - -```sh -curl --request GET \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens -``` - -```json -[ - { - "name": "flow" - }, - { - "name": "fusd" - } -] -``` - ---- - -### Get details of a token type - -`GET /v1/accounts/{address}/fungible-tokens/{tokenName}` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the fungible token (e.g. "flow") - -Example - -```sh -curl --request GET \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens/flow -``` - -```json -{ - "name": "flow", - "balance": "42.0" -} -``` - ---- - -### List all withdrawals of a token type - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the fungible token (e.g. "flow") - ---- - -### Get details of a token withdrawal - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals/{transactionId}` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the fungible token (e.g. "flow") -- `transactionId`: The Flow transaction ID for the withdrawal - ---- - -### Create a token withdrawal - -`POST /v1/accounts/{address}/fungible-tokens/{tokenName}/withdrawals` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the fungible token (e.g. "flow") - -Body (JSON) - -- `amount`: The number of tokens to transfer (e.g. "123.456") - - Must be a fixed-point number with a maximum of 8 decimal places -- `recipient`: The Flow address of the recipient (e.g. "0xf8d6e0586b0a20c7") - -Example - -```sh -curl --request POST \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/fungible-tokens/fusd/withdrawls \ - --header 'Content-Type: application/json' \ - --data '{ "recipient": "0xe467b9dd11fa00df", "amount": "123.456" }' -``` - -```json -{ - "transactionId": "18647b584a03345f3b2d2c4d9ab2c4179ae1b124a7f62ef9f33910e5ca8b353c", - "recipient": "0xe467b9dd11fa00df", - "amount": "123.456" -} -``` - ---- - -## Non-Fungible Tokens - -> :warning: Not yet implemented - -### List all tokens - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/non-fungible-tokens` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") - -Example - -```sh -curl --request GET \ - --url http://localhost:3000/v1/accounts/0xf8d6e0586b0a20c7/non-fungible-tokens -``` - ---- - -### Get details of a token - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") - ---- - -### List all withdrawals of a token type - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") - ---- - -### Get details of a token withdrawal - -> :warning: Not yet implemented - -`GET /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals/{transactionId}` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") -- `transactionId`: The Flow transaction ID for the withdrawal - ---- - -#### Create a token withdrawal - -> :warning: Not yet implemented - -`POST /v1/accounts/{address}/non-fungible-tokens/{tokenName}/withdrawals` - -Parameters - -- `address`: The address of the account (e.g. "0xf8d6e0586b0a20c7") -- `tokenName`: The name of the non-fungible token (e.g. "nba-top-shot-moment") - -Body (JSON) - -- `recipient`: The Flow address of the recipient (e.g. "0xf8d6e0586b0a20c7") From b58b253350c233a4bc68d64af4af056d3135ef4a Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 28 Jun 2021 09:08:53 -0700 Subject: [PATCH 081/112] Update README.md --- examples/nextjs-fusd-provider/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/nextjs-fusd-provider/README.md b/examples/nextjs-fusd-provider/README.md index 148c4968..f924031f 100644 --- a/examples/nextjs-fusd-provider/README.md +++ b/examples/nextjs-fusd-provider/README.md @@ -39,6 +39,8 @@ flow project deploy -n emulator In this directory, install and run the Next.js app: ```bash +npm install + npm run dev ``` From afc5579a2ccbe089273438246a56ac2f1f2e8d43 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 28 Jun 2021 09:11:33 -0700 Subject: [PATCH 082/112] Add screenshot --- examples/nextjs-fusd-provider/README.md | 2 ++ examples/nextjs-fusd-provider/screenshot.png | Bin 0 -> 24783 bytes 2 files changed, 2 insertions(+) create mode 100644 examples/nextjs-fusd-provider/screenshot.png diff --git a/examples/nextjs-fusd-provider/README.md b/examples/nextjs-fusd-provider/README.md index f924031f..9ef69afa 100644 --- a/examples/nextjs-fusd-provider/README.md +++ b/examples/nextjs-fusd-provider/README.md @@ -3,6 +3,8 @@ This example application demonstrates how to use the Flow Wallet API to distribute the FUSD stablecoin. +![FUSD Provider Example](screenshot.png) + ## Usage ### Start the API diff --git a/examples/nextjs-fusd-provider/screenshot.png b/examples/nextjs-fusd-provider/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..44c54262d2f0dc0107cb79232cf7840e6ca9de2b GIT binary patch literal 24783 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 literal 0 HcmV?d00001 From 5d53dad96f365161b08008b6bbcb9996efe082da Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 28 Jun 2021 09:13:39 -0700 Subject: [PATCH 083/112] Update screenshot.png --- examples/nextjs-fusd-provider/screenshot.png | Bin 24783 -> 24838 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/nextjs-fusd-provider/screenshot.png b/examples/nextjs-fusd-provider/screenshot.png index 44c54262d2f0dc0107cb79232cf7840e6ca9de2b..921d368800666d86656f6ea6a391ebbf9f749265 100644 GIT binary patch literal 24838 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�#wfg#kZ3rPnutnC9mK*HGKj78B& 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 literal 24783 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 From 959deefdc3aff380007a0d2ee1dcc813404c98f5 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 28 Jun 2021 13:33:20 -0700 Subject: [PATCH 084/112] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 81ca66a1..8c67c17b 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ GOOGLE_KMS_LOCATION_ID= ## Credit -The Flow Wallet API is developed and maintained by [Equilibrium](https://equilibrium.co/). +The Flow Wallet API is developed and maintained by [Equilibrium](https://equilibrium.co/), +with support from the Flow core contributors. Equilibrium From 95442827178b31bad09dd7850017772ddf394d10 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 28 Jun 2021 13:47:24 -0700 Subject: [PATCH 085/112] Update flow.json --- flow.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/flow.json b/flow.json index 88a6c1ae..d427dfb6 100644 --- a/flow.json +++ b/flow.json @@ -30,9 +30,6 @@ "deployments": { "emulator": { "emulator-account": [ "FUSD" ] - }, - "emulator-docker": { - "emulator-account": [ "FUSD" ] } }, "networks": { From f23488b39577679521a5b8ce6bb7b068b1a5be43 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Mon, 28 Jun 2021 14:03:16 -0700 Subject: [PATCH 086/112] Delete equilibrium.png --- equilibrium.png | Bin 435 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 equilibrium.png diff --git a/equilibrium.png b/equilibrium.png deleted file mode 100644 index 1e0956d93f3f352fbd136bf86881ec479dbb4794..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 435 zcmeAS@N?(olHy`uVBq!ia0vp^TR@nJ8Av)Tezy=vv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpaf@tPl)U5*RQ!ac>n+Z z->$UU1t`dv21sKV@MTm(!8`6`=?ea`AL=49U3n_VPkr1_c2Y zM}aTx4gaDKPEv4Cu$wPge)5(!D?AYQ0b63dS&dVy@WOeTHl~>WcGmloziu${s zjlm<2(ezH@hYK<@1cBzMmbgZgq$HN4S|t~y0x1R~10zFS0~1{X^AICrD`NvI6BBI% zBP#;~f&AM`Q8eV{r(~v8;?|I8lq?C<5MC7$Q4*9`u24{vpO%@Es!&o{kgAYbP?F5R zP%-E6CmxQ%Fb$1U{-@7)J`G}ER_4}A<`z~K_MR-lEUe(tU~)KxS$T7a!s#1VP8>ON jMCJ(l=?0GlUV03##05(}IhjrcTEXDy>gTe~DWM4fTc?L` From a587445d8128a3d5327b64ef88614ca38cb5496a Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 29 Jun 2021 09:39:48 -0700 Subject: [PATCH 087/112] Update version to v0.3.1 --- README.md | 2 +- docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8c67c17b..fe06afcc 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ rather than a hosted 3rd-party solution. The Wallet API is provided as a Docker image: ```sh -docker pull gcr.io/flow-container-registry/wallet-api:v0.0.4 +docker pull gcr.io/flow-container-registry/wallet-api:v0.3.1 ``` ### Basic example usage diff --git a/docker-compose.yml b/docker-compose.yml index 1d304962..f1a203d3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: "3.3" services: wallet: - image: gcr.io/flow-container-registry/wallet-api:v0.0.4 + image: gcr.io/flow-container-registry/wallet-api:v0.3.1 ports: - "3000:3000" environment: From 19f786a8ff748eacc402a6a1655f650bc6dbf0ca Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Tue, 29 Jun 2021 09:41:43 -0700 Subject: [PATCH 088/112] Update README.md --- examples/nextjs-fusd-provider/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/nextjs-fusd-provider/README.md b/examples/nextjs-fusd-provider/README.md index 9ef69afa..f45676f4 100644 --- a/examples/nextjs-fusd-provider/README.md +++ b/examples/nextjs-fusd-provider/README.md @@ -41,6 +41,8 @@ flow project deploy -n emulator In this directory, install and run the Next.js app: ```bash +cd examples/nextjs-fusd-provider + npm install npm run dev From ddb38d9dc460ae6516dcbc36fddef47b48d01944 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 2 Jul 2021 10:47:51 -0700 Subject: [PATCH 089/112] Update walletApi.js --- examples/nextjs-fusd-provider/lib/walletApi.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/nextjs-fusd-provider/lib/walletApi.js b/examples/nextjs-fusd-provider/lib/walletApi.js index 513f6592..1b3521f3 100644 --- a/examples/nextjs-fusd-provider/lib/walletApi.js +++ b/examples/nextjs-fusd-provider/lib/walletApi.js @@ -4,7 +4,7 @@ const jobPollAttempts = 40 const jobStatusComplete = "Complete" const jobStatusError = "Error" -const contentType = "application/json" +const contentTypeJson = "application/json" export default class WalletApiClient { @@ -62,6 +62,12 @@ export default class WalletApiClient { ) } + async getFungibleTokenDeposits(from, token) { + return this.get( + `/v1/accounts/${from}/fungible-tokens/${token}/deposits}` + ) + } + async getJob(id) { return this.get(`/v1/jobs/${id}`) } @@ -88,7 +94,7 @@ export default class WalletApiClient { return fetch(this.baseUrl + endpoint, { method: 'POST', headers: { - 'Content-Type': contentType, + 'Content-Type': contentTypeJson, }, body: JSON.stringify(body), }).then(res => res.json()) @@ -98,7 +104,7 @@ export default class WalletApiClient { return fetch(this.baseUrl + endpoint, { method: 'GET', headers: { - 'Content-Type': contentType, + 'Content-Type': contentTypeJson, }, }).then(res => res.json()) } From fe6a7e4a97c28b670cd199f8b388fae7c4174428 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 2 Jul 2021 10:50:18 -0700 Subject: [PATCH 090/112] Update openapi.yml --- openapi.yml | 401 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 318 insertions(+), 83 deletions(-) diff --git a/openapi.yml b/openapi.yml index 1f6ba810..e2c259cc 100644 --- a/openapi.yml +++ b/openapi.yml @@ -1,11 +1,27 @@ -openapi: 3.0.3 +openapi: 3.1.0 + info: title: Flow Wallet API - version: 0.0.1 + version: 0.3.1 + servers: - - url: /v1 + - url: http://localhost:3000/v1 + components: schemas: + debugInfo: + type: string + example: | + url: GET /v1/debug + Headers: + User-Agent: vscode-restclient + Content-Type: application/json + Connection: close + + ver: https://github.com/eqlabs/flow-wallet-api/commit/ + built on: + api version called: v1 + account: type: object properties: @@ -14,10 +30,11 @@ components: example: "0xf8d6e0586b0a20c7" createdAt: type: string - example: "2021-04-27T05:49:53.211Z" + example: "2021-04-27T05:49:53.211+00:00" updatedAt: type: string - example: "2021-04-27T05:49:53.211Z" + example: "2021-04-27T05:49:53.211+00:00" + transaction: type: object properties: @@ -26,33 +43,139 @@ components: example: "9613c9689a50a5ed9198dc43839cd90ef39203dfdd7ab54f0fc5ca12f256eef0" createdAt: type: string - example: "2021-04-27T05:49:53.211Z" - fungibleToken: + example: "2021-04-27T05:49:53.211+00:00" + updatedAt: + type: string + example: "2021-04-27T05:49:53.211+00:00" + + job: + type: object + properties: + jobId: + type: string + example: "717c25c2-4b54-4588-8f83-72f37ae1a0e8" + status: + type: string + example: "Accepted" + result: + type: string + example: "" + createdAt: + type: string + example: "2021-04-27T05:49:53.211+00:00" + updatedAt: + type: string + example: "2021-04-27T05:49:53.211+00:00" + + script: + type: object + properties: + code: + type: string + arguments: + type: array + items: + type: object + properties: + type: + type: string + value: + type: string + + cadenceValue: + type: object + properties: + Value: + type: string + + plainValue: + type: string + + token: type: object properties: name: type: string - example: FLOW - enum: [FLOW, FUSD] + example: "FlowToken" + address: + type: string + example: "0xf8d6e0586b0a20c7" + + tokenDetails: + type: object + properties: + name: + type: string + example: "FlowToken" balance: type: string - example: "42.0" - fungibleTokenWithdrawal: + example: "9999999999.99700000" + + tokenWithdrawal: type: object properties: transactionId: type: string - example: "9613c9689a50a5ed9198dc43839cd90ef39203dfdd7ab54f0fc5ca12f256eef0" + example: "f1e272ee125b370e5129215179705791220764bf71da2aa938c94181b2c06685" + amount: + type: string + example: "1.0" + token: + type: string + example: "FlowToken" recipient: type: string - example: "0xf8d6e0586b0a20c7" + example: "0x01cf0e2f2f715450" + createdAt: + type: string + example: "2021-06-167T12:05:24.613704+03:00" + updatedAt: + type: string + example: "2021-06-16T12:05:24.617898+03:00" + + tokenDeposit: + type: object + properties: + transactionId: + type: string + example: "f1e272ee125b370e5129215179705791220764bf71da2aa938c94181b2c06685" amount: type: string - example: "42.0" + example: "1.0" + token: + type: string + example: "FlowToken" + sender: + type: string + example: "0x01cf0e2f2f715450" createdAt: type: string - example: "2021-04-27T05:49:53.211Z" + example: "2021-06-167T12:05:24.613704+03:00" + updatedAt: + type: string + example: "2021-06-16T12:05:24.617898+03:00" + parameters: + limit: + name: limit + description: The maximum number of items to return. -1 disables the limit. If no limit is given (or limit=0) 1000 is used as a default. + in: query + required: false + schema: + type: integer + minimum: -1 + example: 1000 + + offset: + name: offset + description: The number of items to skip before starting to collect the result set. + in: query + required: false + schema: + type: integer + minimum: 0 + example: 0 + address: name: address in: path @@ -60,26 +183,112 @@ components: schema: type: string example: "0xf8d6e0586b0a20c7" + + jobId: + name: jobId + in: path + required: true + schema: + type: string + example: "717c25c2-4b54-4588-8f83-72f37ae1a0e8" + tokenName: name: tokenName in: path required: true schema: type: string - enum: [FLOW, FUSD] - example: FLOW + enum: [FlowToken, FUSD] + example: FlowToken + transactionId: name: transactionId in: path required: true schema: - type: string - example: "9613c9689a50a5ed9198dc43839cd90ef39203dfdd7ab54f0fc5ca12f256eef0" + type: string + example: "9613c9689a50a5ed9198dc43839cd90ef39203dfdd7ab54f0fc5ca12f256eef0" + paths: + /debug: + get: + summary: Get debug information about the running instance. + responses: + "200": + description: OK + content: + text/plain: + schema: + $ref: "#/components/schemas/debugInfo" + + /fungible-tokens: + get: + summary: Get all fungible tokens + description: Get all fungible tokens that are enabled in this instance. + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/token" + /scripts: + post: + summary: Execute a script on chain + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/script" + responses: + "200": + description: Ok + content: + application/json: + schema: + oneOf: + - $ref: "#/components/schemas/cadenceValue" + - $ref: "#/components/schemas/plainValue" + + /jobs: + get: + summary: List all jobs + parameters: + - $ref: "#/components/parameters/limit" + - $ref: "#/components/parameters/offset" + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/job" + + /jobs/{jobId}: + parameters: + - $ref: "#/components/parameters/jobId" + get: + summary: Get a job + responses: + "200": + description: OK + content: + application/json: + schema: + type: object + $ref: "#/components/schemas/job" + /accounts: get: summary: List all accounts - description: Get a list of all accounts manged by the wallet service. + description: Get a list of all accounts managed by the wallet service. + parameters: + - $ref: "#/components/parameters/limit" + - $ref: "#/components/parameters/offset" responses: "200": description: OK @@ -91,20 +300,21 @@ paths: $ref: "#/components/schemas/account" post: summary: Create an account - description: Create a new account that will be managed by the wallet service. + description: Create a new account that will be managed by the wallet service. Returns a job. responses: "201": description: Created content: application/json: schema: - $ref: "#/components/schemas/account" + $ref: "#/components/schemas/job" + /accounts/{address}: + parameters: + - $ref: "#/components/parameters/address" get: summary: Get an account description: Get the details of a specific account. - parameters: - - $ref: "#/components/parameters/address" responses: "200": description: OK @@ -112,12 +322,16 @@ paths: application/json: schema: $ref: "#/components/schemas/account" + /accounts/{address}/transactions: + parameters: + - $ref: "#/components/parameters/address" get: - summary: List all account transactions + summary: List account raw transactions description: Get a list of all transactions sent by an account. parameters: - - $ref: "#/components/parameters/address" + - $ref: "#/components/parameters/limit" + - $ref: "#/components/parameters/offset" responses: "200": description: OK @@ -128,41 +342,28 @@ paths: items: $ref: "#/components/schemas/transaction" post: - summary: Create a transaction - description: Send a transaction from an account. - parameters: - - $ref: "#/components/parameters/address" + summary: Send a raw transaction + description: Send a transaction from an account. Returns a job. requestBody: content: application/json: schema: - type: object - properties: - code: - type: string - arguments: - type: array - items: - type: object - properties: - type: - type: string - value: - type: string + $ref: "#/components/schemas/script" responses: "201": description: Created content: application/json: schema: - $ref: "#/components/schemas/transaction" + $ref: "#/components/schemas/job" + /accounts/{address}/transactions/{transactionId}: - get: - summary: Get a transaction - description: Get the details of a transaction sent by an account. - parameters: + parameters: - $ref: "#/components/parameters/address" - $ref: "#/components/parameters/transactionId" + get: + summary: Get a raw transaction + description: Get the details of a raw transaction sent by an account. responses: "200": description: OK @@ -170,12 +371,13 @@ paths: application/json: schema: $ref: "#/components/schemas/transaction" + /accounts/{address}/fungible-tokens: - get: - summary: List account fungible tokens - description: Get a list of all fungible tokens held by an account. - parameters: + parameters: - $ref: "#/components/parameters/address" + get: + summary: Get account fungible tokens + description: Get all fungible tokens that are enabled for an account. responses: "200": description: OK @@ -184,28 +386,42 @@ paths: schema: type: array items: - $ref: "#/components/schemas/fungibleToken" + $ref: "#/components/schemas/token" + /accounts/{address}/fungible-tokens/{tokenName}: - get: - summary: Get an account fungible token - description: Get the details of a fungible token held by an account. - parameters: + parameters: - $ref: "#/components/parameters/address" - $ref: "#/components/parameters/tokenName" + get: + summary: Get fungible tokens details for an account + description: Get details (balance) regarding a fungible token for an account. responses: "200": description: OK content: application/json: schema: - $ref: "#/components/schemas/fungibleToken" + type: array + items: + $ref: "#/components/schemas/tokenDetails" + post: + summary: Enable a fungible token for an account + responses: + "201": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/job" + /accounts/{address}/fungible-tokens/{tokenName}/withdrawals: - get: - summary: List account fungible token withdrawals - description: Get a list of all fungible token withdrawals sent by an account. - parameters: + parameters: - $ref: "#/components/parameters/address" - $ref: "#/components/parameters/tokenName" + get: + summary: List all withdrawals of a fungible token responses: "200": description: OK @@ -214,42 +430,61 @@ paths: schema: type: array items: - $ref: "#/components/schemas/fungibleTokenWithdrawal" + $ref: "#/components/schemas/tokenWithdrawal" post: summary: Create a fungible token withdrawal - description: Send a fungible token withdrawal transaction from an account. - parameters: - - $ref: "#/components/parameters/address" - - $ref: "#/components/parameters/tokenName" - requestBody: - content: - application/json: - schema: - type: object - properties: - recipient: - type: string - amount: - type: string responses: "201": - description: Created + description: OK content: application/json: schema: - $ref: "#/components/schemas/fungibleTokenWithdrawal" + type: array + items: + $ref: "#/components/schemas/job" + /accounts/{address}/fungible-tokens/{tokenName}/withdrawals/{transactionId}: + parameters: + - $ref: "#/components/parameters/address" + - $ref: "#/components/parameters/tokenName" + - $ref: "#/components/parameters/transactionId" get: - summary: Get an account fungible token withdrawal - description: Get the details of a fungible token withdrawal sent by an account. - parameters: + summary: Get details of a fungible token withdrawal + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/tokenWithdrawal" + + /accounts/{address}/fungible-tokens/{tokenName}/deposits: + parameters: + - $ref: "#/components/parameters/address" + - $ref: "#/components/parameters/tokenName" + get: + summary: List all deposits of a fungible token + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/tokenDeposit" + + /accounts/{address}/fungible-tokens/{tokenName}/deposits/{transactionId}: + parameters: - $ref: "#/components/parameters/address" - $ref: "#/components/parameters/tokenName" - $ref: "#/components/parameters/transactionId" + get: + summary: Get details of a fungible token deposit responses: "200": description: OK content: application/json: schema: - $ref: "#/components/schemas/fungibleTokenWithdrawal" + $ref: "#/components/schemas/tokenDeposit" From 8878c30fad540f55ab84c36cc7e4ca77d05fea67 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 2 Jul 2021 11:02:41 -0700 Subject: [PATCH 091/112] Update API documentation --- API.md | 31 ------------------------------- index.html | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 31 deletions(-) delete mode 100644 API.md create mode 100644 index.html diff --git a/API.md b/API.md deleted file mode 100644 index 2afa7bc4..00000000 --- a/API.md +++ /dev/null @@ -1,31 +0,0 @@ -# Wallet API REST HTTP Routes - -TODO: this document will contain the REST -routes provided by the Flow Wallet API. - -These routes are also defined in the [OpenAPI specification for this service](openapi.yml). - -## Functionality - -### Accounts - -- [x] Initialize the API with a root admin account -- [x] Create a new Flow account - -### Transactions - -- [x] Send a transaction signed by an account -- [x] Get the transaction history for an account - -### 3. Fungible Tokens - -- [x] Send a fungible token withdrawal from an account (FLOW, FUSD) -- [x] Detect fungible token deposits to an account (FLOW, FUSD) -- [x] View the fungible token balance of an account - -### 4. Non-Fungible Tokens - -- [ ] Set up an account with non-fungible token collections (`NFT.Collection`) -- [ ] Send a non-fungible token withdrawal from an account -- [ ] Detect non-fungible token deposits to an account -- [ ] View the non-fungible tokens owned by an account diff --git a/index.html b/index.html new file mode 100644 index 00000000..17f6e06b --- /dev/null +++ b/index.html @@ -0,0 +1,24 @@ + + + + Flow Wallet API Documentation + + + + + + + + + + + + + From 756999a442e16064ec324429a6651835395a55ff Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 2 Jul 2021 11:05:31 -0700 Subject: [PATCH 092/112] Update openapi.yml --- openapi.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openapi.yml b/openapi.yml index e2c259cc..d57fcba7 100644 --- a/openapi.yml +++ b/openapi.yml @@ -1,4 +1,4 @@ -openapi: 3.1.0 +openapi: 3.0.0 info: title: Flow Wallet API From 4414fa4a187996c80b66100ea6b56db26f67d9db Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 2 Jul 2021 11:09:11 -0700 Subject: [PATCH 093/112] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fe06afcc..74cff20b 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ rather than a hosted 3rd-party solution. - **Exchange** — a cryptocurrency exchange that is listing FLOW and/or FUSD. Similar to the case above, one or more admin accounts may be used as a hot wallet for outgoing payments, and additional deposit accounts would be created to accept incoming payments. - **Web Wallet** — a user-facing wallet application that is compatible with Flow dapps. Each user account would be created and managed by the wallet service. +## API Specification + +View the [Wallet API documentation and OpenAPI (Swagger) definition](https://onflow.github.io/wallet-api/). + ## Installation The Wallet API is provided as a Docker image: @@ -174,10 +178,6 @@ GOOGLE_KMS_PROJECT_ID= GOOGLE_KMS_LOCATION_ID= ``` -## API Specification - -[View the full Wallet API specification](API.md). - ## Credit The Flow Wallet API is developed and maintained by [Equilibrium](https://equilibrium.co/), From 167224c9f7d407f5fe592df6172a0274b83972bd Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 2 Jul 2021 11:30:05 -0700 Subject: [PATCH 094/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 74cff20b..94299288 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ This service can be used by an application that needs to manage Flow user accoun - _Transfer NFTs (e.g. FLOW, FUSD) (coming soon)_ - _Detect NFT deposits (coming soon)_ -View full list of functionality in the [API specification](API.md). +View full list of functionality in the [API documentation](https://onflow.github.io/wallet-api/). ## Background From 0ae8c1eb22c123a8e2dac837dd44f14eedba3a69 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 2 Jul 2021 11:30:47 -0700 Subject: [PATCH 095/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 94299288..73f8707a 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ rather than a hosted 3rd-party solution. ## API Specification -View the [Wallet API documentation and OpenAPI (Swagger) definition](https://onflow.github.io/wallet-api/). +View the [Wallet API documentation and OpenAPI (Swagger) specification](https://onflow.github.io/wallet-api/). ## Installation From 8deb9b7d0ee4bb051bd1d556552093456f8b005e Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 2 Jul 2021 11:31:23 -0700 Subject: [PATCH 096/112] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73f8707a..838c286e 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Deploy the FUSD contract to the emulator: flow project deploy -n emulator ``` -You can now access the API at http://localhost:3000. +You can now access the API at http://localhost:3000/v1/accounts. Next, see the [FUSD sample app](/examples/nextjs-fusd-provider) for an example of how to use this configuration as part of From 1a21f3ba34c46cb9f2788c2700d288bcdfbb0ad8 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 2 Jul 2021 11:37:18 -0700 Subject: [PATCH 097/112] Update openapi.yml --- openapi.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/openapi.yml b/openapi.yml index d57fcba7..d310745b 100644 --- a/openapi.yml +++ b/openapi.yml @@ -225,6 +225,8 @@ paths: get: summary: Get all fungible tokens description: Get all fungible tokens that are enabled in this instance. + tags: + - Fungible Tokens responses: "200": description: OK @@ -255,6 +257,8 @@ paths: /jobs: get: summary: List all jobs + tags: + - Jobs parameters: - $ref: "#/components/parameters/limit" - $ref: "#/components/parameters/offset" @@ -273,6 +277,8 @@ paths: - $ref: "#/components/parameters/jobId" get: summary: Get a job + tags: + - Jobs responses: "200": description: OK @@ -286,6 +292,8 @@ paths: get: summary: List all accounts description: Get a list of all accounts managed by the wallet service. + tags: + - Accounts parameters: - $ref: "#/components/parameters/limit" - $ref: "#/components/parameters/offset" @@ -301,6 +309,8 @@ paths: post: summary: Create an account description: Create a new account that will be managed by the wallet service. Returns a job. + tags: + - Accounts responses: "201": description: Created @@ -315,6 +325,8 @@ paths: get: summary: Get an account description: Get the details of a specific account. + tags: + - Accounts responses: "200": description: OK @@ -329,6 +341,8 @@ paths: get: summary: List account raw transactions description: Get a list of all transactions sent by an account. + tags: + - Transactions parameters: - $ref: "#/components/parameters/limit" - $ref: "#/components/parameters/offset" @@ -344,6 +358,8 @@ paths: post: summary: Send a raw transaction description: Send a transaction from an account. Returns a job. + tags: + - Transactions requestBody: content: application/json: @@ -364,6 +380,8 @@ paths: get: summary: Get a raw transaction description: Get the details of a raw transaction sent by an account. + tags: + - Transactions responses: "200": description: OK @@ -378,6 +396,8 @@ paths: get: summary: Get account fungible tokens description: Get all fungible tokens that are enabled for an account. + tags: + - Fungible Tokens responses: "200": description: OK @@ -395,6 +415,8 @@ paths: get: summary: Get fungible tokens details for an account description: Get details (balance) regarding a fungible token for an account. + tags: + - Fungible Tokens responses: "200": description: OK @@ -406,6 +428,8 @@ paths: $ref: "#/components/schemas/tokenDetails" post: summary: Enable a fungible token for an account + tags: + - Fungible Tokens responses: "201": description: OK @@ -422,6 +446,8 @@ paths: - $ref: "#/components/parameters/tokenName" get: summary: List all withdrawals of a fungible token + tags: + - Fungible Tokens responses: "200": description: OK @@ -433,6 +459,8 @@ paths: $ref: "#/components/schemas/tokenWithdrawal" post: summary: Create a fungible token withdrawal + tags: + - Fungible Tokens responses: "201": description: OK @@ -450,6 +478,8 @@ paths: - $ref: "#/components/parameters/transactionId" get: summary: Get details of a fungible token withdrawal + tags: + - Fungible Tokens responses: "200": description: OK @@ -464,6 +494,8 @@ paths: - $ref: "#/components/parameters/tokenName" get: summary: List all deposits of a fungible token + tags: + - Fungible Tokens responses: "200": description: OK @@ -481,6 +513,8 @@ paths: - $ref: "#/components/parameters/transactionId" get: summary: Get details of a fungible token deposit + tags: + - Fungible Tokens responses: "200": description: OK From 1098e84c3426863b390f3ded0af3f0235fa5b611 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 2 Jul 2021 11:43:15 -0700 Subject: [PATCH 098/112] Update openapi.yml --- openapi.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openapi.yml b/openapi.yml index d310745b..9fdf5c02 100644 --- a/openapi.yml +++ b/openapi.yml @@ -209,6 +209,18 @@ components: type: string example: "9613c9689a50a5ed9198dc43839cd90ef39203dfdd7ab54f0fc5ca12f256eef0" +tags: + - name: Accounts + description: Create and lists accounts managed by this Wallet API. + - name: Transactions + description: Send transactions from accounts managed by this Wallet API. + - name: Scripts + description: Execute read-only scripts against the blockchain. + - name: Fungible Tokens + description: Initialize tokens, withdraw funds and detect deposits of fungible tokens. + - name: Jobs + description: View the status of asynchronous tasks being completed by the Wallet API. + paths: /debug: get: @@ -239,6 +251,8 @@ paths: /scripts: post: summary: Execute a script on chain + tags: + - Scripts requestBody: content: application/json: From 3b6f597280f3f1172656abc6dc2010fc7f11aaa6 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Fri, 2 Jul 2021 12:45:17 -0700 Subject: [PATCH 099/112] Update openapi.yml --- openapi.yml | 116 ++++++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/openapi.yml b/openapi.yml index 9fdf5c02..346c9008 100644 --- a/openapi.yml +++ b/openapi.yml @@ -35,6 +35,21 @@ components: type: string example: "2021-04-27T05:49:53.211+00:00" + transactionRequest: + type: object + properties: + code: + type: string + arguments: + type: array + items: + type: object + properties: + type: + type: string + value: + type: string + transaction: type: object properties: @@ -67,31 +82,7 @@ components: type: string example: "2021-04-27T05:49:53.211+00:00" - script: - type: object - properties: - code: - type: string - arguments: - type: array - items: - type: object - properties: - type: - type: string - value: - type: string - - cadenceValue: - type: object - properties: - Value: - type: string - - plainValue: - type: string - - token: + fungibleToken: type: object properties: name: @@ -101,7 +92,7 @@ components: type: string example: "0xf8d6e0586b0a20c7" - tokenDetails: + fungibleTokenDetails: type: object properties: name: @@ -111,7 +102,17 @@ components: type: string example: "9999999999.99700000" - tokenWithdrawal: + fungibleTokenWithdrawalRequest: + type: object + properties: + recipient: + type: string + example: "0xf8d6e0586b0a20c7" + amount: + type: string + example: "1.0" + + fungibleTokenWithdrawal: type: object properties: transactionId: @@ -133,7 +134,7 @@ components: type: string example: "2021-06-16T12:05:24.617898+03:00" - tokenDeposit: + fungibleTokenDeposit: type: object properties: transactionId: @@ -214,8 +215,6 @@ tags: description: Create and lists accounts managed by this Wallet API. - name: Transactions description: Send transactions from accounts managed by this Wallet API. - - name: Scripts - description: Execute read-only scripts against the blockchain. - name: Fungible Tokens description: Initialize tokens, withdraw funds and detect deposits of fungible tokens. - name: Jobs @@ -237,6 +236,7 @@ paths: get: summary: Get all fungible tokens description: Get all fungible tokens that are enabled in this instance. + operationId: getFungibleTokens tags: - Fungible Tokens responses: @@ -247,30 +247,12 @@ paths: schema: type: array items: - $ref: "#/components/schemas/token" - /scripts: - post: - summary: Execute a script on chain - tags: - - Scripts - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/script" - responses: - "200": - description: Ok - content: - application/json: - schema: - oneOf: - - $ref: "#/components/schemas/cadenceValue" - - $ref: "#/components/schemas/plainValue" + $ref: "#/components/schemas/fungibleToken" /jobs: get: summary: List all jobs + operationId: getJobs tags: - Jobs parameters: @@ -291,6 +273,7 @@ paths: - $ref: "#/components/parameters/jobId" get: summary: Get a job + operationId: getJob tags: - Jobs responses: @@ -306,6 +289,7 @@ paths: get: summary: List all accounts description: Get a list of all accounts managed by the wallet service. + operationId: getAccounts tags: - Accounts parameters: @@ -323,6 +307,7 @@ paths: post: summary: Create an account description: Create a new account that will be managed by the wallet service. Returns a job. + operationId: createAccount tags: - Accounts responses: @@ -339,6 +324,7 @@ paths: get: summary: Get an account description: Get the details of a specific account. + operationId: getAccountByAddress tags: - Accounts responses: @@ -355,6 +341,7 @@ paths: get: summary: List account raw transactions description: Get a list of all transactions sent by an account. + operationId: getAccountTransactions tags: - Transactions parameters: @@ -372,13 +359,14 @@ paths: post: summary: Send a raw transaction description: Send a transaction from an account. Returns a job. + operationId: sendAccountTransaction tags: - Transactions requestBody: content: application/json: schema: - $ref: "#/components/schemas/script" + $ref: "#/components/schemas/transactionRequest" responses: "201": description: Created @@ -394,6 +382,7 @@ paths: get: summary: Get a raw transaction description: Get the details of a raw transaction sent by an account. + operationId: getAccountTransaction tags: - Transactions responses: @@ -410,6 +399,7 @@ paths: get: summary: Get account fungible tokens description: Get all fungible tokens that are enabled for an account. + operationId: getAccountFungibleTokens tags: - Fungible Tokens responses: @@ -420,7 +410,7 @@ paths: schema: type: array items: - $ref: "#/components/schemas/token" + $ref: "#/components/schemas/fungibleToken" /accounts/{address}/fungible-tokens/{tokenName}: parameters: @@ -429,6 +419,7 @@ paths: get: summary: Get fungible tokens details for an account description: Get details (balance) regarding a fungible token for an account. + operationId: getAccountFungibleToken tags: - Fungible Tokens responses: @@ -439,9 +430,10 @@ paths: schema: type: array items: - $ref: "#/components/schemas/tokenDetails" + $ref: "#/components/schemas/fungibleTokenDetails" post: summary: Enable a fungible token for an account + operationId: initAccountFungibleToken tags: - Fungible Tokens responses: @@ -460,6 +452,7 @@ paths: - $ref: "#/components/parameters/tokenName" get: summary: List all withdrawals of a fungible token + operationId: getAccountFungibleTokenWithdrawals tags: - Fungible Tokens responses: @@ -470,11 +463,17 @@ paths: schema: type: array items: - $ref: "#/components/schemas/tokenWithdrawal" + $ref: "#/components/schemas/fungibleTokenWithdrawal" post: summary: Create a fungible token withdrawal + operationId: createAccountFungibleTokenWithdrawal tags: - Fungible Tokens + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/fungibleTokenWithdrawalRequest" responses: "201": description: OK @@ -492,6 +491,7 @@ paths: - $ref: "#/components/parameters/transactionId" get: summary: Get details of a fungible token withdrawal + operationId: getAccountFungibleTokenWithdrawal tags: - Fungible Tokens responses: @@ -500,7 +500,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/tokenWithdrawal" + $ref: "#/components/schemas/fungibleTokenWithdrawal" /accounts/{address}/fungible-tokens/{tokenName}/deposits: parameters: @@ -508,6 +508,7 @@ paths: - $ref: "#/components/parameters/tokenName" get: summary: List all deposits of a fungible token + operationId: getAccountFungibleTokenDeposits tags: - Fungible Tokens responses: @@ -518,7 +519,7 @@ paths: schema: type: array items: - $ref: "#/components/schemas/tokenDeposit" + $ref: "#/components/schemas/fungibleTokenDeposit" /accounts/{address}/fungible-tokens/{tokenName}/deposits/{transactionId}: parameters: @@ -527,6 +528,7 @@ paths: - $ref: "#/components/parameters/transactionId" get: summary: Get details of a fungible token deposit + operationId: getAccountFungibleTokenDeposit tags: - Fungible Tokens responses: @@ -535,4 +537,4 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/tokenDeposit" + $ref: "#/components/schemas/fungibleTokenDeposit" From 1ef3b3ef685bb0a673ebab7a0d341c79cb2e0f65 Mon Sep 17 00:00:00 2001 From: Peter Siemens Date: Thu, 8 Jul 2021 16:11:06 -0700 Subject: [PATCH 100/112] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 838c286e..ee3f8aee 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,6 @@ rather than a hosted 3rd-party solution. ### Example use cases -- **Custodial NFT Dapp** — an NFT dapp where each user receives a Flow account that is fully managed by the dapp admin. This application requires that each user account can store and transfer NFTs, but does not need to support fungible token custody. - **FLOW/FUSD Hot Wallet** — an application that allows users to convert fiat currency to FLOW or FUSD. A single admin account would be used as a hot wallet for outgoing payments, and additional deposit accounts would be created to accept incoming payments. - **Exchange** — a cryptocurrency exchange that is listing FLOW and/or FUSD. Similar to the case above, one or more admin accounts may be used as a hot wallet for outgoing payments, and additional deposit accounts would be created to accept incoming payments. - **Web Wallet** — a user-facing wallet application that is compatible with Flow dapps. Each user account would be created and managed by the wallet service. From 5b54d2f1704b269e838213066b42f5bafaeca395 Mon Sep 17 00:00:00 2001 From: Navid TehraniFar Date: Wed, 4 Aug 2021 19:20:45 -0700 Subject: [PATCH 101/112] wip --- .env.example | 4 +- accounts/accounts.go | 8 +++- accounts/config.go | 1 + accounts/service.go | 27 +++++++---- cadence/transactions/add_proposer.cdc | 14 ++++++ keys/basic/keys.go | 54 +++++++++++++++++++++ keys/keys.go | 16 +++++++ keys/store.go | 3 ++ keys/store_gorm.go | 29 +++++++++++ main.go | 5 +- main_test.go | 69 +++++++++++++++++++++++---- transactions/service.go | 7 ++- transactions/transactions.go | 12 +++++ 13 files changed, 227 insertions(+), 22 deletions(-) create mode 100644 cadence/transactions/add_proposer.cdc diff --git a/.env.example b/.env.example index 343c4be7..c7de63b3 100644 --- a/.env.example +++ b/.env.example @@ -4,7 +4,7 @@ ADMIN_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf19870 DATABASE_TYPE=psql DATABASE_DSN=postgresql://wallet:wallet@localhost:5432/wallet -ACCESS_API_HOST=emulator:3569 +ACCESS_API_HOST=localhost:3569 CHAIN_ID=flow-emulator # When set to "local", private keys are generated by the API @@ -14,3 +14,5 @@ DEFAULT_KEY_TYPE=local # This symmetrical key is used to encrypt private keys # that are stored in the database. ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8e + +ENABLED_TOKENS=FUSD:0xf8d6e0586b0a20c7,FlowToken:0x0ae53cb6e3f42a79 \ No newline at end of file diff --git a/accounts/accounts.go b/accounts/accounts.go index fcecee38..860f61a1 100644 --- a/accounts/accounts.go +++ b/accounts/accounts.go @@ -55,8 +55,13 @@ func New(a *Account, k *keys.Private, ctx context.Context, fc *client.Client, km tx := flow_templates.CreateAccount([]*flow.AccountKey{accountKey}, nil, auth.Address) b := templates.NewBuilderFromTx(tx) + proposer, err := km.AdminProposer(ctx) + if err != nil { + return err + } + t := transactions.Transaction{} - if err := transactions.New(&t, id, b, transactions.General, auth, auth, nil); err != nil { + if err := transactions.New(&t, id, b, transactions.General, proposer, auth, nil); err != nil { return err } @@ -128,6 +133,7 @@ func AddContract( b.Tx.AddAuthorizer(adminAuth.Address) t := transactions.Transaction{} + // check here if err := transactions.New(&t, id, b, transactions.General, userAuth, adminAuth, nil); err != nil { return nil, err } diff --git a/accounts/config.go b/accounts/config.go index c81c6e1a..0188ac4e 100644 --- a/accounts/config.go +++ b/accounts/config.go @@ -9,6 +9,7 @@ import ( type Config struct { AdminAccountAddress string `env:"ADMIN_ADDRESS,notEmpty"` ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + // AdminProposerKeyCount uint16 `env:"ADMIN_PROPOSER_COUNT" envDefault:"1"` } // ParseConfig parses environment variables to a valid Config. diff --git a/accounts/service.go b/accounts/service.go index 732e6024..66f34437 100644 --- a/accounts/service.go +++ b/accounts/service.go @@ -19,13 +19,14 @@ import ( // Service defines the API for account management. type Service struct { - store Store - km keys.Manager - fc *client.Client - wp *jobs.WorkerPool - transactions *transactions.Service - templates *templates.Service - cfg Config + store Store + km keys.Manager + fc *client.Client + wp *jobs.WorkerPool + transactions *transactions.Service + templates *templates.Service + cfg Config + adminProposerCount uint16 } // NewService initiates a new account service. @@ -38,10 +39,10 @@ func NewService( tes *templates.Service, ) *Service { cfg := ParseConfig() - return &Service{store, km, fc, wp, txs, tes, cfg} + return &Service{store, km, fc, wp, txs, tes, cfg, 0} } -func (s *Service) InitAdminAccount() { +func (s *Service) InitAdminAccount(ctx context.Context) error { a, err := s.store.Account(s.cfg.AdminAccountAddress) if err != nil { if !strings.Contains(err.Error(), "record not found") { @@ -57,6 +58,14 @@ func (s *Service) InitAdminAccount() { Address: flow.HexToAddress(s.cfg.AdminAccountAddress), }) } + + keyCount, err := s.km.InitAdminProposerKeys(ctx) + s.adminProposerCount = keyCount + return err +} + +func (s *Service) GetAdminProposerKeyCount() uint16 { + return s.adminProposerCount } // List returns all accounts in the datastore. diff --git a/cadence/transactions/add_proposer.cdc b/cadence/transactions/add_proposer.cdc new file mode 100644 index 00000000..4282bbeb --- /dev/null +++ b/cadence/transactions/add_proposer.cdc @@ -0,0 +1,14 @@ +transaction(numProposers: UInt16) { + prepare(acc: AuthAccount) { + let key = acc.keys.get(keyIndex: 0)! + var count: UInt16 = 0 + while count < numProposers { + acc.keys.add( + publicKey: key.publicKey, + hashAlgorithm: key.hashAlgorithm, + weight: 0.0 + ) + count = count + 1 + } + } +} \ No newline at end of file diff --git a/keys/basic/keys.go b/keys/basic/keys.go index 4a5c3d8a..2df5f70b 100644 --- a/keys/basic/keys.go +++ b/keys/basic/keys.go @@ -148,3 +148,57 @@ func (s *KeyManager) MakeAuthorizer(ctx context.Context, address flow.Address) ( Signer: sig, }, nil } + +func (s *KeyManager) InitAdminProposerKeys(ctx context.Context) (uint16, error) { + adminAddress := flow.HexToAddress(s.cfg.AdminAccountAddress) + + adminAccount, err := s.fc.GetAccount(ctx, adminAddress) + if err != nil { + return 0, err + } + + err = s.store.DeleteAllProposers() + if err != nil { + return 0, err + } + + var count uint16 + for _, k := range adminAccount.Keys { + if !k.Revoked { + err = s.store.InsertProposer(keys.Proposer{ + KeyIndex: k.Index, + }) + if err != nil { + return count, err + } + count += 1 + } + } + + return count, nil +} + +func (s *KeyManager) AdminProposer(ctx context.Context) (keys.Authorizer, error) { + adminAcc := flow.HexToAddress(s.cfg.AdminAccountAddress) + + index, err := s.store.Proposer() + if err != nil { + return keys.Authorizer{}, err + } + + acc, err := s.fc.GetAccount(ctx, adminAcc) + if err != nil { + return keys.Authorizer{}, err + } + + sig, err := local.Signer(s.adminAccountKey) + if err != nil { + return keys.Authorizer{}, err + } + + return keys.Authorizer{ + Address: adminAcc, + Key: acc.Keys[index], + Signer: sig, + }, nil +} diff --git a/keys/keys.go b/keys/keys.go index 59c87cf2..ce93d39e 100644 --- a/keys/keys.go +++ b/keys/keys.go @@ -29,6 +29,11 @@ type Manager interface { AdminAuthorizer(context.Context) (Authorizer, error) // UserAuthorizer returns an Authorizer for the given address. UserAuthorizer(ctx context.Context, address flow.Address) (Authorizer, error) + // InitAdminProposerKeys will init the admin proposer keys in the database + // and return current count. + InitAdminProposerKeys(ctx context.Context) (uint16, error) + // AdminProposer returns Authorizer to be used as proposer. + AdminProposer(ctx context.Context) (Authorizer, error) } // Storable struct represents a storable account private key. @@ -53,6 +58,13 @@ func (Storable) TableName() string { return "storable_keys" } +type Proposer struct { + ID int `json:"-" gorm:"primaryKey"` + KeyIndex int `gorm:"unique"` + CreatedAt time.Time + UpdatedAt time.Time +} + // Private is an "in flight" account private key meaning its Value should be the actual // private key or resource id (unencrypted). type Private struct { @@ -69,3 +81,7 @@ type Authorizer struct { Key *flow.AccountKey Signer crypto.Signer } + +func (a *Authorizer) Equals(t Authorizer) bool { + return a.Address.Hex() == t.Address.Hex() && a.Key.Index == t.Key.Index +} diff --git a/keys/store.go b/keys/store.go index faf1e42d..51affa10 100644 --- a/keys/store.go +++ b/keys/store.go @@ -3,4 +3,7 @@ package keys // Store is the interface required by key manager for data storage. type Store interface { AccountKey(address string) (Storable, error) + Proposer() (int, error) + InsertProposer(proposer Proposer) error + DeleteAllProposers() error } diff --git a/keys/store_gorm.go b/keys/store_gorm.go index d8cc5dab..8b78ca91 100644 --- a/keys/store_gorm.go +++ b/keys/store_gorm.go @@ -1,6 +1,8 @@ package keys import ( + "fmt" + "gorm.io/gorm" ) @@ -11,6 +13,7 @@ type GormStore struct { func NewGormStore(db *gorm.DB) *GormStore { db.Migrator().RenameTable("storables", "storable_keys") // Ignore error db.AutoMigrate(&Storable{}) + db.AutoMigrate(&Proposer{}) return &GormStore{db} } @@ -19,3 +22,29 @@ func (s *GormStore) AccountKey(address string) (k Storable, err error) { s.db.Save(&k) // Update the UpdatedAt field return } + +func (s *GormStore) Proposer() (i int, err error) { + p := Proposer{} + err = s.db.Model(&Proposer{}).Order("updated_at asc").First(&p).Error + s.db.Save(&p) // Update the UpdatedAt field + fmt.Println("proposer fetched") + fmt.Println(p.KeyIndex) + var ps []Proposer + s.db.Find(&ps) + fmt.Println("all proposers") + for _, i := range ps { + fmt.Println(i) + } + i = p.KeyIndex + return +} + +func (s *GormStore) InsertProposer(p Proposer) error { + fmt.Println("insert") + fmt.Println(p.KeyIndex) + return s.db.Create(&p).Error +} + +func (s *GormStore) DeleteAllProposers() error { + return s.db.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&Proposer{}).Error +} diff --git a/main.go b/main.go index 0ac9c65e..8ffa33e7 100644 --- a/main.go +++ b/main.go @@ -148,7 +148,10 @@ func runServer(disableRawTx, disableFt, disableNft, disableChainEvents bool) { TokenService: tokenService, }) - accountService.InitAdminAccount() + err = accountService.InitAdminAccount(context.Background()) + if err != nil { + ls.Fatal(err) + } // HTTP handling diff --git a/main_test.go b/main_test.go index 66b7ad9c..7a1976c7 100644 --- a/main_test.go +++ b/main_test.go @@ -38,8 +38,10 @@ import ( ) const ( - testDbDSN = "test.db" - testDbType = "sqlite" + testDbDSN = "test.db" + testDbType = "sqlite" + cadenceTxBasePath = "./cadence/transactions" + numProposers = 3 ) var ( @@ -73,6 +75,28 @@ type httpTestStep struct { sync bool } +func InitAdminAccount(ctx context.Context, accService *accounts.Service, txService *transactions.Service, numProposers uint16) error { + err := accService.InitAdminAccount(ctx) + if err != nil { + return err + } + + addProposerTx, err := ioutil.ReadFile(filepath.Join(cadenceTxBasePath, "add_proposer.cdc")) + if err != nil { + return err + } + + fmt.Println("add proposer") + _, _, err = txService.Create(ctx, true, cfg.AdminAddress, templates.Raw{ + Code: string(addProposerTx), + Arguments: []templates.Argument{ + cadence.NewUInt16(numProposers), + }, + }, transactions.General) + + return accService.InitAdminAccount(ctx) +} + func handleStepRequest(s httpTestStep, r *mux.Router, t *testing.T) *httptest.ResponseRecorder { req, err := http.NewRequest(s.method, s.url, s.body) if err != nil { @@ -158,6 +182,7 @@ func TestAccountServices(t *testing.T) { jobStore := jobs.NewGormStore(db) accountStore := accounts.NewGormStore(db) keyStore := keys.NewGormStore(db) + txStore := transactions.NewGormStore(db) templateStore := templates.NewGormStore(db) templateService := templates.NewService(templateStore) @@ -169,9 +194,13 @@ func TestAccountServices(t *testing.T) { wp.AddWorker(1) service := accounts.NewService(accountStore, km, fc, wp, nil, templateService) + txService := transactions.NewService(txStore, km, fc, wp) t.Run("admin init", func(t *testing.T) { - service.InitAdminAccount() + err = InitAdminAccount(context.Background(), service, txService, numProposers) + if err != nil { + t.Fatal(err) + } }) t.Run("sync create", func(t *testing.T) { @@ -253,6 +282,7 @@ func TestAccountHandlers(t *testing.T) { jobStore := jobs.NewGormStore(db) keyStore := keys.NewGormStore(db) + txStore := transactions.NewGormStore(db) templateStore := templates.NewGormStore(db) templateService := templates.NewService(templateStore) @@ -265,8 +295,16 @@ func TestAccountHandlers(t *testing.T) { store := accounts.NewGormStore(db) service := accounts.NewService(store, km, fc, wp, nil, templateService) + txService := transactions.NewService(txStore, km, fc, wp) h := handlers.NewAccounts(logger, service) + t.Run("admin init", func(t *testing.T) { + err = InitAdminAccount(context.Background(), service, txService, numProposers) + if err != nil { + t.Fatal(err) + } + }) + router := mux.NewRouter() router.Handle("/", h.List()).Methods(http.MethodGet) router.Handle("/", h.Create()).Methods(http.MethodPost) @@ -286,7 +324,7 @@ func TestAccountHandlers(t *testing.T) { name: "list db empty", method: http.MethodGet, url: "/", - expected: `(?m)^\[\]$`, + expected: fmt.Sprintf(`(?m)^\[{"address":"%s".*}\]$`, cfg.AdminAddress), status: http.StatusOK, }, { @@ -768,6 +806,13 @@ func TestTokenServices(t *testing.T) { accountService := accounts.NewService(accountStore, km, fc, wp, transactionService, templateService) tokenService := tokens.NewService(tokenStore, km, fc, transactionService, templateService, accountService) + t.Run("admin init", func(t *testing.T) { + err = InitAdminAccount(context.Background(), accountService, transactionService, numProposers) + if err != nil { + t.Fatal(err) + } + }) + t.Run("account can make a transaction", func(t *testing.T) { // Create an account _, account, err := accountService.Create(context.Background(), true) @@ -953,6 +998,13 @@ func TestTokenHandlers(t *testing.T) { accountService := accounts.NewService(accountStore, km, fc, wp, transactionService, templateService) tokenService := tokens.NewService(tokenStore, km, fc, transactionService, templateService, accountService) + t.Run("admin init", func(t *testing.T) { + err = InitAdminAccount(context.Background(), accountService, transactionService, numProposers) + if err != nil { + t.Fatal(err) + } + }) + tokenHandler := handlers.NewTokens(logger, tokenService) router := mux.NewRouter() @@ -997,18 +1049,17 @@ func TestTokenHandlers(t *testing.T) { } // ExampleNFT - basePath := "./cadence/transactions" - setupBytes, err := ioutil.ReadFile(filepath.Join(basePath, "setup_exampleNFT.cdc")) + setupBytes, err := ioutil.ReadFile(filepath.Join(cadenceTxBasePath, "setup_exampleNFT.cdc")) fatal(t, err) - transferBytes, err := ioutil.ReadFile(filepath.Join(basePath, "transfer_exampleNFT.cdc")) + transferBytes, err := ioutil.ReadFile(filepath.Join(cadenceTxBasePath, "transfer_exampleNFT.cdc")) fatal(t, err) - balanceBytes, err := ioutil.ReadFile(filepath.Join(basePath, "balance_exampleNFT.cdc")) + balanceBytes, err := ioutil.ReadFile(filepath.Join(cadenceTxBasePath, "balance_exampleNFT.cdc")) fatal(t, err) - mintBytes, err := ioutil.ReadFile(filepath.Join(basePath, "mint_exampleNFT.cdc")) + mintBytes, err := ioutil.ReadFile(filepath.Join(cadenceTxBasePath, "mint_exampleNFT.cdc")) fatal(t, err) exampleNft := templates.Token{ diff --git a/transactions/service.go b/transactions/service.go index eedfe42d..f092598f 100644 --- a/transactions/service.go +++ b/transactions/service.go @@ -74,7 +74,12 @@ func (s *Service) Create(c context.Context, sync bool, address string, raw templ return "", err } - if err := New(t, id, b, tType, a, a, aa); err != nil { + proposer, err := s.km.AdminProposer(ctx) + if err != nil { + return "", err + } + + if err := New(t, id, b, tType, proposer, a, aa); err != nil { return t.TransactionId, err } diff --git a/transactions/transactions.go b/transactions/transactions.go index 223d7b8e..cc041cf8 100644 --- a/transactions/transactions.go +++ b/transactions/transactions.go @@ -42,6 +42,8 @@ func New( builder.Tx.AddAuthorizer(a.Address) } + signWithProposer := !proposer.Equals(payer) + // Authorizers sign the payload // TODO: support multiple keys per account? for _, a := range authorizers { @@ -50,11 +52,21 @@ func New( continue } + if a.Equals(proposer) { + signWithProposer = false + } + if err := builder.Tx.SignPayload(a.Address, a.Key.Index, a.Signer); err != nil { return err } } + if signWithProposer { + if err := builder.Tx.SignPayload(proposer.Address, proposer.Key.Index, proposer.Signer); err != nil { + return err + } + } + // Payer signs the envelope // TODO: support multiple keys per account? if err := builder.Tx.SignEnvelope(payer.Address, payer.Key.Index, payer.Signer); err != nil { From 6b76721e0c5dadcd05b1f37a7a2c1d0dccefa96e Mon Sep 17 00:00:00 2001 From: Navid TehraniFar Date: Wed, 4 Aug 2021 19:33:42 -0700 Subject: [PATCH 102/112] cleanup --- .env.example | 2 +- accounts/accounts.go | 1 - accounts/config.go | 1 - keys/store_gorm.go | 12 ------------ main_test.go | 1 - 5 files changed, 1 insertion(+), 16 deletions(-) diff --git a/.env.example b/.env.example index c7de63b3..051c9af0 100644 --- a/.env.example +++ b/.env.example @@ -15,4 +15,4 @@ DEFAULT_KEY_TYPE=local # that are stored in the database. ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8e -ENABLED_TOKENS=FUSD:0xf8d6e0586b0a20c7,FlowToken:0x0ae53cb6e3f42a79 \ No newline at end of file +ENABLED_TOKENS=FUSD:0xf8d6e0586b0a20c7:fusd,FlowToken:0x0ae53cb6e3f42a79:flowToken \ No newline at end of file diff --git a/accounts/accounts.go b/accounts/accounts.go index 860f61a1..986986d2 100644 --- a/accounts/accounts.go +++ b/accounts/accounts.go @@ -133,7 +133,6 @@ func AddContract( b.Tx.AddAuthorizer(adminAuth.Address) t := transactions.Transaction{} - // check here if err := transactions.New(&t, id, b, transactions.General, userAuth, adminAuth, nil); err != nil { return nil, err } diff --git a/accounts/config.go b/accounts/config.go index 0188ac4e..c81c6e1a 100644 --- a/accounts/config.go +++ b/accounts/config.go @@ -9,7 +9,6 @@ import ( type Config struct { AdminAccountAddress string `env:"ADMIN_ADDRESS,notEmpty"` ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` - // AdminProposerKeyCount uint16 `env:"ADMIN_PROPOSER_COUNT" envDefault:"1"` } // ParseConfig parses environment variables to a valid Config. diff --git a/keys/store_gorm.go b/keys/store_gorm.go index 8b78ca91..d4f33ee5 100644 --- a/keys/store_gorm.go +++ b/keys/store_gorm.go @@ -1,8 +1,6 @@ package keys import ( - "fmt" - "gorm.io/gorm" ) @@ -27,21 +25,11 @@ func (s *GormStore) Proposer() (i int, err error) { p := Proposer{} err = s.db.Model(&Proposer{}).Order("updated_at asc").First(&p).Error s.db.Save(&p) // Update the UpdatedAt field - fmt.Println("proposer fetched") - fmt.Println(p.KeyIndex) - var ps []Proposer - s.db.Find(&ps) - fmt.Println("all proposers") - for _, i := range ps { - fmt.Println(i) - } i = p.KeyIndex return } func (s *GormStore) InsertProposer(p Proposer) error { - fmt.Println("insert") - fmt.Println(p.KeyIndex) return s.db.Create(&p).Error } diff --git a/main_test.go b/main_test.go index 7a1976c7..258f7caf 100644 --- a/main_test.go +++ b/main_test.go @@ -86,7 +86,6 @@ func InitAdminAccount(ctx context.Context, accService *accounts.Service, txServi return err } - fmt.Println("add proposer") _, _, err = txService.Create(ctx, true, cfg.AdminAddress, templates.Raw{ Code: string(addProposerTx), Arguments: []templates.Argument{ From 9160fd02dbb0df72154459d3c5d66ec693e3b3b3 Mon Sep 17 00:00:00 2001 From: Navid TehraniFar Date: Fri, 6 Aug 2021 21:07:37 -0700 Subject: [PATCH 103/112] add new config for proposer key count --- .env.test | 20 +++++++++ accounts/config.go | 5 ++- accounts/service.go | 47 ++++++++++++++-------- main.go | 2 +- main_test.go | 29 ++----------- templates/template_strings/transactions.go | 17 ++++++++ 6 files changed, 75 insertions(+), 45 deletions(-) create mode 100644 .env.test diff --git a/.env.test b/.env.test new file mode 100644 index 00000000..806e1936 --- /dev/null +++ b/.env.test @@ -0,0 +1,20 @@ +ADMIN_ADDRESS=0xf8d6e0586b0a20c7 +ADMIN_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068 + +DATABASE_TYPE=psql +DATABASE_DSN=postgresql://wallet:wallet@localhost:5432/wallet + +ACCESS_API_HOST=localhost:3569 +CHAIN_ID=flow-emulator + +# When set to "local", private keys are generated by the API +# and stored as encrypted text in the database. +DEFAULT_KEY_TYPE=local + +# This symmetrical key is used to encrypt private keys +# that are stored in the database. +ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8e + +ENABLED_TOKENS=FUSD:0xf8d6e0586b0a20c7:fusd,FlowToken:0x0ae53cb6e3f42a79:flowToken + +ADMIN_PROPOSER_COUNT=100 diff --git a/accounts/config.go b/accounts/config.go index c81c6e1a..f8947bda 100644 --- a/accounts/config.go +++ b/accounts/config.go @@ -7,8 +7,9 @@ import ( // Config struct for account service. type Config struct { - AdminAccountAddress string `env:"ADMIN_ADDRESS,notEmpty"` - ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + AdminAccountAddress string `env:"ADMIN_ADDRESS,notEmpty"` + ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + AdminProposerKeyCount uint16 `env:"ADMIN_PROPOSER_COUNT" envDefault:"1"` } // ParseConfig parses environment variables to a valid Config. diff --git a/accounts/service.go b/accounts/service.go index 66f34437..66120d27 100644 --- a/accounts/service.go +++ b/accounts/service.go @@ -12,21 +12,22 @@ import ( "github.com/eqlabs/flow-wallet-api/jobs" "github.com/eqlabs/flow-wallet-api/keys" "github.com/eqlabs/flow-wallet-api/templates" + "github.com/eqlabs/flow-wallet-api/templates/template_strings" "github.com/eqlabs/flow-wallet-api/transactions" + "github.com/onflow/cadence" "github.com/onflow/flow-go-sdk" "github.com/onflow/flow-go-sdk/client" ) // Service defines the API for account management. type Service struct { - store Store - km keys.Manager - fc *client.Client - wp *jobs.WorkerPool - transactions *transactions.Service - templates *templates.Service - cfg Config - adminProposerCount uint16 + store Store + km keys.Manager + fc *client.Client + wp *jobs.WorkerPool + transactions *transactions.Service + templates *templates.Service + cfg Config } // NewService initiates a new account service. @@ -39,20 +40,20 @@ func NewService( tes *templates.Service, ) *Service { cfg := ParseConfig() - return &Service{store, km, fc, wp, txs, tes, cfg, 0} + return &Service{store, km, fc, wp, txs, tes, cfg} } -func (s *Service) InitAdminAccount(ctx context.Context) error { +func (s *Service) InitAdminAccount(ctx context.Context, txService *transactions.Service) error { a, err := s.store.Account(s.cfg.AdminAccountAddress) if err != nil { if !strings.Contains(err.Error(), "record not found") { - panic(err) + return err } // Admin account not in database a = Account{Address: s.cfg.AdminAccountAddress} err := s.store.InsertAccount(&a) if err != nil { - panic(err) + return err } AccountAdded.Trigger(AccountAddedPayload{ Address: flow.HexToAddress(s.cfg.AdminAccountAddress), @@ -60,12 +61,24 @@ func (s *Service) InitAdminAccount(ctx context.Context) error { } keyCount, err := s.km.InitAdminProposerKeys(ctx) - s.adminProposerCount = keyCount - return err -} + if err != nil { + return err + } + + if keyCount < s.cfg.AdminProposerKeyCount { + numProposers := s.cfg.AdminProposerKeyCount - keyCount -func (s *Service) GetAdminProposerKeyCount() uint16 { - return s.adminProposerCount + _, _, err = txService.Create(ctx, true, s.cfg.AdminAccountAddress, templates.Raw{ + Code: template_strings.AddProposerKeyTransaction, + Arguments: []templates.Argument{ + cadence.NewUInt16(numProposers), + }, + }, transactions.General) + + _, err = s.km.InitAdminProposerKeys(ctx) + } + + return err } // List returns all accounts in the datastore. diff --git a/main.go b/main.go index 8ffa33e7..d446a207 100644 --- a/main.go +++ b/main.go @@ -148,7 +148,7 @@ func runServer(disableRawTx, disableFt, disableNft, disableChainEvents bool) { TokenService: tokenService, }) - err = accountService.InitAdminAccount(context.Background()) + err = accountService.InitAdminAccount(context.Background(), transactionService) if err != nil { ls.Fatal(err) } diff --git a/main_test.go b/main_test.go index 258f7caf..4076d263 100644 --- a/main_test.go +++ b/main_test.go @@ -75,27 +75,6 @@ type httpTestStep struct { sync bool } -func InitAdminAccount(ctx context.Context, accService *accounts.Service, txService *transactions.Service, numProposers uint16) error { - err := accService.InitAdminAccount(ctx) - if err != nil { - return err - } - - addProposerTx, err := ioutil.ReadFile(filepath.Join(cadenceTxBasePath, "add_proposer.cdc")) - if err != nil { - return err - } - - _, _, err = txService.Create(ctx, true, cfg.AdminAddress, templates.Raw{ - Code: string(addProposerTx), - Arguments: []templates.Argument{ - cadence.NewUInt16(numProposers), - }, - }, transactions.General) - - return accService.InitAdminAccount(ctx) -} - func handleStepRequest(s httpTestStep, r *mux.Router, t *testing.T) *httptest.ResponseRecorder { req, err := http.NewRequest(s.method, s.url, s.body) if err != nil { @@ -196,7 +175,7 @@ func TestAccountServices(t *testing.T) { txService := transactions.NewService(txStore, km, fc, wp) t.Run("admin init", func(t *testing.T) { - err = InitAdminAccount(context.Background(), service, txService, numProposers) + err = service.InitAdminAccount(context.Background(), txService) if err != nil { t.Fatal(err) } @@ -298,7 +277,7 @@ func TestAccountHandlers(t *testing.T) { h := handlers.NewAccounts(logger, service) t.Run("admin init", func(t *testing.T) { - err = InitAdminAccount(context.Background(), service, txService, numProposers) + err = service.InitAdminAccount(context.Background(), txService) if err != nil { t.Fatal(err) } @@ -806,7 +785,7 @@ func TestTokenServices(t *testing.T) { tokenService := tokens.NewService(tokenStore, km, fc, transactionService, templateService, accountService) t.Run("admin init", func(t *testing.T) { - err = InitAdminAccount(context.Background(), accountService, transactionService, numProposers) + err = accountService.InitAdminAccount(context.Background(), transactionService) if err != nil { t.Fatal(err) } @@ -998,7 +977,7 @@ func TestTokenHandlers(t *testing.T) { tokenService := tokens.NewService(tokenStore, km, fc, transactionService, templateService, accountService) t.Run("admin init", func(t *testing.T) { - err = InitAdminAccount(context.Background(), accountService, transactionService, numProposers) + err = accountService.InitAdminAccount(context.Background(), transactionService) if err != nil { t.Fatal(err) } diff --git a/templates/template_strings/transactions.go b/templates/template_strings/transactions.go index 76fd8b59..dcd9eeba 100644 --- a/templates/template_strings/transactions.go +++ b/templates/template_strings/transactions.go @@ -73,3 +73,20 @@ transaction { } } ` + +const AddProposerKeyTransaction = ` +transaction(numProposers: UInt16) { + prepare(acc: AuthAccount) { + let key = acc.keys.get(keyIndex: 0)! + var count: UInt16 = 0 + while count < numProposers { + acc.keys.add( + publicKey: key.publicKey, + hashAlgorithm: key.hashAlgorithm, + weight: 0.0 + ) + count = count + 1 + } + } +} +` From caee28a7d8401a8bad1bfd528be18728f673001d Mon Sep 17 00:00:00 2001 From: Navid TehraniFar Date: Fri, 6 Aug 2021 21:09:22 -0700 Subject: [PATCH 104/112] cleanup unwanted cadence file --- .env.example | 4 +++- .gitignore | 1 + cadence/transactions/add_proposer.cdc | 14 -------------- 3 files changed, 4 insertions(+), 15 deletions(-) delete mode 100644 cadence/transactions/add_proposer.cdc diff --git a/.env.example b/.env.example index 051c9af0..bec024f5 100644 --- a/.env.example +++ b/.env.example @@ -15,4 +15,6 @@ DEFAULT_KEY_TYPE=local # that are stored in the database. ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8e -ENABLED_TOKENS=FUSD:0xf8d6e0586b0a20c7:fusd,FlowToken:0x0ae53cb6e3f42a79:flowToken \ No newline at end of file +ENABLED_TOKENS=FUSD:0xf8d6e0586b0a20c7:fusd,FlowToken:0x0ae53cb6e3f42a79:flowToken + +ADMIN_PROPOSER_COUNT=50 \ No newline at end of file diff --git a/.gitignore b/.gitignore index dda87b57..99f1a7ea 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ web_modules/ # dotenv environment variables file .env* !.env.example +!.env.test # parcel-bundler cache (https://parceljs.org/) .cache diff --git a/cadence/transactions/add_proposer.cdc b/cadence/transactions/add_proposer.cdc deleted file mode 100644 index 4282bbeb..00000000 --- a/cadence/transactions/add_proposer.cdc +++ /dev/null @@ -1,14 +0,0 @@ -transaction(numProposers: UInt16) { - prepare(acc: AuthAccount) { - let key = acc.keys.get(keyIndex: 0)! - var count: UInt16 = 0 - while count < numProposers { - acc.keys.add( - publicKey: key.publicKey, - hashAlgorithm: key.hashAlgorithm, - weight: 0.0 - ) - count = count + 1 - } - } -} \ No newline at end of file From bb0832581915bacaf7fa7a249f6161ffb9f591da Mon Sep 17 00:00:00 2001 From: Navid TehraniFar Date: Fri, 6 Aug 2021 21:16:11 -0700 Subject: [PATCH 105/112] use admin proposers only for admin account --- transactions/config.go | 3 ++- transactions/service.go | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/transactions/config.go b/transactions/config.go index d942109a..66a59366 100644 --- a/transactions/config.go +++ b/transactions/config.go @@ -7,7 +7,8 @@ import ( // Config struct for account service. type Config struct { - ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + AdminAccountAddress string `env:"ADMIN_ADDRESS,notEmpty"` } // ParseConfig parses environment variables to a valid Config. diff --git a/transactions/service.go b/transactions/service.go index f092598f..b8071c19 100644 --- a/transactions/service.go +++ b/transactions/service.go @@ -74,9 +74,12 @@ func (s *Service) Create(c context.Context, sync bool, address string, raw templ return "", err } - proposer, err := s.km.AdminProposer(ctx) - if err != nil { - return "", err + proposer := a + if address == s.cfg.AdminAccountAddress { + proposer, err = s.km.AdminProposer(ctx) + if err != nil { + return "", err + } } if err := New(t, id, b, tType, proposer, a, aa); err != nil { From bb9c04463bda82301e82f56be469352438aa3811 Mon Sep 17 00:00:00 2001 From: Navid TehraniFar Date: Tue, 10 Aug 2021 22:05:37 -0700 Subject: [PATCH 106/112] address review --- .env.example | 14 +++++- accounts/accounts.go | 2 +- accounts/config.go | 4 +- accounts/service.go | 34 +++++++++----- docker-compose.yml | 44 ++++++++----------- examples/nextjs-fusd-provider/.env.example | 18 ++++++++ examples/nextjs-fusd-provider/README.md | 8 ---- .../nextjs-fusd-provider/docker-compose.yml | 39 ++++++++++++++++ examples/nextjs-fusd-provider/flow.json | 42 ++++++++++++++++++ keys/basic/keys.go | 10 ++--- keys/keys.go | 14 +++--- keys/store.go | 6 +-- keys/store_gorm.go | 14 +++--- main.go | 2 +- main_test.go | 3 +- templates/config.go | 2 +- templates/service.go | 6 +-- templates/template_strings/transactions.go | 12 ++--- templates/templates.go | 2 +- tokens/config.go | 2 +- tokens/service.go | 18 ++++---- transactions/config.go | 2 +- transactions/service.go | 8 ++-- 23 files changed, 208 insertions(+), 98 deletions(-) create mode 100644 examples/nextjs-fusd-provider/.env.example create mode 100644 examples/nextjs-fusd-provider/docker-compose.yml create mode 100644 examples/nextjs-fusd-provider/flow.json diff --git a/.env.example b/.env.example index bec024f5..1ad6c438 100644 --- a/.env.example +++ b/.env.example @@ -4,9 +4,18 @@ ADMIN_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf19870 DATABASE_TYPE=psql DATABASE_DSN=postgresql://wallet:wallet@localhost:5432/wallet +# emulator ACCESS_API_HOST=localhost:3569 CHAIN_ID=flow-emulator +# testnet +# ACCESS_API_HOST=access.testnet.nodes.onflow.org:9000 +# CHAIN_ID=flow-testnet + +# mainnet +# ACCESS_API_HOST=access.mainnet.nodes.onflow.org:9000 +# CHAIN_ID=flow-mainnet + # When set to "local", private keys are generated by the API # and stored as encrypted text in the database. DEFAULT_KEY_TYPE=local @@ -17,4 +26,7 @@ ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8e ENABLED_TOKENS=FUSD:0xf8d6e0586b0a20c7:fusd,FlowToken:0x0ae53cb6e3f42a79:flowToken -ADMIN_PROPOSER_COUNT=50 \ No newline at end of file +// This sets the number of proposal keys to be used on the admin account. +// You can increase transaction throughput by using multiple proposal keys for +// parallel transaction execution. +ADMIN_PROPOSAL_KEY_COUNT=50 \ No newline at end of file diff --git a/accounts/accounts.go b/accounts/accounts.go index 986986d2..d44103ab 100644 --- a/accounts/accounts.go +++ b/accounts/accounts.go @@ -55,7 +55,7 @@ func New(a *Account, k *keys.Private, ctx context.Context, fc *client.Client, km tx := flow_templates.CreateAccount([]*flow.AccountKey{accountKey}, nil, auth.Address) b := templates.NewBuilderFromTx(tx) - proposer, err := km.AdminProposer(ctx) + proposer, err := km.AdminProposalKey(ctx) if err != nil { return err } diff --git a/accounts/config.go b/accounts/config.go index f8947bda..3d1ad9f4 100644 --- a/accounts/config.go +++ b/accounts/config.go @@ -8,8 +8,8 @@ import ( // Config struct for account service. type Config struct { AdminAccountAddress string `env:"ADMIN_ADDRESS,notEmpty"` - ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` - AdminProposerKeyCount uint16 `env:"ADMIN_PROPOSER_COUNT" envDefault:"1"` + ChainID flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + AdminProposalKeyCount uint16 `env:"ADMIN_PROPOSAL_KEY_COUNT" envDefault:"1"` } // ParseConfig parses environment variables to a valid Config. diff --git a/accounts/service.go b/accounts/service.go index 66120d27..cb5af344 100644 --- a/accounts/service.go +++ b/accounts/service.go @@ -60,24 +60,34 @@ func (s *Service) InitAdminAccount(ctx context.Context, txService *transactions. }) } - keyCount, err := s.km.InitAdminProposerKeys(ctx) + keyCount, err := s.km.InitAdminProposalKeys(ctx) if err != nil { return err } - if keyCount < s.cfg.AdminProposerKeyCount { - numProposers := s.cfg.AdminProposerKeyCount - keyCount - - _, _, err = txService.Create(ctx, true, s.cfg.AdminAccountAddress, templates.Raw{ - Code: template_strings.AddProposerKeyTransaction, - Arguments: []templates.Argument{ - cadence.NewUInt16(numProposers), - }, - }, transactions.General) + if keyCount < s.cfg.AdminProposalKeyCount { + err = s.addAdminProposalKeys(ctx, s.cfg.AdminProposalKeyCount-keyCount, txService) + if err != nil { + return err + } - _, err = s.km.InitAdminProposerKeys(ctx) + _, err = s.km.InitAdminProposalKeys(ctx) + if err != nil { + return err + } } + return nil +} + +func (s *Service) addAdminProposalKeys(ctx context.Context, count uint16, txService *transactions.Service) error { + _, _, err := txService.Create(ctx, true, s.cfg.AdminAccountAddress, templates.Raw{ + Code: template_strings.AddProposalKeyTransaction, + Arguments: []templates.Argument{ + cadence.NewUInt16(count), + }, + }, transactions.General) + return err } @@ -143,7 +153,7 @@ func (s *Service) Create(c context.Context, sync bool) (*jobs.Job, *Account, err // Details returns a specific account. func (s *Service) Details(address string) (Account, error) { // Check if the input is a valid address - address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainId) + address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { return Account{}, err } diff --git a/docker-compose.yml b/docker-compose.yml index f1a203d3..abe0ac31 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,39 +1,33 @@ -version: "3.3" +version: "3.9" services: + db: + image: postgres:13-alpine + environment: + - POSTGRES_DB=wallet + - POSTGRES_USER=wallet + - POSTGRES_PASSWORD=wallet + wallet: - image: gcr.io/flow-container-registry/wallet-api:v0.3.1 + build: + context: . + dockerfile: ./docker/wallet/Dockerfile ports: - "3000:3000" + env_file: + - ./.env environment: - - DATABASE_TYPE=psql - - DATABASE_DSN=postgresql://wallet:wallet@db:5432/wallet - - ACCESS_API_HOST=emulator:3569 - - CHAIN_ID=flow-emulator - - ADMIN_ADDRESS=${ADMIN_ADDRESS} - - ADMIN_PRIVATE_KEY=${ADMIN_PRIVATE_KEY} - - DEFAULT_KEY_TYPE=local - - ENCRYPTION_KEY=${ENCRYPTION_KEY} - - ENABLED_TOKENS=FUSD:${ADMIN_ADDRESS}:fusd,FlowToken:0x0ae53cb6e3f42a79:flow + DATABASE_DSN: postgresql://wallet:wallet@db:5432/wallet + DATABASE_TYPE: psql + ACCESS_API_HOST: emulator:3569 + CHAIN_ID: flow-emulator depends_on: - db - emulator emulator: - image: gcr.io/flow-container-registry/emulator:v0.22.0 - ports: - - "3569:3569" - - "8080:8080" + image: gcr.io/flow-container-registry/emulator:latest + command: emulator -v environment: - FLOW_SERVICEPRIVATEKEY=${ADMIN_PRIVATE_KEY} - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 - FLOW_SERVICEKEYHASHALGO=SHA3_256 - - FLOW_VERBOSE=true - - db: - image: postgres:13-alpine - ports: - - "5432:5432" - environment: - - POSTGRES_DB=wallet - - POSTGRES_USER=wallet - - POSTGRES_PASSWORD=wallet diff --git a/examples/nextjs-fusd-provider/.env.example b/examples/nextjs-fusd-provider/.env.example new file mode 100644 index 00000000..051c9af0 --- /dev/null +++ b/examples/nextjs-fusd-provider/.env.example @@ -0,0 +1,18 @@ +ADMIN_ADDRESS=0xf8d6e0586b0a20c7 +ADMIN_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068 + +DATABASE_TYPE=psql +DATABASE_DSN=postgresql://wallet:wallet@localhost:5432/wallet + +ACCESS_API_HOST=localhost:3569 +CHAIN_ID=flow-emulator + +# When set to "local", private keys are generated by the API +# and stored as encrypted text in the database. +DEFAULT_KEY_TYPE=local + +# This symmetrical key is used to encrypt private keys +# that are stored in the database. +ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8e + +ENABLED_TOKENS=FUSD:0xf8d6e0586b0a20c7:fusd,FlowToken:0x0ae53cb6e3f42a79:flowToken \ No newline at end of file diff --git a/examples/nextjs-fusd-provider/README.md b/examples/nextjs-fusd-provider/README.md index f45676f4..72fadc89 100644 --- a/examples/nextjs-fusd-provider/README.md +++ b/examples/nextjs-fusd-provider/README.md @@ -12,12 +12,6 @@ to distribute the FUSD stablecoin. This example uses the sample emulator configuration from the root of this repository. -Change to the repository root: - -```ls -cd ../../ -``` - Create a configuration file: ```sh @@ -41,8 +35,6 @@ flow project deploy -n emulator In this directory, install and run the Next.js app: ```bash -cd examples/nextjs-fusd-provider - npm install npm run dev diff --git a/examples/nextjs-fusd-provider/docker-compose.yml b/examples/nextjs-fusd-provider/docker-compose.yml new file mode 100644 index 00000000..f1a203d3 --- /dev/null +++ b/examples/nextjs-fusd-provider/docker-compose.yml @@ -0,0 +1,39 @@ +version: "3.3" +services: + wallet: + image: gcr.io/flow-container-registry/wallet-api:v0.3.1 + ports: + - "3000:3000" + environment: + - DATABASE_TYPE=psql + - DATABASE_DSN=postgresql://wallet:wallet@db:5432/wallet + - ACCESS_API_HOST=emulator:3569 + - CHAIN_ID=flow-emulator + - ADMIN_ADDRESS=${ADMIN_ADDRESS} + - ADMIN_PRIVATE_KEY=${ADMIN_PRIVATE_KEY} + - DEFAULT_KEY_TYPE=local + - ENCRYPTION_KEY=${ENCRYPTION_KEY} + - ENABLED_TOKENS=FUSD:${ADMIN_ADDRESS}:fusd,FlowToken:0x0ae53cb6e3f42a79:flow + depends_on: + - db + - emulator + + emulator: + image: gcr.io/flow-container-registry/emulator:v0.22.0 + ports: + - "3569:3569" + - "8080:8080" + environment: + - FLOW_SERVICEPRIVATEKEY=${ADMIN_PRIVATE_KEY} + - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 + - FLOW_SERVICEKEYHASHALGO=SHA3_256 + - FLOW_VERBOSE=true + + db: + image: postgres:13-alpine + ports: + - "5432:5432" + environment: + - POSTGRES_DB=wallet + - POSTGRES_USER=wallet + - POSTGRES_PASSWORD=wallet diff --git a/examples/nextjs-fusd-provider/flow.json b/examples/nextjs-fusd-provider/flow.json new file mode 100644 index 00000000..17cf4ec0 --- /dev/null +++ b/examples/nextjs-fusd-provider/flow.json @@ -0,0 +1,42 @@ +{ + "emulators": { + "default": { + "port": 3569, + "serviceAccount": "emulator-account" + } + }, + "contracts": { + "FungibleToken": { + "source": "../../cadence/contracts/FungibleToken.cdc", + "aliases": { + "emulator": "0xee82856bf20e2aa6", + "testnet": "0x9a0766d93b6608b7" + } + }, + "FUSD": { + "source": "../../cadence/contracts/FUSD.cdc", + "aliases": { + "testnet": "0xe223d8a629e49c68" + } + } + }, + "deployments": { + "emulator": { + "emulator-account": [ + "FUSD" + ] + } + }, + "networks": { + "emulator": "127.0.0.1:3569", + "mainnet": "access.mainnet.nodes.onflow.org:9000", + "testnet": "access.devnet.nodes.onflow.org:9000" + }, + "accounts": { + "emulator-account": { + "address": "f8d6e0586b0a20c7", + "keys": "91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068", + "chain": "flow-emulator" + } + } +} diff --git a/keys/basic/keys.go b/keys/basic/keys.go index 2df5f70b..d2fd5a3c 100644 --- a/keys/basic/keys.go +++ b/keys/basic/keys.go @@ -149,7 +149,7 @@ func (s *KeyManager) MakeAuthorizer(ctx context.Context, address flow.Address) ( }, nil } -func (s *KeyManager) InitAdminProposerKeys(ctx context.Context) (uint16, error) { +func (s *KeyManager) InitAdminProposalKeys(ctx context.Context) (uint16, error) { adminAddress := flow.HexToAddress(s.cfg.AdminAccountAddress) adminAccount, err := s.fc.GetAccount(ctx, adminAddress) @@ -157,7 +157,7 @@ func (s *KeyManager) InitAdminProposerKeys(ctx context.Context) (uint16, error) return 0, err } - err = s.store.DeleteAllProposers() + err = s.store.DeleteAllProposalKeys() if err != nil { return 0, err } @@ -165,7 +165,7 @@ func (s *KeyManager) InitAdminProposerKeys(ctx context.Context) (uint16, error) var count uint16 for _, k := range adminAccount.Keys { if !k.Revoked { - err = s.store.InsertProposer(keys.Proposer{ + err = s.store.InsertProposalKey(keys.ProposalKey{ KeyIndex: k.Index, }) if err != nil { @@ -178,10 +178,10 @@ func (s *KeyManager) InitAdminProposerKeys(ctx context.Context) (uint16, error) return count, nil } -func (s *KeyManager) AdminProposer(ctx context.Context) (keys.Authorizer, error) { +func (s *KeyManager) AdminProposalKey(ctx context.Context) (keys.Authorizer, error) { adminAcc := flow.HexToAddress(s.cfg.AdminAccountAddress) - index, err := s.store.Proposer() + index, err := s.store.ProposalKey() if err != nil { return keys.Authorizer{}, err } diff --git a/keys/keys.go b/keys/keys.go index ce93d39e..d808d375 100644 --- a/keys/keys.go +++ b/keys/keys.go @@ -29,11 +29,11 @@ type Manager interface { AdminAuthorizer(context.Context) (Authorizer, error) // UserAuthorizer returns an Authorizer for the given address. UserAuthorizer(ctx context.Context, address flow.Address) (Authorizer, error) - // InitAdminProposerKeys will init the admin proposer keys in the database + // InitAdminProposalKeys will init the admin proposal keys in the database // and return current count. - InitAdminProposerKeys(ctx context.Context) (uint16, error) - // AdminProposer returns Authorizer to be used as proposer. - AdminProposer(ctx context.Context) (Authorizer, error) + InitAdminProposalKeys(ctx context.Context) (uint16, error) + // AdminProposalKey returns Authorizer to be used as proposer. + AdminProposalKey(ctx context.Context) (Authorizer, error) } // Storable struct represents a storable account private key. @@ -58,13 +58,17 @@ func (Storable) TableName() string { return "storable_keys" } -type Proposer struct { +type ProposalKey struct { ID int `json:"-" gorm:"primaryKey"` KeyIndex int `gorm:"unique"` CreatedAt time.Time UpdatedAt time.Time } +func (ProposalKey) TableName() string { + return "proposal_keys" +} + // Private is an "in flight" account private key meaning its Value should be the actual // private key or resource id (unencrypted). type Private struct { diff --git a/keys/store.go b/keys/store.go index 51affa10..62317456 100644 --- a/keys/store.go +++ b/keys/store.go @@ -3,7 +3,7 @@ package keys // Store is the interface required by key manager for data storage. type Store interface { AccountKey(address string) (Storable, error) - Proposer() (int, error) - InsertProposer(proposer Proposer) error - DeleteAllProposers() error + ProposalKey() (int, error) + InsertProposalKey(proposalKey ProposalKey) error + DeleteAllProposalKeys() error } diff --git a/keys/store_gorm.go b/keys/store_gorm.go index d4f33ee5..936083e7 100644 --- a/keys/store_gorm.go +++ b/keys/store_gorm.go @@ -11,7 +11,7 @@ type GormStore struct { func NewGormStore(db *gorm.DB) *GormStore { db.Migrator().RenameTable("storables", "storable_keys") // Ignore error db.AutoMigrate(&Storable{}) - db.AutoMigrate(&Proposer{}) + db.AutoMigrate(&ProposalKey{}) return &GormStore{db} } @@ -21,18 +21,18 @@ func (s *GormStore) AccountKey(address string) (k Storable, err error) { return } -func (s *GormStore) Proposer() (i int, err error) { - p := Proposer{} - err = s.db.Model(&Proposer{}).Order("updated_at asc").First(&p).Error +func (s *GormStore) ProposalKey() (i int, err error) { + p := ProposalKey{} + err = s.db.Model(&ProposalKey{}).Order("updated_at asc").First(&p).Error s.db.Save(&p) // Update the UpdatedAt field i = p.KeyIndex return } -func (s *GormStore) InsertProposer(p Proposer) error { +func (s *GormStore) InsertProposalKey(p ProposalKey) error { return s.db.Create(&p).Error } -func (s *GormStore) DeleteAllProposers() error { - return s.db.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&Proposer{}).Error +func (s *GormStore) DeleteAllProposalKeys() error { + return s.db.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&ProposalKey{}).Error } diff --git a/main.go b/main.go index d446a207..489328f6 100644 --- a/main.go +++ b/main.go @@ -40,7 +40,7 @@ type Config struct { Host string `env:"HOST"` Port int `env:"PORT" envDefault:"3000"` AccessAPIHost string `env:"ACCESS_API_HOST,notEmpty"` - ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + ChainID flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` } func main() { diff --git a/main_test.go b/main_test.go index 4076d263..bdc0f694 100644 --- a/main_test.go +++ b/main_test.go @@ -41,7 +41,6 @@ const ( testDbDSN = "test.db" testDbType = "sqlite" cadenceTxBasePath = "./cadence/transactions" - numProposers = 3 ) var ( @@ -52,7 +51,7 @@ var ( type testConfig struct { AccessAPIHost string `env:"ACCESS_API_HOST,notEmpty"` AdminAddress string `env:"ADMIN_ADDRESS,notEmpty"` - ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + ChainID flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` } type TestLogger struct { diff --git a/templates/config.go b/templates/config.go index 8f0e1476..f06446e1 100644 --- a/templates/config.go +++ b/templates/config.go @@ -9,7 +9,7 @@ import ( // config struct for templates. type config struct { - ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + ChainID flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` EnvEnabledTokens []string `env:"ENABLED_TOKENS" envSeparator:","` enabledTokens map[string]Token } diff --git a/templates/service.go b/templates/service.go index 17f8b237..8be9bb87 100644 --- a/templates/service.go +++ b/templates/service.go @@ -46,7 +46,7 @@ func NewService(store Store) *Service { func (s *Service) AddToken(t *Token) error { // Check if the input is a valid address - address, err := flow_helpers.ValidateAddress(t.Address, s.cfg.ChainId) + address, err := flow_helpers.ValidateAddress(t.Address, s.cfg.ChainID) if err != nil { return err } @@ -87,7 +87,7 @@ func (s *Service) TokenFromEvent(e flow.Event) (*Token, error) { ss := strings.Split(e.Type, ".") // Token address from event - eAddress, err := flow_helpers.ValidateAddress(ss[1], s.cfg.ChainId) + eAddress, err := flow_helpers.ValidateAddress(ss[1], s.cfg.ChainID) if err != nil { return nil, err } @@ -98,7 +98,7 @@ func (s *Service) TokenFromEvent(e flow.Event) (*Token, error) { } // Token address from database - tAddress, err := flow_helpers.ValidateAddress(token.Address, s.cfg.ChainId) + tAddress, err := flow_helpers.ValidateAddress(token.Address, s.cfg.ChainID) if err != nil { return nil, err } diff --git a/templates/template_strings/transactions.go b/templates/template_strings/transactions.go index dcd9eeba..b7232fcc 100644 --- a/templates/template_strings/transactions.go +++ b/templates/template_strings/transactions.go @@ -74,13 +74,13 @@ transaction { } ` -const AddProposerKeyTransaction = ` -transaction(numProposers: UInt16) { - prepare(acc: AuthAccount) { - let key = acc.keys.get(keyIndex: 0)! +const AddProposalKeyTransaction = ` +transaction(numProposalKeys: UInt16) { + prepare(account: AuthAccount) { + let key = account.keys.get(keyIndex: 0)! var count: UInt16 = 0 - while count < numProposers { - acc.keys.add( + while count < numProposalKeys { + account.keys.add( publicKey: key.publicKey, hashAlgorithm: key.hashAlgorithm, weight: 0.0 diff --git a/templates/templates.go b/templates/templates.go index 2615a981..c378823a 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -86,7 +86,7 @@ func TokenCode(token *Token, tmplStr string) string { "TOKEN_BALANCE", fmt.Sprintf("%sBalance", token.NameLowerCase), ) - knownAddressesReplacer := knownAddressesReplacers[parseConfig().ChainId] + knownAddressesReplacer := knownAddressesReplacers[parseConfig().ChainID] code := tmplStr diff --git a/tokens/config.go b/tokens/config.go index bf13087b..46bc5f2d 100644 --- a/tokens/config.go +++ b/tokens/config.go @@ -7,7 +7,7 @@ import ( // Config struct for account service. type Config struct { - ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + ChainID flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` } // ParseConfig parses environment variables to a valid Config. diff --git a/tokens/service.go b/tokens/service.go index 5c44fcc4..4d1cba3b 100644 --- a/tokens/service.go +++ b/tokens/service.go @@ -48,7 +48,7 @@ func NewService( func (s *Service) Setup(ctx context.Context, sync bool, tokenName, address string) (*jobs.Job, *transactions.Transaction, error) { // Check if the input is a valid address - address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainId) + address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { return nil, nil, err } @@ -96,7 +96,7 @@ func (s *Service) Setup(ctx context.Context, sync bool, tokenName, address strin func (s *Service) AccountTokens(address string, tType *templates.TokenType) ([]AccountToken, error) { // Check if the input is a valid address - address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainId) + address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { return nil, err } @@ -107,7 +107,7 @@ func (s *Service) AccountTokens(address string, tType *templates.TokenType) ([]A // Details is used to get the accounts balance (or similar for NFTs) for a token. func (s *Service) Details(ctx context.Context, tokenName, address string) (*Details, error) { // Check if the input is a valid address - address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainId) + address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { return nil, err } @@ -143,13 +143,13 @@ func (s *Service) Details(ctx context.Context, tokenName, address string) (*Deta func (s *Service) CreateWithdrawal(ctx context.Context, runSync bool, sender string, request WithdrawalRequest) (*jobs.Job, *transactions.Transaction, error) { // Check if the sender is a valid address - sender, err := flow_helpers.ValidateAddress(sender, s.cfg.ChainId) + sender, err := flow_helpers.ValidateAddress(sender, s.cfg.ChainID) if err != nil { return nil, nil, err } // Check if the recipient is a valid address - recipient, err := flow_helpers.ValidateAddress(request.Recipient, s.cfg.ChainId) + recipient, err := flow_helpers.ValidateAddress(request.Recipient, s.cfg.ChainID) if err != nil { return nil, nil, err } @@ -216,7 +216,7 @@ func (s *Service) CreateWithdrawal(ctx context.Context, runSync bool, sender str func (s *Service) ListTransfers(queryType, address, tokenName string) ([]*TokenTransfer, error) { // Check if the input is a valid address - address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainId) + address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { return nil, err } @@ -272,7 +272,7 @@ func (s *Service) ListDeposits(address, tokenName string) ([]*TokenDeposit, erro func (s *Service) GetTransfer(queryType, address, tokenName, transactionId string) (*TokenTransfer, error) { // Check if the input is a valid address - address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainId) + address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { return nil, err } @@ -341,7 +341,7 @@ func (s *Service) RegisterDeposit(token *templates.Token, transactionId, amountO } // Check if the input address is a valid address - recipient, err := flow_helpers.ValidateAddress(recipient, s.cfg.ChainId) + recipient, err := flow_helpers.ValidateAddress(recipient, s.cfg.ChainID) if err != nil { return err } @@ -402,7 +402,7 @@ func (s *Service) RegisterDeposit(token *templates.Token, transactionId, amountO // DeployTokenContractForAccount is mainly used for testing purposes. func (s *Service) DeployTokenContractForAccount(ctx context.Context, runSync bool, tokenName, address string) (*jobs.Job, *transactions.Transaction, error) { // Check if the input is a valid address - address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainId) + address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { return nil, nil, err } diff --git a/transactions/config.go b/transactions/config.go index 66a59366..9c76559c 100644 --- a/transactions/config.go +++ b/transactions/config.go @@ -7,7 +7,7 @@ import ( // Config struct for account service. type Config struct { - ChainId flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + ChainID flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` AdminAccountAddress string `env:"ADMIN_ADDRESS,notEmpty"` } diff --git a/transactions/service.go b/transactions/service.go index b8071c19..caa9f6bb 100644 --- a/transactions/service.go +++ b/transactions/service.go @@ -47,7 +47,7 @@ func (s *Service) Create(c context.Context, sync bool, address string, raw templ } // Check if the input is a valid address - address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainId) + address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { return "", err } @@ -76,7 +76,7 @@ func (s *Service) Create(c context.Context, sync bool, address string, raw templ proposer := a if address == s.cfg.AdminAccountAddress { - proposer, err = s.km.AdminProposer(ctx) + proposer, err = s.km.AdminProposalKey(ctx) if err != nil { return "", err } @@ -127,7 +127,7 @@ func (s *Service) Create(c context.Context, sync bool, address string, raw templ // List returns all transactions in the datastore for a given account. func (s *Service) List(tType Type, address string, limit, offset int) ([]Transaction, error) { // Check if the input is a valid address - address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainId) + address, err := flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { return []Transaction{}, err } @@ -140,7 +140,7 @@ func (s *Service) List(tType Type, address string, limit, offset int) ([]Transac // Details returns a specific transaction. func (s *Service) Details(tType Type, address, transactionId string) (result Transaction, err error) { // Check if the input is a valid address - address, err = flow_helpers.ValidateAddress(address, s.cfg.ChainId) + address, err = flow_helpers.ValidateAddress(address, s.cfg.ChainID) if err != nil { return } From b2c0d98ad175a7186efeee159ea2c2c359aa0369 Mon Sep 17 00:00:00 2001 From: Navid TehraniFar Date: Wed, 11 Aug 2021 13:23:29 -0700 Subject: [PATCH 107/112] fix env files --- .env.example | 6 +++--- .env.test | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index 1ad6c438..0efb6611 100644 --- a/.env.example +++ b/.env.example @@ -26,7 +26,7 @@ ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8e ENABLED_TOKENS=FUSD:0xf8d6e0586b0a20c7:fusd,FlowToken:0x0ae53cb6e3f42a79:flowToken -// This sets the number of proposal keys to be used on the admin account. -// You can increase transaction throughput by using multiple proposal keys for -// parallel transaction execution. +# This sets the number of proposal keys to be used on the admin account. +# You can increase transaction throughput by using multiple proposal keys for +# parallel transaction execution. ADMIN_PROPOSAL_KEY_COUNT=50 \ No newline at end of file diff --git a/.env.test b/.env.test index 806e1936..e99f9aee 100644 --- a/.env.test +++ b/.env.test @@ -1,9 +1,6 @@ ADMIN_ADDRESS=0xf8d6e0586b0a20c7 ADMIN_PRIVATE_KEY=91a22fbd87392b019fbe332c32695c14cf2ba5b6521476a8540228bdf1987068 -DATABASE_TYPE=psql -DATABASE_DSN=postgresql://wallet:wallet@localhost:5432/wallet - ACCESS_API_HOST=localhost:3569 CHAIN_ID=flow-emulator @@ -17,4 +14,4 @@ ENCRYPTION_KEY=faae4ed1c30f4e4555ee3a71f1044a8e ENABLED_TOKENS=FUSD:0xf8d6e0586b0a20c7:fusd,FlowToken:0x0ae53cb6e3f42a79:flowToken -ADMIN_PROPOSER_COUNT=100 +ADMIN_PROPOSAL_KEY_COUNT=100 From b35c9416399074d0b0303fda96316f05b3e6c345 Mon Sep 17 00:00:00 2001 From: Navid TehraniFar Date: Wed, 11 Aug 2021 16:43:13 -0700 Subject: [PATCH 108/112] fix tests with docker-compose --- docker-compose.yml | 5 ++++- main_test.go | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index abe0ac31..53f1032b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,6 +15,7 @@ services: - "3000:3000" env_file: - ./.env + restart: unless-stopped environment: DATABASE_DSN: postgresql://wallet:wallet@db:5432/wallet DATABASE_TYPE: psql @@ -25,8 +26,10 @@ services: - emulator emulator: - image: gcr.io/flow-container-registry/emulator:latest + image: gcr.io/flow-container-registry/emulator:v0.23.0 command: emulator -v + ports: + - "3569:3569" environment: - FLOW_SERVICEPRIVATEKEY=${ADMIN_PRIVATE_KEY} - FLOW_SERVICEKEYSIGALGO=ECDSA_P256 diff --git a/main_test.go b/main_test.go index bdc0f694..f9ff4cc1 100644 --- a/main_test.go +++ b/main_test.go @@ -700,7 +700,7 @@ func TestScriptsHandlers(t *testing.T) { "arguments":[] }`), contentType: "application/json", - expected: "1000000000000000000", + expected: "100000000000000000", status: http.StatusOK, }, { From 9c7f16022ffa3b1f45fabe4cc95cd1556a45edeb Mon Sep 17 00:00:00 2001 From: Navid TehraniFar Date: Wed, 11 Aug 2021 17:00:15 -0700 Subject: [PATCH 109/112] add test for proposer key count --- main_test.go | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/main_test.go b/main_test.go index f9ff4cc1..b629bcc9 100644 --- a/main_test.go +++ b/main_test.go @@ -49,9 +49,10 @@ var ( ) type testConfig struct { - AccessAPIHost string `env:"ACCESS_API_HOST,notEmpty"` - AdminAddress string `env:"ADMIN_ADDRESS,notEmpty"` - ChainID flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + AccessAPIHost string `env:"ACCESS_API_HOST,notEmpty"` + AdminAddress string `env:"ADMIN_ADDRESS,notEmpty"` + ChainID flow.ChainID `env:"CHAIN_ID" envDefault:"flow-emulator"` + AdminProposalKeyCount uint16 `env:"ADMIN_PROPOSAL_KEY_COUNT" envDefault:"10"` } type TestLogger struct { @@ -174,10 +175,26 @@ func TestAccountServices(t *testing.T) { txService := transactions.NewService(txStore, km, fc, wp) t.Run("admin init", func(t *testing.T) { - err = service.InitAdminAccount(context.Background(), txService) + ctx := context.Background() + if err := env.Parse(&cfg); err != nil { + t.Fatal(err) + } + + err = service.InitAdminAccount(ctx, txService) + if err != nil { + t.Fatal(err) + } + + // make sure all requested proposal keys are created + + keyCount, err := km.InitAdminProposalKeys(ctx) if err != nil { t.Fatal(err) } + + if keyCount != cfg.AdminProposalKeyCount { + t.Fatal("incorrect number of admin proposal keys") + } }) t.Run("sync create", func(t *testing.T) { From e2002e6a8fc30e72050cf24373b61300728c0465 Mon Sep 17 00:00:00 2001 From: Lauri Junkkari Date: Fri, 13 Aug 2021 10:17:35 +0300 Subject: [PATCH 110/112] Merge README.md --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bf8a6358..7816fa29 100644 --- a/README.md +++ b/README.md @@ -84,14 +84,15 @@ docker-compose down ### Enabled fungible tokens -A comma separated list of fungible tokens and their corresponding addresses enabled for this instance. Make sure to name each token exactly as it is in the corresponding cadence code (FlowToken, FUSD etc.). Include at least FlowToken as functionality without it is undetermined. +A comma separated list of _fungible tokens_ and their corresponding addresses enabled for this instance. Make sure to name each token exactly as it is in the corresponding cadence code (FlowToken, FUSD etc.). Include at least FlowToken as functionality without it is undetermined. + +**NOTE:** It is necessary to add a 3rd parameter "lowercamelcase" name for each token. For FlowToken this would be "flowToken" and for FUSD "fusd". This is used to construct the vault name, receiver name and balance name in generic transaction templates. Consult the contract code for each token to derive the proper name (search for `.*Vault`, `.*Receiver`, `.*Balance`) + +**NOTE:** Non-fungible tokens can _not_ be enabled using environment variables. Use the API endpoints for that. Examples: -```sh -ENABLED_TOKENS=FlowToken:0x0ae53cb6e3f42a79 -ENABLED_TOKENS=FlowToken:0x0ae53cb6e3f42a79,FUSD:0xf8d6e0586b0a20c7 -``` + ENABLED_TOKENS=FlowToken:0x0ae53cb6e3f42a79:flowToken,FUSD:0xf8d6e0586b0a20c7:fusd ### Database @@ -175,6 +176,7 @@ ENCRYPTION_KEY= GOOGLE_APPLICATION_CREDENTIALS= GOOGLE_KMS_PROJECT_ID= GOOGLE_KMS_LOCATION_ID= +GOOGLE_KMS_KEYRING_ID= ``` ## Credit From 89f9affb8e1bc4c3a8f39dfd6d93bf325b5804b2 Mon Sep 17 00:00:00 2001 From: Lauri Junkkari Date: Fri, 13 Aug 2021 12:07:45 +0300 Subject: [PATCH 111/112] Add copyright notice --- COPYRIGHT_NOTICE | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 COPYRIGHT_NOTICE diff --git a/COPYRIGHT_NOTICE b/COPYRIGHT_NOTICE new file mode 100644 index 00000000..4b80a0c2 --- /dev/null +++ b/COPYRIGHT_NOTICE @@ -0,0 +1,13 @@ + Copyright 2021 Equilibrium, OnFlow + + This project is licensed under the Apache License, Version 2.0 (the "License"); + you may not use the material in produced by this project except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 5213e1c66af708b580ebb571490a50eb4c92322f Mon Sep 17 00:00:00 2001 From: Lauri Junkkari Date: Fri, 13 Aug 2021 12:21:22 +0300 Subject: [PATCH 112/112] Bump version number from 0.4.1 to 0.5.0 --- api/openapi.yaml | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/openapi.yaml b/api/openapi.yaml index d3b59cc2..7082a948 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -2,7 +2,7 @@ openapi: 3.1.0 info: title: Flow Wallet API - version: 0.4.1 + version: 0.5.0 servers: - url: http://localhost:3000/v1 diff --git a/main.go b/main.go index 489328f6..529e9455 100644 --- a/main.go +++ b/main.go @@ -29,7 +29,7 @@ import ( "google.golang.org/grpc" ) -const version = "0.4.1" +const version = "0.5.0" var ( sha1ver string // sha1 revision used to build the program