diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..e31b9a30 --- /dev/null +++ b/.envrc @@ -0,0 +1,5 @@ +#!/bin/sh +set -e +PATH_add node_modules/.bin +PATH_add ui/libs/*/bin +PATH_add ui/apps/*/bin diff --git a/.gitignore b/.gitignore index 45d84209..8d4068e9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ ui/node .hc* *.happ .cargo +.idea +.rollup.cache +*.iml diff --git a/Makefile b/Makefile index 71fc974c..908c3a3d 100644 --- a/Makefile +++ b/Makefile @@ -55,17 +55,17 @@ test-unit: test-dna: @echo "Starting Scenario tests in $$(pwd)..."; \ - cd tests && npm test + cd tests && ./node_modules/.bin/pnpm test test-dna-debug: @echo "Starting Scenario tests in $$(pwd)..."; \ - cd tests && npm run test-debug + cd tests && ./node_modules/.bin/pnpm run test-debug test-e2e: test-node test-dna test-node: @echo "Setting up Scenario/Stress test Javascript..."; \ - cd tests && npm install && cd .. + cd tests && ./node_modules/.bin/pnpm install && cd .. # Generic targets; does not require a Nix environment .PHONY: clean diff --git a/README.md b/README.md index 50607036..7628fcab 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ cd ui npm install npm run dev ``` -Now open two tabs in your browser pointing at `http://localhost:5000`. In the second tab, change the `appPort` to `8889`. Then click 'Connect'. Now you should be able to see both agents and start editing text on either tab and see it appear on the other. +Now open two tabs in your browser pointing at `http://localhost:5000`. In the second tab, change the `app_port` to `8889`. Then click 'Connect'. Now you should be able to see both agents and start editing text on either tab and see it appear on the other. ### Testing diff --git a/bin/tsc-build.sh b/bin/tsc-build.sh new file mode 100755 index 00000000..ab677a99 --- /dev/null +++ b/bin/tsc-build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +tsc -b $(find ui/libs -maxdepth 1 -mindepth 1) $@ diff --git a/package.json b/package.json new file mode 100644 index 00000000..19953f5e --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "@syn-ui/dev", + "private": true, + "version": "0.0.0", + "scripts": {}, + "workspaces": { + "packages": [ + "ui/apps/*", + "ui/libs/*" + ] + }, + "devDependencies": { + "@babel/runtime": "^7.14.0", + "@changesets/cli": "^2.16.0", + "@ctx-core/function": "^17.8.2", + "@ctx-core/jetbrains": "^4.0.35", + "@ctx-core/monorepo": "^15.0.57", + "@ctx-core/pnpm-tools": "^6.0.11", + "pnpm": "^6.2.5", + "typescript": "4.2.4" + }, + "noUpdate": [] +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..fb14b624 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4501 @@ +lockfileVersion: 5.3 + +importers: + + .: + specifiers: + '@babel/runtime': ^7.14.0 + '@changesets/cli': ^2.16.0 + '@ctx-core/function': ^17.8.2 + '@ctx-core/jetbrains': ^4.0.35 + '@ctx-core/monorepo': ^15.0.57 + '@ctx-core/pnpm-tools': ^6.0.11 + pnpm: ^6.2.5 + typescript: 4.2.4 + devDependencies: + '@babel/runtime': 7.14.0 + '@changesets/cli': 2.16.0 + '@ctx-core/function': 17.8.2 + '@ctx-core/jetbrains': 4.0.35 + '@ctx-core/monorepo': 15.0.57 + '@ctx-core/pnpm-tools': 6.0.11 + pnpm: 6.2.5 + typescript: 4.2.4 + + tests: + specifiers: + '@ctx-core/combinators': ^6.0.10 + '@ctx-core/store': ^24.7.17 + '@holochain/conductor-api': 0.0.4 + '@holochain/tryorama': 0.4.2 + '@syn-ui/model': workspace:^1.0.0 + '@syn-ui/utils': workspace:^1.0.0 + '@syn-ui/zome-client': workspace:^1.0.0 + '@types/lodash': ^4.14.168 + '@types/node': ^15.0.2 + ava: ^3.15.0 + esm: ^3.2.25 + lodash: ^4.17.21 + pnpm: ^6.2.5 + tape: ^5.2.2 + ts-node: ^9.1.1 + typescript: ^4.2.4 + uuidv4: ^6.2.7 + dependencies: + esm: 3.2.25 + lodash: 4.17.21 + tape: 5.2.2 + ts-node: 9.1.1_typescript@4.2.4 + typescript: 4.2.4 + uuidv4: 6.2.7 + devDependencies: + '@ctx-core/combinators': 6.0.10 + '@ctx-core/store': 24.7.17 + '@holochain/conductor-api': 0.0.4 + '@holochain/tryorama': 0.4.2 + '@syn-ui/model': link:../ui/libs/model + '@syn-ui/utils': link:../ui/libs/utils + '@syn-ui/zome-client': link:../ui/libs/zome-client + '@types/lodash': 4.14.168 + '@types/node': 15.0.2 + ava: 3.15.0 + pnpm: 6.2.5 + + ui/apps/app: + specifiers: + '@ctx-core/combinators': ^6.0.10 + '@ctx-core/object': ^17.5.24 + '@ctx-core/store': ^24.7.17 + '@holochain/conductor-api': 0.0.4 + '@rollup/plugin-commonjs': ^18.1.0 + '@rollup/plugin-node-resolve': ^13.0.0 + '@rollup/plugin-replace': ^2.4.2 + '@rollup/plugin-typescript': ^8.2.1 + '@syn-ui/model': workspace:^1.0.0 + '@syn-ui/zome-client': workspace:^1.0.0 + '@tsconfig/svelte': ^1.0.10 + buffer: ^6.0.3 + rollup: ^2.47.0 + rollup-plugin-css-only: ^3.1.0 + rollup-plugin-livereload: ^2.0.0 + rollup-plugin-node-polyfills: ^0.2.1 + rollup-plugin-svelte: ^7.1.0 + rollup-plugin-terser: ^7.0.2 + sirv-cli: ^1.0.11 + svelte: ^3.38.2 + svelte-check: ^1.5.2 + svelte-fa: ^2.2.0 + svelte-preprocess: ^4.7.3 + tslib: ^2.2.0 + typescript: ^4.2.4 + dependencies: + '@ctx-core/combinators': 6.0.10 + '@holochain/conductor-api': 0.0.4 + sirv-cli: 1.0.11 + svelte-fa: 2.2.0 + devDependencies: + '@ctx-core/object': 17.5.24 + '@ctx-core/store': 24.7.17 + '@rollup/plugin-commonjs': 18.1.0_rollup@2.47.0 + '@rollup/plugin-node-resolve': 13.0.0_rollup@2.47.0 + '@rollup/plugin-replace': 2.4.2_rollup@2.47.0 + '@rollup/plugin-typescript': 8.2.1_810b5a6d1c7477a73baaceebd2991e4b + '@syn-ui/model': link:../../libs/model + '@syn-ui/zome-client': link:../../libs/zome-client + '@tsconfig/svelte': 1.0.10 + buffer: 6.0.3 + rollup: 2.47.0 + rollup-plugin-css-only: 3.1.0_rollup@2.47.0 + rollup-plugin-livereload: 2.0.0 + rollup-plugin-node-polyfills: 0.2.1 + rollup-plugin-svelte: 7.1.0_rollup@2.47.0+svelte@3.38.2 + rollup-plugin-terser: 7.0.2_rollup@2.47.0 + svelte: 3.38.2 + svelte-check: 1.5.2_svelte@3.38.2 + svelte-preprocess: 4.7.3_svelte@3.38.2+typescript@4.2.4 + tslib: 2.2.0 + typescript: 4.2.4 + + ui/apps/headless: + specifiers: + '@ctx-core/combinators': ^6.0.10 + '@ctx-core/object': ^17.5.24 + '@ctx-core/store': ^24.7.17 + '@holochain/conductor-api': 0.0.4 + '@rollup/plugin-commonjs': ^18.1.0 + '@rollup/plugin-node-resolve': ^13.0.0 + '@rollup/plugin-replace': ^2.4.2 + '@rollup/plugin-typescript': ^8.2.1 + rollup: ^2.47.0 + rollup-plugin-css-only: ^3.1.0 + rollup-plugin-livereload: ^2.0.0 + rollup-plugin-svelte: ^7.1.0 + rollup-plugin-terser: ^7.0.2 + sirv-cli: ^1.0.11 + svelte: ^3.38.2 + svelte-fa: ^2.2.0 + svelte-preprocess: ^4.7.3 + typescript: ^4.2.4 + dependencies: + '@ctx-core/combinators': 6.0.10 + '@holochain/conductor-api': 0.0.4 + sirv-cli: 1.0.11 + svelte-fa: 2.2.0 + devDependencies: + '@ctx-core/object': 17.5.24 + '@ctx-core/store': 24.7.17 + '@rollup/plugin-commonjs': 18.1.0_rollup@2.47.0 + '@rollup/plugin-node-resolve': 13.0.0_rollup@2.47.0 + '@rollup/plugin-replace': 2.4.2_rollup@2.47.0 + '@rollup/plugin-typescript': 8.2.1_rollup@2.47.0+typescript@4.2.4 + rollup: 2.47.0 + rollup-plugin-css-only: 3.1.0_rollup@2.47.0 + rollup-plugin-livereload: 2.0.0 + rollup-plugin-svelte: 7.1.0_rollup@2.47.0+svelte@3.38.2 + rollup-plugin-terser: 7.0.2_rollup@2.47.0 + svelte: 3.38.2 + svelte-preprocess: 4.7.3_svelte@3.38.2+typescript@4.2.4 + typescript: 4.2.4 + + ui/libs/model: + specifiers: + '@ctx-core/combinators': ^6.0.10 + '@ctx-core/object': ^17.5.24 + '@ctx-core/store': ^24.7.17 + '@holochain/conductor-api': 0.0.4 + '@syn-ui/utils': workspace:^1.0.0 + '@syn-ui/zome-client': workspace:^1.0.0 + svelte: ^3.38.2 + typescript: ^4.2.4 + devDependencies: + '@ctx-core/combinators': 6.0.10 + '@ctx-core/object': 17.5.24 + '@ctx-core/store': 24.7.17 + '@holochain/conductor-api': 0.0.4 + '@syn-ui/utils': link:../utils + '@syn-ui/zome-client': link:../zome-client + svelte: 3.38.2 + typescript: 4.2.4 + + ui/libs/utils: + specifiers: + typescript: ^4.2.4 + devDependencies: + typescript: 4.2.4 + + ui/libs/zome-client: + specifiers: + '@ctx-core/function': ^17.8.2 + '@ctx-core/object': ^17.5.24 + '@ctx-core/store': ^24.7.17 + '@holochain/conductor-api': ^0.0.4 + '@syn-ui/utils': workspace:^1.0.0 + buffer: ^6.0.3 + typescript: ^4.2.4 + devDependencies: + '@ctx-core/function': 17.8.2 + '@ctx-core/object': 17.5.24 + '@ctx-core/store': 24.7.17 + '@holochain/conductor-api': 0.0.4 + '@syn-ui/utils': link:../utils + buffer: 6.0.3 + typescript: 4.2.4 + +packages: + + /101/1.6.3: + resolution: {integrity: sha512-4dmQ45yY0Dx24Qxp+zAsNLlMF6tteCyfVzgbulvSyC7tCyd3V8sW76sS0tHq8NpcbXfWTKasfyfzU1Kd86oKzw==} + dependencies: + clone: 1.0.4 + deep-eql: 0.1.3 + keypather: 1.10.2 + dev: true + + /@babel/code-frame/7.12.13: + resolution: {integrity: sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==} + dependencies: + '@babel/highlight': 7.14.0 + dev: true + + /@babel/helper-validator-identifier/7.14.0: + resolution: {integrity: sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==} + dev: true + + /@babel/highlight/7.14.0: + resolution: {integrity: sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==} + dependencies: + '@babel/helper-validator-identifier': 7.14.0 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/runtime/7.14.0: + resolution: {integrity: sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==} + dependencies: + regenerator-runtime: 0.13.7 + dev: true + + /@changesets/apply-release-plan/5.0.0: + resolution: {integrity: sha512-SE+5nPNSKUyUociPnAvnjYSVF+diciEhX9ZHSqKWMlydswCDjiaq9gz67qwWCmwgEgEOz0TS7VrQBoOlzbitvA==} + dependencies: + '@babel/runtime': 7.14.0 + '@changesets/config': 1.6.0 + '@changesets/get-version-range-type': 0.3.2 + '@changesets/git': 1.1.1 + '@changesets/types': 4.0.0 + '@manypkg/get-packages': 1.1.1 + detect-indent: 6.0.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 1.19.1 + resolve-from: 5.0.0 + semver: 5.7.1 + dev: true + + /@changesets/assemble-release-plan/5.0.0: + resolution: {integrity: sha512-LElDXTCBUkPSmdXlCisoUWw2paX48snatBmw/hKnGiSvnyZqdTIylLojAGQWG0/vOO9v3s/DvJ4hdagIquxJjg==} + dependencies: + '@babel/runtime': 7.14.0 + '@changesets/errors': 0.1.4 + '@changesets/get-dependents-graph': 1.2.1 + '@changesets/types': 4.0.0 + '@manypkg/get-packages': 1.1.1 + semver: 5.7.1 + dev: true + + /@changesets/cli/2.16.0: + resolution: {integrity: sha512-VFkXSyyk/WRjjUoBI7g7cDy09qBjPbBQOloPMEshTzMo/NY9muWHl2yB/FSSkV/6PxGimPtJ7aEJPYfk8HCfXw==} + hasBin: true + dependencies: + '@babel/runtime': 7.14.0 + '@changesets/apply-release-plan': 5.0.0 + '@changesets/assemble-release-plan': 5.0.0 + '@changesets/config': 1.6.0 + '@changesets/errors': 0.1.4 + '@changesets/get-dependents-graph': 1.2.1 + '@changesets/get-release-plan': 3.0.0 + '@changesets/git': 1.1.1 + '@changesets/logger': 0.0.5 + '@changesets/pre': 1.0.6 + '@changesets/read': 0.4.7 + '@changesets/types': 4.0.0 + '@changesets/write': 0.1.4 + '@manypkg/get-packages': 1.1.1 + '@types/semver': 6.2.2 + boxen: 1.3.0 + chalk: 2.4.2 + enquirer: 2.3.6 + external-editor: 3.1.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + is-ci: 2.0.0 + meow: 6.1.1 + outdent: 0.5.0 + p-limit: 2.3.0 + preferred-pm: 3.0.3 + semver: 5.7.1 + spawndamnit: 2.0.0 + term-size: 2.2.1 + tty-table: 2.8.13 + dev: true + + /@changesets/config/1.6.0: + resolution: {integrity: sha512-vMY/OpMFSDC2crDKb5Nq2kMX9hozcXL4dY5Rr+a1JQ044Rz+jqjJPpdTP2yQ+j7qmeGcUTvwjJoEMeekYwfqhg==} + dependencies: + '@changesets/errors': 0.1.4 + '@changesets/get-dependents-graph': 1.2.1 + '@changesets/logger': 0.0.5 + '@changesets/types': 4.0.0 + '@manypkg/get-packages': 1.1.1 + fs-extra: 7.0.1 + micromatch: 4.0.4 + dev: true + + /@changesets/errors/0.1.4: + resolution: {integrity: sha512-HAcqPF7snsUJ/QzkWoKfRfXushHTu+K5KZLJWPb34s4eCZShIf8BFO3fwq6KU8+G7L5KdtN2BzQAXOSXEyiY9Q==} + dependencies: + extendable-error: 0.1.7 + dev: true + + /@changesets/get-dependents-graph/1.2.1: + resolution: {integrity: sha512-vJOibo9SkqhVbgfq5AHIlQ7tzkYQIXh3tPAnlNLy2bPZsU+SByd74GaxHYWt1zOBlncU25WKrIM6J7XBB+GVUg==} + dependencies: + '@changesets/types': 4.0.0 + '@manypkg/get-packages': 1.1.1 + chalk: 2.4.2 + fs-extra: 7.0.1 + semver: 5.7.1 + dev: true + + /@changesets/get-release-plan/3.0.0: + resolution: {integrity: sha512-7VLiqpcWZyjwIXYgkubBC/9cdwqUJEhLMRT9/Y9+ctHqrpsXmJg15QQPTOh3HT9yGN5fJPL1WwuZkc1HXUhK0g==} + dependencies: + '@babel/runtime': 7.14.0 + '@changesets/assemble-release-plan': 5.0.0 + '@changesets/config': 1.6.0 + '@changesets/pre': 1.0.6 + '@changesets/read': 0.4.7 + '@changesets/types': 4.0.0 + '@manypkg/get-packages': 1.1.1 + dev: true + + /@changesets/get-version-range-type/0.3.2: + resolution: {integrity: sha512-SVqwYs5pULYjYT4op21F2pVbcrca4qA/bAA3FmFXKMN7Y+HcO8sbZUTx3TAy2VXulP2FACd1aC7f2nTuqSPbqg==} + dev: true + + /@changesets/git/1.1.1: + resolution: {integrity: sha512-Z12TcKwgU33YE3r76cyU+X81RchOXljDZ5s3G2u0Zd+ODyrwlDb91IO55+6R0Ha6ouPz8ioont0gA70c1RFngg==} + dependencies: + '@babel/runtime': 7.14.0 + '@changesets/errors': 0.1.4 + '@changesets/types': 4.0.0 + '@manypkg/get-packages': 1.1.1 + is-subdir: 1.2.0 + spawndamnit: 2.0.0 + dev: true + + /@changesets/logger/0.0.5: + resolution: {integrity: sha512-gJyZHomu8nASHpaANzc6bkQMO9gU/ib20lqew1rVx753FOxffnCrJlGIeQVxNWCqM+o6OOleCo/ivL8UAO5iFw==} + dependencies: + chalk: 2.4.2 + dev: true + + /@changesets/parse/0.3.8: + resolution: {integrity: sha512-0S7Dc7XbMOKamBtd48vVuWL2aFZyaglw6lJsXNddn9forFf8oMKMmdyJ/HQPyeEChDDOhDF1/ya7m/zpt4Dk4w==} + dependencies: + '@changesets/types': 4.0.0 + js-yaml: 3.14.1 + dev: true + + /@changesets/pre/1.0.6: + resolution: {integrity: sha512-ZwFFQLjhTmA4hj8+Cf9pm6nD9Tp+AiBz1wJLaGum4Ae1fPXMwDnJfHknFUTytqZBlC0gHkiGSj6QkUuetWvckg==} + dependencies: + '@babel/runtime': 7.14.0 + '@changesets/errors': 0.1.4 + '@changesets/types': 4.0.0 + '@manypkg/get-packages': 1.1.1 + fs-extra: 7.0.1 + dev: true + + /@changesets/read/0.4.7: + resolution: {integrity: sha512-E70QrYQpSCMF0nC0dlPU7i6A9zht+8zkQczrKMbOUwDVrfidcvgojxfuJSQbzptYSb9OKYh8GOLd+bsq9+uO9Q==} + dependencies: + '@babel/runtime': 7.14.0 + '@changesets/git': 1.1.1 + '@changesets/logger': 0.0.5 + '@changesets/parse': 0.3.8 + '@changesets/types': 4.0.0 + chalk: 2.4.2 + fs-extra: 7.0.1 + p-filter: 2.1.0 + dev: true + + /@changesets/types/4.0.0: + resolution: {integrity: sha512-whLmPx2wgJRoOtxVZop+DJ71z1gTSkij7osiHgN+pe//FiE6bb4ffvBBb0rACs2cUPfAkWxgSPzqkECgKS1jvQ==} + dev: true + + /@changesets/write/0.1.4: + resolution: {integrity: sha512-uco+vS3mo2JqflLciIU707har+6AEFOeP8pgu3vVC1M2WcKukQgR1KylHFqZJxKQWahf8mQnuUSbgR4yJQuhmA==} + dependencies: + '@babel/runtime': 7.14.0 + '@changesets/types': 4.0.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + prettier: 1.19.1 + dev: true + + /@concordance/react/2.0.0: + resolution: {integrity: sha512-huLSkUuM2/P+U0uy2WwlKuixMsTODD8p4JVQBI4VKeopkiN0C7M3N9XYVawb4M+4spN5RrO/eLhk7KoQX6nsfA==} + engines: {node: '>=6.12.3 <7 || >=8.9.4 <9 || >=10.0.0'} + dependencies: + arrify: 1.0.1 + dev: true + + /@ctx-core/array/20.2.27: + resolution: {integrity: sha512-JOuBl8h78AOltpwkBIrspFrHO5rAAWOA0MfA3tAABErYiJN9IsF5epDish0N4utfP5aKGX+zpXFCmwG8S2lbPg==} + dependencies: + '@ctx-core/combinators': 6.0.10 + '@ctx-core/function': 17.8.2 + '@ctx-core/object': 17.5.24 + '@ctx-core/set': 9.0.28 + dev: true + + /@ctx-core/cli-args/7.2.36: + resolution: {integrity: sha512-RnF77Nm0sY4X3AOyFifokc/t+/lqzmDB1voMO9H+O5mod6K9fQfQS7K8YfIn6lFwx6UKkFPkTpTH56mTK2PlVw==} + dependencies: + '@ctx-core/array': 20.2.27 + '@ctx-core/function': 17.8.2 + '@ctx-core/object': 17.5.24 + dev: true + + /@ctx-core/combinators/6.0.10: + resolution: {integrity: sha512-l16FC/IsyfTOffJkRq5kZSgfnjNa8Wz7m0HpyGQu6//fcePpOvFL+HuQvXgnsMREF6DiayW03QXdStk1kCDBjw==} + + /@ctx-core/ctx-core-package-tools/6.0.50: + resolution: {integrity: sha512-657P7H3uLHQLX/qzQa9BFRy0Z87hkblTx3ry+x3m3cXyC72MZ/GHQP7YxEHTiqQ/9lS+3Mbxjki6Z2cEXWh03g==} + hasBin: true + dependencies: + '@ctx-core/array': 20.2.27 + dev: true + + /@ctx-core/function/17.8.2: + resolution: {integrity: sha512-dn1+mBlaDUfvmN0j61kD5peC0FRCXL/zkEy9YrPkcWeCMaOFN4zSUVbLt55zv1yXPARDTz6B2NR78vq1ojMXsQ==} + dev: true + + /@ctx-core/jetbrains/4.0.35: + resolution: {integrity: sha512-BSSwxdENKxxCcFsTD4lL1h7Pokonkgu2P+aKgLMtNBUu32rDNydYSw/wWlA/tXJ+6XZpaGW7r3m3pkhfnZpkeA==} + hasBin: true + dependencies: + '@ctx-core/cli-args': 7.2.36 + '@ctx-core/queue': 3.0.10 + esm: 3.2.25 + globby: 11.0.3 + dev: true + + /@ctx-core/monorepo/15.0.57: + resolution: {integrity: sha512-2eJtIsvEoiYASAPIz9KBPmkrnzoa4t8c35baVE+HhPSKn7FJXPqkeh88MDAQflvWE+vQVubQsLNX247depbkOA==} + hasBin: true + dependencies: + '@ctx-core/array': 20.2.27 + '@ctx-core/cli-args': 7.2.36 + '@ctx-core/ctx-core-package-tools': 6.0.50 + '@ctx-core/package': 6.1.22 + '@ctx-core/queue': 3.0.10 + detect-indent: 6.0.0 + esm: 3.2.25 + globby: 11.0.3 + ora: 5.4.0 + semver: 7.3.5 + dev: true + + /@ctx-core/object/17.5.24: + resolution: {integrity: sha512-BuG85Ts+LfAnbCabq4+Vw90zRKt8wbMyz573kVdAaYcHYMYwZtfcwbS+HGhNfiHitnvIWYZ9W10BR6nCZorM/A==} + dependencies: + '@ctx-core/function': 17.8.2 + dev: true + + /@ctx-core/package/6.1.22: + resolution: {integrity: sha512-3CMMIbjx5tJPugWGTV9DuRm+OdyLd0aEMhBZ/Y4BUyTVjCIeoBdXt0lF+BEIRYGBO7vYaCdH1XkcUrCnX63noQ==} + hasBin: true + dependencies: + '@ctx-core/cli-args': 7.2.36 + esm: 3.2.25 + resolve: 1.20.0 + dev: true + + /@ctx-core/pnpm-tools/6.0.11: + resolution: {integrity: sha512-BGOL3cXBZLvqsSSro7pLtO+P+9wQ0VrbMeb5MiMqRXbL7Wg7W8oHRUf0lX3FNN1dpG9/tRdNczWmcty22q2H0A==} + hasBin: true + dependencies: + esm: 3.2.25 + globby: 11.0.3 + js-yaml: 4.1.0 + dev: true + + /@ctx-core/queue/3.0.10: + resolution: {integrity: sha512-SUX8v4klI5/Xy4LeQ87V5h1WfK8k15YvFvsTw74fLYWYEaquy3nYnuduRsAY8L58xZ0uY/OQ++XHTiyIUCX9aA==} + dev: true + + /@ctx-core/set/9.0.28: + resolution: {integrity: sha512-VOqt/rs4Q08gRVWPF959JIVvwCGv4cXmacQoDyCs1HbwSDJ5T8t73jHFPnlUoL8ARlzMjAv/+J/2/X1ZKVpzIg==} + dependencies: + '@ctx-core/function': 17.8.2 + dev: true + + /@ctx-core/store/24.7.17: + resolution: {integrity: sha512-ruuhQZj+3tZ6k27JhiX+/y2zpGN4ezZ/Xsh0wTDuZ5VzBeGqh7TfOTB2EKbFiSiFXXd6u2cFgesbgd2Rs/0GKQ==} + dependencies: + '@ctx-core/array': 20.2.27 + '@ctx-core/combinators': 6.0.10 + '@ctx-core/function': 17.8.2 + '@ctx-core/object': 17.5.24 + svelte: 3.38.2 + dev: true + + /@dabh/diagnostics/2.0.2: + resolution: {integrity: sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==} + dependencies: + colorspace: 1.1.2 + enabled: 2.0.0 + kuler: 2.0.0 + dev: true + + /@holochain/conductor-api/0.0.3: + resolution: {integrity: sha512-wok/uiyGoX4m59wMhT3VX2Ma6bXBfZwp8F8iqTb/XiHz0UpROOCrUELXnmN9Ee1sYnmJcTLYRhRTCZ73WRgkdA==} + dependencies: + '@msgpack/msgpack': 2.4.0 + '@types/ws': 7.4.2 + isomorphic-ws: 4.0.1_ws@7.4.5 + nanoid: 3.1.22 + ws: 7.4.5 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /@holochain/conductor-api/0.0.4: + resolution: {integrity: sha512-s52H+JMtiWID8wsqkKVkbjNDrHeVD88X+XWEllIi9INc7md2nBkMKBVChc/qgpRFqrpj2X8j/Es9MtvIJYnQqg==} + dependencies: + '@msgpack/msgpack': 2.4.0 + '@types/ws': 7.4.2 + isomorphic-ws: 4.0.1_ws@7.4.5 + nanoid: 3.1.22 + ws: 7.4.5 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + /@holochain/hachiko/0.5.2: + resolution: {integrity: sha512-w6Aca1HPTenVzqT0rAgRch+FZwGri0EvbAK6ETKV+ZAP+dxPBlQqUgTCrbv2AuwFTU/mobYXniaFlrrAtEnXjQ==} + dependencies: + colors: 1.4.0 + lodash: 4.17.21 + winston: 3.3.3 + winston-null: 2.0.0_winston@3.3.3 + dev: true + + /@holochain/tryorama/0.4.2: + resolution: {integrity: sha512-2Da3zWfXRO/xItRMhzxhRdu+6c9ce0lCZ7hXLjJ2HQ8pspdU6oVHVPhlblwr1Bk1n0W43LM11XVC1YbgzFWZBw==} + dependencies: + '@holochain/conductor-api': 0.0.3 + '@holochain/hachiko': 0.5.2 + '@iarna/toml': 2.2.5 + '@msgpack/msgpack': 2.6.3 + async-mutex: 0.1.4 + axios: 0.19.2 + base-64: 0.1.0 + colors: 1.4.0 + fp-ts: 2.10.5 + get-port: 5.1.1 + io-ts: 2.2.16_fp-ts@2.10.5 + io-ts-reporters: 1.2.2_fp-ts@2.10.5+io-ts@2.2.16 + lodash: 4.17.21 + memoizee: 0.4.15 + ramda: 0.26.1 + rpc-websockets: 4.6.1 + uuid: 8.3.2 + winston: 3.3.3 + yaml: 1.10.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /@iarna/toml/2.2.5: + resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} + dev: true + + /@manypkg/find-root/1.1.0: + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + dependencies: + '@babel/runtime': 7.14.0 + '@types/node': 12.20.12 + find-up: 4.1.0 + fs-extra: 8.1.0 + dev: true + + /@manypkg/get-packages/1.1.1: + resolution: {integrity: sha512-J6VClfQSVgR6958eIDTGjfdCrELy1eT+SHeoSMomnvRQVktZMnEA5edIr5ovRFNw5y+Bk/jyoevPzGYod96mhw==} + dependencies: + '@babel/runtime': 7.14.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.0.3 + read-yaml-file: 1.1.0 + dev: true + + /@msgpack/msgpack/2.4.0: + resolution: {integrity: sha512-5qzv53J43V8GaYsaETs29Q0Ehw9Dog6SG18MASZRQDuZYXtA5T7pymGE2S40NL0X8sjl8+TybmRa5O8d45V7MQ==} + engines: {node: '>= 10'} + + /@msgpack/msgpack/2.6.3: + resolution: {integrity: sha512-dSJ1TBB9Hreienm5SFVbpKIcRRekQNULxvbmDVXQwRCZWYCIxZ23X+FlFWkg1LmGG+DIQZXH+2IW01QwxofT/g==} + engines: {node: '>= 10'} + dev: true + + /@nodelib/fs.scandir/2.1.4: + resolution: {integrity: sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.4 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat/2.0.4: + resolution: {integrity: sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk/1.2.6: + resolution: {integrity: sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.4 + fastq: 1.11.0 + dev: true + + /@polka/url/1.0.0-next.12: + resolution: {integrity: sha512-6RglhutqrGFMO1MNUXp95RBuYIuc8wTnMAV5MUhLmjTOy78ncwOw7RgeQ/HeymkKXRhZd0s2DNrM1rL7unk3MQ==} + dev: false + + /@rollup/plugin-commonjs/18.1.0_rollup@2.47.0: + resolution: {integrity: sha512-h3e6T9rUxVMAQswpDIobfUHn/doMzM9sgkMrsMWCFLmB84PSoC8mV8tOloAJjSRwdqhXBqstlX2BwBpHJvbhxg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^2.30.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.47.0 + commondir: 1.0.1 + estree-walker: 2.0.2 + glob: 7.1.6 + is-reference: 1.2.1 + magic-string: 0.25.7 + resolve: 1.20.0 + rollup: 2.47.0 + dev: true + + /@rollup/plugin-node-resolve/13.0.0_rollup@2.47.0: + resolution: {integrity: sha512-41X411HJ3oikIDivT5OKe9EZ6ud6DXudtfNrGbC4nniaxx2esiWjkLOzgnZsWq1IM8YIeL2rzRGLZLBjlhnZtQ==} + engines: {node: '>= 10.0.0'} + peerDependencies: + rollup: ^2.42.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.47.0 + '@types/resolve': 1.17.1 + builtin-modules: 3.2.0 + deepmerge: 4.2.2 + is-module: 1.0.0 + resolve: 1.20.0 + rollup: 2.47.0 + dev: true + + /@rollup/plugin-replace/2.4.2_rollup@2.47.0: + resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.47.0 + magic-string: 0.25.7 + rollup: 2.47.0 + dev: true + + /@rollup/plugin-typescript/8.2.1_810b5a6d1c7477a73baaceebd2991e4b: + resolution: {integrity: sha512-Qd2E1pleDR4bwyFxqbjt4eJf+wB0UKVMLc7/BAFDGVdAXQMCsD4DUv5/7/ww47BZCYxWtJqe1Lo0KVNswBJlRw==} + engines: {node: '>=8.0.0'} + peerDependencies: + rollup: ^2.14.0 + tslib: '*' + typescript: '>=3.7.0' + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.47.0 + resolve: 1.20.0 + rollup: 2.47.0 + tslib: 2.2.0 + typescript: 4.2.4 + dev: true + + /@rollup/plugin-typescript/8.2.1_rollup@2.47.0+typescript@4.2.4: + resolution: {integrity: sha512-Qd2E1pleDR4bwyFxqbjt4eJf+wB0UKVMLc7/BAFDGVdAXQMCsD4DUv5/7/ww47BZCYxWtJqe1Lo0KVNswBJlRw==} + engines: {node: '>=8.0.0'} + peerDependencies: + rollup: ^2.14.0 + tslib: '*' + typescript: '>=3.7.0' + dependencies: + '@rollup/pluginutils': 3.1.0_rollup@2.47.0 + resolve: 1.20.0 + rollup: 2.47.0 + typescript: 4.2.4 + dev: true + + /@rollup/pluginutils/3.1.0_rollup@2.47.0: + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.2.3 + rollup: 2.47.0 + dev: true + + /@rollup/pluginutils/4.1.0_rollup@2.47.0: + resolution: {integrity: sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + dependencies: + estree-walker: 2.0.2 + picomatch: 2.2.3 + rollup: 2.47.0 + dev: true + + /@sindresorhus/is/0.14.0: + resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==} + engines: {node: '>=6'} + dev: true + + /@szmarczak/http-timer/1.1.2: + resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==} + engines: {node: '>=6'} + dependencies: + defer-to-connect: 1.1.3 + dev: true + + /@tsconfig/svelte/1.0.10: + resolution: {integrity: sha512-EBrpH2iXXfaf/9z81koiDYkp2mlwW2XzFcAqn6qh7VKyP8zBvHHAQzNhY+W9vH5arAjmGAm5g8ElWq6YmXm3ig==} + dev: true + + /@types/estree/0.0.39: + resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + dev: true + + /@types/estree/0.0.47: + resolution: {integrity: sha512-c5ciR06jK8u9BstrmJyO97m+klJrrhCf9u3rLu3DEAJBirxRqSCvDQoYKmxuYwQI5SZChAWu+tq9oVlGRuzPAg==} + dev: true + + /@types/lodash/4.14.168: + resolution: {integrity: sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==} + dev: true + + /@types/minimist/1.2.1: + resolution: {integrity: sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==} + dev: true + + /@types/node/12.20.12: + resolution: {integrity: sha512-KQZ1al2hKOONAs2MFv+yTQP1LkDWMrRJ9YCVRalXltOfXsBmH5IownLxQaiq0lnAHwAViLnh2aTYqrPcRGEbgg==} + dev: true + + /@types/node/15.0.2: + resolution: {integrity: sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==} + + /@types/normalize-package-data/2.4.0: + resolution: {integrity: sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==} + dev: true + + /@types/pug/2.0.4: + resolution: {integrity: sha1-h3L80EGOPNLMFxVV1zAHQVBR9LI=} + dev: true + + /@types/resolve/1.17.1: + resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} + dependencies: + '@types/node': 15.0.2 + dev: true + + /@types/sass/1.16.0: + resolution: {integrity: sha512-2XZovu4NwcqmtZtsBR5XYLw18T8cBCnU2USFHTnYLLHz9fkhnoEMoDsqShJIOFsFhn5aJHjweiUUdTrDGujegA==} + dependencies: + '@types/node': 15.0.2 + dev: true + + /@types/semver/6.2.2: + resolution: {integrity: sha512-RxAwYt4rGwK5GyoRwuP0jT6ZHAVTdz2EqgsHmX0PYNjGsko+OeT4WFXXTs/lM3teJUJodM+SNtAL5/pXIJ61IQ==} + dev: true + + /@types/uuid/8.3.0: + resolution: {integrity: sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==} + dev: false + + /@types/ws/7.4.2: + resolution: {integrity: sha512-PbeN0Eydl7LQl4OIav29YmkO2LxbVuz3nZD/kb19lOS+wLgIkRbWMNmU/QQR7ABpOJ7D7xDOU8co7iohObewrw==} + dependencies: + '@types/node': 15.0.2 + + /acorn-walk/8.1.0: + resolution: {integrity: sha512-mjmzmv12YIG/G8JQdQuz2MUDShEJ6teYpT5bmWA4q7iwoGen8xtt3twF3OvzIUl+Q06aWIjvnwQUKvQ6TtMRjg==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn/8.2.4: + resolution: {integrity: sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /aggregate-error/3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + dev: true + + /ansi-align/2.0.0: + resolution: {integrity: sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=} + dependencies: + string-width: 2.1.1 + dev: true + + /ansi-align/3.0.0: + resolution: {integrity: sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==} + dependencies: + string-width: 3.1.0 + dev: true + + /ansi-colors/4.1.1: + resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} + engines: {node: '>=6'} + dev: true + + /ansi-regex/3.0.0: + resolution: {integrity: sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=} + engines: {node: '>=4'} + dev: true + + /ansi-regex/4.1.0: + resolution: {integrity: sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==} + engines: {node: '>=6'} + dev: true + + /ansi-regex/5.0.0: + resolution: {integrity: sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==} + engines: {node: '>=8'} + dev: true + + /ansi-styles/3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + + /ansi-styles/4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles/5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /anymatch/3.1.2: + resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.2.3 + dev: true + + /arg/4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: false + + /argparse/1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /argparse/2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-filter/1.0.0: + resolution: {integrity: sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=} + dev: false + + /array-find-index/1.0.2: + resolution: {integrity: sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=} + engines: {node: '>=0.10.0'} + dev: true + + /array-union/2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /arrgv/1.0.2: + resolution: {integrity: sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==} + engines: {node: '>=8.0.0'} + dev: true + + /arrify/1.0.1: + resolution: {integrity: sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=} + engines: {node: '>=0.10.0'} + dev: true + + /arrify/2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + dev: true + + /assert-args/1.2.1: + resolution: {integrity: sha1-QEEDoUUqMv53iYgR5U5ZCoqTc70=} + dependencies: + '101': 1.6.3 + compound-subject: 0.0.1 + debug: 2.6.9 + get-prototype-of: 0.0.0 + is-capitalized: 1.0.0 + is-class: 0.0.4 + dev: true + + /astral-regex/2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + dev: true + + /async-limiter/1.0.1: + resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + dev: true + + /async-mutex/0.1.4: + resolution: {integrity: sha512-zVWTmAnxxHaeB2B1te84oecI8zTDJ/8G49aVBblRX6be0oq6pAybNcUSxwfgVOmOjSCvN4aYZAqwtyNI8e1YGw==} + dev: true + + /async/3.2.0: + resolution: {integrity: sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==} + dev: true + + /ava/3.15.0: + resolution: {integrity: sha512-HGAnk1SHPk4Sx6plFAUkzV/XC1j9+iQhOzt4vBly18/yo0AV8Oytx7mtJd/CR8igCJ5p160N/Oo/cNJi2uSeWA==} + engines: {node: '>=10.18.0 <11 || >=12.14.0 <12.17.0 || >=12.17.0 <13 || >=14.0.0 <15 || >=15'} + hasBin: true + dependencies: + '@concordance/react': 2.0.0 + acorn: 8.2.4 + acorn-walk: 8.1.0 + ansi-styles: 5.2.0 + arrgv: 1.0.2 + arrify: 2.0.1 + callsites: 3.1.0 + chalk: 4.1.1 + chokidar: 3.5.1 + chunkd: 2.0.1 + ci-info: 2.0.0 + ci-parallel-vars: 1.0.1 + clean-yaml-object: 0.1.0 + cli-cursor: 3.1.0 + cli-truncate: 2.1.0 + code-excerpt: 3.0.0 + common-path-prefix: 3.0.0 + concordance: 5.0.4 + convert-source-map: 1.7.0 + currently-unhandled: 0.4.1 + debug: 4.3.1 + del: 6.0.0 + emittery: 0.8.1 + equal-length: 1.0.1 + figures: 3.2.0 + globby: 11.0.3 + ignore-by-default: 2.0.0 + import-local: 3.0.2 + indent-string: 4.0.0 + is-error: 2.2.2 + is-plain-object: 5.0.0 + is-promise: 4.0.0 + lodash: 4.17.21 + matcher: 3.0.0 + md5-hex: 3.0.1 + mem: 8.1.1 + ms: 2.1.3 + ora: 5.4.0 + p-event: 4.2.0 + p-map: 4.0.0 + picomatch: 2.2.3 + pkg-conf: 3.1.0 + plur: 4.0.0 + pretty-ms: 7.0.1 + read-pkg: 5.2.0 + resolve-cwd: 3.0.0 + slash: 3.0.0 + source-map-support: 0.5.19 + stack-utils: 2.0.3 + strip-ansi: 6.0.0 + supertap: 2.0.0 + temp-dir: 2.0.0 + trim-off-newlines: 1.0.1 + update-notifier: 5.1.0 + write-file-atomic: 3.0.3 + yargs: 16.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /available-typed-arrays/1.0.2: + resolution: {integrity: sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==} + engines: {node: '>= 0.4'} + dependencies: + array-filter: 1.0.0 + dev: false + + /axios/0.19.2: + resolution: {integrity: sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==} + deprecated: Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410 + dependencies: + follow-redirects: 1.5.10 + dev: true + + /babel-runtime/6.26.0: + resolution: {integrity: sha1-llxwWGaOgrVde/4E/yM3vItWR/4=} + dependencies: + core-js: 2.6.12 + regenerator-runtime: 0.11.1 + dev: true + + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + /base-64/0.1.0: + resolution: {integrity: sha1-eAqZyE59YAJgNhURxId2E78k9rs=} + dev: true + + /base64-js/1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: true + + /better-path-resolve/1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + dependencies: + is-windows: 1.0.2 + dev: true + + /binary-extensions/2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /bl/4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.0 + dev: true + + /blueimp-md5/2.18.0: + resolution: {integrity: sha512-vE52okJvzsVWhcgUHOv+69OG3Mdg151xyn41aVQN/5W5S+S43qZhxECtYLAEHMSFWX6Mv5IZrzj3T5+JqXfj5Q==} + dev: true + + /boxen/1.3.0: + resolution: {integrity: sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==} + engines: {node: '>=4'} + dependencies: + ansi-align: 2.0.0 + camelcase: 4.1.0 + chalk: 2.4.2 + cli-boxes: 1.0.0 + string-width: 2.1.1 + term-size: 1.2.0 + widest-line: 2.0.1 + dev: true + + /boxen/5.0.1: + resolution: {integrity: sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==} + engines: {node: '>=10'} + dependencies: + ansi-align: 3.0.0 + camelcase: 6.2.0 + chalk: 4.1.1 + cli-boxes: 2.2.1 + string-width: 4.2.2 + type-fest: 0.20.2 + widest-line: 3.1.0 + wrap-ansi: 7.0.0 + dev: true + + /brace-expansion/1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + /braces/3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /breakword/1.0.5: + resolution: {integrity: sha512-ex5W9DoOQ/LUEU3PMdLs9ua/CYZl1678NUkKOdUSi8Aw5F1idieaiRURCBFJCwVcrD1J8Iy3vfWSloaMwO2qFg==} + dependencies: + wcwidth: 1.0.1 + dev: true + + /buffer-from/1.1.1: + resolution: {integrity: sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==} + + /buffer/5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + + /buffer/6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + dev: true + + /builtin-modules/3.2.0: + resolution: {integrity: sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==} + engines: {node: '>=6'} + dev: true + + /cacheable-request/6.1.0: + resolution: {integrity: sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==} + engines: {node: '>=8'} + dependencies: + clone-response: 1.0.2 + get-stream: 5.2.0 + http-cache-semantics: 4.1.0 + keyv: 3.1.0 + lowercase-keys: 2.0.0 + normalize-url: 4.5.0 + responselike: 1.0.2 + dev: true + + /call-bind/1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.1.1 + dev: false + + /callsites/3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase-keys/6.2.2: + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + map-obj: 4.2.1 + quick-lru: 4.0.1 + dev: true + + /camelcase/4.1.0: + resolution: {integrity: sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=} + engines: {node: '>=4'} + dev: true + + /camelcase/5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + + /camelcase/6.2.0: + resolution: {integrity: sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==} + engines: {node: '>=10'} + dev: true + + /chalk/2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + + /chalk/3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chalk/4.1.1: + resolution: {integrity: sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chardet/0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true + + /chokidar/3.5.1: + resolution: {integrity: sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.2 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.1 + normalize-path: 3.0.0 + readdirp: 3.5.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /chunkd/2.0.1: + resolution: {integrity: sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==} + dev: true + + /ci-info/2.0.0: + resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + dev: true + + /ci-parallel-vars/1.0.1: + resolution: {integrity: sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==} + dev: true + + /circular-json/0.5.9: + resolution: {integrity: sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==} + deprecated: CircularJSON is in maintenance only, flatted is its successor. + dev: true + + /clean-stack/2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + dev: true + + /clean-yaml-object/0.1.0: + resolution: {integrity: sha1-Y/sRDcLOGoTcIfbZM0h20BCui2g=} + engines: {node: '>=0.10.0'} + dev: true + + /cli-boxes/1.0.0: + resolution: {integrity: sha1-T6kXw+WclKAEzWH47lCdplFocUM=} + engines: {node: '>=0.10.0'} + dev: true + + /cli-boxes/2.2.1: + resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} + engines: {node: '>=6'} + dev: true + + /cli-cursor/3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + + /cli-spinners/2.6.0: + resolution: {integrity: sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==} + engines: {node: '>=6'} + dev: true + + /cli-truncate/2.1.0: + resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} + engines: {node: '>=8'} + dependencies: + slice-ansi: 3.0.0 + string-width: 4.2.2 + dev: true + + /cliui/6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + dependencies: + string-width: 4.2.2 + strip-ansi: 6.0.0 + wrap-ansi: 6.2.0 + dev: true + + /cliui/7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.2 + strip-ansi: 6.0.0 + wrap-ansi: 7.0.0 + dev: true + + /clone-response/1.0.2: + resolution: {integrity: sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=} + dependencies: + mimic-response: 1.0.1 + dev: true + + /clone/1.0.4: + resolution: {integrity: sha1-2jCcwmPfFZlMaIypAheco8fNfH4=} + engines: {node: '>=0.8'} + dev: true + + /code-excerpt/3.0.0: + resolution: {integrity: sha512-VHNTVhd7KsLGOqfX3SyeO8RyYPMp1GJOg194VITk04WMYCv4plV68YWe6TJZxd9MhobjtpMRnVky01gqZsalaw==} + engines: {node: '>=10'} + dependencies: + convert-to-spaces: 1.0.2 + dev: true + + /color-convert/1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-convert/2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name/1.1.3: + resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=} + dev: true + + /color-name/1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /color-string/1.5.5: + resolution: {integrity: sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: true + + /color/3.0.0: + resolution: {integrity: sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==} + dependencies: + color-convert: 1.9.3 + color-string: 1.5.5 + dev: true + + /colors/1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + dev: true + + /colorspace/1.1.2: + resolution: {integrity: sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==} + dependencies: + color: 3.0.0 + text-hex: 1.0.0 + dev: true + + /commander/2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: true + + /common-path-prefix/3.0.0: + resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} + dev: true + + /commondir/1.0.1: + resolution: {integrity: sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=} + dev: true + + /compound-subject/0.0.1: + resolution: {integrity: sha1-JxVUaYoVrmCLHfyv0wt7oeqJLEs=} + dev: true + + /concat-map/0.0.1: + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + + /concordance/5.0.4: + resolution: {integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==} + engines: {node: '>=10.18.0 <11 || >=12.14.0 <13 || >=14'} + dependencies: + date-time: 3.1.0 + esutils: 2.0.3 + fast-diff: 1.2.0 + js-string-escape: 1.0.1 + lodash: 4.17.21 + md5-hex: 3.0.1 + semver: 7.3.5 + well-known-symbols: 2.0.0 + dev: true + + /configstore/5.0.1: + resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} + engines: {node: '>=8'} + dependencies: + dot-prop: 5.3.0 + graceful-fs: 4.2.6 + make-dir: 3.1.0 + unique-string: 2.0.0 + write-file-atomic: 3.0.3 + xdg-basedir: 4.0.0 + dev: true + + /console-clear/1.1.1: + resolution: {integrity: sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ==} + engines: {node: '>=4'} + dev: false + + /convert-source-map/1.7.0: + resolution: {integrity: sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /convert-to-spaces/1.0.2: + resolution: {integrity: sha1-fj5Iu+bZl7FBfdyihoIEtNPYVxU=} + engines: {node: '>= 4'} + dev: true + + /core-js/2.6.12: + resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} + deprecated: core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js. + requiresBuild: true + dev: true + + /core-util-is/1.0.2: + resolution: {integrity: sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=} + dev: true + + /create-require/1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: false + + /cross-spawn/5.1.0: + resolution: {integrity: sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=} + dependencies: + lru-cache: 4.1.5 + shebang-command: 1.2.0 + which: 1.3.1 + dev: true + + /crypto-random-string/2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + dev: true + + /csv-generate/3.4.0: + resolution: {integrity: sha512-D6yi7c6lL70cpTx3TQIVWKrfxuLiKa0pBizu0zi7fSRXlhmE7u674gk9k1IjCEnxKq2t6xzbXnxcOmSdBbE8vQ==} + dev: true + + /csv-parse/4.15.4: + resolution: {integrity: sha512-OdBbFc0yZhOm17lSxqkirrHlFFVpKRT0wp4DAGoJelsP3LbGzV9LNr7XmM/lrr0uGkCtaqac9UhP8PDHXOAbMg==} + dev: true + + /csv-stringify/5.6.2: + resolution: {integrity: sha512-n3rIVbX6ylm1YsX2NEug9IaPV8xRnT+9/NNZbrA/bcHgOSSeqtWla6XnI/xmyu57wIw+ASCAoX1oM6EZtqJV0A==} + dev: true + + /csv/5.5.0: + resolution: {integrity: sha512-32tcuxdb4HW3zbk8NBcVQb8/7xuJB5sv+q4BuQ6++E/K6JvHvWoCHcGzB5Au95vVikNH4ztE0XNC/Bws950cfA==} + engines: {node: '>= 0.1.90'} + dependencies: + csv-generate: 3.4.0 + csv-parse: 4.15.4 + csv-stringify: 5.6.2 + stream-transform: 2.1.0 + dev: true + + /currently-unhandled/0.4.1: + resolution: {integrity: sha1-mI3zP+qxke95mmE2nddsF635V+o=} + engines: {node: '>=0.10.0'} + dependencies: + array-find-index: 1.0.2 + dev: true + + /cycle/1.0.3: + resolution: {integrity: sha1-IegLK+hYD5i0aPN5QwZisEbDStI=} + engines: {node: '>=0.4.0'} + dev: true + + /d/1.0.1: + resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} + dependencies: + es5-ext: 0.10.53 + type: 1.2.0 + dev: true + + /date-time/3.1.0: + resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==} + engines: {node: '>=6'} + dependencies: + time-zone: 1.0.0 + dev: true + + /debug/2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + dependencies: + ms: 2.0.0 + dev: true + + /debug/3.1.0: + resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} + dependencies: + ms: 2.0.0 + dev: true + + /debug/4.3.1: + resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /decamelize-keys/1.1.0: + resolution: {integrity: sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=} + engines: {node: '>=0.10.0'} + dependencies: + decamelize: 1.2.0 + map-obj: 1.0.1 + dev: true + + /decamelize/1.2.0: + resolution: {integrity: sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=} + engines: {node: '>=0.10.0'} + dev: true + + /decompress-response/3.3.0: + resolution: {integrity: sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=} + engines: {node: '>=4'} + dependencies: + mimic-response: 1.0.1 + dev: true + + /deep-eql/0.1.3: + resolution: {integrity: sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=} + dependencies: + type-detect: 0.1.1 + dev: true + + /deep-equal/2.0.5: + resolution: {integrity: sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==} + dependencies: + call-bind: 1.0.2 + es-get-iterator: 1.1.2 + get-intrinsic: 1.1.1 + is-arguments: 1.1.0 + is-date-object: 1.0.2 + is-regex: 1.1.2 + isarray: 2.0.5 + object-is: 1.1.5 + object-keys: 1.1.1 + object.assign: 4.1.2 + regexp.prototype.flags: 1.3.1 + side-channel: 1.0.4 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.1 + which-typed-array: 1.1.4 + dev: false + + /deep-extend/0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: true + + /deepmerge/4.2.2: + resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} + engines: {node: '>=0.10.0'} + dev: true + + /defaults/1.0.3: + resolution: {integrity: sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=} + dependencies: + clone: 1.0.4 + dev: true + + /defer-to-connect/1.1.3: + resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==} + dev: true + + /define-properties/1.1.3: + resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==} + engines: {node: '>= 0.4'} + dependencies: + object-keys: 1.1.1 + dev: false + + /defined/1.0.0: + resolution: {integrity: sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=} + dev: false + + /del/6.0.0: + resolution: {integrity: sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==} + engines: {node: '>=10'} + dependencies: + globby: 11.0.3 + graceful-fs: 4.2.6 + is-glob: 4.0.1 + is-path-cwd: 2.2.0 + is-path-inside: 3.0.3 + p-map: 4.0.0 + rimraf: 3.0.2 + slash: 3.0.0 + dev: true + + /detect-indent/6.0.0: + resolution: {integrity: sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA==} + engines: {node: '>=8'} + dev: true + + /diff/4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: false + + /dir-glob/3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /dot-prop/5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + dependencies: + is-obj: 2.0.0 + dev: true + + /dotignore/0.1.2: + resolution: {integrity: sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==} + hasBin: true + dependencies: + minimatch: 3.0.4 + dev: false + + /duplexer3/0.1.4: + resolution: {integrity: sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=} + dev: true + + /emittery/0.8.1: + resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==} + engines: {node: '>=10'} + dev: true + + /emoji-regex/7.0.3: + resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} + dev: true + + /emoji-regex/8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /enabled/2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + dev: true + + /end-of-stream/1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: true + + /enquirer/2.3.6: + resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} + engines: {node: '>=8.6'} + dependencies: + ansi-colors: 4.1.1 + dev: true + + /equal-length/1.0.1: + resolution: {integrity: sha1-IcoRLUirJLTh5//A5TOdMf38J0w=} + engines: {node: '>=4'} + dev: true + + /error-ex/1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-abstract/1.18.0: + resolution: {integrity: sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw==} + engines: {node: '>= 0.4'} + dependencies: + 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.10.2 + 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 + dev: false + + /es-get-iterator/1.1.2: + resolution: {integrity: sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.1 + has-symbols: 1.0.2 + is-arguments: 1.1.0 + is-map: 2.0.2 + is-set: 2.0.2 + is-string: 1.0.5 + isarray: 2.0.5 + dev: false + + /es-to-primitive/1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.3 + is-date-object: 1.0.2 + is-symbol: 1.0.3 + dev: false + + /es5-ext/0.10.53: + resolution: {integrity: sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==} + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.3 + next-tick: 1.0.0 + dev: true + + /es6-iterator/2.0.3: + resolution: {integrity: sha1-p96IkUGgWpSwhUQDstCg+/qY87c=} + dependencies: + d: 1.0.1 + es5-ext: 0.10.53 + es6-symbol: 3.1.3 + dev: true + + /es6-symbol/3.1.3: + resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==} + dependencies: + d: 1.0.1 + ext: 1.4.0 + dev: true + + /es6-weak-map/2.0.3: + resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.53 + es6-iterator: 2.0.3 + es6-symbol: 3.1.3 + dev: true + + /escalade/3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-goat/2.1.1: + resolution: {integrity: sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==} + engines: {node: '>=8'} + dev: true + + /escape-string-regexp/1.0.5: + resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=} + engines: {node: '>=0.8.0'} + dev: true + + /escape-string-regexp/2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + dev: true + + /escape-string-regexp/4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /esm/3.2.25: + resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} + engines: {node: '>=6'} + + /esprima/4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /estree-walker/0.6.1: + resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==} + dev: true + + /estree-walker/1.0.1: + resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} + dev: true + + /estree-walker/2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: true + + /esutils/2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /event-emitter/0.3.5: + resolution: {integrity: sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=} + dependencies: + d: 1.0.1 + es5-ext: 0.10.53 + dev: true + + /eventemitter3/3.1.2: + resolution: {integrity: sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==} + dev: true + + /execa/0.7.0: + resolution: {integrity: sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=} + engines: {node: '>=4'} + dependencies: + cross-spawn: 5.1.0 + get-stream: 3.0.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.3 + strip-eof: 1.0.0 + dev: true + + /ext/1.4.0: + resolution: {integrity: sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==} + dependencies: + type: 2.5.0 + dev: true + + /extendable-error/0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + dev: true + + /external-editor/3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + dev: true + + /fast-diff/1.2.0: + resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} + dev: true + + /fast-glob/3.2.5: + resolution: {integrity: sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==} + engines: {node: '>=8'} + dependencies: + '@nodelib/fs.stat': 2.0.4 + '@nodelib/fs.walk': 1.2.6 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.4 + picomatch: 2.2.3 + dev: true + + /fast-safe-stringify/2.0.7: + resolution: {integrity: sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==} + dev: true + + /fastq/1.11.0: + resolution: {integrity: sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==} + dependencies: + reusify: 1.0.4 + dev: true + + /fecha/2.3.3: + resolution: {integrity: sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==} + dev: true + + /fecha/4.2.1: + resolution: {integrity: sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q==} + dev: true + + /figures/3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + + /fill-range/7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up/3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + dependencies: + locate-path: 3.0.0 + dev: true + + /find-up/4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /find-up/5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /find-yarn-workspace-root2/1.2.16: + resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} + dependencies: + micromatch: 4.0.4 + pkg-dir: 4.2.0 + dev: true + + /fn.name/1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + dev: true + + /follow-redirects/1.5.10: + resolution: {integrity: sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==} + engines: {node: '>=4.0'} + dependencies: + debug: 3.1.0 + dev: true + + /for-each/0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.3 + dev: false + + /foreach/2.0.5: + resolution: {integrity: sha1-C+4AUBiusmDQo6865ljdATbsG5k=} + dev: false + + /fp-ts/2.10.5: + resolution: {integrity: sha512-X2KfTIV0cxIk3d7/2Pvp/pxL/xr2MV1WooyEzKtTWYSc1+52VF4YzjBTXqeOlSiZsPCxIBpDGfT9Dyo7WEY0DQ==} + dev: true + + /fs-extra/7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.6 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: true + + /fs-extra/8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.6 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: true + + /fs.realpath/1.0.0: + resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} + + /fsevents/2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + dev: true + optional: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + + /get-caller-file/2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /get-intrinsic/1.1.1: + resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.2 + dev: false + + /get-port/3.2.0: + resolution: {integrity: sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=} + engines: {node: '>=4'} + dev: false + + /get-port/5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} + dev: true + + /get-prototype-of/0.0.0: + resolution: {integrity: sha1-mHcr0QcW0W3rSzIlFsRp78oorEQ=} + dev: true + + /get-stream/3.0.0: + resolution: {integrity: sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=} + engines: {node: '>=4'} + dev: true + + /get-stream/4.1.0: + resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} + engines: {node: '>=6'} + dependencies: + pump: 3.0.0 + dev: true + + /get-stream/5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: true + + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.1 + dev: true + + /glob/7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.0.4 + once: 1.4.0 + path-is-absolute: 1.0.1 + + /global-dirs/3.0.0: + resolution: {integrity: sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==} + engines: {node: '>=10'} + dependencies: + ini: 2.0.0 + dev: true + + /globby/11.0.3: + resolution: {integrity: sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.5 + ignore: 5.1.8 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /got/9.6.0: + resolution: {integrity: sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==} + engines: {node: '>=8.6'} + dependencies: + '@sindresorhus/is': 0.14.0 + '@szmarczak/http-timer': 1.1.2 + cacheable-request: 6.1.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.1.0 + to-readable-stream: 1.0.0 + url-parse-lax: 3.0.0 + dev: true + + /graceful-fs/4.2.6: + resolution: {integrity: sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==} + dev: true + + /grapheme-splitter/1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /hard-rejection/2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + dev: true + + /has-bigints/1.0.1: + resolution: {integrity: sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==} + dev: false + + /has-flag/3.0.0: + resolution: {integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=} + engines: {node: '>=4'} + dev: true + + /has-flag/4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-symbols/1.0.2: + resolution: {integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==} + engines: {node: '>= 0.4'} + dev: false + + /has-yarn/2.1.0: + resolution: {integrity: sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==} + engines: {node: '>=8'} + dev: true + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + + /hosted-git-info/2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + dev: true + + /http-cache-semantics/4.1.0: + resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==} + dev: true + + /human-id/1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + dev: true + + /iconv-lite/0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: true + + /ieee754/1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: true + + /ignore-by-default/2.0.0: + resolution: {integrity: sha512-+mQSgMRiFD3L3AOxLYOCxjIq4OnAmo5CIuC+lj5ehCJcPtV++QacEV7FdpzvYxH6DaOySWzQU6RR0lPLy37ckA==} + engines: {node: '>=10 <11 || >=12 <13 || >=14'} + dev: true + + /ignore/5.1.8: + resolution: {integrity: sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==} + engines: {node: '>= 4'} + dev: true + + /import-fresh/3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-lazy/2.1.0: + resolution: {integrity: sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=} + engines: {node: '>=4'} + dev: true + + /import-local/3.0.2: + resolution: {integrity: sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + + /imurmurhash/0.1.4: + resolution: {integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=} + engines: {node: '>=0.8.19'} + dev: true + + /indent-string/4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true + + /inflight/1.0.6: + resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + /ini/1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true + + /ini/2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + dev: true + + /io-ts-reporters/1.2.2_fp-ts@2.10.5+io-ts@2.2.16: + resolution: {integrity: sha512-igASwWWkDY757OutNcM6zTtdJf/eTZYkoe2ymsX2qpm5bKZLo74FJYjsCtMQOEdY7dRHLLEulCyFQwdN69GBCg==} + peerDependencies: + fp-ts: ^2.0.2 + io-ts: ^2.0.0 + dependencies: + fp-ts: 2.10.5 + io-ts: 2.2.16_fp-ts@2.10.5 + dev: true + + /io-ts/2.2.16_fp-ts@2.10.5: + resolution: {integrity: sha512-y5TTSa6VP6le0hhmIyN0dqEXkrZeJLeC5KApJq6VLci3UEKF80lZ+KuoUs02RhBxNWlrqSNxzfI7otLX1Euv8Q==} + peerDependencies: + fp-ts: ^2.5.0 + dependencies: + fp-ts: 2.10.5 + dev: true + + /irregular-plurals/3.3.0: + resolution: {integrity: sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g==} + engines: {node: '>=8'} + dev: true + + /is-arguments/1.1.0: + resolution: {integrity: sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + dev: false + + /is-arrayish/0.2.1: + resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=} + dev: true + + /is-arrayish/0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: true + + /is-bigint/1.0.2: + resolution: {integrity: sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==} + dev: false + + /is-binary-path/2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-boolean-object/1.1.0: + resolution: {integrity: sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + dev: false + + /is-callable/1.2.3: + resolution: {integrity: sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==} + engines: {node: '>= 0.4'} + dev: false + + /is-capitalized/1.0.0: + resolution: {integrity: sha1-TIRktNkdPk7rRIid0s2PGwrEwTY=} + dev: true + + /is-ci/2.0.0: + resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} + hasBin: true + dependencies: + ci-info: 2.0.0 + dev: true + + /is-class/0.0.4: + resolution: {integrity: sha1-4FdFFwW7NOOePjNZjJOpg3KWtzY=} + dev: true + + /is-core-module/2.3.0: + resolution: {integrity: sha512-xSphU2KG9867tsYdLD4RWQ1VqdFl4HTO9Thf3I/3dLEfr0dbPTWKsuCKrgqMljg4nPE+Gq0VCnzT3gr0CyBmsw==} + dependencies: + has: 1.0.3 + + /is-date-object/1.0.2: + resolution: {integrity: sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==} + engines: {node: '>= 0.4'} + dev: false + + /is-error/2.2.2: + resolution: {integrity: sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==} + dev: true + + /is-extglob/2.1.1: + resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point/2.0.0: + resolution: {integrity: sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=} + engines: {node: '>=4'} + dev: true + + /is-fullwidth-code-point/3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-glob/4.0.1: + resolution: {integrity: sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-installed-globally/0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} + dependencies: + global-dirs: 3.0.0 + is-path-inside: 3.0.3 + dev: true + + /is-interactive/1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true + + /is-map/2.0.2: + resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} + dev: false + + /is-module/1.0.0: + resolution: {integrity: sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=} + dev: true + + /is-negative-zero/2.0.1: + resolution: {integrity: sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==} + engines: {node: '>= 0.4'} + dev: false + + /is-npm/5.0.0: + resolution: {integrity: sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==} + engines: {node: '>=10'} + dev: true + + /is-number-object/1.0.4: + resolution: {integrity: sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==} + engines: {node: '>= 0.4'} + dev: false + + /is-number/7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-obj/2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + dev: true + + /is-path-cwd/2.2.0: + resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} + engines: {node: '>=6'} + dev: true + + /is-path-inside/3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-plain-obj/1.1.0: + resolution: {integrity: sha1-caUMhCnfync8kqOQpKA7OfzVHT4=} + engines: {node: '>=0.10.0'} + dev: true + + /is-plain-object/5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + dev: true + + /is-promise/2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + dev: true + + /is-promise/4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + dev: true + + /is-reference/1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + dependencies: + '@types/estree': 0.0.47 + dev: true + + /is-regex/1.1.2: + resolution: {integrity: sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-symbols: 1.0.2 + dev: false + + /is-set/2.0.2: + resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} + dev: false + + /is-stream/1.1.0: + resolution: {integrity: sha1-EtSj3U5o4Lec6428hBc66A2RykQ=} + engines: {node: '>=0.10.0'} + dev: true + + /is-stream/2.0.0: + resolution: {integrity: sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==} + engines: {node: '>=8'} + dev: true + + /is-string/1.0.5: + resolution: {integrity: sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==} + engines: {node: '>= 0.4'} + dev: false + + /is-subdir/1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + dependencies: + better-path-resolve: 1.0.0 + dev: true + + /is-symbol/1.0.3: + resolution: {integrity: sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.2 + dev: false + + /is-typed-array/1.1.5: + resolution: {integrity: sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.2 + call-bind: 1.0.2 + es-abstract: 1.18.0 + foreach: 2.0.5 + has-symbols: 1.0.2 + dev: false + + /is-typedarray/1.0.0: + resolution: {integrity: sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=} + dev: true + + /is-unicode-supported/0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + + /is-weakmap/2.0.1: + resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + dev: false + + /is-weakset/2.0.1: + resolution: {integrity: sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==} + dev: false + + /is-windows/1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + dev: true + + /is-yarn-global/0.3.0: + resolution: {integrity: sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==} + dev: true + + /isarray/1.0.0: + resolution: {integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=} + dev: true + + /isarray/2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: false + + /isexe/2.0.0: + resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=} + dev: true + + /isomorphic-ws/4.0.1_ws@7.4.5: + resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} + peerDependencies: + ws: '*' + dependencies: + ws: 7.4.5 + + /jest-worker/26.6.2: + resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/node': 15.0.2 + merge-stream: 2.0.0 + supports-color: 7.2.0 + dev: true + + /js-string-escape/1.0.1: + resolution: {integrity: sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=} + engines: {node: '>= 0.8'} + dev: true + + /js-tokens/4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml/3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /js-yaml/4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-buffer/3.0.0: + resolution: {integrity: sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=} + dev: true + + /json-parse-better-errors/1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + dev: true + + /json-parse-even-better-errors/2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /jsonfile/4.0.0: + resolution: {integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=} + optionalDependencies: + graceful-fs: 4.2.6 + dev: true + + /keypather/1.10.2: + resolution: {integrity: sha1-4ESWMtSz5RbyHMAUznxWRP3c5hQ=} + dependencies: + '101': 1.6.3 + dev: true + + /keyv/3.1.0: + resolution: {integrity: sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==} + dependencies: + json-buffer: 3.0.0 + dev: true + + /kind-of/6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: true + + /kleur/3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: false + + /kuler/2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + dev: true + + /latest-version/5.1.0: + resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==} + engines: {node: '>=8'} + dependencies: + package-json: 6.5.0 + dev: true + + /lines-and-columns/1.1.6: + resolution: {integrity: sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=} + dev: true + + /livereload-js/3.3.2: + resolution: {integrity: sha512-w677WnINxFkuixAoUEXOStewzLYGI76XVag+0JWMMEyjJQKs0ibWZMxkTlB96Lm3EjZ7IeOxVziBEbtxVQqQZA==} + dev: true + + /livereload/0.9.3: + resolution: {integrity: sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==} + engines: {node: '>=8.0.0'} + hasBin: true + dependencies: + chokidar: 3.5.1 + livereload-js: 3.3.2 + opts: 2.0.2 + ws: 7.4.5 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /load-json-file/5.3.0: + resolution: {integrity: sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==} + engines: {node: '>=6'} + dependencies: + graceful-fs: 4.2.6 + parse-json: 4.0.0 + pify: 4.0.1 + strip-bom: 3.0.0 + type-fest: 0.3.1 + dev: true + + /load-yaml-file/0.2.0: + resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} + engines: {node: '>=6'} + dependencies: + graceful-fs: 4.2.6 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + dev: true + + /local-access/1.1.0: + resolution: {integrity: sha512-XfegD5pyTAfb+GY6chk283Ox5z8WexG56OvM06RWLpAc/UHozO8X6xAxEkIitZOtsSMM1Yr3DkHgW5W+onLhCw==} + engines: {node: '>=6'} + dev: false + + /locate-path/3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + dev: true + + /locate-path/5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + + /locate-path/6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.startcase/4.4.0: + resolution: {integrity: sha1-lDbjTtJgk+1/+uGTYUQ1CRXZrdg=} + dev: true + + /lodash/4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + /log-symbols/4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.1 + is-unicode-supported: 0.1.0 + dev: true + + /logform/1.10.0: + resolution: {integrity: sha512-em5ojIhU18fIMOw/333mD+ZLE2fis0EzXl1ZwHx4iQzmpQi6odNiY/t+ITNr33JZhT9/KEaH+UPIipr6a9EjWg==} + dependencies: + colors: 1.4.0 + fast-safe-stringify: 2.0.7 + fecha: 2.3.3 + ms: 2.1.3 + triple-beam: 1.3.0 + dev: true + + /logform/2.2.0: + resolution: {integrity: sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==} + dependencies: + colors: 1.4.0 + fast-safe-stringify: 2.0.7 + fecha: 4.2.1 + ms: 2.1.3 + triple-beam: 1.3.0 + dev: true + + /lowercase-keys/1.0.1: + resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==} + engines: {node: '>=0.10.0'} + dev: true + + /lowercase-keys/2.0.0: + resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} + engines: {node: '>=8'} + dev: true + + /lru-cache/4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + dev: true + + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /lru-queue/0.1.0: + resolution: {integrity: sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=} + dependencies: + es5-ext: 0.10.53 + dev: true + + /magic-string/0.25.7: + resolution: {integrity: sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==} + dependencies: + sourcemap-codec: 1.4.8 + dev: true + + /make-dir/3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + dependencies: + semver: 6.3.0 + dev: true + + /make-error/1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: false + + /map-age-cleaner/0.1.3: + resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==} + engines: {node: '>=6'} + dependencies: + p-defer: 1.0.0 + dev: true + + /map-obj/1.0.1: + resolution: {integrity: sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=} + engines: {node: '>=0.10.0'} + dev: true + + /map-obj/4.2.1: + resolution: {integrity: sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==} + engines: {node: '>=8'} + dev: true + + /matcher/3.0.0: + resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} + engines: {node: '>=10'} + dependencies: + escape-string-regexp: 4.0.0 + dev: true + + /md5-hex/3.0.1: + resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==} + engines: {node: '>=8'} + dependencies: + blueimp-md5: 2.18.0 + dev: true + + /mem/8.1.1: + resolution: {integrity: sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==} + engines: {node: '>=10'} + dependencies: + map-age-cleaner: 0.1.3 + mimic-fn: 3.1.0 + dev: true + + /memoizee/0.4.15: + resolution: {integrity: sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.53 + es6-weak-map: 2.0.3 + event-emitter: 0.3.5 + is-promise: 2.2.2 + lru-queue: 0.1.0 + next-tick: 1.1.0 + timers-ext: 0.1.7 + dev: true + + /meow/6.1.1: + resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} + engines: {node: '>=8'} + dependencies: + '@types/minimist': 1.2.1 + camelcase-keys: 6.2.2 + decamelize-keys: 1.1.0 + hard-rejection: 2.1.0 + minimist-options: 4.1.0 + normalize-package-data: 2.5.0 + read-pkg-up: 7.0.1 + redent: 3.0.0 + trim-newlines: 3.0.0 + type-fest: 0.13.1 + yargs-parser: 18.1.3 + dev: true + + /merge-stream/2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2/1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch/4.0.4: + resolution: {integrity: sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.2.3 + dev: true + + /mime/2.5.2: + resolution: {integrity: sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==} + engines: {node: '>=4.0.0'} + hasBin: true + dev: false + + /mimic-fn/2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /mimic-fn/3.1.0: + resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} + engines: {node: '>=8'} + dev: true + + /mimic-response/1.0.1: + resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} + engines: {node: '>=4'} + dev: true + + /min-indent/1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch/3.0.4: + resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} + dependencies: + brace-expansion: 1.1.11 + + /minimist-options/4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + dependencies: + arrify: 1.0.1 + is-plain-obj: 1.1.0 + kind-of: 6.0.3 + dev: true + + /minimist/1.2.5: + resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} + + /mixme/0.5.1: + resolution: {integrity: sha512-NaeZIckeBFT7i0XBEpGyFcAE0/bLcQ9MHErTpnU3bLWVE5WZbxG5Y3fDsMxYGifTo5khDA42OquXCC2ngKJB+g==} + engines: {node: '>= 8.0.0'} + dev: true + + /mri/1.1.6: + resolution: {integrity: sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==} + engines: {node: '>=4'} + dev: false + + /ms/2.0.0: + resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} + dev: true + + /ms/2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms/2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /nanoid/3.1.22: + resolution: {integrity: sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + /next-tick/1.0.0: + resolution: {integrity: sha1-yobR/ogoFpsBICCOPchCS524NCw=} + dev: true + + /next-tick/1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + dev: true + + /normalize-package-data/2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.20.0 + semver: 5.7.1 + validate-npm-package-license: 3.0.4 + dev: true + + /normalize-path/3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-url/4.5.0: + resolution: {integrity: sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==} + engines: {node: '>=8'} + dev: true + + /npm-run-path/2.0.2: + resolution: {integrity: sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=} + engines: {node: '>=4'} + dependencies: + path-key: 2.0.1 + dev: true + + /object-inspect/1.10.2: + resolution: {integrity: sha512-gz58rdPpadwztRrPjZE9DZLOABUpTGdcANUgOwBFO1C+HZZhePoP83M65WGDmbpwFYJSWqavbl4SgDn4k8RYTA==} + dev: false + + /object-is/1.1.5: + resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + dev: false + + /object-keys/1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: false + + /object.assign/4.1.2: + resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + has-symbols: 1.0.2 + object-keys: 1.1.1 + dev: false + + /once/1.4.0: + resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} + dependencies: + wrappy: 1.0.2 + + /one-time/1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + dependencies: + fn.name: 1.1.0 + dev: true + + /onetime/5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /opts/2.0.2: + resolution: {integrity: sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==} + dev: true + + /ora/5.4.0: + resolution: {integrity: sha512-1StwyXQGoU6gdjYkyVcqOLnVlbKj+6yPNNOxJVgpt9t4eksKjiriiHuxktLYkgllwk+D6MbC4ihH84L1udRXPg==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.1 + cli-cursor: 3.1.0 + cli-spinners: 2.6.0 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.0 + wcwidth: 1.0.1 + dev: true + + /os-tmpdir/1.0.2: + resolution: {integrity: sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=} + engines: {node: '>=0.10.0'} + dev: true + + /outdent/0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + dev: true + + /p-cancelable/1.1.0: + resolution: {integrity: sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==} + engines: {node: '>=6'} + dev: true + + /p-defer/1.0.0: + resolution: {integrity: sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=} + engines: {node: '>=4'} + dev: true + + /p-event/4.2.0: + resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} + engines: {node: '>=8'} + dependencies: + p-timeout: 3.2.0 + dev: true + + /p-filter/2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + dependencies: + p-map: 2.1.0 + dev: true + + /p-finally/1.0.0: + resolution: {integrity: sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=} + engines: {node: '>=4'} + dev: true + + /p-limit/2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit/3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate/3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate/4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate/5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-map/2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + dev: true + + /p-map/4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + dependencies: + aggregate-error: 3.1.0 + dev: true + + /p-timeout/3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + dependencies: + p-finally: 1.0.0 + dev: true + + /p-try/2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /package-json/6.5.0: + resolution: {integrity: sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==} + engines: {node: '>=8'} + dependencies: + got: 9.6.0 + registry-auth-token: 4.2.1 + registry-url: 5.1.0 + semver: 6.3.0 + dev: true + + /parent-module/1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json/4.0.0: + resolution: {integrity: sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=} + engines: {node: '>=4'} + dependencies: + error-ex: 1.3.2 + json-parse-better-errors: 1.0.2 + dev: true + + /parse-json/5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.12.13 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.1.6 + dev: true + + /parse-ms/2.1.0: + resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==} + engines: {node: '>=6'} + dev: true + + /path-exists/3.0.0: + resolution: {integrity: sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=} + engines: {node: '>=4'} + dev: true + + /path-exists/4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute/1.0.1: + resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} + engines: {node: '>=0.10.0'} + + /path-key/2.0.1: + resolution: {integrity: sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=} + engines: {node: '>=4'} + dev: true + + /path-parse/1.0.6: + resolution: {integrity: sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==} + + /path-type/4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picomatch/2.2.3: + resolution: {integrity: sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==} + engines: {node: '>=8.6'} + dev: true + + /pify/4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + dev: true + + /pkg-conf/3.1.0: + resolution: {integrity: sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==} + engines: {node: '>=6'} + dependencies: + find-up: 3.0.0 + load-json-file: 5.3.0 + dev: true + + /pkg-dir/4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: true + + /plur/4.0.0: + resolution: {integrity: sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==} + engines: {node: '>=10'} + dependencies: + irregular-plurals: 3.3.0 + dev: true + + /pnpm/6.2.5: + resolution: {integrity: sha512-klFrPM3JDo+LFA2f7Ih3wnH296ZDDqfFx5BCc3ADZV6zRQFscEL8XIU8jDqsHFv7EZHLP9Z12fzY/HyHca/duw==} + engines: {node: '>=12.17'} + hasBin: true + dev: true + + /preferred-pm/3.0.3: + resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} + engines: {node: '>=10'} + dependencies: + find-up: 5.0.0 + find-yarn-workspace-root2: 1.2.16 + path-exists: 4.0.0 + which-pm: 2.0.0 + dev: true + + /prepend-http/2.0.0: + resolution: {integrity: sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=} + engines: {node: '>=4'} + dev: true + + /prettier/1.19.1: + resolution: {integrity: sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /pretty-ms/7.0.1: + resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} + engines: {node: '>=10'} + dependencies: + parse-ms: 2.1.0 + dev: true + + /process-nextick-args/2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: true + + /pseudomap/1.0.2: + resolution: {integrity: sha1-8FKijacOYYkX7wqKw0wa5aaChrM=} + dev: true + + /pump/3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: true + + /pupa/2.1.1: + resolution: {integrity: sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==} + engines: {node: '>=8'} + dependencies: + escape-goat: 2.1.1 + dev: true + + /queue-microtask/1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /quick-lru/4.0.1: + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} + dev: true + + /ramda/0.26.1: + resolution: {integrity: sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==} + dev: true + + /randombytes/2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /rc/1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.5 + strip-json-comments: 2.0.1 + dev: true + + /read-pkg-up/7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + read-pkg: 5.2.0 + type-fest: 0.8.1 + dev: true + + /read-pkg/5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + dependencies: + '@types/normalize-package-data': 2.4.0 + normalize-package-data: 2.5.0 + parse-json: 5.2.0 + type-fest: 0.6.0 + dev: true + + /read-yaml-file/1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + dependencies: + graceful-fs: 4.2.6 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + dev: true + + /readable-stream/2.3.7: + resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} + dependencies: + core-util-is: 1.0.2 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: true + + /readable-stream/3.6.0: + resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: true + + /readdirp/3.5.0: + resolution: {integrity: sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.2.3 + dev: true + + /redent/3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + dev: true + + /regenerator-runtime/0.11.1: + resolution: {integrity: sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==} + dev: true + + /regenerator-runtime/0.13.7: + resolution: {integrity: sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==} + dev: true + + /regexp.prototype.flags/1.3.1: + resolution: {integrity: sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + dev: false + + /registry-auth-token/4.2.1: + resolution: {integrity: sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==} + engines: {node: '>=6.0.0'} + dependencies: + rc: 1.2.8 + dev: true + + /registry-url/5.1.0: + resolution: {integrity: sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==} + engines: {node: '>=8'} + dependencies: + rc: 1.2.8 + dev: true + + /require-directory/2.1.1: + resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} + engines: {node: '>=0.10.0'} + dev: true + + /require-main-filename/2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + dev: true + + /require-relative/0.8.7: + resolution: {integrity: sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=} + dev: true + + /resolve-cwd/3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + + /resolve-from/4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-from/5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true + + /resolve/1.20.0: + resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==} + dependencies: + is-core-module: 2.3.0 + path-parse: 1.0.6 + dev: true + + /resolve/2.0.0-next.3: + resolution: {integrity: sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==} + dependencies: + is-core-module: 2.3.0 + path-parse: 1.0.6 + dev: false + + /responselike/1.0.2: + resolution: {integrity: sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=} + dependencies: + lowercase-keys: 1.0.1 + dev: true + + /restore-cursor/3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.3 + dev: true + + /resumer/0.0.0: + resolution: {integrity: sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=} + dependencies: + through: 2.3.8 + dev: false + + /reusify/1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf/3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.1.6 + dev: true + + /rollup-plugin-css-only/3.1.0_rollup@2.47.0: + resolution: {integrity: sha512-TYMOE5uoD76vpj+RTkQLzC9cQtbnJNktHPB507FzRWBVaofg7KhIqq1kGbcVOadARSozWF883Ho9KpSPKH8gqA==} + engines: {node: '>=10.12.0'} + peerDependencies: + rollup: 1 || 2 + dependencies: + '@rollup/pluginutils': 4.1.0_rollup@2.47.0 + rollup: 2.47.0 + dev: true + + /rollup-plugin-inject/3.0.2: + resolution: {integrity: sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject. + dependencies: + estree-walker: 0.6.1 + magic-string: 0.25.7 + rollup-pluginutils: 2.8.2 + dev: true + + /rollup-plugin-livereload/2.0.0: + resolution: {integrity: sha512-oC/8NqumGYuphkqrfszOHUUIwzKsaHBICw6QRwT5uD07gvePTS+HW+GFwu6f9K8W02CUuTvtIM9AWJrbj4wE1A==} + engines: {node: '>=8.3'} + dependencies: + livereload: 0.9.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: true + + /rollup-plugin-node-polyfills/0.2.1: + resolution: {integrity: sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==} + dependencies: + rollup-plugin-inject: 3.0.2 + dev: true + + /rollup-plugin-svelte/7.1.0_rollup@2.47.0+svelte@3.38.2: + resolution: {integrity: sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==} + engines: {node: '>=10'} + peerDependencies: + rollup: '>=2.0.0' + svelte: '>=3.5.0' + dependencies: + require-relative: 0.8.7 + rollup: 2.47.0 + rollup-pluginutils: 2.8.2 + svelte: 3.38.2 + dev: true + + /rollup-plugin-terser/7.0.2_rollup@2.47.0: + resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} + peerDependencies: + rollup: ^2.0.0 + dependencies: + '@babel/code-frame': 7.12.13 + jest-worker: 26.6.2 + rollup: 2.47.0 + serialize-javascript: 4.0.0 + terser: 5.7.0 + dev: true + + /rollup-pluginutils/2.8.2: + resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} + dependencies: + estree-walker: 0.6.1 + dev: true + + /rollup/2.47.0: + resolution: {integrity: sha512-rqBjgq9hQfW0vRmz+0S062ORRNJXvwRpzxhFXORvar/maZqY6za3rgQ/p1Glg+j1hnc1GtYyQCPiAei95uTElg==} + engines: {node: '>=10.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /rpc-websockets/4.6.1: + resolution: {integrity: sha512-xyQC6+95hOFQJBuMRIYi2E3/ddKEMMKuql5Sd49r4578CcthP0N9nHHFkVtvrsAgz4OQH6j7zsLurLNY0nOU6g==} + dependencies: + '@babel/runtime': 7.14.0 + assert-args: 1.2.1 + babel-runtime: 6.26.0 + circular-json: 0.5.9 + eventemitter3: 3.1.2 + uuid: 3.4.0 + ws: 5.2.2 + dev: true + + /run-parallel/1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /sade/1.7.4: + resolution: {integrity: sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==} + engines: {node: '>= 6'} + dependencies: + mri: 1.1.6 + dev: false + + /safe-buffer/5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + + /safe-buffer/5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /safer-buffer/2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true + + /semiver/1.1.0: + resolution: {integrity: sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==} + engines: {node: '>=6'} + dev: false + + /semver-diff/3.1.1: + resolution: {integrity: sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==} + engines: {node: '>=8'} + dependencies: + semver: 6.3.0 + dev: true + + /semver/5.7.1: + resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true + dev: true + + /semver/6.3.0: + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true + dev: true + + /semver/7.3.5: + resolution: {integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /serialize-error/7.0.1: + resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} + engines: {node: '>=10'} + dependencies: + type-fest: 0.13.1 + dev: true + + /serialize-javascript/4.0.0: + resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + dependencies: + randombytes: 2.1.0 + dev: true + + /set-blocking/2.0.0: + resolution: {integrity: sha1-BF+XgtARrppoA93TgrJDkrPYkPc=} + dev: true + + /shebang-command/1.2.0: + resolution: {integrity: sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=} + engines: {node: '>=0.10.0'} + dependencies: + shebang-regex: 1.0.0 + dev: true + + /shebang-regex/1.0.0: + resolution: {integrity: sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=} + engines: {node: '>=0.10.0'} + dev: true + + /side-channel/1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.1 + object-inspect: 1.10.2 + dev: false + + /signal-exit/3.0.3: + resolution: {integrity: sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==} + dev: true + + /simple-swizzle/0.2.2: + resolution: {integrity: sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=} + dependencies: + is-arrayish: 0.3.2 + dev: true + + /sirv-cli/1.0.11: + resolution: {integrity: sha512-L8NILoRSBd38VcfFcERYCaVCnWPBLo9G6u/a37UJ8Ysv4DfjizMbFBcM+SswNnndJienhR6qy8KFuAEaeL4g8Q==} + engines: {node: '>= 10'} + hasBin: true + dependencies: + console-clear: 1.1.1 + get-port: 3.2.0 + kleur: 3.0.3 + local-access: 1.1.0 + sade: 1.7.4 + semiver: 1.1.0 + sirv: 1.0.11 + tinydate: 1.3.0 + dev: false + + /sirv/1.0.11: + resolution: {integrity: sha512-SR36i3/LSWja7AJNRBz4fF/Xjpn7lQFI30tZ434dIy+bitLYSP+ZEenHg36i23V2SGEz+kqjksg0uOGZ5LPiqg==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.12 + mime: 2.5.2 + totalist: 1.1.0 + dev: false + + /slash/3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /slice-ansi/3.0.0: + resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /smartwrap/1.2.5: + resolution: {integrity: sha512-bzWRwHwu0RnWjwU7dFy7tF68pDAx/zMSu3g7xr9Nx5J0iSImYInglwEVExyHLxXljy6PWMjkSAbwF7t2mPnRmg==} + deprecated: Backported compatibility to node > 6 + hasBin: true + dependencies: + breakword: 1.0.5 + grapheme-splitter: 1.0.4 + strip-ansi: 6.0.0 + wcwidth: 1.0.1 + yargs: 15.4.1 + dev: true + + /source-map-support/0.5.19: + resolution: {integrity: sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==} + dependencies: + buffer-from: 1.1.1 + source-map: 0.6.1 + + /source-map/0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + /source-map/0.7.3: + resolution: {integrity: sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==} + engines: {node: '>= 8'} + dev: true + + /sourcemap-codec/1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + dev: true + + /spawndamnit/2.0.0: + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + dependencies: + cross-spawn: 5.1.0 + signal-exit: 3.0.3 + dev: true + + /spdx-correct/3.1.1: + resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.7 + dev: true + + /spdx-exceptions/2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse/3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.7 + dev: true + + /spdx-license-ids/3.0.7: + resolution: {integrity: sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==} + dev: true + + /sprintf-js/1.0.3: + resolution: {integrity: sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=} + dev: true + + /stack-trace/0.0.10: + resolution: {integrity: sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=} + dev: true + + /stack-utils/2.0.3: + resolution: {integrity: sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==} + engines: {node: '>=10'} + dependencies: + escape-string-regexp: 2.0.0 + dev: true + + /stream-transform/2.1.0: + resolution: {integrity: sha512-bwQO+75rzQbug7e5OOHnOR3FgbJ0fCjHmDIdynkwUaFzleBXugGmv2dx3sX3aIHUQRLjrcisRPgN9BWl63uGgw==} + dependencies: + mixme: 0.5.1 + dev: true + + /string-width/2.1.1: + resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==} + engines: {node: '>=4'} + dependencies: + is-fullwidth-code-point: 2.0.0 + strip-ansi: 4.0.0 + dev: true + + /string-width/3.1.0: + resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==} + engines: {node: '>=6'} + dependencies: + emoji-regex: 7.0.3 + is-fullwidth-code-point: 2.0.0 + strip-ansi: 5.2.0 + dev: true + + /string-width/4.2.2: + resolution: {integrity: sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.0 + dev: true + + /string.prototype.trim/1.2.4: + resolution: {integrity: sha512-hWCk/iqf7lp0/AgTF7/ddO1IWtSNPASjlzCicV5irAVdE1grjsneK26YG6xACMBEdCvO8fUST0UzDMh/2Qy+9Q==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + es-abstract: 1.18.0 + dev: false + + /string.prototype.trimend/1.0.4: + resolution: {integrity: sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + dev: false + + /string.prototype.trimstart/1.0.4: + resolution: {integrity: sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.1.3 + dev: false + + /string_decoder/1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /string_decoder/1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /strip-ansi/4.0.0: + resolution: {integrity: sha1-qEeQIusaw2iocTibY1JixQXuNo8=} + engines: {node: '>=4'} + dependencies: + ansi-regex: 3.0.0 + dev: true + + /strip-ansi/5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + dependencies: + ansi-regex: 4.1.0 + dev: true + + /strip-ansi/6.0.0: + resolution: {integrity: sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.0 + dev: true + + /strip-bom/3.0.0: + resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=} + engines: {node: '>=4'} + dev: true + + /strip-eof/1.0.0: + resolution: {integrity: sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=} + engines: {node: '>=0.10.0'} + dev: true + + /strip-indent/3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments/2.0.1: + resolution: {integrity: sha1-PFMZQukIwml8DsNEhYwobHygpgo=} + engines: {node: '>=0.10.0'} + dev: true + + /supertap/2.0.0: + resolution: {integrity: sha512-jRzcXlCeDYvKoZGA5oRhYyR3jUIYu0enkSxtmAgHRlD7HwrovTpH4bDSi0py9FtuA8si9cW/fKommJHuaoDHJA==} + engines: {node: '>=10'} + dependencies: + arrify: 2.0.1 + indent-string: 4.0.0 + js-yaml: 3.14.1 + serialize-error: 7.0.1 + strip-ansi: 6.0.0 + dev: true + + /supports-color/5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color/7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /svelte-check/1.5.2_svelte@3.38.2: + resolution: {integrity: sha512-x9Pc13r814TKrMXY70IyqDEmPzuFiqNSpBmsrMKrFpi995MiG+lmqYnyw8iQC+DGh7H3eUt3LIFXbNd396XIFw==} + hasBin: true + peerDependencies: + svelte: ^3.24.0 + dependencies: + chalk: 4.1.1 + chokidar: 3.5.1 + glob: 7.1.6 + import-fresh: 3.3.0 + minimist: 1.2.5 + source-map: 0.7.3 + svelte: 3.38.2 + svelte-preprocess: 4.7.3_svelte@3.38.2+typescript@4.2.4 + typescript: 4.2.4 + transitivePeerDependencies: + - '@babel/core' + - coffeescript + - less + - node-sass + - postcss + - postcss-load-config + - pug + - sass + - stylus + - sugarss + dev: true + + /svelte-fa/2.2.0: + resolution: {integrity: sha512-PN1H8AWDh+OwhwjJKTv4/zXvKvmvOEVSCVhIhji6Onx8XEw+CGf8BDR0BVUIp87IEX+DEqIo9pbyhgz8EoYZyA==} + dev: false + + /svelte-preprocess/4.7.3_svelte@3.38.2+typescript@4.2.4: + resolution: {integrity: sha512-Zx1/xLeGOIBlZMGPRCaXtlMe4ZA0faato5Dc3CosEqwu75MIEPuOstdkH6cy+RYTUYynoxzNaDxkPX4DbrPwRA==} + engines: {node: '>= 9.11.2'} + requiresBuild: true + peerDependencies: + '@babel/core': ^7.10.2 + coffeescript: ^2.5.1 + less: ^3.11.3 + node-sass: '*' + postcss: ^7 || ^8 + postcss-load-config: ^2.1.0 || ^3.0.0 + pug: ^3.0.0 + sass: ^1.26.8 + stylus: ^0.54.7 + sugarss: ^2.0.0 + svelte: ^3.23.0 + typescript: ^3.9.5 || ^4.0.0 + peerDependenciesMeta: + '@babel/core': + optional: true + coffeescript: + optional: true + less: + optional: true + node-sass: + optional: true + postcss: + optional: true + postcss-load-config: + optional: true + pug: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + typescript: + optional: true + dependencies: + '@types/pug': 2.0.4 + '@types/sass': 1.16.0 + detect-indent: 6.0.0 + strip-indent: 3.0.0 + svelte: 3.38.2 + typescript: 4.2.4 + dev: true + + /svelte/3.38.2: + resolution: {integrity: sha512-q5Dq0/QHh4BLJyEVWGe7Cej5NWs040LWjMbicBGZ+3qpFWJ1YObRmUDZKbbovddLC9WW7THTj3kYbTOFmU9fbg==} + engines: {node: '>= 8'} + dev: true + + /tape/5.2.2: + resolution: {integrity: sha512-grXrzPC1ly2kyTMKdqxh5GiLpb0BpNctCuecTB0psHX4Gu0nc+uxWR4xKjTh/4CfQlH4zhvTM2/EXmHXp6v/uA==} + hasBin: true + dependencies: + call-bind: 1.0.2 + deep-equal: 2.0.5 + defined: 1.0.0 + dotignore: 0.1.2 + for-each: 0.3.3 + glob: 7.1.6 + has: 1.0.3 + inherits: 2.0.4 + is-regex: 1.1.2 + minimist: 1.2.5 + object-inspect: 1.10.2 + object-is: 1.1.5 + object.assign: 4.1.2 + resolve: 2.0.0-next.3 + resumer: 0.0.0 + string.prototype.trim: 1.2.4 + through: 2.3.8 + dev: false + + /temp-dir/2.0.0: + resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} + engines: {node: '>=8'} + dev: true + + /term-size/1.2.0: + resolution: {integrity: sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=} + engines: {node: '>=4'} + dependencies: + execa: 0.7.0 + dev: true + + /term-size/2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + dev: true + + /terser/5.7.0: + resolution: {integrity: sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==} + engines: {node: '>=10'} + hasBin: true + dependencies: + commander: 2.20.3 + source-map: 0.7.3 + source-map-support: 0.5.19 + dev: true + + /text-hex/1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + dev: true + + /through/2.3.8: + resolution: {integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=} + dev: false + + /time-zone/1.0.0: + resolution: {integrity: sha1-mcW/VZWJZq9tBtg73zgA3IL67F0=} + engines: {node: '>=4'} + dev: true + + /timers-ext/0.1.7: + resolution: {integrity: sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==} + dependencies: + es5-ext: 0.10.53 + next-tick: 1.1.0 + dev: true + + /tinydate/1.3.0: + resolution: {integrity: sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==} + engines: {node: '>=4'} + dev: false + + /tmp/0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + dependencies: + os-tmpdir: 1.0.2 + dev: true + + /to-readable-stream/1.0.0: + resolution: {integrity: sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==} + engines: {node: '>=6'} + dev: true + + /to-regex-range/5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /totalist/1.1.0: + resolution: {integrity: sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==} + engines: {node: '>=6'} + dev: false + + /trim-newlines/3.0.0: + resolution: {integrity: sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==} + engines: {node: '>=8'} + dev: true + + /trim-off-newlines/1.0.1: + resolution: {integrity: sha1-n5up2e+odkw4dpi8v+sshI8RrbM=} + engines: {node: '>=0.10.0'} + dev: true + + /triple-beam/1.3.0: + resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==} + dev: true + + /ts-node/9.1.1_typescript@4.2.4: + resolution: {integrity: sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==} + engines: {node: '>=10.0.0'} + hasBin: true + peerDependencies: + typescript: '>=2.7' + dependencies: + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + source-map-support: 0.5.19 + typescript: 4.2.4 + yn: 3.1.1 + dev: false + + /tslib/2.2.0: + resolution: {integrity: sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==} + dev: true + + /tty-table/2.8.13: + resolution: {integrity: sha512-eVV/+kB6fIIdx+iUImhXrO22gl7f6VmmYh0Zbu6C196fe1elcHXd7U6LcLXu0YoVPc2kNesWiukYcdK8ZmJ6aQ==} + engines: {node: '>=8.16.0'} + hasBin: true + dependencies: + chalk: 3.0.0 + csv: 5.5.0 + smartwrap: 1.2.5 + strip-ansi: 6.0.0 + wcwidth: 1.0.1 + yargs: 15.4.1 + dev: true + + /type-detect/0.1.1: + resolution: {integrity: sha1-C6XsKohWQORw6k6FBZcZANrFiCI=} + dev: true + + /type-fest/0.13.1: + resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} + engines: {node: '>=10'} + dev: true + + /type-fest/0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest/0.3.1: + resolution: {integrity: sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==} + engines: {node: '>=6'} + dev: true + + /type-fest/0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + dev: true + + /type-fest/0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + dev: true + + /type/1.2.0: + resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} + dev: true + + /type/2.5.0: + resolution: {integrity: sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==} + dev: true + + /typedarray-to-buffer/3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + dependencies: + is-typedarray: 1.0.0 + dev: true + + /typescript/4.2.4: + resolution: {integrity: sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==} + engines: {node: '>=4.2.0'} + hasBin: true + + /unbox-primitive/1.0.1: + resolution: {integrity: sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==} + dependencies: + function-bind: 1.1.1 + has-bigints: 1.0.1 + has-symbols: 1.0.2 + which-boxed-primitive: 1.0.2 + dev: false + + /unique-string/2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + dependencies: + crypto-random-string: 2.0.0 + dev: true + + /universalify/0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: true + + /update-notifier/5.1.0: + resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==} + engines: {node: '>=10'} + dependencies: + boxen: 5.0.1 + chalk: 4.1.1 + configstore: 5.0.1 + has-yarn: 2.1.0 + import-lazy: 2.1.0 + is-ci: 2.0.0 + is-installed-globally: 0.4.0 + is-npm: 5.0.0 + is-yarn-global: 0.3.0 + latest-version: 5.1.0 + pupa: 2.1.1 + semver: 7.3.5 + semver-diff: 3.1.1 + xdg-basedir: 4.0.0 + dev: true + + /url-parse-lax/3.0.0: + resolution: {integrity: sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=} + engines: {node: '>=4'} + dependencies: + prepend-http: 2.0.0 + dev: true + + /util-deprecate/1.0.2: + resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} + dev: true + + /uuid/3.4.0: + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} + hasBin: true + dev: true + + /uuid/8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + /uuidv4/6.2.7: + resolution: {integrity: sha512-3XvpuG5LVO9Bd9H/mjdMd7eh556ojlAFx5jJ7dSiGGjFNaU8XhcKhhnV34YGjyZiRs8kPBwXuhYJoyvrY+EMOA==} + dependencies: + '@types/uuid': 8.3.0 + uuid: 8.3.2 + dev: false + + /validate-npm-package-license/3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + dependencies: + spdx-correct: 3.1.1 + spdx-expression-parse: 3.0.1 + dev: true + + /wcwidth/1.0.1: + resolution: {integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=} + dependencies: + defaults: 1.0.3 + dev: true + + /well-known-symbols/2.0.0: + resolution: {integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==} + engines: {node: '>=6'} + dev: true + + /which-boxed-primitive/1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.2 + is-boolean-object: 1.1.0 + is-number-object: 1.0.4 + is-string: 1.0.5 + is-symbol: 1.0.3 + dev: false + + /which-collection/1.0.1: + resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + dependencies: + is-map: 2.0.2 + is-set: 2.0.2 + is-weakmap: 2.0.1 + is-weakset: 2.0.1 + dev: false + + /which-module/2.0.0: + resolution: {integrity: sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=} + dev: true + + /which-pm/2.0.0: + resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} + engines: {node: '>=8.15'} + dependencies: + load-yaml-file: 0.2.0 + path-exists: 4.0.0 + dev: true + + /which-typed-array/1.1.4: + resolution: {integrity: sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.2 + call-bind: 1.0.2 + es-abstract: 1.18.0 + foreach: 2.0.5 + function-bind: 1.1.1 + has-symbols: 1.0.2 + is-typed-array: 1.1.5 + dev: false + + /which/1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /widest-line/2.0.1: + resolution: {integrity: sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==} + engines: {node: '>=4'} + dependencies: + string-width: 2.1.1 + dev: true + + /widest-line/3.1.0: + resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} + engines: {node: '>=8'} + dependencies: + string-width: 4.2.2 + dev: true + + /winston-compat/0.1.5: + resolution: {integrity: sha512-EPvPcHT604AV3Ji6d3+vX8ENKIml9VSxMRnPQ+cuK/FX6f3hvPP2hxyoeeCOCFvDrJEujalfcKWlWPvAnFyS9g==} + engines: {node: '>= 6.4.0'} + dependencies: + cycle: 1.0.3 + logform: 1.10.0 + triple-beam: 1.3.0 + dev: true + + /winston-null/2.0.0_winston@3.3.3: + resolution: {integrity: sha512-uS5tJB5OkLWOoc3I7/LsWUfTIa5Du38XSviHf/b0TINK659Np9368FJwTt15UoZQYUQVxLpM06lxk2dKET22Xw==} + engines: {node: '>=6'} + peerDependencies: + winston: '>=2.0.0' + dependencies: + semver: 5.7.1 + winston: 3.3.3 + winston-compat: 0.1.5 + winston-transport: 4.4.0 + dev: true + + /winston-transport/4.4.0: + resolution: {integrity: sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==} + engines: {node: '>= 6.4.0'} + dependencies: + readable-stream: 2.3.7 + triple-beam: 1.3.0 + dev: true + + /winston/3.3.3: + resolution: {integrity: sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==} + engines: {node: '>= 6.4.0'} + dependencies: + '@dabh/diagnostics': 2.0.2 + async: 3.2.0 + is-stream: 2.0.0 + logform: 2.2.0 + one-time: 1.0.0 + readable-stream: 3.6.0 + stack-trace: 0.0.10 + triple-beam: 1.3.0 + winston-transport: 4.4.0 + dev: true + + /wrap-ansi/6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.2 + strip-ansi: 6.0.0 + dev: true + + /wrap-ansi/7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.2 + strip-ansi: 6.0.0 + dev: true + + /wrappy/1.0.2: + resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} + + /write-file-atomic/3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + dependencies: + imurmurhash: 0.1.4 + is-typedarray: 1.0.0 + signal-exit: 3.0.3 + typedarray-to-buffer: 3.1.5 + dev: true + + /ws/5.2.2: + resolution: {integrity: sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==} + dependencies: + async-limiter: 1.0.1 + dev: true + + /ws/7.4.5: + resolution: {integrity: sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + /xdg-basedir/4.0.0: + resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} + engines: {node: '>=8'} + dev: true + + /y18n/4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + dev: true + + /y18n/5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist/2.1.2: + resolution: {integrity: sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=} + dev: true + + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yaml/1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true + + /yargs-parser/18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: true + + /yargs-parser/20.2.7: + resolution: {integrity: sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==} + engines: {node: '>=10'} + dev: true + + /yargs/15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.2 + which-module: 2.0.0 + y18n: 4.0.3 + yargs-parser: 18.1.3 + dev: true + + /yargs/16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.2 + y18n: 5.0.8 + yargs-parser: 20.2.7 + dev: true + + /yn/3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: false + + /yocto-queue/0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000..69fdf7c3 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +packages: + - 'ui/apps/*' + - 'ui/libs/*' + - 'tests' diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..a6c7c285 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +*.js diff --git a/tests/common.ts b/tests/common.ts index a8c4b636..e69de29b 100644 --- a/tests/common.ts +++ b/tests/common.ts @@ -1,23 +0,0 @@ -import { HoloHash } from '@holochain/conductor-api' -export type Add = [number, string] -export type Delete = [number, number] -export type Title = string - -// Signal type definitions -export type Delta = { - type: string, - value: Add | Delete | Title, -} - -export type StateForSync = { - snapshot: HoloHash, - commit: HoloHash, - deltas: Delta[], -} - -export type Signal = { - signal_name: string, - signal_payload? -} - -export const delay = ms => new Promise(r => setTimeout(r, ms)); diff --git a/tests/package.json b/tests/package.json index 4a0fce85..b8603f84 100644 --- a/tests/package.json +++ b/tests/package.json @@ -1,4 +1,5 @@ { + "name": "@syn-ui/tests", "main": "index.js", "scripts": { "test": "npm run test:setup && npm run test:standard", @@ -11,16 +12,26 @@ "license": "CAL-1.0", "dependencies": { "esm": "^3.2.25", - "lodash": "^4.17.19", - "tape": "^5.0.1", - "ts-node": "^8.10.2", - "typescript": "^3.9.6", - "uuidv4": "^6.2.3" + "lodash": "^4.17.21", + "tape": "^5.2.2", + "ts-node": "^9.1.1", + "typescript": "^4.2.4", + "uuidv4": "^6.2.7" }, "devDependencies": { - "@holochain/conductor-api": "0.0.1", - "@holochain/tryorama": "0.4.1", - "@types/lodash": "^4.14.158", - "@types/node": "^14.0.14" - } -} + "@ctx-core/combinators": "^6.0.10", + "@ctx-core/store": "^24.7.17", + "@holochain/conductor-api": "0.0.4", + "@holochain/tryorama": "0.4.2", + "@syn-ui/model": "workspace:^1.0.0", + "@syn-ui/utils": "workspace:^1.0.0", + "@syn-ui/zome-client": "workspace:^1.0.0", + "@types/lodash": "^4.14.168", + "@types/node": "^15.0.2", + "ava": "^3.15.0", + "pnpm": "^6.2.5" + }, + "noUpdate": [ + "@holochain/tryorama" + ] +} \ No newline at end of file diff --git a/tests/syn-lib/index.ts b/tests/syn-lib/index.ts index ad2834d1..5bed510f 100644 --- a/tests/syn-lib/index.ts +++ b/tests/syn-lib/index.ts @@ -1,2 +1,3 @@ -require = require("esm")(module/*, options*/) -module.exports = require("./main") +require = require('esm')(module) +export * from './main' +export * from './syn-lib' diff --git a/tests/syn-lib/main.ts b/tests/syn-lib/main.ts index 1c6c7266..c3ec8569 100644 --- a/tests/syn-lib/main.ts +++ b/tests/syn-lib/main.ts @@ -2,7 +2,7 @@ import { Orchestrator } from '@holochain/tryorama' const orchestrator = new Orchestrator() -import {oFn} from './syn-lib' +import { oFn } from './syn-lib' -oFn(orchestrator); +oFn(orchestrator) orchestrator.run() diff --git a/tests/syn-lib/syn-lib.ts b/tests/syn-lib/syn-lib.ts index 10e0ab10..6a4f101f 100644 --- a/tests/syn-lib/syn-lib.ts +++ b/tests/syn-lib/syn-lib.ts @@ -1,88 +1,76 @@ -import {Connection} from './../../ui/src/syn' -import { Config, InstallAgentsHapps } from '@holochain/tryorama' -import { HoloHash } from '@holochain/conductor-api' -import * as _ from 'lodash' import path from 'path' -import {delay, Delta, Signal, StateForSync} from '../common' +import { Config, InstallAgentsHapps } from '@holochain/tryorama' +import { Content, me_b, PubKeyToFolkRecord } from '@syn-ui/zome-client' +import { folks_b, join_session, session_info_scribe_str_b } from '@syn-ui/model' +import { delay } from '../common' -const config = Config.gen(); +const config = Config.gen() const dna = path.join(__dirname, '../../syn.dna') console.log(dna) -const installation: InstallAgentsHapps = [ - // one agents - [[dna]], // contains 1 dna +const installation:InstallAgentsHapps = [ + // one agents + [[dna]], // contains 1 dna ] -process.on('unhandledRejection', error => { +process.on('unhandledRejection', error=>{ // Will print "unhandledRejection err is not defined" - console.log('unhandledRejection', error); -}); + console.log('unhandledRejection', error) +}) // Delta representation could be JSON or not, for now we are using // json so setting this variable to true -const jsonDeltas = true; - -export const oFn = (orchestrator) => { - const defaultContent = {title:'', body:''} -/* orchestrator.registerScenario('syn connect', async (s, t) => { - const [me_player] = await s.players([config]) - const [[me_happ]] = await me_player.installAgentsHapps(installation) - const appPort = me_player._conductor.appClient.client.socket._url.split(":")[2]; - const c = new Connection(appPort, me_happ.hAppId) - t.equal(c.appPort, appPort) - t.equal(c.appId, me_happ.hAppId) - t.equal(c.sessions.length, 0) - t.equal(c.syn, undefined) - t.equal(c.appClient, undefined) +const jsonDeltas = true +export const oFn = (orchestrator)=>{ + const default_content:Content = { title: '', body: '', meta: {} } + /* orchestrator.registerScenario('syn connect', async (s, t) => { + const [me_player] = await s.players([config]) + const [[me_happ]] = await me_player.installAgentsHapps(installation) + const app_port = me_player._conductor.app_ws.client.socket._url.split(":")[2]; + const c = new Connection(app_port, me_happ.hAppId) + t.equal(c.app_port, app_port) + t.equal(c.app_id, me_happ.hAppId) + t.equal(c.sessions.length, 0) + t.equal(c.syn, undefined) + t.equal(c.app_ws, undefined) - await c.open(defaultContent, () => {}) - t.notEqual(c.appClient, undefined) - t.deepEqual(c.syn.defaultContent, defaultContent) - })*/ - orchestrator.registerScenario('syn 2 nodes', async (s, t) => { - const [player1, player2] = await s.players([config, config]) - const [[syn1]] = await player1.installAgentsHapps(installation) - const [[syn2]] = await player2.installAgentsHapps(installation) - await s.shareAllNodes([player1, player2]); - const appPort1 = player1._conductor.appClient.client.socket._url.split(":")[2]; - const appPort2 = player2._conductor.appClient.client.socket._url.split(":")[2]; - const c1 = new Connection(appPort1, syn1.hAppId) - await c1.open(defaultContent, applyDeltas) - await c1.joinSession() - const c2 = new Connection(appPort2, syn2.hAppId) - await c2.open(defaultContent, applyDeltas) - await c2.joinSession() - t.equal(c1.syn.me, c2.session._scribeStr) - while (true) { - const others = Object.keys(c2.session.others) - if (others.length > 0) { - t.equal(c2.session.others[others[0]].pubKey.toString('base64'), c1.syn.me) - break - } else { - await delay(1000) - } - } + await c.open(default_content, () => {}) + t.notEqual(c.app_ws, undefined) + t.deepEqual(c.syn.default_content, default_content) + })*/ + orchestrator.registerScenario('syn 2 nodes', async (s, t)=>{ + const [player1, player2] = await s.players([config, config]) + const [[syn1]] = await player1.installAgentsHapps(installation) + const [[syn2]] = await player2.installAgentsHapps(installation) + await s.shareAllNodes([player1, player2]) + const appPort1:number = player1._conductor.app_ws.client.socket._url.split(':')[2] + const appPort2:number = player2._conductor.app_ws.client.socket._url.split(':')[2] + const c1 = await join_session({ + app_port: appPort1, app_id: syn1.hAppId, }) -} - -const applyDeltas = (content, deltas) => { - for (const delta of deltas) { - switch(delta.type) { - case "Title": - content.title = delta.value - break - case "Add": - const [loc, text] = delta.value - content.body = content.body.slice(0, loc) + text + content.body.slice(loc) - break - case "Delete": - const [start, end] = delta.value - content.body = content.body.slice(0, start) + content.body.slice(end) - break - } + // const c1 = new Connection({}, appPort1, syn1.hAppId) + // await c1.open(default_content, applyDeltas) + // await c1.joinSession() + const c2 = await join_session({ + app_port: appPort2, app_id: syn2.hAppId, + }) + // const c2 = new Connection({}, appPort2, syn2.hAppId) + // await c2.open(default_content, applyDeltas) + // await c2.joinSession() + const c1_me = me_b(c1) + t.equal(c1_me.$, session_info_scribe_str_b(c2).$) + const c2_folks = folks_b(c2) + while (true) { + const $c2_folks = c2_folks.$! + const others = Object.keys($c2_folks) + if (others.length > 0) { + t.equal($c2_folks[others[0]].pubKey.toString('base64'), c1_me.$) + break + } else { + await delay(1000) + } } - return content + }) } diff --git a/tests/unit-test/index.ts b/tests/unit-test/index.ts index 14b94195..2c4e745d 100644 --- a/tests/unit-test/index.ts +++ b/tests/unit-test/index.ts @@ -1,3 +1,4 @@ +require = require('esm')(module) import { Orchestrator } from '@holochain/tryorama' const orchestrator = new Orchestrator() diff --git a/tests/unit-test/syn.ts b/tests/unit-test/syn.ts index 05355a5c..fc0b9b5a 100644 --- a/tests/unit-test/syn.ts +++ b/tests/unit-test/syn.ts @@ -1,38 +1,48 @@ -import { Config, InstallAgentsHapps } from '@holochain/tryorama' -import * as _ from 'lodash' +import { isDeepStrictEqual } from 'util' import path from 'path' -import {delay, Delta, Signal, StateForSync} from '../common' - -const config = Config.gen(); +import { Config, InstallAgentsHapps } from '@holochain/tryorama' +import { _stacktrace_filename_line, noop, run, waitfor } from '@ctx-core/function' +import { assign } from '@ctx-core/object' +import { I } from '@ctx-core/combinators' +import { Readable$, subscribe_wait_timeout, writable$ } from '@ctx-core/store' +import { bufferToBase64, console_b } from '@syn-ui/utils' +import { + content_b, session_info_b, join_session, leave_session, content_hash_b, sessions_b, commit_change_b, + current_commit_header_hash_b, request_change_b, recorded_changes_b, committed_changes_b, folks_b, + getFolkColors +} from '@syn-ui/model' +import { + Delta, my_tag_b, rpc_get_content_b, rpc_get_folks_b, rpc_get_session_b, rpc_hash_content_b, + rpc_send_heartbeat_b, rpc_send_sync_request_b, Signal +} from '@syn-ui/zome-client' + +const config = Config.gen() const dna = path.join(__dirname, '../../syn.dna') console.log(dna) -const installation: InstallAgentsHapps = [ +const installation:InstallAgentsHapps = [ // one agents - [[dna]], // contains 1 dna + [[dna]], // contains 1 dnaT ] -process.on('unhandledRejection', error => { - // Will print "unhandledRejection err is not defined" - console.log('unhandledRejection', error); -}); +process.on('unhandledRejection', error=>{ + // Will print "unhandledRejection err is not defined" + console.log('unhandledRejection', error) +}) - -module.exports = (orchestrator) => { - orchestrator.registerScenario('syn basic zome calls', async (s, t) => { +module.exports = (orchestrator)=>{ + orchestrator.registerScenario('syn basic zome calls', async (s, t)=>{ // Delta representation could be JSON or not, for now we are using // json so setting this variable to true - const jsonDeltas = true; - + const jsonDeltas = true const [me_player, alice_player, bob_player] = await s.players([config, config, config]) const [[me_happ]] = await me_player.installAgentsHapps(installation) const [[alice_happ]] = await alice_player.installAgentsHapps(installation) const [[bob_happ]] = await bob_player.installAgentsHapps(installation) - - await s.shareAllNodes([me_player, alice_player, bob_player]); + await s.shareAllNodes([me_player, alice_player, bob_player]) const me = me_happ.cells[0] const alice = alice_happ.cells[0] @@ -40,254 +50,406 @@ module.exports = (orchestrator) => { const me_pubkey = me.cellId[1] const alice_pubkey = alice.cellId[1] + const alice_pubkey_base64 = bufferToBase64(alice_pubkey) const bob_pubkey = bob.cellId[1] - - let sessions = await me.call('syn', 'get_sessions') - t.equal(sessions.length, 0) - - // create initial session - let sessionInfo = await me.call('syn', 'new_session', {content: {title:"", body:""}}) - // I created the session, so I should be the scribe - t.deepEqual(sessionInfo.scribe, me_pubkey) - // First ever session so content should be default content - t.deepEqual(sessionInfo.snapshot_content, {title:"", body:""}) - let sessionHash = sessionInfo.session; - - // check the hash_content zome call. - let hash = await me.call('syn', 'hash_content', sessionInfo.snapshot_content) - t.deepEqual(sessionInfo.content_hash, hash) - - // check get_sessions utility zome call - sessions = await me.call('syn', 'get_sessions') - t.equal(sessions.length, 1) - t.deepEqual(sessions[0],sessionHash) - - // exercise the get_session zome call - const returnedSessionInfo = await me.call('syn', 'get_session', sessionHash) - t.equal(sessions.length, 1) - t.deepEqual(sessionInfo, returnedSessionInfo) - - // check that initial snapshot was created by using the get_content zome call - const returned_content = await me.call('syn', 'get_content', sessionInfo.content_hash) - t.deepEqual(returned_content, sessionInfo.snapshot_content) - - // set up the pending deltas array - let pendingDeltas = [{type:"Title",value: "foo title"}, {type:"Add", value:[0,"bar content"]}] - - let new_content = applyDeltas(sessionInfo.snapshot_content, pendingDeltas) - const new_content_hash_1 = await me.call('syn', 'hash_content', new_content) - - let deltas = jsonDeltas ? pendingDeltas.map(d=>JSON.stringify(d)) : pendingDeltas; - - // set signal handlers so we can confirm they get sent and received appropriately - let me_signals : Signal[] = [] - me_player.setSignalHandler((signal) => { - console.log("Received Signal for me:",signal) - me_signals.push(signal.data.payload) - }) - - // alice signal handler - let alice_signals : Signal[] = [] - alice_player.setSignalHandler((signal) => { - console.log("Received Signal for alice:",signal) - alice_signals.push(signal.data.payload) - }) - - // bob signal handler - let bob_signals : Signal[] = [] - bob_player.setSignalHandler((signal) => { - console.log("Received Signal for bob:",signal) - bob_signals.push(signal.data.payload) - }) - - // add a content change - let commit = { - snapshot: sessionInfo.content_hash, - change: { - deltas, - content_hash: new_content_hash_1, - previous_change: sessionInfo.content_hash, // this is the first change so same hash as snapshot - meta: { - contributors: [], - witnesses: [], - app_specific: null - } - }, - participants: [] + const bob_pubkey_base64 = bufferToBase64(bob_pubkey) + + t.equal((await me.call('syn', 'get_sessions')).length, 0, _stacktrace_filename_line()) + const me_port:number = parseInt(me_player._conductor.appClient.client.socket.url.split(':')[2]) + const alice_port:number = parseInt(alice_player._conductor.appClient.client.socket.url.split(':')[2]) + const bob_port:number = parseInt(bob_player._conductor.appClient.client.socket.url.split(':')[2]) + + const me_ctx = {}, alice_ctx = {}, bob_ctx = {} + // To enable logs, remove log: noop + const show_full_logs = false + const console_overrides = show_full_logs ? {} : { log: noop } + assign(console_b(me_ctx), console_overrides) + assign(console_b(alice_ctx), console_overrides) + assign(console_b(bob_ctx), console_overrides) + await join_session({ app_port: me_port, app_id: me_happ.hAppId, ctx: me_ctx }) + + try { + t.deepEqual(await rpc_get_folks_b(me_ctx)(), [me_pubkey], _stacktrace_filename_line()) + await subscribe_wait_timeout(session_info_b(me_ctx), I, 10_000) + // I created the session, so I should be the scribe + t.deepEqual(session_info_b(me_ctx).$!.scribe, me_pubkey, _stacktrace_filename_line()) + // First ever session so content should be default content + t.deepEqual(session_info_b(me_ctx).$!.snapshot_content, { title: '', body: '' }, _stacktrace_filename_line()) + let session_hash = session_info_b(me_ctx).$!.session + + // check the hash_content zome call. + t.deepEqual( + session_info_b(me_ctx).$!.content_hash, + await rpc_hash_content_b(me_ctx)(session_info_b(me_ctx).$!.snapshot_content), + _stacktrace_filename_line() + ) + + // check get_sessions utility zome call + const me_sessions = sessions_b(me_ctx) + t.equal(me_sessions.$!.length, 1, _stacktrace_filename_line()) + t.deepEqual(me_sessions.$![0], session_hash, _stacktrace_filename_line()) + + // exercise the get_session zome call + t.equal(me_sessions.$!.length, 1, _stacktrace_filename_line()) + t.deepEqual(session_info_b(me_ctx).$, await rpc_get_session_b(me_ctx)(session_hash), _stacktrace_filename_line()) + + // check that initial snapshot was created by using the get_content zome call + t.deepEqual( + session_info_b(me_ctx).$!.snapshot_content, + await rpc_get_content_b(me_ctx)(session_info_b(me_ctx).$!.content_hash), + _stacktrace_filename_line() + ) + + // set up the pending deltas array + let pending_deltas:Delta[] = [{ type: 'Title', value: 'foo title' }, { type: 'Add', value: [0, 'bar content'] }] + + await request_change_b(me_ctx)(pending_deltas) + const me_content = content_b(me_ctx) + let content_hash = await rpc_hash_content_b(me_ctx)(me_content.$) + + let deltas = jsonDeltas ? pending_deltas.map(d=>JSON.stringify(d)) : pending_deltas + + // set signal handlers so we can confirm they get sent and received appropriately + let me_signals = writable$([]) + me_player.setSignalHandler((signal)=>{ + console_b(me_ctx).log('Received Signal for me:', signal) + me_signals.update($me_signals=>{ + $me_signals.push(signal.data.payload) + return $me_signals + }) + }) + + // alice signal handler + const alice_signals = writable$([]) + alice_player.setSignalHandler((signal)=>{ + console_b(alice_ctx).log('Received Signal for alice:', signal) + alice_signals.update($alice_signals=>{ + $alice_signals.push(signal.data.payload) + return $alice_signals + }) + }) + + // bob signal handler + const bob_signals = writable$([]) + bob_player.setSignalHandler((signal)=>{ + console_b(bob_ctx).log('Received Signal for bob:', signal) + bob_signals.update($bob_signals=>{ + $bob_signals.push(signal.data.payload) + return $bob_signals + }) + }) + + // add a content change + await commit_change_b(me_ctx)() + t.equal(current_commit_header_hash_b(me_ctx).$!.length, 39, _stacktrace_filename_line()) // is a content_hash + t.deepEqual(recorded_changes_b(me_ctx).$, [], _stacktrace_filename_line()) + + // add a second content change + pending_deltas = [ + { type: 'Delete', value: [0, 3] }, + { type: 'Add', value: [0, 'baz'] }, + { type: 'Add', value: [11, ' new'] }, // 'baz content new' + { type: 'Delete', value: [4, 11] }, // 'baz new' + { type: 'Add', value: [4, 'monkey'] }, // 'baz monkey new' + ] + await run(async ()=>{ + const $current_commit_header_hash = current_commit_header_hash_b(me_ctx).$! + await waitfor(async ()=> + (current_commit_header_hash_b(me_ctx).$!) === $current_commit_header_hash, + 500 + ) + }) + + deltas = jsonDeltas ? pending_deltas.map(d=>JSON.stringify(d)) : pending_deltas + await request_change_b(me_ctx)(pending_deltas) + t.deepEqual(recorded_changes_b(me_ctx).$, [ + { delta: { type: 'Delete', value: [0, 3] }, deleted: 'bar' }, + { delta: { type: 'Add', value: [0, 'baz'] } }, + { delta: { type: 'Add', value: [11, ' new'] } }, // 'baz content new' + { delta: { type: 'Delete', value: [4, 11] }, deleted: 'content' }, // 'baz new' + { delta: { type: 'Add', value: [4, 'monkey'] } }, // 'baz monkey new' + ], _stacktrace_filename_line()) + + await commit_change_b(me_ctx)() + // clear the pending_deltas + pending_deltas = [] + + // check that deltas and snapshot content returned add up to the current real content + let me_SyncResp_stack:Signal[], alice_SyncResp_stack:Signal[], me_SyncReq_stack:Signal[] + + t.deepEqual( + me_content.$, + { title: 'foo title', body: 'baz monkey new', meta: { [my_tag_b(me_ctx).$]: 0 } }, // content after two commits + _stacktrace_filename_line() + ) + let alice_FolkLore_stack:Signal[] + // alice joins session + ;[ + [me_SyncReq_stack], + [alice_FolkLore_stack], + [alice_SyncResp_stack] + ] = await waitfor_filtered_signals_change(async ()=> + join_session({ + app_port: alice_port, + app_id: alice_happ.hAppId, + ctx: alice_ctx + }), + [ + [[me_signals], $signals=>filter_signal_name($signals, 'SyncReq')], + [[alice_signals], $signals=>filter_signal_name($signals, 'FolkLore')], + [[alice_signals], $signals=>filter_signal_name($signals, 'SyncResp')] + ] + ) + await waitfor( + async ()=>isDeepStrictEqual(await rpc_get_folks_b(me_ctx)(), [me_pubkey, alice_pubkey]), + 500) + await waitfor(async ()=> + isDeepStrictEqual(await rpc_get_folks_b(alice_ctx)(), [me_pubkey, alice_pubkey]), + 500) + t.deepEqual(await rpc_get_folks_b(me_ctx)(), [me_pubkey, alice_pubkey], _stacktrace_filename_line()) + const alice_session_info = session_info_b(alice_ctx) + const alice_content = content_b(alice_ctx) + // alice should get my session + t.deepEqual(alice_session_info.$!.session, session_hash, _stacktrace_filename_line()) + t.deepEqual(alice_session_info.$!.scribe, me_pubkey, _stacktrace_filename_line()) + t.deepEqual(alice_session_info.$!.snapshot_content, { title: '', body: '' }, _stacktrace_filename_line()) + await waitfor(async ()=> + isDeepStrictEqual(alice_session_info.$!.deltas, [ + '{"type":"Title","value":"foo title"}', + '{"type":"Add","value":[0,"bar content"]}', + '{"type":"Delete","value":[0,3]}', + '{"type":"Add","value":[0,"baz"]}', + '{"type":"Add","value":[11," new"]}', + '{"type":"Delete","value":[4,11]}', + '{"type":"Add","value":[4,"monkey"]}' + ]), 1000) + + await subscribe_wait_timeout( + alice_content, + $alice_content=>{ + return isDeepStrictEqual( + $alice_content, + { title: 'foo title', body: 'baz monkey new', meta: { [my_tag_b(alice_ctx).$]: 0 } }) + }, + 1000 + ) + await leave_session({ ctx: alice_ctx }) + ;[ + [me_SyncReq_stack], + [alice_SyncResp_stack], + ] = await waitfor_filtered_signals_change(async ()=> + join_session({ + app_port: alice_port, + app_id: alice_happ.hAppId, + ctx: alice_ctx + }), + [ + [[me_signals], $signals=>filter_signal_name($signals, 'SyncReq')], + [[alice_signals], $signals=>filter_signal_name($signals, 'SyncResp')], + ]) + t.deepEqual( + alice_content.$, + { title: 'foo title', body: 'baz monkey new', meta: { [my_tag_b(alice_ctx).$]: 0 } },// content after two commits + _stacktrace_filename_line() + ) + + // confirm that the session_info_b(me_ctx)'s content content_hash matches the content_hash + // generated by applying deltas + content_hash = await rpc_hash_content_b(alice_ctx)(content_b(alice_ctx).$) + const alice_content_hash = content_hash_b(alice_ctx) + t.deepEqual(alice_content_hash.$, content_hash, _stacktrace_filename_line()) + + // I should receive alice's request for the state as she joins the session + t.deepEqual(me_SyncReq_stack![0], { signal_name: 'SyncReq', signal_payload: alice_pubkey }, _stacktrace_filename_line()) + + // I add some pending deltas which I will then need to send to Alice as part of her Joining. + pending_deltas = [{ type: 'Title', value: 'I haven\'t committed yet' }, { type: 'Add', value: [14, '\nBut made a new line! 🍑'] }] + + deltas = jsonDeltas ? pending_deltas.map(d=>JSON.stringify(d)) : pending_deltas + + let alice_Change_stack:Signal[] + let me_index = _index(me_ctx) + ;[[alice_Change_stack]] = await waitfor_filtered_signals_change(async ()=> + request_change_b(me_ctx)(pending_deltas), + [ + [[alice_signals], $alice_signals=>filter_signal_name($alice_signals, 'Change')] + ]) + + // Alice should have received uncommitted deltas + t.equal(alice_Change_stack[0].signal_name, 'Change', _stacktrace_filename_line()) + t.deepEqual(alice_Change_stack[0].signal_payload[0], me_index, _stacktrace_filename_line()) // deltas, commit, and snapshot match + t.deepEqual(alice_Change_stack[0].signal_payload[1], pending_deltas.map(d=>JSON.stringify(d)), _stacktrace_filename_line()) // deltas, commit, and snapshot match + + t.deepEqual(me_signals.$.map(ms=>ms.signal_name), ['SyncReq', 'SyncReq'], _stacktrace_filename_line()) + // alice sends me a change req and I should receive it + const alice_delta:Delta = { type: 'Title', value: 'Alice in Wonderland' } + let delta = jsonDeltas ? JSON.stringify(alice_delta) : alice_delta + + let alice_index = _index(alice_ctx) + let [[me_ChangeReq_stack]] = await waitfor_filtered_signals_change(async ()=> + request_change_b(alice_ctx)([alice_delta]), + [[[me_signals], $signals=>filter_signal_name($signals, 'ChangeReq')]] + ) + t.deepEqual(me_ChangeReq_stack[0].signal_name, 'ChangeReq', _stacktrace_filename_line()) + t.equal(me_ChangeReq_stack[0].signal_payload[0], alice_index, _stacktrace_filename_line()) + const receiveDelta = jsonDeltas ? JSON.parse(me_ChangeReq_stack[0].signal_payload[1]) : me_ChangeReq_stack[0].signal_payload[1] + t.deepEqual(receiveDelta, alice_delta, _stacktrace_filename_line()) // delta_matches + + let alice_SyncReq_stack:Signal[], bob_SyncResp_stack:Signal[], bob_FolkLore_stack:Signal[] + ;[ + [me_SyncReq_stack], + [alice_FolkLore_stack, bob_FolkLore_stack], + [bob_SyncResp_stack] + ] = await waitfor_filtered_signals_change(async ()=> + join_session({ + app_port: bob_port, + app_id: bob_happ.hAppId, + ctx: bob_ctx + }), + [ + [[me_signals], $signals=>filter_signal_name($signals, 'SyncReq')], + [[alice_signals, bob_signals], $signals=>filter_signal_name($signals, 'FolkLore')], + [[bob_signals], $signals=>filter_signal_name($signals, 'SyncResp')] + ] + ) + + // bob joins session + const bob_$session_info = session_info_b(bob_ctx).$! + // const bob_$session_info = await rpc_get_session_b(bob_ctx)(session_hash) + // bob should get my session + t.deepEqual(bob_$session_info.scribe, me_pubkey, _stacktrace_filename_line()) + await waitfor(async ()=> + isDeepStrictEqual(folks_b(me_ctx).$[alice_pubkey_base64]?.pubKey, alice_pubkey) + && isDeepStrictEqual(folks_b(me_ctx).$[bob_pubkey_base64]?.pubKey, bob_pubkey), + 1000) + await waitfor(async ()=> + isDeepStrictEqual(folks_b(alice_ctx).$[alice_pubkey_base64]?.pubKey, alice_pubkey) + && isDeepStrictEqual(folks_b(alice_ctx).$[bob_pubkey_base64]?.pubKey, bob_pubkey), + 1000) + await waitfor(async ()=> + isDeepStrictEqual(folks_b(bob_ctx).$[alice_pubkey_base64]?.pubKey, alice_pubkey) + && isDeepStrictEqual(folks_b(bob_ctx).$[bob_pubkey_base64]?.pubKey, bob_pubkey), + 1000) + t.deepEqual(folks_b(me_ctx).$[alice_pubkey_base64].pubKey, alice_pubkey, _stacktrace_filename_line()) + t.deepEqual(folks_b(me_ctx).$[alice_pubkey_base64].colors, getFolkColors(alice_pubkey), _stacktrace_filename_line()) + t.deepEqual(folks_b(me_ctx).$[alice_pubkey_base64].inSession, true, _stacktrace_filename_line()) + t.ok(folks_b(me_ctx).$[alice_pubkey_base64].lastSeen! <= Date.now(), _stacktrace_filename_line()) + t.deepEqual(folks_b(me_ctx).$[bob_pubkey_base64].pubKey, bob_pubkey, _stacktrace_filename_line()) + t.deepEqual(folks_b(me_ctx).$[bob_pubkey_base64].colors, getFolkColors(bob_pubkey), _stacktrace_filename_line()) + t.deepEqual(folks_b(me_ctx).$[bob_pubkey_base64].inSession, true, _stacktrace_filename_line()) + t.ok(folks_b(me_ctx).$[bob_pubkey_base64].lastSeen! <= Date.now(), _stacktrace_filename_line()) + t.deepEqual(folks_b(alice_ctx).$[alice_pubkey_base64].pubKey, alice_pubkey, _stacktrace_filename_line()) + t.deepEqual(folks_b(alice_ctx).$[alice_pubkey_base64].colors, getFolkColors(alice_pubkey), _stacktrace_filename_line()) + t.deepEqual(folks_b(alice_ctx).$[bob_pubkey_base64].pubKey, bob_pubkey, _stacktrace_filename_line()) + t.deepEqual(folks_b(alice_ctx).$[bob_pubkey_base64].colors, getFolkColors(bob_pubkey), _stacktrace_filename_line()) + t.deepEqual(folks_b(bob_ctx).$[alice_pubkey_base64].pubKey, alice_pubkey, _stacktrace_filename_line()) + t.deepEqual(folks_b(bob_ctx).$[alice_pubkey_base64].colors, getFolkColors(alice_pubkey), _stacktrace_filename_line()) + t.deepEqual(folks_b(bob_ctx).$[bob_pubkey_base64].pubKey, bob_pubkey, _stacktrace_filename_line()) + t.deepEqual(folks_b(bob_ctx).$[bob_pubkey_base64].colors, getFolkColors(bob_pubkey), _stacktrace_filename_line()) + + t.equal(alice_FolkLore_stack[0].signal_name, 'FolkLore', _stacktrace_filename_line()) + t.equal(bob_FolkLore_stack[0].signal_name, 'FolkLore', _stacktrace_filename_line()) + run(()=>{ + const alice_signal = JSON.parse(alice_FolkLore_stack[0].signal_payload) + t.deepEqual(alice_signal.participants[alice_pubkey_base64].pubKey, alice_pubkey_base64, _stacktrace_filename_line()) + t.deepEqual(alice_signal.participants[bob_pubkey_base64].pubKey, bob_pubkey_base64, _stacktrace_filename_line()) + }) + run(()=>{ + const bob_signal = JSON.parse(bob_FolkLore_stack[0].signal_payload) + t.deepEqual(bob_signal.participants[alice_pubkey_base64].pubKey, alice_pubkey_base64, _stacktrace_filename_line()) + t.deepEqual(bob_signal.participants[bob_pubkey_base64].pubKey, bob_pubkey_base64, _stacktrace_filename_line()) + }) + + let my_deltas:Delta[] = [{ type: 'Add', value: [0, 'Whoops!\n'] }, { type: 'Title', value: 'Alice in Wonderland' }] + deltas = jsonDeltas ? my_deltas.map(d=>JSON.stringify(d)) : deltas + let bob_Change_stack:Signal[] + // I send a change, and alice and bob should receive it. + me_index = _index(me_ctx) + ;[[alice_Change_stack, bob_Change_stack]] = await waitfor_filtered_signals_change(async ()=> + request_change_b(me_ctx)(my_deltas), + [ + [[alice_signals, bob_signals], $signals=>filter_signal_name($signals, 'Change')] + ] + ) + t.equal(alice_Change_stack[0].signal_name, 'Change', _stacktrace_filename_line()) + t.equal(bob_Change_stack[0].signal_name, 'Change', _stacktrace_filename_line()) + t.deepEqual(alice_Change_stack[0].signal_payload[0], me_index, _stacktrace_filename_line()) // delta_matches + t.deepEqual(alice_Change_stack[0].signal_payload[1], deltas, _stacktrace_filename_line()) // delta_matches + t.deepEqual(bob_Change_stack[0].signal_payload[0], me_index, _stacktrace_filename_line()) // delta_matches + t.deepEqual(bob_Change_stack[0].signal_payload[1], deltas, _stacktrace_filename_line()) // delta_matches + + let me_Heartbeat:Signal[] + ;[[me_Heartbeat]] = await waitfor_filtered_signals_change(async ()=> + rpc_send_heartbeat_b(alice_ctx)({ + scribe: me_pubkey, + data: 'Hello' + }), + [[[me_signals], $signals=>filter_signal_name($signals, 'Heartbeat')]] + ) + let me_sig = me_Heartbeat[0] + t.equal(me_sig.signal_name, 'Heartbeat', _stacktrace_filename_line()) + t.deepEqual(me_sig.signal_payload[1], 'Hello', _stacktrace_filename_line()) + t.deepEqual(me_sig.signal_payload[0], alice_pubkey, _stacktrace_filename_line()) + + // alice asks for a sync request + let me_SyncReq:Signal[] + ;[[me_SyncReq]] = await waitfor_filtered_signals_change(async ()=> + rpc_send_sync_request_b(alice_ctx)(me_pubkey), + [[[me_signals], $signals=>filter_signal_name($signals, 'SyncReq')]] + ) + t.equal(me_SyncReq[0].signal_name, 'SyncReq', _stacktrace_filename_line()) + + // confirm that all agents got added to the folks anchor + // TODO figure out why init doesn't happen immediately. + await waitfor(async ()=>{ + const $folks = await rpc_get_folks_b(me_ctx)() + return isDeepStrictEqual($folks, [me_pubkey, alice_pubkey, bob_pubkey]) + }, + 500) + /**/ + t.pass(_stacktrace_filename_line()) + } finally { + console.debug('finally|leave_session') + await Promise.all([ + leave_session({ ctx: me_ctx }), + leave_session({ ctx: alice_ctx }), + leave_session({ ctx: bob_ctx }), + ]) } - let commit_header_hash = await me.call('syn', 'commit', commit) - t.equal(commit_header_hash.length, 39) // is a hash - - // add a second content change - pendingDeltas = [ - {type:"Delete", value:[0,3]}, - {type:"Add", value:[0,"baz"]}, - {type:"Add", value:[11," new"]}, // 'baz content new' - {type:"Delete", value:[4,11]}, // 'baz new' - {type:"Add", value:[4,"monkey"]}, // 'baz monkey new' - ] - new_content = applyDeltas(new_content, pendingDeltas) - const new_content_hash_2 = await me.call('syn', 'hash_content', new_content) - - deltas = jsonDeltas ? pendingDeltas.map(d=>JSON.stringify(d)) : pendingDeltas; - commit = { - snapshot: sessionInfo.content_hash, - change: { - deltas, - content_hash: new_content_hash_2, - previous_change: new_content_hash_1, // this is the second change so previous commit's hash - meta: { - contributors: [], - witnesses: [], - app_specific: null - } - }, - participants: [] + function filter_signal_name($signals:Signal[], signal_name:string) { + return $signals.filter(s=>s.signal_name === signal_name) } - commit_header_hash = await me.call('syn', 'commit', commit) - // clear the pendingDeltas - pendingDeltas = [] - - // alice joins session - const aliceSessionInfo = await alice.call('syn', 'get_session', sessionHash) - // alice should get my session - t.deepEqual(aliceSessionInfo.session, sessionHash) - t.deepEqual(aliceSessionInfo.scribe, me_pubkey) - t.deepEqual(aliceSessionInfo.snapshot_content, {title:'', body:''}) - await alice.call('syn', 'send_sync_request', {scribe: me_pubkey }) - - // check that deltas and snapshot content returned add up to the current real content - await delay(500) // make time for integrating new data - const receivedDeltas = jsonDeltas ? aliceSessionInfo.deltas.map(d=>JSON.parse(d)) : aliceSessionInfo.deltas; - t.deepEqual( - applyDeltas(aliceSessionInfo.snapshot_content, receivedDeltas), - {title: "foo title", body: "baz monkey new"} // content after two commits - ) - - // confirm that the session_info's content hash matches the content_hash - // generated by applying deltas - hash = await alice.call('syn', 'hash_content', aliceSessionInfo.snapshot_content) - t.deepEqual(aliceSessionInfo.content_hash, hash) - - // I should receive alice's request for the state as she joins the session - t.deepEqual(me_signals[0], {signal_name: "SyncReq", signal_payload: alice_pubkey}) - - // I add some pending deltas which I will then need to send to Alice as part of her Joining. - pendingDeltas = [{type:"Title",value: "I haven't committed yet"},{type:"Add", value:[14,"\nBut made a new line! 🍑"]}] - - deltas = jsonDeltas ? pendingDeltas.map(d=>JSON.stringify(d)) : pendingDeltas; - - const state = { - snapshot: sessionInfo.content_hash, - commit: commit_header_hash, - commit_content_hash: new_content_hash_2, - deltas: deltas, + function _index(ctx) { + return committed_changes_b(ctx).$.length + recorded_changes_b(ctx).$.length + } + async function waitfor_filtered_signals_change( + fn:()=>Promise, + script_a1:[Readable$[], ($signals:Signal[])=>Signal[]][], + timeout = 1000, + err = new Error() + ) { + const filtered_signals_a2 = script_a1.map(script=>{ + const [signals_a1, _filtered_signals] = script + return signals_a1.map(signals=>_filtered_signals(signals.$)) + }) + await fn() + return await Promise.all( + script_a1.map(async (script, script_idx)=>{ + try { + const [signals_a1, _filtered_signals] = script + await Promise.all(signals_a1.map((signals, idx)=> + subscribe_wait_timeout(signals, + $signals=>{ + return _filtered_signals($signals).length > filtered_signals_a2[script_idx][idx].length + }, timeout) + )) + return signals_a1.map(signals=>_filtered_signals(signals.$).reverse()) + } catch (e) { + err.message = e.message + throw err + } + }) + ) } - await me.call('syn', 'send_sync_response', { - participant: alice_pubkey, - state, - }) - - // Alice should have recieved uncommitted deltas - await delay(500) // make time for signal to arrive - t.equal(alice_signals[0].signal_name, "SyncResp") - let receivedState = alice_signals[0].signal_payload; - t.deepEqual(receivedState, state) // deltas, commit, and snapshot match - - // bob joins session - const bobSessionInfo = await alice.call('syn', 'get_session', sessionHash) - // bob should get my session - t.deepEqual(bobSessionInfo.scribe, me_pubkey) - await bob.call('syn', 'send_sync_request', {scribe: me_pubkey }) - - // alice sends me a change req and I should receive it - const alice_delta: Delta = {type: "Title", value: "Alice in Wonderland"} - let delta = jsonDeltas ? JSON.stringify(alice_delta) : alice_delta; - await alice.call('syn', 'send_change_request', { - scribe: aliceSessionInfo.scribe, - change: [1, [delta]]}) - await delay(500) // make time for signal to arrive - const sig = me_signals[2] - t.equal(sig.signal_name, "ChangeReq") - const [sig_index, sig_delta] = sig.signal_payload - t.equal(sig_index,1) - const receiveDelta = jsonDeltas ? JSON.parse(sig_delta) : sig_delta; - t.deepEqual(receiveDelta, alice_delta) // delta_matches - - - let my_deltas = [{type: "Add", value:[0, "Whoops!\n"]},{type: "Title", value: "Alice in Wonderland"}]; - deltas = jsonDeltas ? my_deltas.map(d=>JSON.stringify(d)) : deltas; - // I send a change, and alice and bob should receive it. - await me.call('syn', 'send_change', { - participants: [alice_pubkey, bob_pubkey], - change: [2, deltas]}) - await delay(500) // make time for signal to arrive - let a_sig = alice_signals[1] - let b_sig = bob_signals[0] - t.equal(a_sig.signal_name, "Change") - t.equal(b_sig.signal_name, "Change") - t.deepEqual(a_sig.signal_payload, [2, deltas]) // delta_matches - t.deepEqual(b_sig.signal_payload, [2, deltas]) // delta_matches - - - await alice.call('syn', 'send_heartbeat', { - scribe: me_pubkey, - data: "Hello"}) - await delay(500) // make time for signal to arrive - let me_sig = me_signals[3] - t.equal(me_sig.signal_name, "Heartbeat") - t.deepEqual(me_sig.signal_payload[1], "Hello") - t.deepEqual(me_sig.signal_payload[0], alice_pubkey) - - await me.call('syn', 'send_folk_lore', { - participants: [alice_pubkey, bob_pubkey], - data: "Alice said hello"}) - await delay(500) // make time for signal to arrive - - a_sig = alice_signals[2] - b_sig = bob_signals[1] - t.equal(a_sig.signal_name, "FolkLore") - t.equal(b_sig.signal_name, "FolkLore") - t.deepEqual(a_sig.signal_payload, "Alice said hello") - t.deepEqual(b_sig.signal_payload, "Alice said hello") - - // alice asks for a sync request - await alice.call('syn', 'send_sync_request', { - scribe: me_pubkey}) - await delay(500) // make time for signal to arrive - me_sig = me_signals[4] - t.equal(me_sig.signal_name, "SyncReq") - - // confirm that all agents got added to the folks anchor - // TODO figure out why init doesn't happen immediately. - let folks = await me.call('syn', 'get_folks') - t.equal(folks.length, 3) - }) } - -/* - Fake UI functions - - applyDeltas - - takes a content and a list of deltas - - returns the new content with those deltas applied -*/ - -const applyDeltas = (content, deltas) => { - for (const delta of deltas) { - switch(delta.type) { - case "Title": - content.title = delta.value - break - case "Add": - const [loc, text] = delta.value - content.body = content.body.slice(0, loc) + text + content.body.slice(loc) - break - case "Delete": - const [start, end] = delta.value - content.body = content.body.slice(0, start) + content.body.slice(end) - break - } - } - return content -} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..f47b4e93 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Svelte", + "compileOnSave": true, + "compilerOptions": { + "experimentalDecorators": true, + "preserveSymlinks": false, + "esModuleInterop": true, + "noEmitOnError": true, + "noErrorTruncation": true, + "module": "esnext", + "moduleResolution": "node", + "target": "es2017", + "isolatedModules": true, + "sourceMap": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "importsNotUsedAsValues": "error", + "types": [ + "node", + "svelte" + ], + "noImplicitAny": false, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "typeRoots": [ + "./node_modules/@types" + ], + "lib": [ + "ES2019", + "ES6", + "dom" + ] + }, + "exclude": [ + "node_modules/**", + "**/node_modules/**", + "**/dist/**" + ], + "references": [] +} diff --git a/ui/apps/app/.gitignore b/ui/apps/app/.gitignore new file mode 100644 index 00000000..c6f271f7 --- /dev/null +++ b/ui/apps/app/.gitignore @@ -0,0 +1 @@ +public/build diff --git a/ui/apps/app/.vscode/extensions.json b/ui/apps/app/.vscode/extensions.json new file mode 100644 index 00000000..bdef8201 --- /dev/null +++ b/ui/apps/app/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["svelte.svelte-vscode"] +} diff --git a/ui/apps/app/README.md b/ui/apps/app/README.md new file mode 100644 index 00000000..7c15b594 --- /dev/null +++ b/ui/apps/app/README.md @@ -0,0 +1 @@ +# Holochain Syn App diff --git a/ui/apps/app/package.json b/ui/apps/app/package.json new file mode 100644 index 00000000..117d4748 --- /dev/null +++ b/ui/apps/app/package.json @@ -0,0 +1,39 @@ +{ + "name": "@syn-ui/app", + "version": "1.0.0", + "scripts": { + "build": "rollup -c", + "dev": "rollup -c -w", + "start": "sirv public", + "validate": "svelte-check" + }, + "dependencies": { + "@ctx-core/combinators": "^6.0.10", + "@holochain/conductor-api": "0.0.4", + "sirv-cli": "^1.0.11", + "svelte-fa": "^2.2.0" + }, + "devDependencies": { + "@ctx-core/object": "^17.5.24", + "@ctx-core/store": "^24.7.17", + "@rollup/plugin-commonjs": "^18.1.0", + "@rollup/plugin-node-resolve": "^13.0.0", + "@rollup/plugin-replace": "^2.4.2", + "@rollup/plugin-typescript": "^8.2.1", + "@syn-ui/model": "workspace:^1.0.0", + "@syn-ui/zome-client": "workspace:^1.0.0", + "@tsconfig/svelte": "^1.0.10", + "buffer": "^6.0.3", + "rollup": "^2.47.0", + "rollup-plugin-css-only": "^3.1.0", + "rollup-plugin-livereload": "^2.0.0", + "rollup-plugin-node-polyfills": "^0.2.1", + "rollup-plugin-svelte": "^7.1.0", + "rollup-plugin-terser": "^7.0.2", + "svelte": "^3.38.2", + "svelte-check": "^1.5.2", + "svelte-preprocess": "^4.7.3", + "tslib": "^2.2.0", + "typescript": "^4.2.4" + } +} \ No newline at end of file diff --git a/ui/public/favicon.png b/ui/apps/app/public/favicon.png similarity index 100% rename from ui/public/favicon.png rename to ui/apps/app/public/favicon.png diff --git a/ui/public/global.css b/ui/apps/app/public/global.css similarity index 100% rename from ui/public/global.css rename to ui/apps/app/public/global.css diff --git a/ui/public/index.html b/ui/apps/app/public/index.html similarity index 75% rename from ui/public/index.html rename to ui/apps/app/public/index.html index 5da7ed3e..f7a58d68 100644 --- a/ui/public/index.html +++ b/ui/apps/app/public/index.html @@ -8,9 +8,9 @@ - + - + diff --git a/ui/apps/app/rollup.config.js b/ui/apps/app/rollup.config.js new file mode 100644 index 00000000..403620e3 --- /dev/null +++ b/ui/apps/app/rollup.config.js @@ -0,0 +1,98 @@ +import svelte from 'rollup-plugin-svelte' +import commonjs from '@rollup/plugin-commonjs' +import replace from '@rollup/plugin-replace' +import resolve from '@rollup/plugin-node-resolve' +import livereload from 'rollup-plugin-livereload' +import { terser } from 'rollup-plugin-terser' +import css from 'rollup-plugin-css-only' +import polyfills from 'rollup-plugin-node-polyfills' +import typescript from '@rollup/plugin-typescript' +import preprocess from 'svelte-preprocess' + +const production = !process.env.ROLLUP_WATCH + +function serve() { + let server + + function toExit() { + if (server) server.kill(0) + } + + return { + writeBundle() { + if (server) return + server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { + stdio: ['ignore', 'inherit', 'inherit'], + shell: true + }) + + process.on('SIGTERM', toExit) + process.on('exit', toExit) + } + } +} + +export default { + input: 'src/main.ts', + output: { + sourcemap: true, + format: 'iife', + name: 'app', + dir: 'public/build', + intro: 'const global = window;' + }, + plugins: [ + replace({ + preventAssignment: true, + 'process.env.NODE_ENV': JSON.stringify(process.env.NODE), + }), + svelte({ + compilerOptions: { + // enable run-time checks when not in production + dev: !production + }, + preprocess: preprocess({ + sourcemap: !production, + defaults: { + script: 'typescript', + style: 'scss' + } + }) + }), + // we'll extract any component CSS out into + // a separate file - better for performance + css({ output: 'main.css' }), + polyfills(), + + // If you have external dependencies installed from + // npm, you'll most likely need these plugins. In + // some cases you'll need additional configuration - + // consult the documentation for details: + // https://github.com/rollup/plugins/tree/master/packages/commonjs + resolve({ + browser: true, + dedupe: ['svelte'] + }), + typescript(), + commonjs(), + typescript({ + sourceMap: !production, + inlineSources: !production + }), + + // In dev mode, call `npm run start` once + // the bundle has been generated + !production && serve(), + + // Watch the `public` directory and refresh the + // browser on changes when not in production + !production && livereload('public'), + + // If we're building for production (npm run build + // instead of npm run dev), minify + production && terser() + ], + watch: { + clearScreen: false + } +} diff --git a/ui/apps/app/src/App.svelte b/ui/apps/app/src/App.svelte new file mode 100644 index 00000000..fd77ee76 --- /dev/null +++ b/ui/apps/app/src/App.svelte @@ -0,0 +1,309 @@ + + + + + + +
+

SynText

+
+ request_change(event.detail)}/> + </div> +</div> +<main> + <div class:noscribe> + <Editor on:request_change={(event) => request_change(event.detail)}/> + </div> + + + <Syn undoFn={undo} bind:this={syn}/> +</main> + +<div class='folks-tray'> + <Folks/> +</div> + +<div class='tab' class:shown={tabShown} class:drawer-hidden={drawerHidden} on:mouseenter={showTab} + on:mouseleave={hideTab}> + <div class='tab-inner' class:shown={tabShown} on:click={drawerHidden ? showDrawer() : hideDrawer()}> + <i class:drawer-hidden={drawerHidden} + class="tab-icon fas {drawerHidden ? 'fa-chevron-up' : 'fa-chevron-down'}"></i> + </div> +</div> +<div class='debug-drawer' bind:this={resizeable} use:initResizeable on:mouseenter={showTab} on:mouseleave={hideTab} + class:hidden={drawerHidden}> + <div class='handle' bind:this={resizeHandle} on:mousedown={startDragging}></div> + <div class='debug-content'> + <History changeToTextFn={changeToText}/> + <Debug/> + </div> +</div> + +<style global> + main { + padding: 1em; + background: hsla(100, 20%, 50%, .2); + grid-column: 1 / 2; + } + + .toolbar { + background: hsla(19, 20%, 50%, .2); + padding: 2rem; + grid-column: 1 / 2; + } + + .folks-tray { + min-width: calc((var(--folks-padding) * 2) + var(--folk-hex-width)); + width: auto; + background: hsla(255, 20%, 50%, .2); + grid-column: 2 / 3; + grid-row: 1/4; + } + + :global(:root) { + --resizeable-height: 200px; + --tab-width: 60px; + } + + .debug-drawer { + width: 100%; + box-sizing: border-box; + height: var(--resizeable-height); + min-height: var(--min-height); + max-height: var(--max-height); + background: hsla(180, 30%, 85%, 1); + position: absolute; + bottom: 0; + text-align: left; + grid-column: 1 / 2; + overflow: hidden; + z-index: 90; + } + + .hidden { + height: 0; + min-height: 0; + } + + .handle { + height: 1px; + width: 100%; + background-color: hsla(180, 15%, 65%, 1); + z-index: 100; + } + + .handle::after { + content: ''; + height: 9px; + position: absolute; + left: 0; + right: 0; + margin-bottom: -4px; + background-color: transparent; + cursor: ns-resize; + z-index: 101; + } + + /* tab styling reverse-engineered from Atom */ + .tab { + z-index: 130; + position: absolute; + width: var(--tab-width); + height: calc(var(--tab-width) / 2); + left: calc(50% - (var(--tab-width) / 2)); + bottom: var(--resizeable-height); + overflow: hidden; + margin-bottom: -2px; + border-top-left-radius: calc(var(--tab-width) / 2); + border-top-right-radius: calc(var(--tab-width) / 2); + + pointer-events: none; + } + + .tab-inner { + position: absolute; + box-sizing: border-box; /* borders included in size */ + width: var(--tab-width); + height: var(--tab-width); + background: hsla(180, 30%, 85%); + border: 1px solid hsla(180, 20%, 65%, 1); + color: hsla(180, 20%, 50%, 1); /* color of chevron */ + border-radius: calc(var(--tab-width) / 2); + cursor: pointer; + text-align: center; + + top: calc(var(--tab-width) / 2); + transition: transform 0.2s ease-out 0.2s; + } + + .tab.shown { + pointer-events: all; + } + + .tab-inner.shown { + transform: translateY(-50%); + transition: transform 0.2s ease-out 0s; + } + + /* allow the tab to pop up when drawer is hidden */ + .tab.drawer-hidden { + bottom: 0; + pointer-events: all; + } + + .tab-icon { + margin-top: 9px; + } + + .debug-content { + padding: 1rem; + word-wrap: break-word; + height: 100%; + overflow-y: scroll; + box-sizing: border-box; + z-index: 90; + } + + body { + font-family: system-ui, sans-serif; + } + + h1 { + color: #ff3e00; + text-transform: uppercase; + font-size: 4em; + font-weight: 100; + margin: auto; + } + + @media (min-width: 640px) { + main { + max-width: none; + } + } +</style> +u diff --git a/ui/apps/app/src/Debug.svelte b/ui/apps/app/src/Debug.svelte new file mode 100644 index 00000000..55bc16dd --- /dev/null +++ b/ui/apps/app/src/Debug.svelte @@ -0,0 +1,30 @@ +<script lang="ts"> + import { getContext } from 'svelte' + import { content_hash_str_b, next_index_b, session_info_scribe_str_b, session_info_b } from '@syn-ui/model' + import { dna_b } from '@syn-ui/zome-client' + const ctx = getContext('ctx') + const dna = dna_b(ctx) + const session_info_scribe_str = session_info_scribe_str_b(ctx) + const next_index = next_index_b(ctx) + const session_info = session_info_b(ctx) + const content_hash_str = content_hash_str_b(ctx) +</script> +<style> +ul { + margin-bottom: 1rem; +} +</style> +<div> + <ul> + <li> + {#if $dna} + Connected to Dna: {$dna} + {:else} + No connection + {/if} + <li>lastCommitedContentHash: {$content_hash_str ? $content_hash_str : ''} + <li>session: {JSON.stringify($session_info)} + <li>next_index: {$next_index} + <li>scribe: {$session_info_scribe_str} + </ul> +</div> diff --git a/ui/apps/app/src/Editor.svelte b/ui/apps/app/src/Editor.svelte new file mode 100644 index 00000000..9b00bca5 --- /dev/null +++ b/ui/apps/app/src/Editor.svelte @@ -0,0 +1,117 @@ +<script lang="ts"> + import { createEventDispatcher, getContext } from 'svelte' + import { my_tag_b } from '@syn-ui/zome-client' + import type { Delta } from '@syn-ui/zome-client' + import { content_b, CSSifyHSL, my_colors_b, session_info_b } from '@syn-ui/model' + + const ctx = getContext('ctx') + const dispatch = createEventDispatcher() + const content = content_b(ctx) + const session_info = session_info_b(ctx) + const my_tag = my_tag_b(ctx) + const my_colors = my_colors_b(ctx) + + function getLoc(tag) { + return $content.meta ? ($content.meta[tag] ? $content.meta[tag] : 0) : 0 + } + + let editor:HTMLElement + let editor_content1:string, editor_content2:string + $: editor_content1 = $content?.body?.slice(0, getLoc($my_tag)) || '' + $: editor_content2 = $content?.body?.slice(getLoc($my_tag)) || '' + + function addText(text) { + const loc = getLoc($my_tag) + const deltas:Delta[] = [{ type: 'Add', value: [loc, text] }] + for (const [tag, tagLoc] of Object.entries($content.meta)) { + if (tagLoc >= loc) { + deltas.push({ type: 'Meta', value: { setLoc: [tag, tagLoc + text.length] } }) + } + } + dispatch('request_change', deltas) + } + + function handleInput(event) { + const loc = getLoc($my_tag) + const key = event.key + if (key.length == 1) { + addText(key) + } else { + switch (key) { + case 'ArrowRight': + if (loc < $content.body.length) { + dispatch('request_change', [ + { type: 'Meta', value: { setLoc: [$my_tag, loc + 1] } } + ]) + } + break + case 'ArrowLeft': + if (loc > 0) { + dispatch('request_change', [ + { type: 'Meta', value: { setLoc: [$my_tag, loc - 1] } } + ]) + } + break + case 'Enter': + addText('\n') + break + case 'Backspace': + if (loc > 0) { + const deltas:Delta[] = [{ type: 'Delete', value: [loc - 1, loc] }] + for (const [tag, tagLoc] of Object.entries($content.meta)) { + if (tagLoc >= loc) { + deltas.push({ type: 'Meta', value: { setLoc: [tag, tagLoc - 1] } }) + } + } + dispatch('request_change', deltas) + } + } + } + console.log('input', event.key) + } + function handleClick(e) { + const offset = window.getSelection().focusOffset + let loc = offset > 0 ? offset : 0 + if (window.getSelection().focusNode.parentElement == editor.lastChild) { + loc += editor_content1.length + } + if (loc != getLoc($my_tag)) { + dispatch('request_change', [ + { type: 'Meta', value: { setLoc: [$my_tag, loc] } } + ]) + } + } + + let cursor + $: { + // wait for cursor and connection and color inside connection to exist + // before updating the cursor color + if ($my_colors) { + cursor.style['border-color'] = CSSifyHSL($my_colors.primary) + } + } + +</script> +<style> + editor { + width: auto; + min-height: 10em; + border: 1px solid black; + background-color: hsla(0, 0%, 100%, .6); + font-family: Arial; + display: block; + white-space: pre-wrap; + margin: 1em 0 .4em 0; + padding: 4px; + } + .cursor { + display: inline; + border-left: solid 2px; /* Should be the Folk's main color */ + margin-right: -2px; + z-index: 10; + position: relative; + } +</style> +<editor on:click={handleClick} on:keydown={handleInput} tabindex=0 start=0 bind:this={editor}> + <span>{editor_content1}</span><span class='cursor' bind:this={cursor}></span><span>{editor_content2}</span> +</editor> diff --git a/ui/apps/app/src/Editor_I.ts b/ui/apps/app/src/Editor_I.ts new file mode 100644 index 00000000..6e4220b6 --- /dev/null +++ b/ui/apps/app/src/Editor_I.ts @@ -0,0 +1,5 @@ +import type { SvelteComponentTyped } from 'svelte' +import type { request_change_T } from './request_change_T' +export interface Editor_I extends SvelteComponentTyped<{}, { + request_change: request_change_T +}> {} diff --git a/ui/src/Folk.svelte b/ui/apps/app/src/Folk.svelte similarity index 61% rename from ui/src/Folk.svelte rename to ui/apps/app/src/Folk.svelte index 89aeda01..d41038b4 100644 --- a/ui/src/Folk.svelte +++ b/ui/apps/app/src/Folk.svelte @@ -1,24 +1,30 @@ -<script> - import { scribeStr, folks, connection } from './stores.js' - import { CSSifyHSL } from './colors.js' +<script lang="ts"> + import { getContext } from 'svelte' + import { CSSifyHSL, folks_b, my_colors_b, session_info_scribe_str_b, session_info_b } from '@syn-ui/model' + const ctx = getContext('ctx') + const folks = folks_b(ctx) + const session_info_scribe_str = session_info_scribe_str_b(ctx) + const my_colors = my_colors_b(ctx) + const session_info = session_info_b(ctx) export let pubKeyStr = '' - export let pubKey export let me = false - $: scribe = pubKeyStr == $scribeStr + let scribe + $: scribe = pubKeyStr == $session_info_scribe_str - $: outOfSession = (!$folks[pubKeyStr] || !$folks[pubKeyStr].inSession) && !me + let outOfSession + $: outOfSession = $folks?.[pubKeyStr]?.inSession && !me function setUpHex(hexEl) { - let colors - if (me) { - colors = $connection.syn.myColors - } else { - colors = $folks[pubKeyStr].colors - } - hexEl.style['background-color'] = CSSifyHSL(colors.primary) - // hex element's first child is its picture/hexagonColor div - hexEl.firstChild.style['background-color'] = CSSifyHSL(colors.hexagon) + let colors + if (me) { + colors = $my_colors + } else { + colors = $folks[pubKeyStr].colors + } + hexEl.style['background-color'] = CSSifyHSL(colors.primary) + // hex element's first child is its picture/hexagonColor div + hexEl.firstChild.style['background-color'] = CSSifyHSL(colors.hexagon) } </script> @@ -59,7 +65,7 @@ width: var(--folk-hex-width); height: var(--folk-hex-height); /* https://www.desmos.com/calculator/bgt97otugr */ - clip-path: polygon(25%0%,75%0%,100%50%,75%100%,25%100%,12.5% 75%,calc(12.5% + 1.732px) calc(75% - 1px),calc(25% + 1.15px) calc(100% - 2px),50% calc(100% - 2px),50% 100%,75% 100%,87.5% 75%,calc(87.5% - 1.732px) calc(75% - 1px),calc(100% - 2.31px) 50%,calc(87.5% - 1.732px) calc(25% + 1px),87.5% 25%,75%0%,50%0%,50% calc(0% + 2px),calc(25% + 1.15px) calc(0% + 2px),calc(12.5% + 1.732px) calc(25% + 1px),12.5% 25%); + clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 12.5% 75%, calc(12.5% + 1.732px) calc(75% - 1px), calc(25% + 1.15px) calc(100% - 2px), 50% calc(100% - 2px), 50% 100%, 75% 100%, 87.5% 75%, calc(87.5% - 1.732px) calc(75% - 1px), calc(100% - 2.31px) 50%, calc(87.5% - 1.732px) calc(25% + 1px), 87.5% 25%, 75% 0%, 50% 0%, 50% calc(0% + 2px), calc(25% + 1.15px) calc(0% + 2px), calc(12.5% + 1.732px) calc(25% + 1px), 12.5% 25%); background-color: hsl(0, 0%, 10%); position: absolute; } @@ -79,7 +85,7 @@ } </style> -{#if $connection && $connection.syn} +{#if $session_info} {#if scribe} <div class='scribe-wrapper'> <div use:setUpHex class='folk scribe' class:me class:out-of-session={outOfSession}> diff --git a/ui/apps/app/src/Folks.svelte b/ui/apps/app/src/Folks.svelte new file mode 100644 index 00000000..abd1da80 --- /dev/null +++ b/ui/apps/app/src/Folks.svelte @@ -0,0 +1,31 @@ +<script lang="ts"> + import { getContext } from 'svelte' + import { folks_b } from '@syn-ui/model' + import { me_b } from '@syn-ui/zome-client' + import Folk from './Folk.svelte' + const ctx = getContext('ctx') + const me = me_b(ctx) + const folks = folks_b(ctx) + let folks_keys = [] + $: folks_keys = $folks ? Object.keys($folks) : [] +</script> +<style> + :global(:root) { + --folks-padding: .75em; + --folks-grid-gap: 1rem; + } + .folks { + display: grid; + grid-gap: var(--folks-grid-gap); + padding: var(--folks-grid-gap) var(--folks-padding) var(--folks-padding); + place-items: center; + } +</style> +<div class='folks'> + {#if $me} + <Folk me={true} pubKeyStr={$me}/> + {/if} + {#each folks_keys as p} + <Folk pubKeyStr={p}/> + {/each} +</div> diff --git a/ui/src/History.svelte b/ui/apps/app/src/History.svelte similarity index 50% rename from ui/src/History.svelte rename to ui/apps/app/src/History.svelte index 80e684f2..2272a031 100644 --- a/ui/src/History.svelte +++ b/ui/apps/app/src/History.svelte @@ -1,37 +1,41 @@ -<script> - import { requestedChanges, recordedChanges, committedChanges } from './stores.js' - import { afterUpdate } from 'svelte' +<script lang="ts"> + import { afterUpdate, getContext } from 'svelte' + import { committed_changes_b, recorded_changes_b, requested_changes_b } from '@syn-ui/model' import HistoryEntry from './HistoryEntry.svelte' - + const ctx = getContext('ctx') + const requested_changes = requested_changes_b(ctx) + const recorded_changes = recorded_changes_b(ctx) + const committed_changes = committed_changes_b(ctx) + export let changeToTextFn // returns a list of historyEntry objects with some text // and a status (for styling) function changesToEntriesList(changes, status) { - let entriesList = [] - for (let i=changes.length-1; i>=0; i--) { - const text = changeToTextFn(changes[i]) - entriesList.push({'text': text, 'status': status}) - } - return entriesList + let entriesList = [] + for (let i = changes.length - 1; i >= 0; i--) { + const text = changeToTextFn(changes[i]) + entriesList.push({ 'text': text, 'status': status }) + } + return entriesList } let requestedH let recordedH let committedH let historyEntries = [] - $: {requestedH = changesToEntriesList($requestedChanges, 'requested')} - $: {recordedH = changesToEntriesList($recordedChanges, 'recorded')} - $: {committedH = changesToEntriesList($committedChanges, 'committed')} + $: {requestedH = changesToEntriesList($requested_changes, 'requested')} + $: {recordedH = changesToEntriesList($recorded_changes, 'recorded')} + $: {committedH = changesToEntriesList($committed_changes, 'committed')} $: {historyEntries = [...requestedH, ...recordedH, ...committedH]} // when updating the list, also scroll to the newest historyEntry - afterUpdate(async () => { - let entryElem = document.getElementsByClassName('history-entries')[0] - if (entryElem.firstChild !== null) { - entryElem.firstChild.scrollIntoView(false) - } - }) + afterUpdate(async ()=>{ + let entryElem = document.getElementsByClassName('history-entries')[0] + if (entryElem.firstChild !== null) { + entryElem.firstChild.scrollIntoView(false) + } + }) </script> <style> diff --git a/ui/src/HistoryEntry.svelte b/ui/apps/app/src/HistoryEntry.svelte similarity index 96% rename from ui/src/HistoryEntry.svelte rename to ui/apps/app/src/HistoryEntry.svelte index eda1909b..1dc46d77 100644 --- a/ui/src/HistoryEntry.svelte +++ b/ui/apps/app/src/HistoryEntry.svelte @@ -1,4 +1,4 @@ -<script> +<script lang="ts"> export let text export let status </script> diff --git a/ui/apps/app/src/Syn.svelte b/ui/apps/app/src/Syn.svelte new file mode 100644 index 00000000..984efb02 --- /dev/null +++ b/ui/apps/app/src/Syn.svelte @@ -0,0 +1,84 @@ +<script lang="ts"> + import { createEventDispatcher, getContext } from 'svelte' + import { + commit_change_b, session_info_scribe_str_b, session_info_b, sessions_b, toggle_session + } from '@syn-ui/model' + import { bufferToBase64 } from '@syn-ui/utils' + const ctx = getContext('ctx') + const session_info = session_info_b(ctx) + // this is the list of sessions returned by the DNA + const sessions = sessions_b(ctx) + const session_info_scribe_str = session_info_scribe_str_b(ctx) + const commit_change = commit_change_b(ctx) + + // this properties are the app-defined functions to apply and undo changes + export let undoFn + + // ----------------------------------------------------------------------- + + const dispatch = createEventDispatcher() + + let adminPort = 1234 + let app_port = 8888 + let app_id = 'syn' + async function toggle() { + await toggle_session({ app_port, app_id, ctx }) + if (!$session_info) { + console.log('disconnected') + } + } + + $: noscribe = $session_info_scribe_str === '' +</script> +<style> + :global(.noscribe) { + pointer-events: none; + position: relative; + } + + :global(.noscribe:after) { + content: ' '; + z-index: 20; + display: block; + position: absolute; + height: 100%; + top: 0; + left: 0; + right: 0; + background: rgba(255, 255, 255, 0.7); + } + input { + width: 4em; + border-radius: 4px; + } + .session { + border-radius: 4px; + background-color: pink + } + button { + cursor: pointer; + } +</style> +<button class:noscribe on:click={evt=>commit_change()}>Commit</button> + +<div> + <h4>Holochain Connection:</h4> + App Port: <input bind:value={app_port}> + AppId: <input bind:value={app_id}> + <button on:click={toggle}> + {#if $session_info} + Disconnect + {:else} + Connect + {/if} + </button> +</div> + +<div class='sessions'> + Sessions: + {#each $sessions || [] as session} + <span class='session'> + Id: {bufferToBase64(session).slice(-4)} + </span> + {/each} +</div> diff --git a/ui/apps/app/src/Title.svelte b/ui/apps/app/src/Title.svelte new file mode 100644 index 00000000..7ff3a374 --- /dev/null +++ b/ui/apps/app/src/Title.svelte @@ -0,0 +1,109 @@ +<script lang="ts"> + import { createEventDispatcher, getContext, tick } from 'svelte' + import { content_b } from '@syn-ui/model' + import type { Delta } from '@syn-ui/zome-client' + const ctx = getContext('ctx') + const dispatch = createEventDispatcher() + const content = content_b(ctx) + + let titleBeingTyped = '' + + let editingTitle = false + function saveTitle() { + if (editingTitle) { + // only dispatch a changeReq if the title trying to be saved is different + // than the current title + if (titleBeingTyped !== $content.title) { + let delta:Delta = { type: 'Title', value: titleBeingTyped } + dispatch('request_change', [delta]) + } + titleBeingTyped = '' + editingTitle = false + } else { + console.log('Can\'t run saveTitle when it wasn\'t being edited!') + } + } + + let titleEl // variable to bind the title input to when it's created + async function beginEditTitle() { + titleHover = false + titleBeingTyped = $content.title // fill the field with the current title + editingTitle = true + await tick() // wait for the title input element to be created + titleEl.focus() + } + + function handleTitleKeypress() { + if (event.key == 'Enter') { + saveTitle() + } else if (event.key == 'Escape') { + // don't save new title & discard changes + titleBeingTyped = '' + // turn off editing + editingTitle = false + } + } + + // keep track of whether the doc is untitled + let untitled + $: untitled = ($content.title === '') + + let titleHover // whether the title is being hovered + + +</script> +<style> + .title-wrapper { + height: 1.6em; + } + + .title { + /* min-width: 10em; + min-height: 1em; */ + font-weight: bold; + display: inline-block; + padding: 0.4em; + -webkit-padding: 0.4em 0; + margin: 0 0 0.5em 0; + border-width: 1px; + border-color: #00000000; + border-style: solid; + border-radius: 2px; + } + + .title-hover { + border-style: dashed; + border-color: #aaa; + } + + /* input has to be below hover so it overrides */ + .title-input { + border-style: solid; + border-color: #ccc; + font-weight: bold; + margin-bottom: 0; + } + + .untitled { + color: lightgray; + } +</style> + +<div class='title-wrapper'> + Title: + {#if editingTitle} + <input class='title-input' bind:value={titleBeingTyped} on:keydown={handleTitleKeypress} on:blur={saveTitle} + bind:this={titleEl}/> + {:else} + <div class='title' class:title-hover={titleHover} on:mouseenter={()=>{titleHover=true}} + on:mouseleave={()=>{titleHover=false}} on:click={beginEditTitle}> + <span class:untitled> + {#if untitled} + Untitled Document + {:else} + {$content.title} + {/if} + </span> + </div> + {/if} +</div> diff --git a/ui/apps/app/src/Title_I.ts b/ui/apps/app/src/Title_I.ts new file mode 100644 index 00000000..9e4dbd3a --- /dev/null +++ b/ui/apps/app/src/Title_I.ts @@ -0,0 +1,5 @@ +import type { SvelteComponentTyped } from 'svelte' +import type { request_change_T } from './request_change_T' +export interface Title_I extends SvelteComponentTyped<{}, { + request_change: request_change_T +}> {} diff --git a/ui/apps/app/src/index.ts b/ui/apps/app/src/index.ts new file mode 100644 index 00000000..54b2bd36 --- /dev/null +++ b/ui/apps/app/src/index.ts @@ -0,0 +1,15 @@ +import 'svelte' +import App from './App.svelte' +import Debug from './Debug.svelte' +import Editor from './Editor.svelte' +import Folk from './Folk.svelte' +import Folks from './Folks.svelte' +import History from './History.svelte' +import HistoryEntry from './HistoryEntry.svelte' +import Syn from './Syn.svelte' +import Title from './Title.svelte' +export { + App, Debug, Editor, Folk, Folks, History, HistoryEntry, Syn, Title +} +export * from './Editor_I' +export * from './Title_I' diff --git a/ui/src/main.js b/ui/apps/app/src/main.ts similarity index 55% rename from ui/src/main.js rename to ui/apps/app/src/main.ts index 52b8ac4f..5c366e9d 100644 --- a/ui/src/main.js +++ b/ui/apps/app/src/main.ts @@ -1,9 +1,9 @@ +import 'svelte' import App from './App.svelte' const app = new App({ - target: document.body, - props: { - } -}); + target: document.body, + props: {} +}) export default app diff --git a/ui/apps/app/src/request_change_T.ts b/ui/apps/app/src/request_change_T.ts new file mode 100644 index 00000000..e20b4c94 --- /dev/null +++ b/ui/apps/app/src/request_change_T.ts @@ -0,0 +1,2 @@ +import type { Delta } from '@syn-ui/zome-client' +export type request_change_T = CustomEvent<Delta[]> diff --git a/ui/apps/app/tsconfig.json b/ui/apps/app/tsconfig.json new file mode 100644 index 00000000..b082e968 --- /dev/null +++ b/ui/apps/app/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + + "include": ["src/**/*"], + "exclude": ["node_modules/*", "__sapper__/*", "public/*"] +} \ No newline at end of file diff --git a/ui/apps/headless/README.md b/ui/apps/headless/README.md new file mode 100644 index 00000000..d87757fe --- /dev/null +++ b/ui/apps/headless/README.md @@ -0,0 +1,3 @@ +# Holochain Syn Headless App + +TODO: Implement diff --git a/ui/apps/headless/package.json b/ui/apps/headless/package.json new file mode 100644 index 00000000..485fd5ae --- /dev/null +++ b/ui/apps/headless/package.json @@ -0,0 +1,31 @@ +{ + "name": "@syn-ui/headless", + "version": "1.0.0", + "scripts": { + "build": "rollup -c", + "dev": "rollup -c -w", + "start": "sirv public" + }, + "dependencies": { + "@ctx-core/combinators": "^6.0.10", + "@holochain/conductor-api": "0.0.4", + "sirv-cli": "^1.0.11", + "svelte-fa": "^2.2.0" + }, + "devDependencies": { + "@ctx-core/object": "^17.5.24", + "@ctx-core/store": "^24.7.17", + "@rollup/plugin-commonjs": "^18.1.0", + "@rollup/plugin-node-resolve": "^13.0.0", + "@rollup/plugin-replace": "^2.4.2", + "@rollup/plugin-typescript": "^8.2.1", + "rollup": "^2.47.0", + "rollup-plugin-css-only": "^3.1.0", + "rollup-plugin-livereload": "^2.0.0", + "rollup-plugin-svelte": "^7.1.0", + "rollup-plugin-terser": "^7.0.2", + "svelte": "^3.38.2", + "svelte-preprocess": "^4.7.3", + "typescript": "^4.2.4" + } +} \ No newline at end of file diff --git a/ui/apps/headless/src/index.ts b/ui/apps/headless/src/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/ui/apps/headless/tsconfig.json b/ui/apps/headless/tsconfig.json new file mode 100644 index 00000000..4082f16a --- /dev/null +++ b/ui/apps/headless/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/ui/libs/model/.gitignore b/ui/libs/model/.gitignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/ui/libs/model/.gitignore @@ -0,0 +1 @@ +dist diff --git a/ui/libs/model/README.md b/ui/libs/model/README.md new file mode 100644 index 00000000..410269ba --- /dev/null +++ b/ui/libs/model/README.md @@ -0,0 +1 @@ +# @syn-ui/model diff --git a/ui/libs/model/package.json b/ui/libs/model/package.json new file mode 100644 index 00000000..85b9d37a --- /dev/null +++ b/ui/libs/model/package.json @@ -0,0 +1,18 @@ +{ + "name": "@syn-ui/model", + "version": "1.0.0", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./src/index.ts", + "scripts": {}, + "devDependencies": { + "@ctx-core/combinators": "^6.0.10", + "@ctx-core/object": "^17.5.24", + "@ctx-core/store": "^24.7.17", + "@holochain/conductor-api": "0.0.4", + "@syn-ui/utils": "workspace:^1.0.0", + "@syn-ui/zome-client": "workspace:^1.0.0", + "svelte": "^3.38.2", + "typescript": "^4.2.4" + } +} \ No newline at end of file diff --git a/ui/libs/model/src/actions/index.ts b/ui/libs/model/src/actions/index.ts new file mode 100644 index 00000000..b808c85d --- /dev/null +++ b/ui/libs/model/src/actions/index.ts @@ -0,0 +1,3 @@ +export * from './join_session' +export * from './leave_session' +export * from './toggle_session' diff --git a/ui/libs/model/src/actions/join_session.ts b/ui/libs/model/src/actions/join_session.ts new file mode 100644 index 00000000..09e160a0 --- /dev/null +++ b/ui/libs/model/src/actions/join_session.ts @@ -0,0 +1,46 @@ +import { + app_id_b, app_port_b, app_ws_b, rpc_get_session_b, rpc_new_session_b, rpc_send_sync_request_b, SessionInfo, +} from '@syn-ui/zome-client' +import { am_i_scribe_b, session_info_b, session_info_session_str_b, sessions_b, sessions_str_a1_b } from '../session' +import { request_checker_timer_b, scribe_heartbeat_timer_b } from '../timers' +import { app_ws_cb_b } from '../signals' +export async function join_session(params:join_session_params_T) { + const ctx = params.ctx || {} + const sessions = sessions_b(ctx) + const app_id = app_id_b(ctx) + app_id.$ = params.app_id + const app_port = app_port_b(ctx) + app_port.$ = params.app_port + const app_ws = app_ws_b(ctx) + await app_ws.load(app_ws_cb_b(ctx)) + const $sessions = await sessions.load() + sessions_str_a1_b(ctx) + session_info_session_str_b(ctx) + let $session_info:SessionInfo + const session_info = session_info_b(ctx) + if ($sessions.length === 0) { + const rpc_new_session = rpc_new_session_b(ctx) + $session_info = await rpc_new_session() + session_info.$ = $session_info + sessions.unshift($session_info.session) + } else { + const rpc_get_session = rpc_get_session_b(ctx) + $session_info = await rpc_get_session($sessions[0]) + session_info.$ = $session_info + const am_i_scribe = am_i_scribe_b(ctx) + if (am_i_scribe.$ === false) { + const rpc_send_sync_request = rpc_send_sync_request_b(ctx) + await rpc_send_sync_request($session_info.scribe) + } + } + const request_checker_timer = request_checker_timer_b(ctx) + request_checker_timer.start() + const scribe_heartbeat_timer = scribe_heartbeat_timer_b(ctx) + scribe_heartbeat_timer.start() + return ctx +} +export interface join_session_params_T { + app_port:number + app_id:string + ctx?:object +} diff --git a/ui/libs/model/src/actions/leave_session.ts b/ui/libs/model/src/actions/leave_session.ts new file mode 100644 index 00000000..12ed551c --- /dev/null +++ b/ui/libs/model/src/actions/leave_session.ts @@ -0,0 +1,18 @@ +import { session_info_b } from '../session' +import { request_checker_timer_b, scribe_heartbeat_timer_b } from '../timers' +import { _$content, content_b } from '../content' +export async function leave_session(params:leave_session_params_I) { + const ctx = params.ctx || {} + const session_info = session_info_b(ctx) + session_info.$ = undefined + const content = content_b(ctx) + content.$ = _$content() + const request_checker_timer = request_checker_timer_b(ctx) + request_checker_timer.stop() + const scribe_heartbeat_timer = scribe_heartbeat_timer_b(ctx) + scribe_heartbeat_timer.stop() + return ctx +} +export interface leave_session_params_I { + ctx?:object +} diff --git a/ui/libs/model/src/actions/toggle_session.ts b/ui/libs/model/src/actions/toggle_session.ts new file mode 100644 index 00000000..e35696e6 --- /dev/null +++ b/ui/libs/model/src/actions/toggle_session.ts @@ -0,0 +1,16 @@ +import { clone } from '@ctx-core/object' +import { session_info_b } from '../session' +import type { join_session_params_T } from './join_session' +import { leave_session } from './leave_session' +import { join_session } from './join_session' +export async function toggle_session(params:join_session_params_T) { + const ctx = params.ctx || {} + const session_info = session_info_b(ctx) + return ( + session_info.$ + ? leave_session({ ctx }) + : join_session( + clone<join_session_params_T>(params, { ctx }) + ) + ) +} diff --git a/ui/libs/model/src/colors/CSSifyHSL.ts b/ui/libs/model/src/colors/CSSifyHSL.ts new file mode 100644 index 00000000..1378fbd2 --- /dev/null +++ b/ui/libs/model/src/colors/CSSifyHSL.ts @@ -0,0 +1,5 @@ +import type { HSL } from '@syn-ui/zome-client' +export function CSSifyHSL(hslArray:HSL) { + const [h, s, l] = hslArray + return `hsl(${h} ${s}% ${l}%)` +} diff --git a/ui/libs/model/src/colors/arrayBufferToHex.ts b/ui/libs/model/src/colors/arrayBufferToHex.ts new file mode 100644 index 00000000..3aeb2693 --- /dev/null +++ b/ui/libs/model/src/colors/arrayBufferToHex.ts @@ -0,0 +1,9 @@ +import type { HoloHash } from '@holochain/conductor-api' +// returns binary input as hex number string (e.g. 'a293b8e1a') +export function arrayBufferToHex(buffer:HoloHash) { + let hexString = '' + buffer.forEach(byte=> + hexString += byte.toString(16) + ) + return hexString +} diff --git a/ui/libs/model/src/colors/clamp.ts b/ui/libs/model/src/colors/clamp.ts new file mode 100644 index 00000000..0b6a9f7b --- /dev/null +++ b/ui/libs/model/src/colors/clamp.ts @@ -0,0 +1,4 @@ +// Source: https://stackoverflow.com/questions/5842747 +export function clamp(value, min, max) { + return Math.min(Math.max(value, min), max) +} diff --git a/ui/libs/model/src/colors/getFolkColors.ts b/ui/libs/model/src/colors/getFolkColors.ts new file mode 100644 index 00000000..40a68a0d --- /dev/null +++ b/ui/libs/model/src/colors/getFolkColors.ts @@ -0,0 +1,35 @@ +import type { AgentPubKey } from '@holochain/conductor-api' +import type { FolkColors } from '@syn-ui/zome-client' +import { arrayBufferToHex } from './arrayBufferToHex' +import { rgbToHsl } from './rgbToHsl' +import { clamp } from './clamp' +// Generate an object of colors for a folk from their pubKey +// returns Object: +// primary: Color, // used for hex outline and norrmal cursor +// hexagon: Color, // used for hexagon picture placeholder +// selection: Color, // used for normal selection +// lookingSelection: Color, // used for selection when "looking at" +// lookingCursor: Color, // used for cursor when "looking at" +// where Color is array: [h, s, l] +// used in `use:setColor` on new Folk components +export function getFolkColors(pubKey:AgentPubKey):FolkColors { + // get a hex color from the folk's public key + const hexColor = '#' + arrayBufferToHex(pubKey).slice(-6) + // extract the RGB components from the hex color notation. + // Source: https://stackoverflow.com/questions/3732046 + const r = parseInt(hexColor.substr(1, 2), 16) // Grab the hex representation of red (chars 1-2) and convert to decimal (base 10). + const g = parseInt(hexColor.substr(3, 2), 16) + const b = parseInt(hexColor.substr(5, 2), 16) + // convert to HSL + let hsl = rgbToHsl(r, g, b) + // limit color to be bright enough and not too bright + hsl[1] = clamp(hsl[1], 10, 90) // limit s + const [h, s] = hsl // destructure + return { + primary: [h, s, 50], + hexagon: [h, s, 25], + selection: [h, s, 90], // placeholder values from here down + lookingSelection: [h, s, 80], + lookingCursor: [h, s + 10, 40], + } +} diff --git a/ui/libs/model/src/colors/index.ts b/ui/libs/model/src/colors/index.ts new file mode 100644 index 00000000..f60c3b2c --- /dev/null +++ b/ui/libs/model/src/colors/index.ts @@ -0,0 +1,6 @@ +export * from './arrayBufferToHex' +export * from './clamp' +export * from './CSSifyHSL' +export * from './getFolkColors' +export * from './my_colors_b' +export * from './rgbToHsl' diff --git a/ui/libs/model/src/colors/my_colors_b.ts b/ui/libs/model/src/colors/my_colors_b.ts new file mode 100644 index 00000000..8e1a1876 --- /dev/null +++ b/ui/libs/model/src/colors/my_colors_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { agent_pub_key_b } from '@syn-ui/zome-client' +import { getFolkColors } from './getFolkColors' +export const my_colors_b = _b('my_colors', (ctx)=>{ + const agent_pub_key = agent_pub_key_b(ctx) + return derived$(agent_pub_key, $agent_pub_key=> + $agent_pub_key ? getFolkColors($agent_pub_key) : null + ) +}) diff --git a/ui/libs/model/src/colors/rgbToHsl.ts b/ui/libs/model/src/colors/rgbToHsl.ts new file mode 100644 index 00000000..f4381e29 --- /dev/null +++ b/ui/libs/model/src/colors/rgbToHsl.ts @@ -0,0 +1,24 @@ +// converts RGB to HSL +// Source: https://gist.github.com/mjackson/5311256 +export function rgbToHsl(r:number, g:number, b:number) { + r /= 255, g /= 255, b /= 255 + let max = Math.max(r, g, b), min = Math.min(r, g, b) + let h, s, l = (max + min) / 2 + if (max == min) { h = s = 0} else { + let d = max - min + s = l > 0.5 ? d / (2 - max - min) : d / (max + min) + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0) + break + case g: + h = (b - r) / d + 2 + break + case b: + h = (r - g) / d + 4 + break + } + h /= 6 + } + return [h * 360, s * 100, l * 100] +} diff --git a/ui/libs/model/src/content/content_b.ts b/ui/libs/model/src/content/content_b.ts new file mode 100644 index 00000000..23e7ee9a --- /dev/null +++ b/ui/libs/model/src/content/content_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import { writable$ } from '@ctx-core/store' +import type { Content } from '@syn-ui/zome-client' +export const content_b = _b('content', ()=>{ + const content = writable$<Content>(_$content()) + return content +}) +export function _$content() { + return { title: '', body: '', meta: {} } +} diff --git a/ui/libs/model/src/content/index.ts b/ui/libs/model/src/content/index.ts new file mode 100644 index 00000000..82066eec --- /dev/null +++ b/ui/libs/model/src/content/index.ts @@ -0,0 +1 @@ +export * from './content_b' diff --git a/ui/libs/model/src/delta/ApplyDelta.ts b/ui/libs/model/src/delta/ApplyDelta.ts new file mode 100644 index 00000000..23d92f1e --- /dev/null +++ b/ui/libs/model/src/delta/ApplyDelta.ts @@ -0,0 +1,10 @@ +import type { Content, Delta } from '@syn-ui/zome-client' +export interface ApplyDelta { + delta:Delta + deleted?:ApplyDelta_deleted_T + id?:string + at?:number +} +export type ApplyDelta_deleted_T = string|[string, number] +export type apply_delta_ret_T = [Content, ApplyDelta] +export type apply_delta_fn_T = (content:Content, delta:Delta)=>apply_delta_ret_T diff --git a/ui/libs/model/src/delta/StateForSync.ts b/ui/libs/model/src/delta/StateForSync.ts new file mode 100644 index 00000000..a09fb200 --- /dev/null +++ b/ui/libs/model/src/delta/StateForSync.ts @@ -0,0 +1,14 @@ +import type { HoloHash } from '@holochain/conductor-api' +import type { DeltaValue } from '@syn-ui/zome-client' +export interface StateForSync { + snapshot:HoloHash, + commit:HoloHash, + commit_content_hash:HoloHash + deltas:DeltaValue[], +} +export interface SerializedStateForSync { + snapshot:HoloHash, + commit:HoloHash, + commit_content_hash:HoloHash + deltas:string[], +} diff --git a/ui/libs/model/src/delta/_scribe_signal_folk_pubKey_a1_b.ts b/ui/libs/model/src/delta/_scribe_signal_folk_pubKey_a1_b.ts new file mode 100644 index 00000000..ac510986 --- /dev/null +++ b/ui/libs/model/src/delta/_scribe_signal_folk_pubKey_a1_b.ts @@ -0,0 +1,9 @@ +import type { AgentPubKey } from '@holochain/conductor-api' +import { _b } from '@ctx-core/object' +import { folks_b } from '../session' +export const _scribe_signal_folk_pubKey_a1_b = _b('_scribe_signal_folk_pubKey_a1', (ctx)=>{ + const folks = folks_b(ctx) + return function _scribe_signal_folk_pubKey_a1():AgentPubKey[] { + return Object.values(folks.$!).filter(v=>v.inSession).map(v=>v.pubKey) + } +}) diff --git a/ui/libs/model/src/delta/apply_deltas_b.ts b/ui/libs/model/src/delta/apply_deltas_b.ts new file mode 100644 index 00000000..3f13968b --- /dev/null +++ b/ui/libs/model/src/delta/apply_deltas_b.ts @@ -0,0 +1,74 @@ +import { _b } from '@ctx-core/object' +import { Content, Delta, AddDelta, DeleteDelta, MetaDelta, TitleDelta, my_tag_b } from '@syn-ui/zome-client' +import { content_b } from '../content' +import { session_info_b, session_info_deltas_b } from '../session' +import type { ApplyDelta } from './ApplyDelta' +import { committed_changes_b } from './committed_changes_b' +export const apply_deltas_b = _b('apply_deltas', (ctx)=>{ + const content = content_b(ctx) + const my_tag = my_tag_b(ctx) + const session_info = session_info_b(ctx) + const session_info_deltas = session_info_deltas_b(ctx) + const committed_changes = committed_changes_b(ctx) + session_info.subscribe(async $session_info=>{ + if (!$session_info) return + const { snapshot_content } = $session_info + const $my_tag = my_tag.$ + const $content:Content = { + title: snapshot_content.title, + body: snapshot_content.body, + meta: {} + } + if ($my_tag) { + $content.meta[$my_tag] = 0 + } + content.$ = $content + committed_changes.$ = [] + const undoable_changes = await apply_deltas(session_info_deltas.$ || []) + committed_changes.$ = undoable_changes + }) + return apply_deltas + async function apply_deltas(deltas:Delta[]):Promise<ApplyDelta[]> { + const $content = content.$ + const undoable_changes:ApplyDelta[] = [] + for (const delta of deltas) { + switch (delta.type) { + case 'Title': { + const deleted = $content.title + const Title_delta = delta as TitleDelta + $content.title = Title_delta.value + undoable_changes.push({ delta, deleted }) + break + } + case 'Add': { + const Add_delta = delta as AddDelta + const [loc, text] = Add_delta.value + $content.body = $content.body.slice(0, loc) + text + $content.body.slice(loc) + undoable_changes.push({ delta }) + break + } + case 'Delete': { + const Delete_delta = delta as DeleteDelta + const [start, end] = Delete_delta.value + const deleted = $content.body.slice(start, end) + $content.body = $content.body.slice(0, start) + $content.body.slice(end) + undoable_changes.push({ delta, deleted }) + break + } + case 'Meta': { + const Meta_delta = delta as MetaDelta + const [tag, loc] = Meta_delta.value.setLoc + const deleted:[string, number] = [tag, $content.meta[tag]] + $content.meta[tag] = loc + undoable_changes.push({ delta, deleted }) + break + } + default: { + throw `Unsupported delta.type ${delta.type}` + } + } + } + content.$ = $content + return undoable_changes + } +}) diff --git a/ui/libs/model/src/delta/commit_change_b.ts b/ui/libs/model/src/delta/commit_change_b.ts new file mode 100644 index 00000000..3db7c57b --- /dev/null +++ b/ui/libs/model/src/delta/commit_change_b.ts @@ -0,0 +1,81 @@ +import { _b } from '@ctx-core/object' +import { Commit, rpc_commit_b, rpc_hash_content_b } from '@syn-ui/zome-client' +import { bufferToBase64, console_b } from '@syn-ui/utils' +import { content_b } from '../content' +import { + am_i_scribe_b, commit_in_progress_b, content_hash_b, content_hash_str_b, + current_commit_header_hash_b, session_info_snapshot_hash_b, session_info_snapshot_hash_str_b +} from '../session' +import { recorded_changes_b } from './recorded_changes_b' +import { _scribe_signal_folk_pubKey_a1_b } from './_scribe_signal_folk_pubKey_a1_b' +import { committed_changes_b } from './committed_changes_b' +export const commit_change_b = _b('commit_change', (ctx)=>{ + const console = console_b(ctx) + const recorded_changes = recorded_changes_b(ctx) + const commit_in_progress = commit_in_progress_b(ctx) + const rpc_hash_content = rpc_hash_content_b(ctx) + const content = content_b(ctx) + const session_info_snapshot_hash_str = session_info_snapshot_hash_str_b(ctx) + const content_hash = content_hash_b(ctx) + const content_hash_str = content_hash_str_b(ctx) + const session_info_snapshot_hash = session_info_snapshot_hash_b(ctx) + const _scribe_signal_folk_pubKey_a1 = _scribe_signal_folk_pubKey_a1_b(ctx) + const rpc_commit = rpc_commit_b(ctx) + const current_commit_header_hash = current_commit_header_hash_b(ctx) + const committed_changes = committed_changes_b(ctx) + const am_i_scribe = am_i_scribe_b(ctx) + return async function commit_change() { + if (am_i_scribe.$ === true) { + await try_commit() + } else { + alert(`You ain't the scribe!`) + } + } + async function try_commit() { + if (recorded_changes.$.length == 0) { + const msg = 'No changes to commit!' + if (typeof window === 'undefined') { + console.info(msg) + } else { + window.alert(msg) + } + return + } + commit_in_progress.$ = true + try { + const new_content_hash = await rpc_hash_content(content.$) + console.log('committing from snapshot', session_info_snapshot_hash_str.$) + console.log(' prev_hash:', content_hash_str.$) + console.log(' new_hash:', bufferToBase64(new_content_hash)) + const commit:Commit = { + snapshot: session_info_snapshot_hash.$!, + change: { + deltas: recorded_changes.$.map(c=>JSON.stringify(c.delta)), + content_hash: new_content_hash, + previous_change: content_hash.$!, + meta: { + contributors: [], + witnesses: [], + app_specific: null + } + }, + participants: _scribe_signal_folk_pubKey_a1() + } + try { + current_commit_header_hash.$ = await rpc_commit(commit) + // if commit successful we need to update the content hash and its string in the session + content_hash.$ = new_content_hash + committed_changes.update($committed_changes=>{ + $committed_changes.push(...recorded_changes.$) + return $committed_changes + }) + recorded_changes.$ = [] + } catch (e) { + console.trace('Error:', e) + throw e + } + } finally { + commit_in_progress.$ = false + } + } +}) diff --git a/ui/libs/model/src/delta/committed_changes_b.ts b/ui/libs/model/src/delta/committed_changes_b.ts new file mode 100644 index 00000000..bfd1948e --- /dev/null +++ b/ui/libs/model/src/delta/committed_changes_b.ts @@ -0,0 +1,6 @@ +import { _b } from '@ctx-core/object' +import { writable$ } from '@ctx-core/store' +import type { ApplyDelta } from './ApplyDelta' +export const committed_changes_b = _b('committed_changes', ()=>{ + return writable$<ApplyDelta[]>([]) +}) diff --git a/ui/libs/model/src/delta/index.ts b/ui/libs/model/src/delta/index.ts new file mode 100644 index 00000000..4ea426d1 --- /dev/null +++ b/ui/libs/model/src/delta/index.ts @@ -0,0 +1,12 @@ +export * from './ApplyDelta' +export * from './commit_change_b' +export * from './committed_changes_b' +export * from './next_index_b' +export * from './record_deltas_b' +export * from './recorded_changes_b' +export * from './request_change_b' +export * from './requested_changes_b' +export * from './apply_deltas_b' +export * from './_scribe_signal_folk_pubKey_a1_b' +export * from './send_change_b' +export * from './StateForSync' diff --git a/ui/libs/model/src/delta/next_index_b.ts b/ui/libs/model/src/delta/next_index_b.ts new file mode 100644 index 00000000..89c3eb1e --- /dev/null +++ b/ui/libs/model/src/delta/next_index_b.ts @@ -0,0 +1,13 @@ +import { derived$ } from '@ctx-core/store' +import { _b } from '@ctx-core/object' +import { recorded_changes_b } from './recorded_changes_b' +import { committed_changes_b } from './committed_changes_b' +export const next_index_b = _b('next_index', (ctx)=>{ + const committed_changes = committed_changes_b(ctx) + const recorded_changes = recorded_changes_b(ctx) + return derived$( + [committed_changes, recorded_changes], + ([$committed_changes, $recorded_changes])=> + $committed_changes.length + $recorded_changes.length + ) +}) diff --git a/ui/libs/model/src/delta/record_deltas_b.ts b/ui/libs/model/src/delta/record_deltas_b.ts new file mode 100644 index 00000000..76d8fcd0 --- /dev/null +++ b/ui/libs/model/src/delta/record_deltas_b.ts @@ -0,0 +1,47 @@ +import { _b } from '@ctx-core/object' +import type { Delta } from '@syn-ui/zome-client' +import { console_b } from '@syn-ui/utils' +import { requested_changes_b } from './requested_changes_b' +import { recorded_changes_b } from './recorded_changes_b' +import { apply_deltas_b } from './apply_deltas_b' +export const record_deltas_b = _b('record_deltas', (ctx)=>{ + const console = console_b(ctx) + const requested_changes = requested_changes_b(ctx) + return async function record_deltas(deltas:Delta[]) { + const $requested_changes = requested_changes.$ + console.log('record_deltas REQUESTED', $requested_changes) + const apply_deltas_a1:Delta[] = [] + for (const delta of deltas) { + if ($requested_changes.length > 0) { + // if this change is our next requested change then remove it + if (JSON.stringify(delta) === JSON.stringify($requested_changes[0].delta)) { + const recorded_changes = recorded_changes_b(ctx) + const $recorded_changes = recorded_changes.$ + $recorded_changes.push($requested_changes.shift()!) + recorded_changes.$ = $recorded_changes + requested_changes.$ = $requested_changes + } else { + // TODO rebase? + console.log('REBASE NEEDED?') + console.log('requested ', $requested_changes[0].delta) + console.log('to be recorded ', delta) + } + } else { + // no requested changes so this must be from someone else so we don't have + // to check our requested changes + // TODO: do we need to check if this is a change that we did send and have already + // integrated somehow and ignore if so. (Seems unlikely?) + // apply the deltas to the content which returns the undoable change + apply_deltas_a1.push(delta) + } + } + const apply_deltas = apply_deltas_b(ctx) + const undoable_changes = await apply_deltas(apply_deltas_a1) + // append changes to the recorded history + const recorded_changes = recorded_changes_b(ctx) + recorded_changes.update($recorded_changes=>{ + $recorded_changes.push(...undoable_changes) + return $recorded_changes + }) + } +}) diff --git a/ui/libs/model/src/delta/recorded_changes_b.ts b/ui/libs/model/src/delta/recorded_changes_b.ts new file mode 100644 index 00000000..31f579b1 --- /dev/null +++ b/ui/libs/model/src/delta/recorded_changes_b.ts @@ -0,0 +1,7 @@ +import { _b } from '@ctx-core/object' +import { writable$ } from '@ctx-core/store' +import type { ApplyDelta } from './ApplyDelta' +export const recorded_changes_b = _b('recorded_changes', ()=> + writable$<$recorded_changes_T>([]) +) +export type $recorded_changes_T = ApplyDelta[] diff --git a/ui/libs/model/src/delta/request_change_b.ts b/ui/libs/model/src/delta/request_change_b.ts new file mode 100644 index 00000000..a194f723 --- /dev/null +++ b/ui/libs/model/src/delta/request_change_b.ts @@ -0,0 +1,54 @@ +import { _b } from '@ctx-core/object' +import { Delta, my_tag_b, rpc_send_change_request_b } from '@syn-ui/zome-client' +import { console_b } from '@syn-ui/utils' +import { am_i_scribe_b, session_info_scribe_b } from '../session' +import { next_index_b } from './next_index_b' +import { send_change_b } from './send_change_b' +import { requested_changes_b } from './requested_changes_b' +import { apply_deltas_b } from './apply_deltas_b' +import { record_deltas_b } from './record_deltas_b' +export const request_change_b = _b('request_change', (ctx)=>{ + const console = console_b(ctx) + let request_counter = 0 + return async function request_change(deltas:Delta[]) { + // any requested made by the scribe should be recorded immediately + const am_i_scribe = am_i_scribe_b(ctx) + const next_index = next_index_b(ctx) + if (am_i_scribe.$ === true) { + const $next_index = next_index.$ + const record_deltas = record_deltas_b(ctx) + await record_deltas(deltas) + const send_change = send_change_b(ctx) + await send_change({ index: $next_index, deltas }) + } else { + // otherwise apply the change and queue it to requested changes for + // confirmation later and send request change to scribe + // create a unique id for each change + // TODO: this should be part of actual changeReqs + const my_tag = my_tag_b(ctx) + const change_id = my_tag.$ + '.' + request_counter + const change_at = Date.now() + const request_changes = requested_changes_b(ctx) + // we want to apply this to current next_index plus any previously + // requested changes that haven't yet be recorded + const index = next_index.$ + request_changes.$.length + const apply_deltas = apply_deltas_b(ctx) + const undoable_changes = await apply_deltas(deltas) + for (const undoable_change of undoable_changes) { + undoable_change.id = change_id + undoable_change.at = change_at + } + request_changes.update($request_changes=>{ + $request_changes.push(...undoable_changes) + console.log('REQUESTED', $request_changes) + return $request_changes + }) + const rpc_send_change_request = rpc_send_change_request_b(ctx) + const scribe = session_info_scribe_b(ctx) + await rpc_send_change_request({ + index, deltas, scribe: scribe.$! + }) + request_counter += 1 + } + } +}) diff --git a/ui/libs/model/src/delta/requested_changes_b.ts b/ui/libs/model/src/delta/requested_changes_b.ts new file mode 100644 index 00000000..a98146b7 --- /dev/null +++ b/ui/libs/model/src/delta/requested_changes_b.ts @@ -0,0 +1,6 @@ +import { _b } from '@ctx-core/object' +import { writable$ } from '@ctx-core/store' +import type { ApplyDelta } from './ApplyDelta' +export const requested_changes_b = _b('requested_changes', ()=>{ + return writable$<ApplyDelta[]>([]) +}) diff --git a/ui/libs/model/src/delta/send_change_b.ts b/ui/libs/model/src/delta/send_change_b.ts new file mode 100644 index 00000000..b9e6d343 --- /dev/null +++ b/ui/libs/model/src/delta/send_change_b.ts @@ -0,0 +1,18 @@ +import { _b } from '@ctx-core/object' +import type { Delta } from '@syn-ui/zome-client' +import { _scribe_signal_folk_pubKey_a1_b } from './_scribe_signal_folk_pubKey_a1_b' +import { rpc_send_change_b } from '@syn-ui/zome-client' +export const send_change_b = _b('send_change', (ctx)=>{ + const _scribe_signal_folk_pubKey_a1 = _scribe_signal_folk_pubKey_a1_b(ctx) + const rpc_send_change = rpc_send_change_b(ctx) + return async function send_change({ index, deltas }:send_change_params_I) { + const participants = _scribe_signal_folk_pubKey_a1() + return rpc_send_change({ + index, deltas, participants + }) + } +}) +export interface send_change_params_I { + index:number + deltas:Delta[] +} diff --git a/ui/libs/model/src/index.ts b/ui/libs/model/src/index.ts new file mode 100644 index 00000000..6482f695 --- /dev/null +++ b/ui/libs/model/src/index.ts @@ -0,0 +1,6 @@ +export * from './actions' +export * from './colors' +export * from './content' +export * from './delta' +export * from './session' +export * from './timers' diff --git a/ui/libs/model/src/session/am_i_scribe_b.ts b/ui/libs/model/src/session/am_i_scribe_b.ts new file mode 100644 index 00000000..09283c81 --- /dev/null +++ b/ui/libs/model/src/session/am_i_scribe_b.ts @@ -0,0 +1,11 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { me_b } from '@syn-ui/zome-client' +import { session_info_scribe_str_b } from './session_info_scribe_str_b' +export const am_i_scribe_b = _b('am_i_scribe', (ctx)=>{ + const session_info_scribe_str = session_info_scribe_str_b(ctx) + const me = me_b(ctx) + return derived$(([session_info_scribe_str, me]), ([$session_info_scribe_str, $me])=> + $session_info_scribe_str ? $session_info_scribe_str === $me : null + ) +}) diff --git a/ui/libs/model/src/session/commit_in_progress_b.ts b/ui/libs/model/src/session/commit_in_progress_b.ts new file mode 100644 index 00000000..f4d4646f --- /dev/null +++ b/ui/libs/model/src/session/commit_in_progress_b.ts @@ -0,0 +1,5 @@ +import { _b } from '@ctx-core/object' +import { writable$ } from '@ctx-core/store' +export const commit_in_progress_b = _b('commit_in_progress', ()=> + writable$<boolean>(false) +) diff --git a/ui/libs/model/src/session/content_hash_b.ts b/ui/libs/model/src/session/content_hash_b.ts new file mode 100644 index 00000000..40d06851 --- /dev/null +++ b/ui/libs/model/src/session/content_hash_b.ts @@ -0,0 +1,12 @@ +import { _b } from '@ctx-core/object' +import { writable$ } from '@ctx-core/store' +import type { EntryHash } from '@syn-ui/utils/dist' +import { session_info_b } from './session_info_b' +export const content_hash_b = _b('content_hash', (ctx)=>{ + const session_info = session_info_b(ctx) + const content_hash = writable$<undefined|EntryHash>(undefined) + session_info.subscribe($session_info=>{ + content_hash.$ = $session_info?.content_hash + }) + return content_hash +}) diff --git a/ui/libs/model/src/session/content_hash_str_b.ts b/ui/libs/model/src/session/content_hash_str_b.ts new file mode 100644 index 00000000..fb45cfb0 --- /dev/null +++ b/ui/libs/model/src/session/content_hash_str_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import { content_hash_b } from './content_hash_b' +import { derived$ } from '@ctx-core/store' +import { bufferToBase64 } from '@syn-ui/utils' +export const content_hash_str_b = _b('content_hash_str', (ctx)=>{ + const content_hash = content_hash_b(ctx) + return derived$(content_hash, $content_hash=> + $content_hash ? bufferToBase64($content_hash) : null + ) +}) diff --git a/ui/libs/model/src/session/current_commit_header_hash_b.ts b/ui/libs/model/src/session/current_commit_header_hash_b.ts new file mode 100644 index 00000000..9f002ae2 --- /dev/null +++ b/ui/libs/model/src/session/current_commit_header_hash_b.ts @@ -0,0 +1,6 @@ +import type { HoloHash } from '@holochain/conductor-api' +import { _b } from '@ctx-core/object' +import { writable$ } from '@ctx-core/store' +export const current_commit_header_hash_b = _b('current_commit_header_hash', ()=> + writable$<undefined|HoloHash>(undefined) +) diff --git a/ui/libs/model/src/session/folks_b.ts b/ui/libs/model/src/session/folks_b.ts new file mode 100644 index 00000000..0388cdce --- /dev/null +++ b/ui/libs/model/src/session/folks_b.ts @@ -0,0 +1,7 @@ +import { _b } from '@ctx-core/object' +import { writable$ } from '@ctx-core/store' +import type { PubKeyToFolkRecord } from '@syn-ui/zome-client' +export const folks_b = _b('folks', ()=>{ + return writable$<$folks_T>({}) +}) +export type $folks_T = PubKeyToFolkRecord diff --git a/ui/libs/model/src/session/index.ts b/ui/libs/model/src/session/index.ts new file mode 100644 index 00000000..97b16969 --- /dev/null +++ b/ui/libs/model/src/session/index.ts @@ -0,0 +1,18 @@ +export * from './am_i_scribe_b' +export * from './commit_in_progress_b' +export * from './content_hash_b' +export * from './content_hash_str_b' +export * from './current_commit_header_hash_b' +export * from './folks_b' +export * from './session_info_scribe_b' +export * from './session_info_scribe_str_b' +export * from './session_info_b' +export * from './session_info_deltas_b' +export * from './session_info_session_b' +export * from './session_info_session_str_b' +export * from './session_info_snapshot_content_b' +export * from './sessions_b' +export * from './sessions_str_a1_b' +export * from './session_info_snapshot_hash_b' +export * from './session_info_snapshot_hash_str_b' +export * from './update_folks_b' diff --git a/ui/libs/model/src/session/session_info_b.ts b/ui/libs/model/src/session/session_info_b.ts new file mode 100644 index 00000000..72714228 --- /dev/null +++ b/ui/libs/model/src/session/session_info_b.ts @@ -0,0 +1,20 @@ +import { _b, assign } from '@ctx-core/object' +import { writable$, Writable$ } from '@ctx-core/store' +import { rpc_get_session_b, SessionInfo } from '@syn-ui/zome-client' +export const session_info_b = _b<session_info_I>('session_info', (ctx)=>{ + const rpc_get_session = rpc_get_session_b(ctx) + const session_info = assign(writable$<$session_info_T>(undefined), { + refresh + }) + return session_info + async function refresh() { + const $session_info = session_info.$ + if ($session_info) { + await rpc_get_session($session_info.session) + } + } +}) +export type $session_info_T = undefined|SessionInfo +export interface session_info_I extends Writable$<$session_info_T> { + refresh():Promise<void> +} diff --git a/ui/libs/model/src/session/session_info_deltas_b.ts b/ui/libs/model/src/session/session_info_deltas_b.ts new file mode 100644 index 00000000..c072dede --- /dev/null +++ b/ui/libs/model/src/session/session_info_deltas_b.ts @@ -0,0 +1,12 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import type { Delta } from '@syn-ui/zome-client' +import { session_info_b } from '../session' +export const session_info_deltas_b = _b('session_info_deltas', (ctx)=>{ + const session_info = session_info_b(ctx) + return derived$(session_info, $session_info=> + $session_info ? $session_info.deltas.map( + serialized_delta=>JSON.parse(serialized_delta) + ) as Delta[] : null + ) +}) diff --git a/ui/libs/model/src/session/session_info_scribe_b.ts b/ui/libs/model/src/session/session_info_scribe_b.ts new file mode 100644 index 00000000..54cbeaae --- /dev/null +++ b/ui/libs/model/src/session/session_info_scribe_b.ts @@ -0,0 +1,9 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { session_info_b } from './session_info_b' +export const session_info_scribe_b = _b('session_info_scribe', (ctx)=>{ + const session_info = session_info_b(ctx) + return derived$(session_info, $session_info=> + $session_info?.scribe + )! +}) diff --git a/ui/libs/model/src/session/session_info_scribe_str_b.ts b/ui/libs/model/src/session/session_info_scribe_str_b.ts new file mode 100644 index 00000000..ca706a24 --- /dev/null +++ b/ui/libs/model/src/session/session_info_scribe_str_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { bufferToBase64 } from '@syn-ui/utils' +import { session_info_scribe_b } from './session_info_scribe_b' +export const session_info_scribe_str_b = _b('session_info_scribe_str', (ctx)=>{ + const session_info_scribe = session_info_scribe_b(ctx) + return derived$(session_info_scribe, $session_info_scribe=> + $session_info_scribe ? bufferToBase64($session_info_scribe) : null + ) +}) diff --git a/ui/libs/model/src/session/session_info_session_b.ts b/ui/libs/model/src/session/session_info_session_b.ts new file mode 100644 index 00000000..7ef449fc --- /dev/null +++ b/ui/libs/model/src/session/session_info_session_b.ts @@ -0,0 +1,9 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { session_info_b } from './session_info_b' +export const session_info_session_b = _b('session_info_session', (ctx)=>{ + const session_info = session_info_b(ctx) + return derived$(session_info, $session_info=> + $session_info?.session + ) +}) diff --git a/ui/libs/model/src/session/session_info_session_str_b.ts b/ui/libs/model/src/session/session_info_session_str_b.ts new file mode 100644 index 00000000..5194921a --- /dev/null +++ b/ui/libs/model/src/session/session_info_session_str_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { bufferToBase64 } from '@syn-ui/utils' +import { session_info_session_b } from './session_info_session_b' +export const session_info_session_str_b = _b('session_info_session_str', (ctx)=>{ + const session = session_info_session_b(ctx) + return derived$(session, $session=> + $session ? bufferToBase64($session) : undefined + ) +}) diff --git a/ui/libs/model/src/session/session_info_snapshot_content_b.ts b/ui/libs/model/src/session/session_info_snapshot_content_b.ts new file mode 100644 index 00000000..6333fc1b --- /dev/null +++ b/ui/libs/model/src/session/session_info_snapshot_content_b.ts @@ -0,0 +1,12 @@ +import { _b } from '@ctx-core/object' +import { derived$, Readable$ } from '@ctx-core/store' +import type { Content } from '@syn-ui/zome-client' +import { session_info_b } from '../session' +export const session_info_snapshot_content_b = _b('session_info_snapshot_content', (ctx)=>{ + const session_info = session_info_b(ctx) + const snapshot_content:snapshot_content_T = derived$(session_info, $session_info=> + $session_info?.snapshot_content + ) + return snapshot_content +}) +export interface snapshot_content_T extends Readable$<undefined|Content> {} diff --git a/ui/libs/model/src/session/session_info_snapshot_hash_b.ts b/ui/libs/model/src/session/session_info_snapshot_hash_b.ts new file mode 100644 index 00000000..816177d4 --- /dev/null +++ b/ui/libs/model/src/session/session_info_snapshot_hash_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import { derived$, Readable$ } from '@ctx-core/store' +import type { HeaderHash } from '@syn-ui/utils/dist' +import { session_info_b } from './session_info_b' +export const session_info_snapshot_hash_b = _b<Readable$<undefined|HeaderHash>>('session_info_snapshot_hash', ctx=>{ + const session_info = session_info_b(ctx) + return derived$(session_info, $session_info=> + $session_info?.snapshot_hash + ) +}) diff --git a/ui/libs/model/src/session/session_info_snapshot_hash_str_b.ts b/ui/libs/model/src/session/session_info_snapshot_hash_str_b.ts new file mode 100644 index 00000000..06aa8984 --- /dev/null +++ b/ui/libs/model/src/session/session_info_snapshot_hash_str_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { bufferToBase64 } from '@syn-ui/utils' +import { session_info_snapshot_hash_b } from './session_info_snapshot_hash_b' +export const session_info_snapshot_hash_str_b = _b('session_info_snapshot_hash_str', (ctx)=>{ + const session_info_snapshot_hash = session_info_snapshot_hash_b(ctx) + return derived$(session_info_snapshot_hash, $session_info_snapshot_hash=> + $session_info_snapshot_hash ? bufferToBase64($session_info_snapshot_hash) : null + ) +}) diff --git a/ui/libs/model/src/session/sessions_b.ts b/ui/libs/model/src/session/sessions_b.ts new file mode 100644 index 00000000..9b70cd04 --- /dev/null +++ b/ui/libs/model/src/session/sessions_b.ts @@ -0,0 +1,42 @@ +import { _b, assign } from '@ctx-core/object' +import { _readable_set_ctx$, Writable$ } from '@ctx-core/store' +import { rpc_get_sessions_b, SessionInfo } from '@syn-ui/zome-client' +import type { EntryHash } from '@syn-ui/utils' +import { session_info_b } from './session_info_b' +export const sessions_b = _b('sessions', (ctx)=>{ + const { store: sessions, set } = _readable_set_ctx$<EntryHash[]|null>(null) + const { store: busy, set: set_busy } = _readable_set_ctx$<boolean>(false) + const out_sessions = sessions as sessions_T + assign(out_sessions, { busy, load, unshift }) + const session_info = session_info_b(ctx) + let $session_info:undefined|SessionInfo + session_info.subscribe(in_$session_info=>{ + if ($session_info && !in_$session_info) { + set(null) + } + $session_info = in_$session_info + }) + return out_sessions + async function load() { + set_busy(true) + try { + const rpc_get_sessions = rpc_get_sessions_b(ctx) + const $sessions = await rpc_get_sessions() + set($sessions) + } finally { + set_busy(false) + } + return sessions.$ + } + function unshift(...session_hash_a1:EntryHash[]) { + const $sessions = sessions.$! + $sessions.unshift(...session_hash_a1) + set($sessions) + return $sessions + } +}) +export interface sessions_T extends Writable$<EntryHash[]|null> { + busy:Writable$<boolean> + load():Promise<EntryHash[]> + unshift(...session_hash_a1:EntryHash[]):EntryHash[] +} diff --git a/ui/libs/model/src/session/sessions_str_a1_b.ts b/ui/libs/model/src/session/sessions_str_a1_b.ts new file mode 100644 index 00000000..8c2ef7d3 --- /dev/null +++ b/ui/libs/model/src/session/sessions_str_a1_b.ts @@ -0,0 +1,13 @@ +import { _b } from '@ctx-core/object' +import { sessions_b } from './sessions_b' +import { derived$, Readable$ } from '@ctx-core/store' +import { bufferToBase64 } from '@syn-ui/utils/dist' +export const sessions_str_a1_b = _b<sessions_str_a1_T>('sessions_str_a1', (ctx)=>{ + const sessions = sessions_b(ctx) + const sessions_str_a1 = derived$(sessions, $sessions=> + $sessions?.map(session=>bufferToBase64(session)) + ) as sessions_str_a1_T + return sessions_str_a1 +}) +export type $sessions_str_a1_T = null|string[] +export interface sessions_str_a1_T extends Readable$<$sessions_str_a1_T> {} diff --git a/ui/libs/model/src/session/update_folks_b.ts b/ui/libs/model/src/session/update_folks_b.ts new file mode 100644 index 00000000..de7a3aba --- /dev/null +++ b/ui/libs/model/src/session/update_folks_b.ts @@ -0,0 +1,38 @@ +import type { AgentPubKey } from '@holochain/conductor-api' +import { _b } from '@ctx-core/object' +import { bufferToBase64 } from '@syn-ui/utils' +import { + Folk, FOLK_GONE, FOLK_SEEN, FOLK_UNKNOWN, FolkStatus, PubKeyToFolkRecord +} from '@syn-ui/zome-client' +import { getFolkColors } from '../colors' +import { folks_b } from './folks_b' +export const update_folks_b = _b('update_folks', (ctx)=>{ + const folks = folks_b(ctx) + return update_folks + function update_folks(pubKey:AgentPubKey, status:FolkStatus, meta?:number) { + const pubKeyStr = bufferToBase64(pubKey) + // if we don't have this key, create a record for it + // including the default color + const $folks = folks.$ + _other($folks, pubKeyStr, pubKey) + if (meta) { + $folks[pubKeyStr]['meta'] = meta + } + switch (status) { + case FOLK_SEEN: + $folks[pubKeyStr]['inSession'] = true + $folks[pubKeyStr]['lastSeen'] = Date.now() + break + case FOLK_GONE: + case FOLK_UNKNOWN: + $folks[pubKeyStr]['inSession'] = false + } + folks.$ = $folks + } + function _other($folks:PubKeyToFolkRecord, pubKeyStr:string, pubKey:AgentPubKey) { + if (!(pubKeyStr in $folks)) { + const colors = getFolkColors(pubKey) + $folks[pubKeyStr] = { pubKey, colors } as Folk + } + } +}) diff --git a/ui/libs/model/src/signals/ChangeReq_SignalOps_b.ts b/ui/libs/model/src/signals/ChangeReq_SignalOps_b.ts new file mode 100644 index 00000000..8ba726cd --- /dev/null +++ b/ui/libs/model/src/signals/ChangeReq_SignalOps_b.ts @@ -0,0 +1,41 @@ +import { _b } from '@ctx-core/object' +import type { Delta } from '@syn-ui/zome-client' +import { console_b } from '@syn-ui/utils' +import { next_index_b, record_deltas_b, send_change_b } from '../delta' +import { am_i_scribe_b } from '../session' +import type { SignalOps } from './SignalOps' +export const ChangeReq_SignalOps_b = _b<SignalOps>('ChangeReq_SignalOps', (ctx)=>{ + const console = console_b(ctx) + const am_i_scribe = am_i_scribe_b(ctx) + const next_index = next_index_b(ctx) + const send_change = send_change_b(ctx) + return { + ChangeReq: async (signal)=>{ + const [index, serialized_deltas]:[number, string[]] = signal.data.payload.signal_payload + const deltas:Delta[] = serialized_deltas.map(d=>JSON.parse(d)) + const change:[number, Delta[]] = [index, deltas] + if (am_i_scribe.$ === true) { + let [index, deltas] = change + const $next_index = next_index.$ + if ($next_index != index) { + console.log('Scribe is receiving change out of order!') + console.log(`next_index: ${$next_index}, changeIndex:${index} for deltas:`, deltas) + if (index < $next_index) { + // change is too late, $next_index has moved on + // TODO: rebase? notify sender? + return + } else { + // change is in the future, possibly some other change was dropped or is slow in arriving + // TODO: wait a bit? Ask sender for other changes? + return + } + } + const record_deltas = record_deltas_b(ctx) + await record_deltas(deltas) + // notify all participants of the change + await send_change({ index, deltas }) + } + // connection.session.changeReq([index, deltas]) + } + } +}) diff --git a/ui/libs/model/src/signals/Change_SignalOps_b.ts b/ui/libs/model/src/signals/Change_SignalOps_b.ts new file mode 100644 index 00000000..a4b727e9 --- /dev/null +++ b/ui/libs/model/src/signals/Change_SignalOps_b.ts @@ -0,0 +1,30 @@ +import type { AppSignal } from '@holochain/conductor-api' +import { _b } from '@ctx-core/object' +import { console_b } from '@syn-ui/utils' +import { next_index_b, record_deltas_b } from '../delta' +import { am_i_scribe_b } from '../session' +import type { SignalOps } from './SignalOps' +export const Change_SignalOps_b = _b<SignalOps>('Change_SignalOps', (ctx)=>{ + const console = console_b(ctx) + const next_index = next_index_b(ctx) + return { + Change: async (signal:AppSignal)=>{ + const [index, serialized_deltas] = signal.data.payload.signal_payload + const deltas = serialized_deltas.map(d=>JSON.parse(d)) + const am_i_scribe = am_i_scribe_b(ctx) + if (am_i_scribe.$ === true) { + console.log(`change received but I'm the scribe, so I'm ignoring this!`) + } else { + console.log(`change arrived for ${index}:`, deltas) + const $next_index = next_index.$ + if ($next_index === index) { + const record_deltas = record_deltas_b(ctx) + await record_deltas(deltas) + } else { + console.log(`change arrived out of sequence next_index: ${$next_index}, change index:${index}`) + // TODO either call for sync, or do some waiting algorithm + } + } + } + } +}) diff --git a/ui/libs/model/src/signals/CommitNotice_SignalOps_b.ts b/ui/libs/model/src/signals/CommitNotice_SignalOps_b.ts new file mode 100644 index 00000000..9692f58d --- /dev/null +++ b/ui/libs/model/src/signals/CommitNotice_SignalOps_b.ts @@ -0,0 +1,43 @@ +import { _b } from '@ctx-core/object' +import { bufferToBase64, console_b, EntryHash, HeaderHash } from '@syn-ui/utils' +import { committed_changes_b, next_index_b, recorded_changes_b } from '../delta' +import { content_hash_b, content_hash_str_b } from '../session' +import type { SignalOps } from './SignalOps' +export const CommitNotice_SignalOps_b = _b<SignalOps>('CommitNotice_SignalOps', (ctx)=>{ + const console = console_b(ctx) + const content_hash = content_hash_b(ctx) + const content_hash_str = content_hash_str_b(ctx) + const next_index = next_index_b(ctx) + const committed_changes = committed_changes_b(ctx) + const recorded_changes = recorded_changes_b(ctx) + return { + CommitNotice: async (signal)=>{ + const commit_info:CommitInfo = signal.data.payload.signal_payload + // make sure we are at the right place to be able to just move forward with the commit + const $content_hash_str = content_hash_str.$ + if ($content_hash_str == bufferToBase64(commit_info.previous_content_hash) && + next_index.$ === commit_info.deltas_committed) { + content_hash.$ = commit_info.commit_content_hash + committed_changes.update($committed_changes=>{ + $committed_changes.push(...recorded_changes.$) + return $committed_changes + }) + recorded_changes.$ = [] + } else { + console.log('received commit notice for beyond our last commit, gotta resync') + console.log('commit.commit_content_hash:', bufferToBase64(commit_info.commit_content_hash)) + console.log('commit.previous_content_hash:', bufferToBase64(commit_info.previous_content_hash)) + console.log('commit.deltas_committed:', commit_info.deltas_committed) + console.log('my $session.contentHashStr', $content_hash_str) + console.log('my next_index', next_index.$) + // TODO resync + } + } + } +}) +export interface CommitInfo { + deltas_committed:number + commit_content_hash:EntryHash + previous_content_hash:EntryHash + commit:HeaderHash +} diff --git a/ui/libs/model/src/signals/FolkLore_SignalOps_b.ts b/ui/libs/model/src/signals/FolkLore_SignalOps_b.ts new file mode 100644 index 00000000..138d2e5c --- /dev/null +++ b/ui/libs/model/src/signals/FolkLore_SignalOps_b.ts @@ -0,0 +1,35 @@ +import { _b } from '@ctx-core/object' +import { decodeJson, FOLK_GONE, FOLK_UNKNOWN } from '@syn-ui/zome-client' +import { console_b } from '@syn-ui/utils' +import { am_i_scribe_b, update_folks_b } from '../session' +import type { SignalOps } from './SignalOps' +export const FolkLore_SignalOps_b = _b<SignalOps>('FolkLore_SignalOps', (ctx)=>{ + const console = console_b(ctx) + const am_i_scribe = am_i_scribe_b(ctx) + const update_folks = update_folks_b(ctx) + return { + FolkLore: async (signal)=>{ + const data = decodeJson(signal.data.payload.signal_payload) + console.log('got folklore', data) + if (am_i_scribe.$) { + console.log(`folklore received but I'm the scribe!`) + } else { + const { gone, participants } = data + if (gone) { + for (const pubKey of gone) { + update_folks(pubKey, FOLK_GONE) + } + } + // TODO move last seen into p.meta so that we can update that value + // as hearsay. + if (participants) { + Object.values(participants).forEach( + p=>{ + update_folks(p.pubKey, FOLK_UNKNOWN, p.meta) + } + ) + } + } + } + } +}) diff --git a/ui/libs/model/src/signals/Heartbeat_SignalOps_b.ts b/ui/libs/model/src/signals/Heartbeat_SignalOps_b.ts new file mode 100644 index 00000000..e41af4a3 --- /dev/null +++ b/ui/libs/model/src/signals/Heartbeat_SignalOps_b.ts @@ -0,0 +1,22 @@ +import { _b } from '@ctx-core/object' +import { FOLK_SEEN } from '@syn-ui/zome-client' +import { console_b } from '@syn-ui/utils' +import { am_i_scribe_b, update_folks_b } from '../session' +import type { SignalOps } from './SignalOps' +export const Heartbeat_SignalOps_b = _b<SignalOps>('Heartbeat_SignalOps', (ctx)=>{ + const console = console_b(ctx) + const am_i_scribe = am_i_scribe_b(ctx) + const update_folks = update_folks_b(ctx) + return { + Heartbeat: async (signal)=>{ + let [from, msg] = signal.data.payload.signal_payload + console.log('got heartbeat', msg, 'from:', from) + if (am_i_scribe.$ === true) { + // I am the scribe and I've received a heartbeat from a concerned Folk + update_folks(from, FOLK_SEEN) + } else { + console.log(`heartbeat received but I'm not the scribe.`) + } + } + } +}) diff --git a/ui/libs/model/src/signals/SignalOps.ts b/ui/libs/model/src/signals/SignalOps.ts new file mode 100644 index 00000000..2ccb8b68 --- /dev/null +++ b/ui/libs/model/src/signals/SignalOps.ts @@ -0,0 +1,3 @@ +import type { AppSignal } from '@holochain/conductor-api' +export type SignalOp = (signal:AppSignal)=>Promise<void> +export interface SignalOps extends Record<string, SignalOp> {} diff --git a/ui/libs/model/src/signals/SyncReq_SignalOps_b.ts b/ui/libs/model/src/signals/SyncReq_SignalOps_b.ts new file mode 100644 index 00000000..d9555cff --- /dev/null +++ b/ui/libs/model/src/signals/SyncReq_SignalOps_b.ts @@ -0,0 +1,59 @@ +import type { AgentPubKey } from '@holochain/conductor-api' +import { _b } from '@ctx-core/object' +import { + agent_pub_key_b, FOLK_SEEN, me_b, rpc_send_folk_lore_b, rpc_send_sync_response_b, StateForSync +} from '@syn-ui/zome-client' +import { console_b } from '@syn-ui/utils' +import { _scribe_signal_folk_pubKey_a1_b, recorded_changes_b } from '../delta' +import { + am_i_scribe_b, content_hash_b, current_commit_header_hash_b, folks_b, + session_info_snapshot_hash_b, update_folks_b +} from '../session' +import type { SignalOps } from './SignalOps' +export const SyncReq_SignalOps_b = _b<SignalOps>('SyncReq_SignalOps', (ctx)=>{ + const console = console_b(ctx) + const me = me_b(ctx) + const folks = folks_b(ctx) + const rpc_send_sync_response = rpc_send_sync_response_b(ctx) + const recorded_changes = recorded_changes_b(ctx) + const content_hash = content_hash_b(ctx) + const session_info_snapshot_hash = session_info_snapshot_hash_b(ctx) + const current_commit_header_hash = current_commit_header_hash_b(ctx) + const _scribe_signal_folk_pubKey_a1 = _scribe_signal_folk_pubKey_a1_b(ctx) + return { + SyncReq: async (signal)=>{ + const participant:AgentPubKey = signal.data.payload.signal_payload + const am_i_scribe = am_i_scribe_b(ctx) + if (am_i_scribe.$ === true) { + const update_folks = update_folks_b(ctx) + update_folks(participant, FOLK_SEEN) + const state:StateForSync = { + snapshot: session_info_snapshot_hash.$!, + commit_content_hash: content_hash.$!, + deltas: recorded_changes.$.map(c=>c.delta) + } + const $current_commit_header_hash = current_commit_header_hash.$ + if ($current_commit_header_hash) { + state['commit'] = $current_commit_header_hash + } + // send a sync response to the sender + await rpc_send_sync_response({ participant, state }) + // and send everybody a folk lore p2p message with new participants + const $folks = folks.$ + let p = { ...$folks } + const agent_pub_key = agent_pub_key_b(ctx) + p[me.$] = { + pubKey: agent_pub_key.$ + } + const data = { participants: p } + const rpc_send_folk_lore = rpc_send_folk_lore_b(ctx) + await rpc_send_folk_lore({ + participants: _scribe_signal_folk_pubKey_a1(), + data, + }) + } else { + console.log('syncReq received but I\'m not the scribe!') + } + } + } +}) diff --git a/ui/libs/model/src/signals/SyncResp_SignalOps_b.ts b/ui/libs/model/src/signals/SyncResp_SignalOps_b.ts new file mode 100644 index 00000000..66f9da02 --- /dev/null +++ b/ui/libs/model/src/signals/SyncResp_SignalOps_b.ts @@ -0,0 +1,35 @@ +import type { HoloHash } from '@holochain/conductor-api' +import { _b, assign } from '@ctx-core/object' +import type { Delta } from '@syn-ui/zome-client' +import { bufferToBase64, console_b } from '@syn-ui/utils' +import type { SerializedStateForSync, StateForSync } from '../delta' +import { record_deltas_b } from '../delta' +import { content_hash_str_b } from '../session' +import type { SignalOps } from './SignalOps' +export const SyncResp_SignalOps_b = _b<SignalOps>('SyncResp_SignalOps', (ctx)=>{ + const console = console_b(ctx) + const content_hash_str = content_hash_str_b(ctx) + return { + SyncResp: async (signal)=>{ + const serialized_state:SerializedStateForSync = signal.data.payload.signal_payload + const state:SyncResp_state_I = assign({} as StateForSync, serialized_state, { + deltas: serialized_state.deltas.map(d=>JSON.parse(d)) + }) + // Make sure that we are working off the same snapshot and commit + const commit_content_hash_str = bufferToBase64(state.commit_content_hash) + if (commit_content_hash_str == content_hash_str.$) { + const record_deltas = record_deltas_b(ctx) + await record_deltas(state.deltas) + } else { + console.log('WHOA, sync response has different current state assumptions') + // TODO: resync somehow + } + } + } +}) +export interface SyncResp_state_I extends Omit<StateForSync, 'deltas'> { + snapshot:HoloHash, + commit:HoloHash, + commit_content_hash:HoloHash + deltas:Delta[] +} diff --git a/ui/libs/model/src/signals/app_ws_cb_b.ts b/ui/libs/model/src/signals/app_ws_cb_b.ts new file mode 100644 index 00000000..c7a22229 --- /dev/null +++ b/ui/libs/model/src/signals/app_ws_cb_b.ts @@ -0,0 +1,36 @@ +import type { AppSignal } from '@holochain/conductor-api' +import { _b, clone } from '@ctx-core/object' +import { bufferToBase64, console_b } from '@syn-ui/utils' +import { me_b } from '@syn-ui/zome-client' +import { + Change_SignalOps_b, ChangeReq_SignalOps_b, CommitNotice_SignalOps_b, + FolkLore_SignalOps_b, Heartbeat_SignalOps_b, + SignalOps, SyncReq_SignalOps_b, SyncResp_SignalOps_b +} from '../signals' +export const app_ws_cb_b = _b('app_ws_cb', (ctx)=>{ + const console = console_b(ctx) + const me = me_b(ctx) + const signal_ops:SignalOps = clone( + Change_SignalOps_b(ctx), + ChangeReq_SignalOps_b(ctx), + CommitNotice_SignalOps_b(ctx), + FolkLore_SignalOps_b(ctx), + Heartbeat_SignalOps_b(ctx), + SyncReq_SignalOps_b(ctx), + SyncResp_SignalOps_b(ctx), + ) + return app_ws_cb + async function app_ws_cb(signal:AppSignal) { + // ignore signals not meant for me + if (bufferToBase64(signal.data.cellId[1]) !== me.$) { + return + } + const { signal_name } = signal.data.payload + console.log('Got Signal', signal_name, signal) + const signal_payload_op = signal_ops[signal_name] + if (!signal_payload_op) { + console.warn(`Undefined SignalOp: ${signal_name}`) + } + await signal_payload_op(signal) + } +}) diff --git a/ui/libs/model/src/signals/index.ts b/ui/libs/model/src/signals/index.ts new file mode 100644 index 00000000..e4bc3cf0 --- /dev/null +++ b/ui/libs/model/src/signals/index.ts @@ -0,0 +1,9 @@ +export * from './app_ws_cb_b' +export * from './ChangeReq_SignalOps_b' +export * from './Change_SignalOps_b' +export * from './CommitNotice_SignalOps_b' +export * from './FolkLore_SignalOps_b' +export * from './Heartbeat_SignalOps_b' +export * from './SignalOps' +export * from './SyncReq_SignalOps_b' +export * from './SyncResp_SignalOps_b' diff --git a/ui/libs/model/src/timers/Timer.ts b/ui/libs/model/src/timers/Timer.ts new file mode 100644 index 00000000..9cad8da5 --- /dev/null +++ b/ui/libs/model/src/timers/Timer.ts @@ -0,0 +1,15 @@ +export class Timer { + interval_id:any + constructor(protected setInterval_fn:()=>void, protected interval_ms:number) {} + start() { + this.stop() + this.interval_id = setInterval(this.setInterval_fn, this.interval_ms) + } + stop() { + const { interval_id } = this + if (interval_id) { + clearInterval(interval_id) + this.interval_id = null + } + } +} diff --git a/ui/libs/model/src/timers/index.ts b/ui/libs/model/src/timers/index.ts new file mode 100644 index 00000000..2e098ed3 --- /dev/null +++ b/ui/libs/model/src/timers/index.ts @@ -0,0 +1,3 @@ +export * from './request_checker_timer_b' +export * from './scribe_heartbeat_timer_b' +export * from './Timer' diff --git a/ui/libs/model/src/timers/request_checker_timer_b.ts b/ui/libs/model/src/timers/request_checker_timer_b.ts new file mode 100644 index 00000000..bb980f34 --- /dev/null +++ b/ui/libs/model/src/timers/request_checker_timer_b.ts @@ -0,0 +1,44 @@ +import { _b } from '@ctx-core/object' +import { rpc_send_sync_request_b } from '@syn-ui/zome-client' +import { console_b } from '@syn-ui/utils' +import { requested_changes_b } from '../delta' +import { session_info_b } from '../session' +import { Timer } from './Timer' +const request_timeout = 1000 +export const request_checker_timer_b = _b('request_checker_timer', (ctx)=>{ + const console = console_b(ctx) + const requested_changes = requested_changes_b(ctx) + const session_info = session_info_b(ctx) + const rpc_send_sync_request = rpc_send_sync_request_b(ctx) + return new Timer(async ()=>{ + const $requested_changes = requested_changes.$ + if ($requested_changes.length > 0) { + const at = $requested_changes[0]?.at + if (at && (Date.now() - at) > request_timeout) { + // for now let's just do the most drastic thing! + /* + console.log('requested change timed out! Undoing all changes', $requested_changes[0]) + // TODO: make sure this is transactional and no request_changes squeak in ! + while ($requested_changes.length > 0) { + requested_changes.update(changes => { + const change = changes.pop() + console.log('undoing ', change) + const undoDelta = undoFn(change) + console.log('undoDelta: ', undoDelta) + const apply_deltas = apply_deltas_b(ctx) + await apply_deltas(undoDelta) + return changes + }) + }*/ + + // and send a sync request incase something just got out of sequence + // TODO: prepare for shifting to new scribe if they went offline + console.log('HERE') + const $session_info = session_info.$ + if ($session_info) { + await rpc_send_sync_request($session_info.scribe) + } + } + } + }, request_timeout / 2) +}) diff --git a/ui/libs/model/src/timers/scribe_heartbeat_timer_b.ts b/ui/libs/model/src/timers/scribe_heartbeat_timer_b.ts new file mode 100644 index 00000000..7607e948 --- /dev/null +++ b/ui/libs/model/src/timers/scribe_heartbeat_timer_b.ts @@ -0,0 +1,45 @@ +import type { AgentPubKey } from '@holochain/conductor-api' +import { _b } from '@ctx-core/object' +import { rpc_send_folk_lore_b, rpc_send_heartbeat_b } from '@syn-ui/zome-client' +import { am_i_scribe_b, folks_b, session_info_scribe_b } from '../session' +import { _scribe_signal_folk_pubKey_a1_b } from '../delta' +import { Timer } from './Timer' +// const outOfSessionTimout = 30 * 1000 +const outOfSessionTimout = 8 * 1000 // testing code :) +// const heartbeatInterval = 15 * 1000 // 15 seconds +const heartbeat_interval = 30 * 1000 // for testing ;) +export const scribe_heartbeat_timer_b = _b<Timer>('scribe_heartbeat_timer', (ctx)=>{ + const am_i_scribe = am_i_scribe_b(ctx) + const folks = folks_b(ctx) + return new Timer(async ()=>{ + if (am_i_scribe.$ === true) { + // examine folks last seen time and see if any have crossed the session out-of-session + // timeout so we can tell everybody else about them having dropped. + const gone:AgentPubKey[] = [] + const $folks = folks.$ + for (const [pubKeyStr, folk] of Object.entries($folks)) { + if (folk.inSession && (Date.now() - ($folks[pubKeyStr].lastSeen || 0) > outOfSessionTimout)) { + folk.inSession = false + gone.push($folks[pubKeyStr].pubKey) + } + } + if (gone.length > 0) { + folks.$ = $folks + const rpc_send_folk_lore = rpc_send_folk_lore_b(ctx) + const _scribe_signal_folk_pubKey_a1 = _scribe_signal_folk_pubKey_a1_b(ctx) + await rpc_send_folk_lore({ + participants: _scribe_signal_folk_pubKey_a1(), + data: { gone } + }) + } + } else { + // I'm not the scribe so send them a heartbeat + const rpc_send_heartbeat = rpc_send_heartbeat_b(ctx) + const session_info_scribe = session_info_scribe_b(ctx) + await rpc_send_heartbeat({ + scribe: session_info_scribe.$!, + data: 'Hello' + }) + } + }, heartbeat_interval) +}) diff --git a/ui/libs/model/tsconfig.json b/ui/libs/model/tsconfig.json new file mode 100644 index 00000000..3667b080 --- /dev/null +++ b/ui/libs/model/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "declarationDir": "dist", + "declaration": true + } +} diff --git a/ui/libs/utils/.gitignore b/ui/libs/utils/.gitignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/ui/libs/utils/.gitignore @@ -0,0 +1 @@ +dist diff --git a/ui/libs/utils/README.md b/ui/libs/utils/README.md new file mode 100644 index 00000000..410269ba --- /dev/null +++ b/ui/libs/utils/README.md @@ -0,0 +1 @@ +# @syn-ui/model diff --git a/ui/libs/utils/package.json b/ui/libs/utils/package.json new file mode 100644 index 00000000..4c02cbbd --- /dev/null +++ b/ui/libs/utils/package.json @@ -0,0 +1,13 @@ +{ + "name": "@syn-ui/utils", + "version": "1.0.0", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./src/index.ts", + "scripts": {}, + "dependencies": { + }, + "devDependencies": { + "typescript": "^4.2.4" + } +} diff --git a/ui/libs/utils/src/EntryHash.ts b/ui/libs/utils/src/EntryHash.ts new file mode 100644 index 00000000..6695d2d8 --- /dev/null +++ b/ui/libs/utils/src/EntryHash.ts @@ -0,0 +1,2 @@ +import type { HoloHash } from '@holochain/conductor-api' +export type EntryHash = HoloHash diff --git a/ui/libs/utils/src/HeaderHash.ts b/ui/libs/utils/src/HeaderHash.ts new file mode 100644 index 00000000..d673f74e --- /dev/null +++ b/ui/libs/utils/src/HeaderHash.ts @@ -0,0 +1,2 @@ +import type { HoloHash } from '@holochain/conductor-api' +export type HeaderHash = HoloHash diff --git a/ui/libs/utils/src/base64ToBuffer.ts b/ui/libs/utils/src/base64ToBuffer.ts new file mode 100644 index 00000000..a3be3aa5 --- /dev/null +++ b/ui/libs/utils/src/base64ToBuffer.ts @@ -0,0 +1,8 @@ +export const base64ToBuffer = base64=>{ + if (!base64) return + if (typeof window !== 'undefined') { + return Uint8Array.from(window.atob(base64), c=>c.charCodeAt(0)) + } else { + return Buffer.from(base64, 'base64') + } +} diff --git a/ui/libs/utils/src/bufferToBase64.ts b/ui/libs/utils/src/bufferToBase64.ts new file mode 100644 index 00000000..e182b3fd --- /dev/null +++ b/ui/libs/utils/src/bufferToBase64.ts @@ -0,0 +1,15 @@ +export const bufferToBase64 = buffer=>{ + if (typeof window !== 'undefined') { + // browser + let binary = '' + const bytes = new Uint8Array(buffer) + const len = bytes.byteLength + for (let i = 0; i < len; i++) { + binary += String.fromCharCode(bytes[i]) + } + return window.btoa(binary) + } else { + // nodejs + return buffer.toString('base64') + } +} diff --git a/ui/libs/utils/src/console_b.ts b/ui/libs/utils/src/console_b.ts new file mode 100644 index 00000000..8dbc225d --- /dev/null +++ b/ui/libs/utils/src/console_b.ts @@ -0,0 +1,13 @@ +import { _b } from '@ctx-core/object' +const in_console = console +export const console_b = _b('console', () => { + const console = { + debug: in_console.debug, + info: in_console.info, + log: in_console.log, + warn: in_console.warn, + error: in_console.error, + trace: in_console.trace, + } + return console +}) diff --git a/ui/libs/utils/src/delay.ts b/ui/libs/utils/src/delay.ts new file mode 100644 index 00000000..b2ef630a --- /dev/null +++ b/ui/libs/utils/src/delay.ts @@ -0,0 +1 @@ +export const delay = ms=>new Promise(r=>setTimeout(r, ms)) diff --git a/ui/libs/utils/src/index.ts b/ui/libs/utils/src/index.ts new file mode 100644 index 00000000..f9121241 --- /dev/null +++ b/ui/libs/utils/src/index.ts @@ -0,0 +1,6 @@ +export * from './base64ToBuffer' +export * from './bufferToBase64' +export * from './console_b' +export * from './delay' +export * from './EntryHash' +export * from './HeaderHash' diff --git a/ui/libs/utils/tsconfig.json b/ui/libs/utils/tsconfig.json new file mode 100644 index 00000000..3667b080 --- /dev/null +++ b/ui/libs/utils/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "declarationDir": "dist", + "declaration": true + } +} diff --git a/ui/libs/zome-client/.gitignore b/ui/libs/zome-client/.gitignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/ui/libs/zome-client/.gitignore @@ -0,0 +1 @@ +dist diff --git a/ui/libs/zome-client/README.md b/ui/libs/zome-client/README.md new file mode 100644 index 00000000..410269ba --- /dev/null +++ b/ui/libs/zome-client/README.md @@ -0,0 +1 @@ +# @syn-ui/model diff --git a/ui/libs/zome-client/package.json b/ui/libs/zome-client/package.json new file mode 100644 index 00000000..960963d4 --- /dev/null +++ b/ui/libs/zome-client/package.json @@ -0,0 +1,17 @@ +{ + "name": "@syn-ui/zome-client", + "version": "1.0.0", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./src/index.ts", + "scripts": {}, + "devDependencies": { + "@ctx-core/function": "^17.8.2", + "@ctx-core/object": "^17.5.24", + "@ctx-core/store": "^24.7.17", + "@holochain/conductor-api": "^0.0.4", + "@syn-ui/utils": "workspace:^1.0.0", + "buffer": "^6.0.3", + "typescript": "^4.2.4" + } +} \ No newline at end of file diff --git a/ui/libs/zome-client/src/ApiResponse.ts b/ui/libs/zome-client/src/ApiResponse.ts new file mode 100644 index 00000000..9ce437f1 --- /dev/null +++ b/ui/libs/zome-client/src/ApiResponse.ts @@ -0,0 +1,7 @@ +import type { AgentPubKey } from '@holochain/conductor-api' +import type { Participant } from './Participant' +export interface ApiResponse extends Record<string, any> { + pubKey:AgentPubKey + gone?:AgentPubKey[] + participants:Participant[] +} diff --git a/ui/libs/zome-client/src/Buffer.ts b/ui/libs/zome-client/src/Buffer.ts new file mode 100644 index 00000000..8dbd1d18 --- /dev/null +++ b/ui/libs/zome-client/src/Buffer.ts @@ -0,0 +1,10 @@ +import { Buffer } from 'buffer' +export { Buffer } +if (typeof window !== 'undefined') { + window.Buffer = Buffer +} +declare global { + interface Window { + Buffer:typeof Buffer + } +} diff --git a/ui/libs/zome-client/src/Commit.ts b/ui/libs/zome-client/src/Commit.ts new file mode 100644 index 00000000..a648e47c --- /dev/null +++ b/ui/libs/zome-client/src/Commit.ts @@ -0,0 +1,17 @@ +import type { AgentPubKey } from '@holochain/conductor-api' +import type { EntryHash, HeaderHash } from '@syn-ui/utils/dist' +import type { Delta } from './Delta' +export interface Commit { + snapshot:HeaderHash + change:{ + deltas:string[]|Delta[] + content_hash:EntryHash + previous_change:EntryHash + meta:{ + contributors:string[] + witnesses:string[] + app_specific:null + } + }, + participants:AgentPubKey[] +} diff --git a/ui/libs/zome-client/src/Content.ts b/ui/libs/zome-client/src/Content.ts new file mode 100644 index 00000000..64b23020 --- /dev/null +++ b/ui/libs/zome-client/src/Content.ts @@ -0,0 +1,5 @@ +export interface Content { + title:string + body:string + meta:Record<string, number> +} diff --git a/ui/libs/zome-client/src/Delta.ts b/ui/libs/zome-client/src/Delta.ts new file mode 100644 index 00000000..e244e969 --- /dev/null +++ b/ui/libs/zome-client/src/Delta.ts @@ -0,0 +1,27 @@ +export type DeltaValue = TitleDeltaValue|AddDeltaValue|DeleteDeltaValue|MetaDeltaValue +export interface Delta { + type:string + value:DeltaValue +} +export type TitleDeltaValue = string +export interface TitleDelta extends Delta { + type:'Title' + value:TitleDeltaValue +} +export type AddDeltaValue = [number, string] +export interface AddDelta extends Delta { + type:'Add' + value:AddDeltaValue +} +export type DeleteDeltaValue = [number, number] +export interface DeleteDelta extends Delta { + type:'Delete' + value:DeleteDeltaValue +} +export interface MetaDeltaValue { + setLoc:[string, number] +} +export interface MetaDelta extends Delta { + type:'Meta' + value:MetaDeltaValue +} diff --git a/ui/libs/zome-client/src/Folk.ts b/ui/libs/zome-client/src/Folk.ts new file mode 100644 index 00000000..ea315a2a --- /dev/null +++ b/ui/libs/zome-client/src/Folk.ts @@ -0,0 +1,22 @@ +import type { AgentPubKey } from '@holochain/conductor-api' +import type { FolkColors } from './FolkColors' +export interface Folk { + pubKey:AgentPubKey + inSession?:boolean + colors?:FolkColors + lastSeen?:number +} +export type pk = 'pubKey' +export type PubKeyToFolkRecord = { + [L in keyof Exclude<any, 'pubKey'>]:Folk; +}&{ + pubKey?:AgentPubKey +} +export enum FolkStatus { + FOLK_SEEN = 1, + FOLK_GONE = 2, + FOLK_UNKNOWN = 3, +} +export const FOLK_SEEN = FolkStatus.FOLK_SEEN +export const FOLK_GONE = FolkStatus.FOLK_GONE +export const FOLK_UNKNOWN = FolkStatus.FOLK_UNKNOWN diff --git a/ui/libs/zome-client/src/FolkColors.ts b/ui/libs/zome-client/src/FolkColors.ts new file mode 100644 index 00000000..320838b9 --- /dev/null +++ b/ui/libs/zome-client/src/FolkColors.ts @@ -0,0 +1,8 @@ +import type { HSL } from './HSL' +export interface FolkColors { + primary:HSL + hexagon:HSL + selection:HSL + lookingSelection:HSL + lookingCursor:HSL +} diff --git a/ui/libs/zome-client/src/HSL.ts b/ui/libs/zome-client/src/HSL.ts new file mode 100644 index 00000000..c183e1fc --- /dev/null +++ b/ui/libs/zome-client/src/HSL.ts @@ -0,0 +1 @@ +export type HSL = [number, number, number] diff --git a/ui/libs/zome-client/src/Participant.ts b/ui/libs/zome-client/src/Participant.ts new file mode 100644 index 00000000..31d2a9cf --- /dev/null +++ b/ui/libs/zome-client/src/Participant.ts @@ -0,0 +1,5 @@ +import type { HoloHash } from '@holochain/conductor-api' +export interface Participant extends HoloHash { + pubKey:HoloHash + meta:number +} diff --git a/ui/libs/zome-client/src/SessionInfo.ts b/ui/libs/zome-client/src/SessionInfo.ts new file mode 100644 index 00000000..55cff66b --- /dev/null +++ b/ui/libs/zome-client/src/SessionInfo.ts @@ -0,0 +1,12 @@ +import type { AgentPubKey } from '@holochain/conductor-api' +import type { EntryHash } from '@syn-ui/utils' +import type { Content } from './Content' +export interface SessionInfo { + type:'SessionInfo' + session:EntryHash + scribe:AgentPubKey + snapshot_content:Content + snapshot_hash:EntryHash + deltas:string[] + content_hash:EntryHash +} diff --git a/ui/libs/zome-client/src/Signal.ts b/ui/libs/zome-client/src/Signal.ts new file mode 100644 index 00000000..fafb5418 --- /dev/null +++ b/ui/libs/zome-client/src/Signal.ts @@ -0,0 +1,4 @@ +export interface Signal { + signal_name:string, + signal_payload? +} diff --git a/ui/libs/zome-client/src/StateForSync.ts b/ui/libs/zome-client/src/StateForSync.ts new file mode 100644 index 00000000..6ba4bb93 --- /dev/null +++ b/ui/libs/zome-client/src/StateForSync.ts @@ -0,0 +1,14 @@ +import type { EntryHash, HeaderHash } from '@syn-ui/utils' +import type { Delta } from './Delta' +export interface StateForSync { + snapshot:EntryHash + commit?:HeaderHash + commit_content_hash:EntryHash + deltas:Delta[] +} +export interface StateForSync_serialized_I { + snapshot:EntryHash + commit?:HeaderHash + commit_content_hash:EntryHash + deltas:string[] +} diff --git a/ui/libs/zome-client/src/agent_pub_key_b.ts b/ui/libs/zome-client/src/agent_pub_key_b.ts new file mode 100644 index 00000000..17c5989c --- /dev/null +++ b/ui/libs/zome-client/src/agent_pub_key_b.ts @@ -0,0 +1,9 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { cell_id_b } from './cell_id_b' +export const agent_pub_key_b = _b('agent_pub_key', (ctx)=>{ + const cell_id = cell_id_b(ctx) + return derived$(cell_id, $cell_id=> + $cell_id?.[1] + ) +}) diff --git a/ui/libs/zome-client/src/appInfo_b.ts b/ui/libs/zome-client/src/appInfo_b.ts new file mode 100644 index 00000000..77f1f446 --- /dev/null +++ b/ui/libs/zome-client/src/appInfo_b.ts @@ -0,0 +1,52 @@ +import type { InstalledAppInfo, AppWebsocket } from '@holochain/conductor-api' +import { _b, assign } from '@ctx-core/object' +import { _readable_set_ctx$, derived$, Readable$, Writable$ } from '@ctx-core/store' +import { app_ws_b } from './app_ws_b' +import { app_id_b } from './app_id_b' +import { app_port_b } from './app_port_b' +export const appInfo_b = _b('appInfo', (ctx)=>{ + const app_id = app_id_b(ctx) + const app_ws = app_ws_b(ctx) + const app_port = app_port_b(ctx) + const { store: appInfo_frame, set: set_appInfo_frame } = _readable_set_ctx$<$appInfo_frame_T>({ + $app_ws: app_ws.$, + $app_id: app_id.$, + $app_port: app_port.$, + done: false, + }) + const appInfo:appInfo_T = assign( + derived$([app_ws, app_id, app_port], ([$app_ws, $app_id, $app_port], set)=>{ + if (!$app_ws || !$app_id || !$app_port) return + const $appInfo_frame = { $app_ws, $app_id, $app_port, done: false } + if (_is_current($appInfo_frame)) return + set_appInfo_frame($appInfo_frame) + ;(async ()=>{ + const $appInfo = await $app_ws.appInfo({ installed_app_id: $app_id }) + if (!_is_current($appInfo_frame)) return + set($appInfo) + set_appInfo_frame(assign($appInfo_frame, { done: true })) + })() + }), { + appInfo_frame + } + ) as appInfo_T + return appInfo + function _is_current(compare_$appInfo_frame:$appInfo_frame_T) { + const $appInfo_frame = appInfo_frame.$ + return ( + $appInfo_frame.$app_ws === compare_$appInfo_frame.$app_ws + && $appInfo_frame.$app_id === compare_$appInfo_frame.$app_id + && $appInfo_frame.$app_port === compare_$appInfo_frame.$app_port + ) + } +}) +export type $appInfo_T = InstalledAppInfo +export interface $appInfo_frame_T { + $app_ws?:AppWebsocket + $app_id?:string + $app_port?:number + done:boolean +} +export interface appInfo_T extends Readable$<$appInfo_T>, $appInfo_frame_T { + appInfo_frame:Writable$<$appInfo_frame_T> +} diff --git a/ui/libs/zome-client/src/app_id_b.ts b/ui/libs/zome-client/src/app_id_b.ts new file mode 100644 index 00000000..0a526d49 --- /dev/null +++ b/ui/libs/zome-client/src/app_id_b.ts @@ -0,0 +1,5 @@ +import { _b } from '@ctx-core/object' +import { writable$ } from '@ctx-core/store' +export const app_id_b = _b('app_id', ()=>{ + return writable$<undefined|string>(undefined) +}) diff --git a/ui/libs/zome-client/src/app_port_b.ts b/ui/libs/zome-client/src/app_port_b.ts new file mode 100644 index 00000000..5c57a249 --- /dev/null +++ b/ui/libs/zome-client/src/app_port_b.ts @@ -0,0 +1,5 @@ +import { _b } from '@ctx-core/object' +import { writable$ } from '@ctx-core/store' +export const app_port_b = _b('app_port', ()=>{ + return writable$<undefined|number>(undefined) +}) diff --git a/ui/libs/zome-client/src/app_ws_b.ts b/ui/libs/zome-client/src/app_ws_b.ts new file mode 100644 index 00000000..c0674ca7 --- /dev/null +++ b/ui/libs/zome-client/src/app_ws_b.ts @@ -0,0 +1,62 @@ +import { AppSignalCb, AppWebsocket } from '@holochain/conductor-api' +import { _b, assign } from '@ctx-core/object' +import { _readable_set_ctx$, Readable$ } from '@ctx-core/store' +import { app_port_b } from './app_port_b' +export const app_ws_b = _b('app_ws', (ctx)=>{ + const app_port = app_port_b(ctx) + const { store, set } = _readable_set_ctx$<$app_ws_T>(undefined) + const { store: app_ws_frame, set: set_app_ws_frame } = _readable_set_ctx$<$app_ws_frame_T>( + _init_$app_ws_frame() + ) + const app_ws:app_ws_T = assign(store, { + load, + close, + app_ws_frame + }) + return app_ws + async function load(signal_fn:AppSignalCb) { + const $app_port = app_port.$ + if (!$app_port || !($app_port > 0)) throw `app_port.$ must have a value > 0` + const $app_ws_frame = app_ws_frame.$ + if ($app_ws_frame.$app_port === $app_port) return + if ($app_ws_frame.$app_ws) { + await $app_ws_frame.$app_ws.client.socket.close() + } + set_app_ws_frame({ $app_port, done: false }) + const $app_ws = await _$app_ws($app_port, signal_fn) + set_app_ws_frame({ $app_ws, $app_port, done: true }) + set($app_ws) + } + async function close() { + const $app_ws = app_ws.$ + if ($app_ws) { + await $app_ws.client.socket.close() + } + set(undefined) + set_app_ws_frame(_init_$app_ws_frame()) + } + function _init_$app_ws_frame() { + return { + $app_ws: store.$, + $app_port: app_port.$, + done: true + } + } +}) +export async function _$app_ws(app_port:number, signal_fn:AppSignalCb) { + return await AppWebsocket.connect( + `ws://localhost:${app_port}`, + 30000, + (signal)=>signal_fn(signal)) +} +export type $app_ws_T = undefined|AppWebsocket +export interface app_ws_T extends Readable$<$app_ws_T> { + load(signal_fn:AppSignalCb):Promise<void> + close():Promise<void> + app_ws_frame:Readable$<$app_ws_frame_T> +} +export interface $app_ws_frame_T { + $app_ws?:$app_ws_T + $app_port?:number + done:boolean +} diff --git a/ui/libs/zome-client/src/cell_id_b.ts b/ui/libs/zome-client/src/cell_id_b.ts new file mode 100644 index 00000000..1ffe05cc --- /dev/null +++ b/ui/libs/zome-client/src/cell_id_b.ts @@ -0,0 +1,9 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { appInfo_b } from './appInfo_b' +export const cell_id_b = _b('cell_id', (ctx)=>{ + const appInfo = appInfo_b(ctx) + return derived$(appInfo, $appInfo=>{ + return $appInfo?.cell_data?.[0]?.cell_id + }) +}) diff --git a/ui/libs/zome-client/src/decodeJson.ts b/ui/libs/zome-client/src/decodeJson.ts new file mode 100644 index 00000000..d97f3031 --- /dev/null +++ b/ui/libs/zome-client/src/decodeJson.ts @@ -0,0 +1,17 @@ +import { base64ToBuffer } from '@syn-ui/utils' +import type { ApiResponse } from './ApiResponse' +export function decodeJson(jsonStr:string):ApiResponse { + return JSON.parse(jsonStr, function (key:string, value:string) { + // the receiver function looks for the typed array flag + try { + if (key == 'pubKey') { + return base64ToBuffer(value) + } + } catch (e) { + console.warn('decodeJson Error:', e) + } + + // if flag not found no conversion is done + return value + }) +} diff --git a/ui/libs/zome-client/src/dna_b.ts b/ui/libs/zome-client/src/dna_b.ts new file mode 100644 index 00000000..ddb9f899 --- /dev/null +++ b/ui/libs/zome-client/src/dna_b.ts @@ -0,0 +1,9 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { cell_id_b } from './cell_id_b' +export const dna_b = _b('dna', (ctx)=>{ + const cell_id = cell_id_b(ctx) + return derived$(cell_id, $cell_id=> + $cell_id?.[0] + ) +}) diff --git a/ui/libs/zome-client/src/dna_str_b.ts b/ui/libs/zome-client/src/dna_str_b.ts new file mode 100644 index 00000000..3a2f202f --- /dev/null +++ b/ui/libs/zome-client/src/dna_str_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { bufferToBase64 } from '@syn-ui/utils' +import { dna_b } from './dna_b' +export const dna_str_b = _b('dna_str', (ctx)=>{ + const dna = dna_b(ctx) + return derived$(dna, $dna=> + $dna ? bufferToBase64($dna) : null + ) +}) diff --git a/ui/libs/zome-client/src/encodeJson.ts b/ui/libs/zome-client/src/encodeJson.ts new file mode 100644 index 00000000..a5555aaf --- /dev/null +++ b/ui/libs/zome-client/src/encodeJson.ts @@ -0,0 +1,13 @@ +import { bufferToBase64 } from '@syn-ui/utils' +export function encodeJson(obj) { + return JSON.stringify(obj, function (key, value) { + if (key === 'pubKey') { + if (typeof window !== 'undefined') { + return bufferToBase64(value) // In the browser it's the actual array + } else { + return bufferToBase64(Buffer.from(value.data)) // In node it's an object + } + } + return value + }) +} diff --git a/ui/libs/zome-client/src/index.ts b/ui/libs/zome-client/src/index.ts new file mode 100644 index 00000000..350012a5 --- /dev/null +++ b/ui/libs/zome-client/src/index.ts @@ -0,0 +1,38 @@ +export * from './agent_pub_key_b' +export * from './ApiResponse' +export * from './app_id_b' +export * from './appInfo_b' +export * from './app_port_b' +export * from './app_ws_b' +export * from './Buffer' +export * from './cell_id_b' +export * from './Commit' +export * from './Content' +export * from './decodeJson' +export * from './Delta' +export * from './dna_b' +export * from './dna_str_b' +export * from './encodeJson' +export * from './Folk' +export * from './FolkColors' +export * from './HSL' +export * from './me_b' +export * from './my_tag_b' +export * from './Participant' +export * from './rpc_b' +export * from './rpc_commit_b' +export * from './rpc_get_content_b' +export * from './rpc_get_folks_b' +export * from './rpc_get_session_b' +export * from './rpc_get_sessions_b' +export * from './rpc_hash_content_b' +export * from './rpc_new_session_b' +export * from './rpc_send_change_b' +export * from './rpc_send_change_request_b' +export * from './rpc_send_folk_lore_b' +export * from './rpc_send_heartbeat_b' +export * from './rpc_send_sync_request_b' +export * from './rpc_send_sync_response_b' +export * from './SessionInfo' +export * from './Signal' +export * from './StateForSync' diff --git a/ui/libs/zome-client/src/me_b.ts b/ui/libs/zome-client/src/me_b.ts new file mode 100644 index 00000000..340f3990 --- /dev/null +++ b/ui/libs/zome-client/src/me_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { bufferToBase64 } from '@syn-ui/utils' +import { agent_pub_key_b } from './agent_pub_key_b' +export const me_b = _b('me', (ctx)=>{ + const agent_pub_key = agent_pub_key_b(ctx) + return derived$(agent_pub_key, $agent_pub_key=> + $agent_pub_key ? bufferToBase64($agent_pub_key) : null + ) +}) diff --git a/ui/libs/zome-client/src/my_tag_b.ts b/ui/libs/zome-client/src/my_tag_b.ts new file mode 100644 index 00000000..6fcd5ac5 --- /dev/null +++ b/ui/libs/zome-client/src/my_tag_b.ts @@ -0,0 +1,9 @@ +import { _b } from '@ctx-core/object' +import { derived$ } from '@ctx-core/store' +import { me_b } from './me_b' +export const my_tag_b = _b('my_tag', (ctx)=>{ + const me = me_b(ctx) + return derived$(me, $me=> + $me ? $me.slice(-4) : null + ) +}) diff --git a/ui/libs/zome-client/src/rpc_b.ts b/ui/libs/zome-client/src/rpc_b.ts new file mode 100644 index 00000000..0c25a9d7 --- /dev/null +++ b/ui/libs/zome-client/src/rpc_b.ts @@ -0,0 +1,44 @@ +import type { AppWebsocket } from '@holochain/conductor-api' +import { _b } from '@ctx-core/object' +import { I } from '@ctx-core/combinators' +import { subscribe_wait_timeout } from '@ctx-core/store' +import { console_b } from '@syn-ui/utils' +import { app_ws_b } from './app_ws_b' +import { cell_id_b } from './cell_id_b' +import { agent_pub_key_b } from './agent_pub_key_b' +import { appInfo_b } from './appInfo_b' +export const rpc_b = _b('rpc', (ctx)=>{ + const console = console_b(ctx) + const app_ws = app_ws_b(ctx) + const cell_id = cell_id_b(ctx) + const agent_pub_key = agent_pub_key_b(ctx) + return rpc + async function rpc(fn_name:string, payload?:any, timeout?:number) { + try { + const zome_name = 'syn' + console.log(`Making zome call ${fn_name} with:`, payload) + const $app_ws = await subscribe_wait_timeout(app_ws, I, 10_000) as AppWebsocket + await subscribe_wait_timeout(appInfo_b(ctx), I, 10_000) + const $cell_id = await subscribe_wait_timeout(cell_id, I, 10_000) + const $agent_pub_key = await subscribe_wait_timeout(agent_pub_key, I, 10_000) + const result = await $app_ws.callZome( + { + cap: null, + cell_id: $cell_id, + zome_name, + fn_name, + provenance: $agent_pub_key, + payload + }, + timeout + ) + return result + } catch (error) { + console.log(`ERROR: rpc ${fn_name}`, { error }) + throw(error) + // if (error == 'Error: Socket is not open') { + // TODO return doResetConnection(dispatch) + // } + } + } +}) diff --git a/ui/libs/zome-client/src/rpc_commit_b.ts b/ui/libs/zome-client/src/rpc_commit_b.ts new file mode 100644 index 00000000..b18db96c --- /dev/null +++ b/ui/libs/zome-client/src/rpc_commit_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import type { HeaderHash } from '@syn-ui/utils' +import { rpc_b } from './rpc_b' +import type { Commit } from './Commit' +export const rpc_commit_b = _b('rpc_commit', (ctx)=>{ + const rpc = rpc_b(ctx) + return async function rpc_commit(commit:Commit):Promise<HeaderHash> { + return rpc('commit', commit) + } +}) diff --git a/ui/libs/zome-client/src/rpc_get_content_b.ts b/ui/libs/zome-client/src/rpc_get_content_b.ts new file mode 100644 index 00000000..9bbbcb7e --- /dev/null +++ b/ui/libs/zome-client/src/rpc_get_content_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import type { EntryHash } from '@syn-ui/utils' +import { rpc_b } from './rpc_b' +import type { SessionInfo } from './SessionInfo' +export const rpc_get_content_b = _b('rpc_get_content', (ctx)=>{ + const rpc = rpc_b(ctx) + return async function rpc_get_content(content_hash:EntryHash):Promise<SessionInfo> { + return rpc('get_content', content_hash) + } +}) diff --git a/ui/libs/zome-client/src/rpc_get_folks_b.ts b/ui/libs/zome-client/src/rpc_get_folks_b.ts new file mode 100644 index 00000000..d9eb34bf --- /dev/null +++ b/ui/libs/zome-client/src/rpc_get_folks_b.ts @@ -0,0 +1,9 @@ +import { _b } from '@ctx-core/object' +import { rpc_b } from './rpc_b' +import type { HoloHash } from '@holochain/conductor-api' +export const rpc_get_folks_b = _b('rpc_get_folks', (ctx)=>{ + const rpc = rpc_b(ctx) + return async function rpc_get_folks():Promise<HoloHash[]> { + return await rpc('get_folks') + } +}) diff --git a/ui/libs/zome-client/src/rpc_get_session_b.ts b/ui/libs/zome-client/src/rpc_get_session_b.ts new file mode 100644 index 00000000..3a6fa2e2 --- /dev/null +++ b/ui/libs/zome-client/src/rpc_get_session_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import type { EntryHash } from '@syn-ui/utils' +import { rpc_b } from './rpc_b' +import type { SessionInfo } from './SessionInfo' +export const rpc_get_session_b = _b('rpc_get_session', (ctx)=>{ + const rpc = rpc_b(ctx) + return async function rpc_get_session(session_hash:EntryHash):Promise<SessionInfo> { + return rpc('get_session', session_hash) + } +}) diff --git a/ui/libs/zome-client/src/rpc_get_sessions_b.ts b/ui/libs/zome-client/src/rpc_get_sessions_b.ts new file mode 100644 index 00000000..2103dfe3 --- /dev/null +++ b/ui/libs/zome-client/src/rpc_get_sessions_b.ts @@ -0,0 +1,9 @@ +import { _b } from '@ctx-core/object' +import type { EntryHash } from '@syn-ui/utils' +import { rpc_b } from './rpc_b' +export const rpc_get_sessions_b = _b('rpc_get_sessions', (ctx)=>{ + const rpc = rpc_b(ctx) + return async function rpc_get_sessions():Promise<EntryHash[]> { + return await rpc('get_sessions') + } +}) diff --git a/ui/libs/zome-client/src/rpc_hash_content_b.ts b/ui/libs/zome-client/src/rpc_hash_content_b.ts new file mode 100644 index 00000000..3028d7f9 --- /dev/null +++ b/ui/libs/zome-client/src/rpc_hash_content_b.ts @@ -0,0 +1,10 @@ +import { _b } from '@ctx-core/object' +import type { EntryHash } from '@syn-ui/utils/dist' +import type { Content } from './Content' +import { rpc_b } from './rpc_b' +export const rpc_hash_content_b = _b('rpc_hash_content', (ctx)=>{ + const rpc = rpc_b(ctx) + return async function rpc_hash_content(content:Content):Promise<EntryHash> { + return rpc('hash_content', content) + } +}) diff --git a/ui/libs/zome-client/src/rpc_new_session_b.ts b/ui/libs/zome-client/src/rpc_new_session_b.ts new file mode 100644 index 00000000..f255d8b4 --- /dev/null +++ b/ui/libs/zome-client/src/rpc_new_session_b.ts @@ -0,0 +1,12 @@ +import { _b } from '@ctx-core/object' +import { rpc_b } from './rpc_b' +import type { Content } from './Content' +import type { SessionInfo } from './SessionInfo' +export const rpc_new_session_b = _b('rpc_new_session', (ctx)=>{ + const rpc = rpc_b(ctx) + return async function rpc_new_session( + content:Content = { title: '', body: '', meta: {} } + ):Promise<SessionInfo> { + return rpc('new_session', { content }) + } +}) diff --git a/ui/libs/zome-client/src/rpc_send_change_b.ts b/ui/libs/zome-client/src/rpc_send_change_b.ts new file mode 100644 index 00000000..062ebe1b --- /dev/null +++ b/ui/libs/zome-client/src/rpc_send_change_b.ts @@ -0,0 +1,23 @@ +import { _b } from '@ctx-core/object' +import type { HoloHash } from '@holochain/conductor-api' +import type { Delta } from './Delta' +import { rpc_b } from './rpc_b' +export const rpc_send_change_b = _b('rpc_send_change', (ctx)=>{ + const rpc = rpc_b(ctx) + return async function rpc_send_change( + { index, deltas, participants }:rpc_send_change_params_I + ):Promise<void> { + if (participants.length > 0) { + const delta_json_a1 = deltas.map(d=>JSON.stringify(d)) + return rpc('send_change', { + participants, + change: [index, delta_json_a1] + }) + } + } +}) +export interface rpc_send_change_params_I { + index:number + deltas:Delta[] + participants:HoloHash[] +} diff --git a/ui/libs/zome-client/src/rpc_send_change_request_b.ts b/ui/libs/zome-client/src/rpc_send_change_request_b.ts new file mode 100644 index 00000000..790a5958 --- /dev/null +++ b/ui/libs/zome-client/src/rpc_send_change_request_b.ts @@ -0,0 +1,24 @@ +import type { HoloHash } from '@holochain/conductor-api' +import { _b } from '@ctx-core/object' +import { rpc_b } from './rpc_b' +import type { Delta } from './Delta' +export const rpc_send_change_request_b = _b('rpc_send_change_request', (ctx)=>{ + const rpc = rpc_b(ctx) + return async function rpc_send_change_request( + { index, deltas, scribe }:rpc_send_change_request_params_I + ):Promise<void> { + const delta_json_a1 = deltas.map(delta=>JSON.stringify(delta)) + return rpc('send_change_request', { + scribe, + change: [ + index, + delta_json_a1 + ] + }) + } +}) +export interface rpc_send_change_request_params_I { + index:number + deltas:Delta[] + scribe:HoloHash +} diff --git a/ui/libs/zome-client/src/rpc_send_folk_lore_b.ts b/ui/libs/zome-client/src/rpc_send_folk_lore_b.ts new file mode 100644 index 00000000..e0efc2df --- /dev/null +++ b/ui/libs/zome-client/src/rpc_send_folk_lore_b.ts @@ -0,0 +1,22 @@ +import type { AgentPubKey } from '@holochain/conductor-api' +import { _b } from '@ctx-core/object' +import { rpc_b } from './rpc_b' +import { encodeJson } from './encodeJson' +import type { PubKeyToFolkRecord } from './Folk' +export const rpc_send_folk_lore_b = _b('rpc_send_folk_lore', (ctx)=>{ + const rpc = rpc_b(ctx) + return function rpc_send_folk_lore({ participants, data }:SendFolkLoreInput) { + if (participants.length) { + const serialized_data = encodeJson(data) + return rpc('send_folk_lore', { participants, data: serialized_data }) + } + } +}) +export interface SendFolkLoreInput { + participants:AgentPubKey[] + data:SendFolkLoreInputData +} +export interface SendFolkLoreInputData { + participants?:PubKeyToFolkRecord, + gone?:AgentPubKey[] +} diff --git a/ui/libs/zome-client/src/rpc_send_heartbeat_b.ts b/ui/libs/zome-client/src/rpc_send_heartbeat_b.ts new file mode 100644 index 00000000..49e7514c --- /dev/null +++ b/ui/libs/zome-client/src/rpc_send_heartbeat_b.ts @@ -0,0 +1,13 @@ +import type { HoloHash } from '@holochain/conductor-api' +import { _b } from '@ctx-core/object' +import { rpc_b } from './rpc_b' +export const rpc_send_heartbeat_b = _b('rpc_send_heartbeat', (ctx)=>{ + const rpc = rpc_b(ctx) + return function rpc_send_heartbeat({ scribe, data }:rpc_send_heartbeat_params_I) { + return rpc('send_heartbeat', { scribe, data }) + } +}) +export interface rpc_send_heartbeat_params_I { + scribe:HoloHash + data:string +} diff --git a/ui/libs/zome-client/src/rpc_send_sync_request_b.ts b/ui/libs/zome-client/src/rpc_send_sync_request_b.ts new file mode 100644 index 00000000..9e01c5d8 --- /dev/null +++ b/ui/libs/zome-client/src/rpc_send_sync_request_b.ts @@ -0,0 +1,9 @@ +import type { AgentPubKey } from '@holochain/conductor-api' +import { _b } from '@ctx-core/object' +import { rpc_b } from './rpc_b' +export const rpc_send_sync_request_b = _b('rpc_send_sync_request', (ctx)=>{ + const rpc = rpc_b(ctx) + return async function rpc_send_sync_request(scribe:AgentPubKey) { + return rpc('send_sync_request', { scribe }) + } +}) diff --git a/ui/libs/zome-client/src/rpc_send_sync_response_b.ts b/ui/libs/zome-client/src/rpc_send_sync_response_b.ts new file mode 100644 index 00000000..54f202fb --- /dev/null +++ b/ui/libs/zome-client/src/rpc_send_sync_response_b.ts @@ -0,0 +1,19 @@ +import type { HoloHash } from '@holochain/conductor-api' +import { _b, assign } from '@ctx-core/object' +import { rpc_b } from './rpc_b' +import type { StateForSync, StateForSync_serialized_I } from './StateForSync' +export const rpc_send_sync_response_b = _b('rpc_send_sync_response', (ctx)=>{ + const rpc = rpc_b(ctx) + return function rpc_send_sync_response({ participant, state }:SendSyncResponseInput) { + const serialized_state:StateForSync_serialized_I = assign({}, state, { + deltas: state.deltas.map(delta=>JSON.stringify(delta)) + }) + return rpc('send_sync_response', { + participant, state: serialized_state + }) + } +}) +export interface SendSyncResponseInput { + participant:HoloHash + state:StateForSync +} diff --git a/ui/libs/zome-client/tsconfig.json b/ui/libs/zome-client/tsconfig.json new file mode 100644 index 00000000..06628288 --- /dev/null +++ b/ui/libs/zome-client/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "outDir": "dist", + "declarationDir": "dist", + "declaration": true + }, + "include": [ + "./src/**/*" + ], + "exclude": [ + "node_modules/**", + "**/node_modules/**", + "**/dist/**" + ] +} diff --git a/ui/package-lock.json b/ui/package-lock.json deleted file mode 100644 index 65c4f61e..00000000 --- a/ui/package-lock.json +++ /dev/null @@ -1,854 +0,0 @@ -{ - "name": "svelte-app", - "version": "1.0.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==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@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.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@holochain/conductor-api": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@holochain/conductor-api/-/conductor-api-0.0.1.tgz", - "integrity": "sha512-Vng5LG8JN1Zh7/s8OMOQKVih/Cz4zJo/6xpXnOYX148XC8bakKuaRXV08sTUWZMKElbq4BqZFTVcxJw3ouPt+w==", - "requires": { - "@msgpack/msgpack": "^2.1.0", - "@types/ws": "^7.2.4", - "isomorphic-ws": "^4.0.1", - "nanoid": "^3.1.9", - "ws": "^7.3.0" - }, - "dependencies": { - "ws": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", - "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==" - } - } - }, - "@msgpack/msgpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.5.1.tgz", - "integrity": "sha512-l44t7u0VxuHZT5D2zCdsRbkUPkrAJMu4wyXTvHr75eXICklf38NZncRqPYA4g9t7rprPuRCPYT5+pTLihuSKRA==", - "requires": { - "tslib": ">=2.0.0" - } - }, - "@polka/url": { - "version": "1.0.0-next.11", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.11.tgz", - "integrity": "sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA==" - }, - "@rollup/plugin-commonjs": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz", - "integrity": "sha512-LuNyypCP3msCGVQJ7ki8PqYdpjfEkE/xtFa5DqlF+7IBD0JsfMZ87C58heSwIMint58sAUZbt3ITqOmdQv/dXw==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "commondir": "^1.0.1", - "estree-walker": "^2.0.1", - "glob": "^7.1.6", - "is-reference": "^1.2.1", - "magic-string": "^0.25.7", - "resolve": "^1.17.0" - } - }, - "@rollup/plugin-node-resolve": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-10.0.0.tgz", - "integrity": "sha512-sNijGta8fqzwA1VwUEtTvWCx2E7qC70NMsDh4ZG13byAXYigBNZMxALhKUSycBks5gupJdq0lFrKumFrRZ8H3A==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.17.0" - } - }, - "@rollup/plugin-replace": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.4.tgz", - "integrity": "sha512-waBhMzyAtjCL1GwZes2jaE9MjuQ/DQF2BatH3fRivUF3z0JBFrU0U6iBNC/4WR+2rLKhaAhPWDNPYp4mI6RqdQ==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "dependencies": { - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - } - } - }, - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "@types/node": { - "version": "14.14.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.16.tgz", - "integrity": "sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==" - }, - "@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/ws": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.0.tgz", - "integrity": "sha512-Y29uQ3Uy+58bZrFLhX36hcI3Np37nqWE7ky5tjiDoy1GDZnIwVxS0CgF+s+1bXMzjKBFy+fqaRfb708iNzdinw==", - "requires": { - "@types/node": "*" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true - }, - "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" - } - }, - "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 - }, - "builtin-modules": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", - "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", - "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.4.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", - "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "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-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==", - "dev": true, - "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=", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "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-clear": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/console-clear/-/console-clear-1.1.1.tgz", - "integrity": "sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ==" - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "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 - }, - "estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "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" - } - }, - "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.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "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 - }, - "get-port": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", - "integrity": "sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=" - }, - "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.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "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-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "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==", - "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-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-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-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-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", - "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-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "requires": { - "@types/estree": "*" - } - }, - "isomorphic-ws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", - "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==" - }, - "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "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" - } - } - } - }, - "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 - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" - }, - "livereload": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.1.tgz", - "integrity": "sha512-9g7sua11kkyZNo2hLRCG3LuZZwqexoyEyecSlV8cAsfAVVCZqLzVir6XDqmH0r+Vzgnd5LrdHDMyjtFnJQLAYw==", - "dev": true, - "requires": { - "chokidar": "^3.3.0", - "livereload-js": "^3.1.0", - "opts": ">= 1.2.0", - "ws": "^6.2.1" - } - }, - "livereload-js": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.3.1.tgz", - "integrity": "sha512-CBu1gTEfzVhlOK1WASKAAJ9Qx1fHECTq0SUB67sfxwQssopTyvzqTlgl+c0h9pZ6V+Fzd2rc510ppuNusg9teQ==", - "dev": true - }, - "local-access": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/local-access/-/local-access-1.1.0.tgz", - "integrity": "sha512-XfegD5pyTAfb+GY6chk283Ox5z8WexG56OvM06RWLpAc/UHozO8X6xAxEkIitZOtsSMM1Yr3DkHgW5W+onLhCw==" - }, - "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "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==", - "dev": true - }, - "mime": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.7.tgz", - "integrity": "sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA==" - }, - "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" - } - }, - "mri": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.6.tgz", - "integrity": "sha512-oi1b3MfbyGa7FJMP9GmLTttni5JoICpYBRlq+x5V16fZbLsnL9N3wFqqIm/nIG43FjUFkFh9Epzp/kzUGUnJxQ==" - }, - "nanoid": { - "version": "3.1.22", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz", - "integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==" - }, - "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 - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "opts": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz", - "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", - "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-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 - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "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" - } - }, - "require-relative": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", - "dev": true - }, - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } - }, - "rollup": { - "version": "2.35.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.35.1.tgz", - "integrity": "sha512-q5KxEyWpprAIcainhVy6HfRttD9kutQpHbeqDTWnqAFNJotiojetK6uqmcydNMymBEtC4I8bCYR+J3mTMqeaUA==", - "dev": true, - "requires": { - "fsevents": "~2.1.2" - } - }, - "rollup-plugin-css-only": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-css-only/-/rollup-plugin-css-only-3.1.0.tgz", - "integrity": "sha512-TYMOE5uoD76vpj+RTkQLzC9cQtbnJNktHPB507FzRWBVaofg7KhIqq1kGbcVOadARSozWF883Ho9KpSPKH8gqA==", - "dev": true, - "requires": { - "@rollup/pluginutils": "4" - }, - "dependencies": { - "@rollup/pluginutils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.1.0.tgz", - "integrity": "sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==", - "dev": true, - "requires": { - "estree-walker": "^2.0.1", - "picomatch": "^2.2.2" - } - } - } - }, - "rollup-plugin-livereload": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.0.tgz", - "integrity": "sha512-oC/8NqumGYuphkqrfszOHUUIwzKsaHBICw6QRwT5uD07gvePTS+HW+GFwu6f9K8W02CUuTvtIM9AWJrbj4wE1A==", - "dev": true, - "requires": { - "livereload": "^0.9.1" - } - }, - "rollup-plugin-svelte": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.0.0.tgz", - "integrity": "sha512-cw4yv/5v1NQV3nPbpOJtikgkB+9mfSJaqKUdq7x5fVQJnwLtcdc2JOszBs5pBY+SemTs5pmJbdEMseEavbUtjQ==", - "dev": true, - "requires": { - "require-relative": "^0.8.7", - "rollup-pluginutils": "^2.8.2" - } - }, - "rollup-plugin-terser": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", - "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "jest-worker": "^26.2.1", - "serialize-javascript": "^4.0.0", - "terser": "^5.0.0" - } - }, - "rollup-pluginutils": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", - "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", - "dev": true, - "requires": { - "estree-walker": "^0.6.1" - }, - "dependencies": { - "estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "dev": true - } - } - }, - "sade": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz", - "integrity": "sha512-y5yauMD93rX840MwUJr7C1ysLFBgMspsdTo4UVrDg3fXDvtwOyIqykhVAAm6fk/3au77773itJStObgK+LKaiA==", - "requires": { - "mri": "^1.1.0" - } - }, - "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==", - "dev": true - }, - "semiver": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semiver/-/semiver-1.1.0.tgz", - "integrity": "sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==" - }, - "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "sirv": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.10.tgz", - "integrity": "sha512-H5EZCoZaggEUQy8ocKsF7WAToGuZhjJlLvM3XOef46CbdIgbNeQ1p32N1PCuCjkVYwrAVOSMacN6CXXgIzuspg==", - "requires": { - "@polka/url": "^1.0.0-next.9", - "mime": "^2.3.1", - "totalist": "^1.0.0" - } - }, - "sirv-cli": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/sirv-cli/-/sirv-cli-1.0.10.tgz", - "integrity": "sha512-8mLTRkvzpZXMyUZJ1whf84YHN/mm2r2+j5sU1ZYr5n2jA8VkFItNPk53oysOo+0QxBVp9aUjggkAsQp1d7L3OQ==", - "requires": { - "console-clear": "^1.1.0", - "get-port": "^3.2.0", - "kleur": "^3.0.0", - "local-access": "^1.0.1", - "sade": "^1.6.0", - "semiver": "^1.0.0", - "sirv": "^1.0.10", - "tinydate": "^1.0.0" - } - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "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 - } - } - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "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" - } - }, - "svelte": { - "version": "3.31.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.31.0.tgz", - "integrity": "sha512-r+n8UJkDqoQm1b+3tA3Lh6mHXKpcfOSOuEuIo5gE2W9wQYi64RYX/qE6CZBDDsP/H4M+N426JwY7XGH4xASvGQ==", - "dev": true - }, - "svelte-fa": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/svelte-fa/-/svelte-fa-2.2.0.tgz", - "integrity": "sha512-PN1H8AWDh+OwhwjJKTv4/zXvKvmvOEVSCVhIhji6Onx8XEw+CGf8BDR0BVUIp87IEX+DEqIo9pbyhgz8EoYZyA==" - }, - "terser": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.5.1.tgz", - "integrity": "sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.7.2", - "source-map-support": "~0.5.19" - } - }, - "tinydate": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tinydate/-/tinydate-1.3.0.tgz", - "integrity": "sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==" - }, - "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" - } - }, - "totalist": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", - "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==" - }, - "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - } - } -} diff --git a/ui/package.json b/ui/package.json deleted file mode 100644 index 9b309aee..00000000 --- a/ui/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "svelte-app", - "version": "1.0.0", - "scripts": { - "build": "rollup -c", - "dev": "rollup -c -w", - "start": "sirv public" - }, - "devDependencies": { - "@rollup/plugin-commonjs": "^16.0.0", - "@rollup/plugin-node-resolve": "^10.0.0", - "@rollup/plugin-replace": "^2.3.4", - "rollup": "^2.3.4", - "rollup-plugin-css-only": "^3.1.0", - "rollup-plugin-livereload": "^2.0.0", - "rollup-plugin-svelte": "^7.0.0", - "rollup-plugin-terser": "^7.0.0", - "svelte": "^3.0.0" - }, - "dependencies": { - "@holochain/conductor-api": "0.0.1", - "sirv-cli": "^1.0.0", - "svelte-fa": "^2.2.0" - } -} diff --git a/ui/rollup.config.js b/ui/rollup.config.js deleted file mode 100644 index 57c1671b..00000000 --- a/ui/rollup.config.js +++ /dev/null @@ -1,80 +0,0 @@ -import svelte from 'rollup-plugin-svelte'; -import commonjs from '@rollup/plugin-commonjs'; -import replace from '@rollup/plugin-replace'; -import resolve from '@rollup/plugin-node-resolve'; -import livereload from 'rollup-plugin-livereload'; -import { terser } from 'rollup-plugin-terser'; -import css from 'rollup-plugin-css-only'; - -const production = !process.env.ROLLUP_WATCH; - -function serve() { - let server; - - function toExit() { - if (server) server.kill(0); - } - - return { - writeBundle() { - if (server) return; - server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { - stdio: ['ignore', 'inherit', 'inherit'], - shell: true - }); - - process.on('SIGTERM', toExit); - process.on('exit', toExit); - } - }; -} - -export default { - input: 'src/main.js', - output: { - sourcemap: true, - format: 'iife', - name: 'app', - file: 'public/build/bundle.js' - }, - plugins: [ - replace({ - 'process.env.NODE_ENV': JSON.stringify(process.env.NODE) - }), - svelte({ - compilerOptions: { - // enable run-time checks when not in production - dev: !production - } - }), - // we'll extract any component CSS out into - // a separate file - better for performance - css({ output: 'bundle.css' }), - - // If you have external dependencies installed from - // npm, you'll most likely need these plugins. In - // some cases you'll need additional configuration - - // consult the documentation for details: - // https://github.com/rollup/plugins/tree/master/packages/commonjs - resolve({ - browser: true, - dedupe: ['svelte'] - }), - commonjs(), - - // In dev mode, call `npm run start` once - // the bundle has been generated - !production && serve(), - - // Watch the `public` directory and refresh the - // browser on changes when not in production - !production && livereload('public'), - - // If we're building for production (npm run build - // instead of npm run dev), minify - production && terser() - ], - watch: { - clearScreen: false - } -}; diff --git a/ui/scripts/setupTypeScript.js b/ui/scripts/setupTypeScript.js deleted file mode 100644 index d4710951..00000000 --- a/ui/scripts/setupTypeScript.js +++ /dev/null @@ -1,117 +0,0 @@ -// @ts-check - -/** This script modifies the project to support TS code in .svelte files like: - - <script lang="ts"> - export let name: string; - </script> - - As well as validating the code for CI. - */ - -/** To work on this script: - rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template -*/ - -const fs = require("fs") -const path = require("path") -const { argv } = require("process") - -const projectRoot = argv[2] || path.join(__dirname, "..") - -// Add deps to pkg.json -const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8")) -packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, { - "svelte-check": "^1.0.0", - "svelte-preprocess": "^4.0.0", - "@rollup/plugin-typescript": "^6.0.0", - "typescript": "^3.9.3", - "tslib": "^2.0.0", - "@tsconfig/svelte": "^1.0.0" -}) - -// Add script for checking -packageJSON.scripts = Object.assign(packageJSON.scripts, { - "validate": "svelte-check" -}) - -// Write the package JSON -fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " ")) - -// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too -const beforeMainJSPath = path.join(projectRoot, "src", "main.js") -const afterMainTSPath = path.join(projectRoot, "src", "main.ts") -fs.renameSync(beforeMainJSPath, afterMainTSPath) - -// Switch the app.svelte file to use TS -const appSveltePath = path.join(projectRoot, "src", "App.svelte") -let appFile = fs.readFileSync(appSveltePath, "utf8") -appFile = appFile.replace("<script>", '<script lang="ts">') -appFile = appFile.replace("export let name;", 'export let name: string;') -fs.writeFileSync(appSveltePath, appFile) - -// Edit rollup config -const rollupConfigPath = path.join(projectRoot, "rollup.config.js") -let rollupConfig = fs.readFileSync(rollupConfigPath, "utf8") - -// Edit imports -rollupConfig = rollupConfig.replace(`'rollup-plugin-terser';`, `'rollup-plugin-terser'; -import sveltePreprocess from 'svelte-preprocess'; -import typescript from '@rollup/plugin-typescript';`) - -// Replace name of entry point -rollupConfig = rollupConfig.replace(`'src/main.js'`, `'src/main.ts'`) - -// Add preprocessor -rollupConfig = rollupConfig.replace( - 'compilerOptions:', - 'preprocess: sveltePreprocess(),\n\t\t\tcompilerOptions:' -); - -// Add TypeScript -rollupConfig = rollupConfig.replace( - 'commonjs(),', - 'commonjs(),\n\t\ttypescript({\n\t\t\tsourceMap: !production,\n\t\t\tinlineSources: !production\n\t\t}),' -); -fs.writeFileSync(rollupConfigPath, rollupConfig) - -// Add TSConfig -const tsconfig = `{ - "extends": "@tsconfig/svelte/tsconfig.json", - - "include": ["src/**/*"], - "exclude": ["node_modules/*", "__sapper__/*", "public/*"] -}` -const tsconfigPath = path.join(projectRoot, "tsconfig.json") -fs.writeFileSync(tsconfigPath, tsconfig) - -// Delete this script, but not during testing -if (!argv[2]) { - // Remove the script - fs.unlinkSync(path.join(__filename)) - - // Check for Mac's DS_store file, and if it's the only one left remove it - const remainingFiles = fs.readdirSync(path.join(__dirname)) - if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') { - fs.unlinkSync(path.join(__dirname, '.DS_store')) - } - - // Check if the scripts folder is empty - if (fs.readdirSync(path.join(__dirname)).length === 0) { - // Remove the scripts folder - fs.rmdirSync(path.join(__dirname)) - } -} - -// Adds the extension recommendation -fs.mkdirSync(path.join(projectRoot, ".vscode")) -fs.writeFileSync(path.join(projectRoot, ".vscode", "extensions.json"), `{ - "recommendations": ["svelte.svelte-vscode"] -} -`) - -console.log("Converted to TypeScript.") - -if (fs.existsSync(path.join(projectRoot, "node_modules"))) { - console.log("\nYou will need to re-run your dependency manager to get started.") -} diff --git a/ui/src/App.svelte b/ui/src/App.svelte deleted file mode 100644 index 46ee8fe8..00000000 --- a/ui/src/App.svelte +++ /dev/null @@ -1,325 +0,0 @@ -<script> - import Editor from './Editor.svelte' - import Title from './Title.svelte' - import Folks from './Folks.svelte' - import Syn from './Syn.svelte' - import Debug from './Debug.svelte' - import History from './History.svelte' - import { content, scribeStr } from './stores.js' - - $: disconnected = false - - // definition of how to apply a delta to the content - // if the delta is destructive also returns what was - // destroyed for use by undo - function applyDelta(content, delta) { - switch(delta.type) { - case 'Title': - { - const deleted = content.title - content.title = delta.value - return [content, {delta, deleted}] - } - case 'Add': - { - const [loc, text] = delta.value - content.body = content.body.slice(0, loc) + text + content.body.slice(loc) - return [content, {delta}] - } - case 'Delete': - { - const [start, end] = delta.value - const deleted = content.body.slice(start,end) - content.body = content.body.slice(0, start) + content.body.slice(end) - return [content, {delta, deleted}] - } - case 'Meta': - { - const [tag, loc] = delta.value.setLoc - const deleted = [tag, content.meta[tag]] - content.meta[tag] = loc - return [content, {delta, deleted}] - } - } - } - - // definition of how to undo a change, returns a delta that will undo the change - function undo(change) { - const delta = change.delta - switch(delta.type) { - case 'Title': - return {type:'Title', value:change.deleted} - break - case 'Add': - const [loc, text] = delta.value - return {type:'Delete', value: [loc, loc+text.length]} - break - case 'Delete': - const [start, end] = delta.value - return {type:'Add', value:[start, change.deleted]} - break - case 'Meta': - return {type:'Meta', value:{setLoc: change.deleted}} - } - } - - // definition of how to convert a change to text for the history renderer - function changeToText(change) { - let delta = change.delta - let detail - switch(delta.type) { - case 'Add': - detail = `${delta.value[1]}@${delta.value[0]}` - break - case 'Delete': - detail = `${change.deleted}@${delta.value[0]}` - break - case 'Title': - detail = `${change.deleted}->${delta.value}` - break - case 'Meta': - detail = '' - } - return `${delta.type}:\n${detail}` - } - - - $: noscribe = $scribeStr === '' - let syn - - - // The debug drawer's ability to resized and hidden - let resizeable - let resizeHandle - const minDrawerSize = 0 - const maxDrawerSize = document.documentElement.clientHeight - 30 - 10 - const initResizeable = (resizeableEl) => { - resizeableEl.style.setProperty('--max-height', `${maxDrawerSize}px`) - resizeableEl.style.setProperty('--min-height', `${minDrawerSize}px`) - } - - const setDrawerHeight = (height) => { - document.documentElement.style.setProperty('--resizeable-height', `${height}px`) - } - const getDrawerHeight = () => { - const pxHeight = getComputedStyle(resizeable) - .getPropertyValue('--resizeable-height') - return parseInt(pxHeight, 10) - } - - const startDragging = (event) => { - event.preventDefault() - const host = resizeable - const startingDrawerHeight = getDrawerHeight() - const yOffset = event.pageY - - const mouseDragHandler = (moveEvent) => { - moveEvent.preventDefault() - const primaryButtonPressed = moveEvent.buttons === 1 - if (!primaryButtonPressed) { - setDrawerHeight(Math.min(Math.max(getDrawerHeight(), minDrawerSize), maxDrawerSize)) - window.removeEventListener('pointermove', mouseDragHandler) - return - } - setDrawerHeight(Math.min(Math.max((yOffset - moveEvent.pageY ) + startingDrawerHeight, minDrawerSize), maxDrawerSize)) - } - const remove = window.addEventListener('pointermove', mouseDragHandler) - } - - let drawerHidden = false - const hideDrawer = () => { - drawerHidden = true - } - const showDrawer = () => { - drawerHidden = false - } - - let tabShown = false; - const showTab = () => { - tabShown = true - } - const hideTab = () => { - tabShown = false - } - -</script> - -<style> - main { - padding: 1em; - background: hsla(100, 20%, 50%, .2); - grid-column: 1 / 2; - } - - .toolbar { - background: hsla(19, 20%, 50%, .2); - padding: 2rem; - grid-column: 1 / 2; - } - - .folks-tray { - min-width: calc((var(--folks-padding) * 2) + var(--folk-hex-width)); - width: auto; - background: hsla(255, 20%, 50%, .2); - grid-column: 2 / 3; - grid-row: 1/4; - } - - :global(:root) { - --resizeable-height: 200px; - --tab-width: 60px; - } - - .debug-drawer { - width: 100%; - box-sizing: border-box; - height: var(--resizeable-height); - min-height: var(--min-height); - max-height: var(--max-height); - background: hsla(180, 30%, 85%, 1); - position: absolute; - bottom: 0; - text-align: left; - grid-column: 1 / 2; - overflow: hidden; - z-index: 90; - } - - .hidden { - height: 0; - min-height: 0; - } - - .handle { - height: 1px; - width: 100%; - background-color: hsla(180, 15%, 65%, 1); - z-index: 100; - } - - .handle::after { - content: ''; - height: 9px; - position: absolute; - left: 0; - right: 0; - margin-bottom: -4px; - background-color: transparent; - cursor: ns-resize; - z-index: 101; - } - - /* tab styling reverse-engineered from Atom */ - .tab { - z-index: 130; - position: absolute; - width: var(--tab-width); - height: calc(var(--tab-width) / 2); - left: calc(50% - (var(--tab-width) / 2)); - bottom: var(--resizeable-height); - overflow: hidden; - margin-bottom: -2px; - border-top-left-radius: calc(var(--tab-width) / 2); - border-top-right-radius: calc(var(--tab-width) / 2); - - pointer-events: none; - } - - .tab-inner { - position: absolute; - box-sizing: border-box; /* borders included in size */ - width: var(--tab-width); - height: var(--tab-width); - background: hsla(180, 30%, 85%); - border: 1px solid hsla(180, 20%, 65%, 1); - color: hsla(180, 20%, 50%, 1); /* color of chevron */ - border-radius: calc(var(--tab-width) / 2); - cursor: pointer; - text-align: center; - - top: calc(var(--tab-width) / 2); - transition: transform 0.2s ease-out 0.2s; - } - - .tab.shown { - pointer-events: all; - } - - .tab-inner.shown { - transform: translateY(-50%); - transition: transform 0.2s ease-out 0s; - } - - /* allow the tab to pop up when drawer is hidden */ - .tab.drawer-hidden { - bottom: 0; - pointer-events: all; - } - - .tab-icon { - margin-top: 9px; - } - - .debug-content { - padding: 1rem; - word-wrap: break-word; - height: 100%; - overflow-y: scroll; - box-sizing: border-box; - z-index: 90; - } - - body { - font-family: system-ui, sans-serif; - } - - h1 { - color: #ff3e00; - text-transform: uppercase; - font-size: 4em; - font-weight: 100; - margin: auto; - } - - @media (min-width: 640px) { - main { - max-width: none; - } - } -</style> - -<svelte:head> - <script src='https://kit.fontawesome.com/80d72fa568.js' crossorigin='anonymous'></script> -</svelte:head> - -<div class='toolbar'> - <h1>SynText</h1> -<div class:noscribe> - <Title on:requestChange={(event) => syn.requestChange(event.detail)}/> -</div> -</div> -<main> -<div class:noscribe> - <Editor on:requestChange={(event) => syn.requestChange(event.detail)}/> -</div> - - -<Syn applyDeltaFn={applyDelta} undoFn={undo} bind:this={syn} /> -</main> - -<div class='folks-tray'> - <Folks /> -</div> - -<div class='tab' class:shown={tabShown} class:drawer-hidden={drawerHidden} on:mouseenter={showTab} on:mouseleave={hideTab}> - <div class='tab-inner' class:shown={tabShown} on:click={drawerHidden ? showDrawer() : hideDrawer()}> - <i class:drawer-hidden={drawerHidden} class="tab-icon fas {drawerHidden ? 'fa-chevron-up' : 'fa-chevron-down'}"></i> - </div> -</div> -<div class='debug-drawer' bind:this={resizeable} use:initResizeable on:mouseenter={showTab} on:mouseleave={hideTab} class:hidden={drawerHidden}> - <div class='handle' bind:this={resizeHandle} on:mousedown={startDragging}></div> - <div class='debug-content'> - <History changeToTextFn={changeToText}/> - <Debug /> - </div> -</div> diff --git a/ui/src/Debug.svelte b/ui/src/Debug.svelte deleted file mode 100644 index 8b54c045..00000000 --- a/ui/src/Debug.svelte +++ /dev/null @@ -1,22 +0,0 @@ -<script> - import { session, connection, scribeStr, nextIndex } from './stores.js' -</script> -<style> -ul { - margin-bottom: 1rem; -} -</style> -<div> - <ul> - <li> - {#if $connection && $connection.syn} - Connected to Dna: {$connection.syn.Dna} - {:else} - No connection - {/if} - <li>lastCommitedContentHash: {$session ? $session.contentHashStr : ''} - <li>session: {JSON.stringify($session)} - <li>nextIndex: {$nextIndex} - <li>scribe: {$scribeStr} - </ul> -</div> diff --git a/ui/src/Editor.svelte b/ui/src/Editor.svelte deleted file mode 100644 index 4d252369..00000000 --- a/ui/src/Editor.svelte +++ /dev/null @@ -1,111 +0,0 @@ -<script> - import { content, connection, session } from './stores.js' - import { createEventDispatcher } from 'svelte' - import { CSSifyHSL } from './colors.js' - - const dispatch = createEventDispatcher() - - function getLoc(tag) { - return $content.meta ? ($content.meta[tag] ? $content.meta[tag] : 0) : 0 - } - - let editor - $: myTag = $session ? $session.myTag : '' - $: editor_content1 = $content.body.slice(0, getLoc(myTag)) - $: editor_content2 = $content.body.slice(getLoc(myTag)) - - function addText(text) { - const loc = getLoc(myTag) - const deltas = [{type:'Add', value:[loc, text]}] - for (const [tag, tagLoc] of Object.entries($content.meta)) { - if (tagLoc >= loc) { - deltas.push({type:'Meta', value: {setLoc: [tag,tagLoc+text.length] }}) - } - } - dispatch('requestChange', deltas) - } - - function handleInput(event) { - const loc = getLoc(myTag) - const key = event.key - if (key.length == 1) { - addText(key) - } else { - switch (key) { - case 'ArrowRight': - if (loc < $content.body.length) { - dispatch('requestChange', [ - {type:'Meta', value:{setLoc: [myTag, loc+1]}} - ]) - } - break - case 'ArrowLeft': - if (loc > 0){ - dispatch('requestChange', [ - {type:'Meta', value:{setLoc: [myTag, loc-1]}} - ]) - } - break - case 'Enter': - addText('\n') - break - case 'Backspace': - if (loc>0) { - const deltas = [{type:'Delete', value:[loc-1, loc]}] - for (const [tag, tagLoc] of Object.entries($content.meta)) { - if (tagLoc >= loc) { - deltas.push({type:'Meta', value: {setLoc: [tag,tagLoc-1] }}) - } - } - dispatch('requestChange', deltas) - } - } - } - console.log('input', event.key) - } - function handleClick(e) { - const offset = window.getSelection().focusOffset - let loc = offset > 0 ? offset : 0 - if (window.getSelection().focusNode.parentElement == editor.lastChild) { - loc += editor_content1.length - } - if (loc != getLoc(myTag)) { - dispatch('requestChange', [ - {type:'Meta', value:{setLoc: [myTag, loc]}} - ]) - } - } - - let cursor - $: { - // wait for cursor and connection and color inside connection to exist - // before updating the cursor color - if (cursor && $connection && $connection.syn && $connection.syn.myColors) { - cursor.style['border-color'] = CSSifyHSL($connection.syn.myColors.primary) - } - } - -</script> -<style> - editor { - width: auto; - min-height: 10em; - border: 1px solid black; - background-color: hsla(0, 0%, 100%, .6); - font-family: Arial; - display: block; - white-space: pre-wrap; - margin: 1em 0 .4em 0; - padding: 4px; - } - .cursor { - display: inline; - border-left: solid 2px; /* Should be the Folk's main color */ - margin-right: -2px; - z-index: 10; - position: relative; - } -</style> -<editor on:click={handleClick} on:keydown={handleInput} tabindex=0 start=0 bind:this={editor}> - <span>{editor_content1}</span><span class='cursor' bind:this={cursor}></span><span>{editor_content2}</span> -</editor> diff --git a/ui/src/Folks.svelte b/ui/src/Folks.svelte deleted file mode 100644 index 0e070fde..00000000 --- a/ui/src/Folks.svelte +++ /dev/null @@ -1,24 +0,0 @@ -<script> - import { folks, connection, scribeStr } from './stores.js' - import Folk from './Folk.svelte' -</script> -<style> - :global(:root) { - --folks-padding: .75em; - --folks-grid-gap: 1rem; - } - .folks { - display: grid; - grid-gap: var(--folks-grid-gap); - padding: var(--folks-grid-gap) var(--folks-padding) var(--folks-padding); - place-items: center; - } -</style> -<div class='folks'> - {#if $connection && $connection.syn && $connection.syn.me} - <Folk me={true} pubKeyStr={$connection.syn.me} pubKey={$connection.syn.agentPubKey}/> - {/if} - {#each Object.keys($folks) as p} - <Folk pubKeyStr={p} pubKey={$folks[p].pubKey}/> - {/each} -</div> diff --git a/ui/src/Syn.svelte b/ui/src/Syn.svelte deleted file mode 100644 index 09bacc96..00000000 --- a/ui/src/Syn.svelte +++ /dev/null @@ -1,105 +0,0 @@ -<script> - import { connection, scribeStr, content, folks } from './stores.js' - import { createEventDispatcher } from 'svelte' - import { Connection} from './syn.js' - import { bufferToBase64 } from './utils.js' - - let session - - // this properties are the app-defined functions to apply and undo changes - export let applyDeltaFn - export let undoFn - - // this is the list of sessions returned by the DNA - let sessions - - export function requestChange(deltas) { - $session.requestChange(deltas) - } - - // ----------------------------------------------------------------------- - - const dispatch = createEventDispatcher() - - let adminPort=1234 - let appPort=8888 - let appId='syn' - async function toggle() { - if (!$connection) { - $connection = new Connection(appPort, appId) - await $connection.open({title:'', body:''}, applyDeltaFn) - - session = $connection.syn.session - - console.log('joining session...') - await $connection.joinSession() - sessions = $connection.sessions - } - else { - $connection.syn.clearState() - sessions = undefined - console.log('disconnected') - } - } - - async function commitChange() { - $session.commitChange() - } - - $: noscribe = $scribeStr === '' -</script> -<style> - :global(.noscribe) { - pointer-events: none; - position: relative; - } - - :global(.noscribe:after) { - content: ' '; - z-index: 20; - display: block; - position: absolute; - height: 100%; - top: 0; - left: 0; - right: 0; - background: rgba(255, 255, 255, 0.7); - } - input { - width: 4em; - //border: 2px solid red; - border-radius: 4px; - } - .session { - border-radius: 4px; - background-color: pink - } - button { - cursor: pointer; - } -</style> -<button class:noscribe on:click={commitChange}>Commit</button> - -<div> - <h4>Holochain Connection:</h4> - App Port: <input bind:value={appPort}> - AppId: <input bind:value={appId}> - <button on:click={toggle}> - {#if $connection} - Disconnect - {:else} - Connect - {/if} - </button> -</div> - -<div class='sessions'> - Sessions: - {#if sessions} - {#each sessions as session} - <span class='session'> - Id: {bufferToBase64(session).slice(-4)} - </span> - {/each} - {/if} -</div> diff --git a/ui/src/Title.svelte b/ui/src/Title.svelte deleted file mode 100644 index f98b3811..00000000 --- a/ui/src/Title.svelte +++ /dev/null @@ -1,104 +0,0 @@ -<script> - import { content } from './stores.js' - import { createEventDispatcher, tick } from 'svelte' - const dispatch = createEventDispatcher() - - let titleBeingTyped = '' - - let editingTitle = false - function saveTitle() { - if (editingTitle) { - // only dispatch a changeReq if the title trying to be saved is different - // than the current title - if (titleBeingTyped !== $content.title) { - let delta = { type: 'Title', value: titleBeingTyped } - dispatch('requestChange', [delta]) - } - titleBeingTyped = '' - editingTitle = false - } else { - console.log("Can't run saveTitle when it wasn't being edited!") - } - } - - let titleEl // variable to bind the title input to when it's created - async function beginEditTitle() { - titleHover=false - titleBeingTyped = $content.title // fill the field with the current title - editingTitle = true - await tick() // wait for the title input element to be created - titleEl.focus() - } - - function handleTitleKeypress() { - if (event.key == 'Enter') { - saveTitle() - } else if (event.key == 'Escape') { - // don't save new title & discard changes - titleBeingTyped = '' - // turn off editing - editingTitle=false - } - } - - // keep track of whether the doc is untitled - let untitled - $: untitled = ($content.title === '') - - let titleHover // whether the title is being hovered - - -</script> -<style> - .title-wrapper { - height: 1.6em; - } - - .title { - /* min-width: 10em; - min-height: 1em; */ - font-weight: bold; - display: inline-block; - padding: 0.4em; - -webkit-padding: 0.4em 0; - margin: 0 0 0.5em 0; - border-width: 1px; - border-color: #00000000; - border-style: solid; - border-radius: 2px; - } - - .title-hover { - border-style: dashed; - border-color: #aaa; - } - - /* input has to be below hover so it overrides */ - .title-input { - border-style: solid; - border-color: #ccc; - font-weight: bold; - margin-bottom: 0; - } - - .untitled { - color: lightgray; - } -</style> - -<div class='title-wrapper'> - Title: - {#if editingTitle} - <input class='title-input' bind:value={titleBeingTyped} on:keydown={handleTitleKeypress} on:blur={saveTitle} bind:this={titleEl}/> - {:else} - <div class='title' class:title-hover={titleHover} on:mouseenter={()=>{titleHover=true}} on:mouseleave={()=>{titleHover=false}} on:click={beginEditTitle}> - <span class:untitled> - {#if untitled} - Untitled Document - {:else} - {$content.title} - {/if} - </span> - </div> - {/if} -</div> diff --git a/ui/src/colors.js b/ui/src/colors.js deleted file mode 100644 index c9653f84..00000000 --- a/ui/src/colors.js +++ /dev/null @@ -1,62 +0,0 @@ -// retruns binary input as hex number string (e.g. 'a293b8e1a') -function arrayBufferToHex(buffer){ - let hexString = '' - for (const byte of buffer) { - hexString += byte.toString(16) - } - return hexString -} - -// converts RGB to HSL -// Source: https://gist.github.com/mjackson/5311256 -function rgbToHsl(r, g, b) { - r /= 255, g /= 255, b /= 255; - let max = Math.max(r, g, b), min = Math.min(r, g, b); let h, s, l = (max + min) / 2; - if (max == min) { h = s = 0;} else { - let d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - switch(max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } h /= 6; } return [h*360, s*100, l*100];} - -// Source: https://stackoverflow.com/questions/5842747 -function clamp(value, min, max) { - return Math.min(Math.max(value, min), max) -} - -// Generate an object of colors for a folk from their pubKey -// returns Object: -// primary: Color, // used for hex outline and norrmal cursor -// hexagon: Color, // used for hexagon picture placeholder -// selection: Color, // used for normal selection -// lookingSelection: Color, // used for selection when "looking at" -// lookingCursor: Color, // used for cursor when "looking at" -// where Color is array: [h, s, l] -// used in `use:setColor` on new Folk components -export function getFolkColors(pubKey) { - // get a hex color from the folk's public key - const hexColor = '#' + arrayBufferToHex(pubKey).slice(-6) - // extract the RGB components from the hex color notation. - // Source: https://stackoverflow.com/questions/3732046 - const r = parseInt(hexColor.substr(1,2), 16) // Grab the hex representation of red (chars 1-2) and convert to decimal (base 10). - const g = parseInt(hexColor.substr(3,2), 16) - const b = parseInt(hexColor.substr(5,2), 16) - // convert to HSL - let hsl = rgbToHsl(r, g, b) - // limit color to be bright enough and not too bright - hsl[1] = clamp(hsl[1], 10, 90) // limit s - const [h,s,l] = hsl // destructure - return { - primary: [h, s, 50], - hexagon: [h, s, 25], - selection: [h, s, 90], // placeholder values from here down - lookingSelection: [h, s, 80], - lookingCursor: [h, s+10, 40], - } -} - -export function CSSifyHSL(hslArray) { - const [h,s,l] = hslArray - return `hsl(${h} ${s}% ${l}%)` -} diff --git a/ui/src/stores.js b/ui/src/stores.js deleted file mode 100644 index 6e8f00ad..00000000 --- a/ui/src/stores.js +++ /dev/null @@ -1,22 +0,0 @@ -import { writable, readable, derived } from 'svelte/store' - -export const session = writable() - -export const content = writable({ title: '', body: '' }) - -export const folks = writable({}) - -export const connection = writable() - -export const scribeStr = writable('') - -export const requestedChanges = writable([]) - -export const recordedChanges = writable([]) - -export const committedChanges = writable([]) - -export const nextIndex = derived( - recordedChanges, - c => c.length -) diff --git a/ui/src/syn.js b/ui/src/syn.js deleted file mode 100644 index cb753ebf..00000000 --- a/ui/src/syn.js +++ /dev/null @@ -1,724 +0,0 @@ -import {AdminWebsocket, AppWebsocket} from '@holochain/conductor-api' -import { getFolkColors } from './colors.js' -import {bufferToBase64, decodeJson, encodeJson} from './utils.js' - -import { session, nextIndex, requestedChanges, recordedChanges, committedChanges, connection, scribeStr, content, folks } from './stores.js' - -export class Zome { - constructor(appClient, appId) { - this.appClient = appClient - this.appId = appId - } - - async attach() { - // setup the syn instance data - this.appInfo = await this.appClient.appInfo({ installed_app_id: this.appId }) - this.cellId = this.appInfo.cell_data[0].cell_id - this.agentPubKey = this.cellId[1] - this.dna = this.cellId[0] - this.dnaStr = bufferToBase64(this.dna) - this.me = bufferToBase64(this.agentPubKey) - } - - attached() { - return this.appInfo != undefined - } - - async call(fn_name, payload, timeout) { - if (!this.attached()) { - console.log("Can't call zome when disconnected from conductor") - return - } - try { - const zome_name = 'syn' - console.log(`Making zome call ${fn_name} with:`, payload) - const result = await this.appClient.callZome( - { - cap: null, - cell_id: this.cellId, - zome_name, - fn_name, - provenance: this.agentPubKey, - payload - }, - timeout - ) - return result - } catch (error) { - console.log('ERROR: callZome threw error', error) - throw(error) - // if (error == 'Error: Socket is not open') { - // TODO return doResetConnection(dispatch) - // } - } - } - -} - -export class Syn { - constructor(defaultContent, applyDeltaFn, appClient, appId) { - this.defaultContent = defaultContent, - this.applyDeltaFn = applyDeltaFn, - this.zome = new Zome(appClient, appId) - this.session = session, - this.folks = folks, - this.connection = connection - } - - async attach() { - await this.zome.attach() - this.agentPubKey = this.zome.agentPubKey - this.me = this.zome.me - this.myColors = getFolkColors(this.agentPubKey) - this.myTag = this.me.slice(-4) - this.Dna = this.zome.dnaStr - - // TODO: others moved into session so we can do it here. - // load up the other folk in this syn instance -// let allFolks = await this.getFolks() -// for (const folk of allFolks) { -// this.updateOthers(folk) -// } - - } - - clearState() { - this.folks.set({}); - this.connection.set(undefined); - this.session.update(s => { - s.scribeStr.set(''); - s._content = this.defaultContent; - s.content.set(s._content); - s.requestedChanges.set([]); - s.recordedChanges.set([]); - s.committedChanges.set([]); - return undefined - }) - } - - async callZome(fn_name, payload, timeout) { - return this.zome.call(fn_name, payload, timeout) - } - - async getFolks() { - return this.callZome('get_folks') - } - - async getSessions() { - return this.callZome('get_sessions') - } - - setSession(sessionInfo) { - let s = new Session(this, sessionInfo) - this._session = s - this.session.set(s) - return s - } - - async getSession(session_hash) { - return this.callZome('get_session', session_hash) - } - - async newSession() { - let rawSessionInfo = await this.callZome('new_session', {content: this.defaultContent}) - let s = this.setSession(rawSessionInfo) - return s - } - - async sendSyncReq() { - return this.callZome('send_sync_request', {scribe: this._session.scribe}) - } - -} - -export const FOLK_SEEN = 1 -export const FOLK_GONE = 2 -export const FOLK_UNKNOWN = 3 -// const outOfSessionTimout = 30 * 1000 -const outOfSessionTimout = 8 * 1000 // testing code :) - -// const heartbeatInterval = 15 * 1000 // 15 seconds -const heartbeatInterval = 30 * 1000 // for testing ;) -let reqTimeout = 1000 - -export class Session { - initTimers(syn) { - var self = this - // Send heartbeat to scribe every [heartbeat interval] - this.heart = setInterval(async () => { - if (self._scribeStr == self.me) { - // examine folks last seen time and see if any have crossed the session out-of-session - // timeout so we can tell everybody else about them having dropped. - let gone = self.updateRecentlyTimedOutFolks() - if (gone.length > 0) { - self.sendFolkLore(self.folksForScribeSignals(), {gone}) - } - } else { - // I'm not the scribe so send them a heartbeat - await self.sendHeartbeat('Hello') - } - }, heartbeatInterval) - - this.requestChecker = setInterval(async () => { - if (self.requested.length > 0) { - if ((Date.now() - self.requested[0].at) > reqTimeout) { - // for now let's just do the most drastic thing! - /* - console.log('requested change timed out! Undoing all changes', $requestedChanges[0]) - // TODO: make sure this is transactional and no requestChanges squeak in ! - while ($requestedChanges.length > 0) { - requestedChanges.update(changes => { - const change = changes.pop() - console.log('undoing ', change) - const undoDelta = undoFn(change) - console.log('undoDelta: ', undoDelta) - applyDeltaFn(undoDelta) - return changes - }) - }*/ - - // and send a sync request incase something just got out of sequence - // TODO: prepare for shifting to new scribe if they went offline - - this.initState(await syn.getSession(self.sessionHash)) - console.log("HERE") - syn.sendSyncReq() - } - } - }, reqTimeout/2) - - } - - initState(sessionInfo) { - this.sessionHash = sessionInfo.session - this.scribe = sessionInfo.scribe - this.snapshot_content = sessionInfo.snapshot_content - this.snapshot_hash = sessionInfo.snapshot_hash - this.content_hash = sessionInfo.content_hash - - this.deltas = sessionInfo.deltas.map(d => JSON.parse(d)) - this.snapshotHashStr = bufferToBase64(sessionInfo.snapshot_hash) - this.contentHashStr = bufferToBase64(sessionInfo.content_hash) - this._scribeStr = bufferToBase64(sessionInfo.scribe) - this.scribeStr.set(this._scribeStr) - - this.recorded = [] - this.requested = [] - this.requestedChanges.set(this.requested) - this.recordedChanges.set(this.recorded) - this.committedChanges.set([]) - this.reqCounter = 0 - - this.committed = [] - let newContent = {... sessionInfo.snapshot_content} // clone so as not to pass by ref - newContent.meta = {} - newContent.meta[this.myTag] = 0 - - for (const delta of this.deltas) { - const [c, change] = this.applyDeltaFn(newContent, delta) - newContent = c - this.committed.push(change) - } - this.committedChanges.set(this.committed) - - this._content = newContent - this.content.set(this._content) - } - - constructor(syn, sessionInfo) { - this.zome = syn.zome - this.applyDeltaFn = syn.applyDeltaFn - this.me = syn.zome.me - this.myTag = syn.zome.me.slice(-4) - - // set up the svelte based state vars - this.content = content - this.folks = folks - this.recordedChanges = recordedChanges - this.requestedChanges = requestedChanges - this.committedChanges = committedChanges - this.scribeStr = scribeStr - - this.others = {} - this.folks.set(this.others) - - this.initState(sessionInfo) - this.initTimers(syn) - console.log('session joined:', sessionInfo) - - } - - _recordDelta(delta) { - // apply the deltas to the content which returns the undoable change - const undoableChange = this._runApplyDelta(delta) - // append changes to the recorded history - this.recorded.push(undoableChange) - this.recordedChanges.set(this.recorded) - } - - _recordDeltas(deltas) { - // apply the deltas to the content which returns the undoable change - for (const delta of deltas) { - this._recordDelta(delta) - } - } - - - // apply changes confirmed as recorded by the scribe while reconciling - // and possibly rebasing our requested changes - recordDeltas(index, deltas) { - console.log("recordDeltas REQUESTED", this.requested) - for (const delta of deltas) { - if (this.requested.length > 0) { - // if this change is our next requested change then remove it - if (JSON.stringify(delta) == JSON.stringify(this.requested[0].delta)) { - this.recorded.push(this.requested.shift()) - this.recordedChanges.set(this.recorded) - this.requestedChanges.set(this.requested) - } else { - // TODO rebase? - console.log('REBASE NEEDED?') - console.log('requested ', this.requested[0].delta) - console.log('to be recorded ', delta) - } - } else { - // no requested changes so this must be from someone else so we don't have - // to check our requested changes - // TODO: do we need to check if this is a change that we did send and have already - // integrated somehow and ignore if so. (Seems unlikely?) - this._recordDelta(delta) - } - } - } - - nextIndex() { - return this.recorded.length - } - - _runApplyDelta(delta) { - const [newContent, undoableChange] = this.applyDeltaFn(this._content, delta) - this._content = newContent - this.content.set(this._content) - return undoableChange - } - - // called when requesting a change to the content as a result of user action - // If we are the scribe, no need to go into the zome - // TODO: prevent reentry - requestChange(deltas) { - // any requested made by the scribe should be recorded immediately - if (this._scribeStr == this.me) { - const index = this.nextIndex() - this._recordDeltas(deltas) - this.sendChange(index,deltas) - } else { - // otherwise apply the change and queue it to requested changes for - // confirmation later and send request change to scribe - - // create a unique id for each change - // TODO: this should be part of actual changeReqs - const changeId = this.myTag+'.'+this.reqCounter - const changeAt = Date.now() - - // we want to apply this to current nextIndex plus any previously - // requested changes that haven't yet be recorded - const index = this.nextIndex() + this.requested.length - - for (const delta of deltas) { - const undoableChange = this._runApplyDelta(delta) - undoableChange.id = changeId - undoableChange.at = changeAt - // append changes to the requested queue - this.requested.push(undoableChange) - this.requestedChanges.set(this.requested) - } - console.log("REQUESTED", this.requested) - this.sendChangeReq(index, deltas) - this.reqCounter+= 1 - } - } - - addChangeAsScribe(change) { - let [index, deltas] = change - const nextIndex = this.nextIndex() - if (nextIndex != index) { - console.log('Scribe is receiving change out of order!') - console.log(`nextIndex: ${nextIndex}, changeIndex:${index} for deltas:`, deltas) - - if (index < nextIndex) { - // change is too late, nextIndex has moved on - // TODO: rebase? notify sender? - return - } else { - // change is in the future, possibly some other change was dropped or is slow in arriving - // TODO: wait a bit? Ask sender for other changes? - return - } - } - - this.recordDeltas(index, deltas) - - // notify all participants of the change - this.sendChange(index, deltas) - } - - async commitChange() { - if (this._scribeStr == this.me) { - if (this.recorded.length == 0) { - alert('No changes to commit!') - return - } - this.commitInProgress = true - - const newContentHash = await this.hashContent(this._content) - console.log('commiting from snapshot', this.snapshotHashStr) - console.log(' prev_hash:', this.contentHashStr) - console.log(' new_hash:', bufferToBase64(newContentHash)) - const commit = { - snapshot: this.snapshot_hash, - change: { - deltas: this.recorded.map(c=>JSON.stringify(c.delta)), - content_hash: newContentHash, - previous_change: this.content_hash, - meta: { - contributors: [], - witnesses: [], - app_specific: null - } - }, - participants: this.folksForScribeSignals() - } - try { - this.currentCommitHeaderHash = await this.zome.call('commit', commit) - // if commit successfull we need to update the content hash and its string in the session - this.content_hash = newContentHash - this.contentHashStr = bufferToBase64(this.content_hash) - this.committed = this.committed.concat(this.recorded) - this.recorded = [] - this.recordedChanges.set(this.recorded) - this.committedChanges.set(this.committed) - } - catch (e) { - console.log("Error:", e) - } - this.commitInProgress = false - } else { - alert("You ain't the scribe!") - } - } - - - - // Folks -------------------------------------------------------- - - _newOther(pubKeyStr, pubKey) { - if (!(pubKeyStr in this.others)) { - const colors = getFolkColors(pubKey) - this.others[pubKeyStr] = {pubKey, colors} - } - } - - updateOthers(pubKey, status, meta) { - const pubKeyStr = bufferToBase64(pubKey) - if (pubKeyStr == this.me) { - return - } - - // if we don't have this key, create a record for it - // including the default color - this._newOther(pubKeyStr, pubKey) - - - if (meta) { - this.others[pubKeyStr]["meta"] = meta - } - - switch(status) { - case FOLK_SEEN: - this.others[pubKeyStr]["inSession"] = true - this.others[pubKeyStr]["lastSeen"] = Date.now() - break; - case FOLK_GONE: - case FOLK_UNKNOWN: - this.others[pubKeyStr]["inSession"] = false - } - - this.folks.set(this.others) - } - - folksForScribeSignals() { - return Object.values(this.others).filter(v=>v.inSession).map(v=>v.pubKey) - } - - // updates folks in-session status by checking their last-seen time - updateRecentlyTimedOutFolks() { - let result = [] - for (const [pubKeyStr, folk] of Object.entries(this.others)) { - if (folk.inSession && (Date.now() - this.others[pubKeyStr].lastSeen > outOfSessionTimout)) { - folk.inSession = false - result.push(this.others[pubKeyStr].pubKey) - } - } - if (result.length > 0) { - this.folks.set(this.others) - } - return result - } - - async hashContent(content) { - return this.zome.call('hash_content', content) - } - - // senders --------------------------------------------------------------------- - // These are the functions that send signals in the context of a session - - async sendHeartbeat(data) { - data = encodeJson(data) - return this.zome.call('send_heartbeat', {scribe: this.scribe, data}) - } - - async sendChangeReq(index, deltas) { - deltas = deltas.map(d=>JSON.stringify(d)) - return this.zome.call('send_change_request', {scribe: this.scribe, change: [index, deltas]}) - } - - - async sendChange(index, deltas) { - const participants = this.folksForScribeSignals() - if (participants.length > 0) { - deltas = deltas.map(d=>JSON.stringify(d)) - return this.zome.call('send_change', {participants, change: [index, deltas]}) - } - } - - async sendFolkLore(participants, data) { - if (participants.length > 0) { - data = encodeJson(data) - return this.zome.call('send_folk_lore', {participants, data}) - } - } - - async sendSyncResp(to, state) { - state.deltas = state.deltas.map(d=>JSON.stringify(d)) - return this.zome.call('send_sync_response', { - participant: to, - state - }) - } - - - // signal handlers ------------------------------------------ - - - // handler for the changeReq event - changeReq(change) { - if (this._scribeStr == this.me) { - this.addChangeAsScribe(change) - } else { - console.log("change requested but I'm not the scribe.") - } - } - - // handler for the change event - change(index, deltas) { - if (this._scribeStr == this.me) { - console.log("change received but I'm the scribe, so I'm ignoring this!") - } else { - console.log(`change arrived for ${index}:`, deltas) - if (this.nextIndex() == index) { - this.recordDeltas(index, deltas) - } else { - console.log(`change arrived out of sequence nextIndex: ${this.nextIndex()}, change index:${index}`) - // TODO either call for sync, or do some waiting algorithm - } - } - } - - // handler for the syncReq event - syncReq(request) { - const from = request.from - if (this._scribeStr == this.me) { - this.updateOthers(from, FOLK_SEEN, request.meta) - let state = { - snapshot: this.snapshot_hash, - commit_content_hash: this.content_hash, - deltas: this.recorded.map(c => c.delta) - } - if (this.currentCommitHeaderHash) { - state['commit'] = this.currentCommitHeaderHash - } - // send a sync response to the sender - this.sendSyncResp(from, state) - // and send everybody a folk lore p2p message with new participants - let p = {...this.others} - p[this.me] = { - pubKey: this.zome.agentPubKey - } - const data = { - participants: p - } - this.sendFolkLore(this.folksForScribeSignals(), data) - } - else { - console.log("syncReq received but I'm not the scribe!") - } - } - - // handler for the syncResp event - syncResp(stateForSync) { - // Make sure that we are working off the same snapshot and commit - const commitContentHashStr = bufferToBase64(stateForSync.commit_content_hash) - if (commitContentHashStr == this.contentHashStr) { - this._recordDeltas(stateForSync.deltas) - } else { - console.log('WHOA, sync response has different current state assumptions') - // TODO: resync somehow - } - } - - // handler for the heartbeat event - heartbeat(from, data) { - console.log('got heartbeat', data, 'from:', from) - if (this._scribeStr != this.me) { - console.log("heartbeat received but I'm not the scribe.") - } - else { - // I am the scribe and I've recieved a heartbeat from a concerned Folk - this.updateOthers(from, FOLK_SEEN) - } - } - - // handler for the folklore event - folklore(data) { - console.log('got folklore', data) - if (this._scribeStr == this.me) { - console.log("folklore received but I'm the scribe!") - } - else { - if (data.gone) { - Object.values(data.participants).forEach( - pubKey => { - this.updateOthers(pubKey, FOLK_GONE) - } - ) - } - // TODO move last seen into p.meta so that we can update that value - // as hearsay. - if (data.participants) { - Object.values(data.participants).forEach( - p => { - this.updateOthers(p.pubKey, FOLK_UNKNOWN, p.meta) - } - ) - } - } - } - - // handler for the commit notice event - commitNotice(commitInfo) { - // make sure we are at the right place to be able to just move forward with the commit - if (this.contentHashStr == bufferToBase64(commitInfo.previous_content_hash) && - this.nextIndex() == commitInfo.deltas_committed) { - this.contentHashStr = bufferToBase64(commitInfo.commit_content_hash) - this.committed = this.committed.concat(this.recorded) - this.recorded = [] - this.committedChanges.set(this.committed) - this.recordedChanges.set(this.recorded) - } else { - console.log('received commit notice for beyond our last commit, gotta resync') - console.log('commit.commit_content_hash:', bufferToBase64(commitInfo.commit_content_hash)) - console.log('commit.previous_content_hash:', bufferToBase64(commitInfo.previous_content_hash)) - console.log('commit.deltas_committed:', commitInfo.deltas_committed) - console.log('my $session.contentHashStr', this.contentHashStr) - console.log('my nextIndex', this.nextIndex()) - // TODO resync - } - } -} - -export class Connection { - constructor(appPort, appId) { - this.appPort = appPort; - this.appId = appId; - this.sessions = [] - } - - async open(defaultContent, applyDeltaFn) { - var self = this; - this.appClient = await AppWebsocket.connect( - `ws://localhost:${this.appPort}`, - 30000, - (signal) => signalHandler(self, signal)) - - console.log('connection established:', this) - - // TODO: in the future we should be able manage and to attach to multiple syn happs - this.syn = new Syn(defaultContent, applyDeltaFn, this.appClient, this.appId) - await this.syn.attach(this.appId) - this.sessions = await this.syn.getSessions() - } - - async joinSession() { - if (!this.syn ) { - console.log("join session called without syn app opened") - return - } - if (this.sessions.length == 0) { - this.session = await this.syn.newSession() - this.sessions[0] = this.session.sessionHash - } else { - const sessionInfo = await this.syn.getSession(this.sessions[0]) - this.session = this.syn.setSession(sessionInfo) - if (this.session._scribeStr != this.syn.me) { - await this.syn.sendSyncReq() - } - } - } -} - -function signalHandler(connection, signal) { - // ignore signals not meant for me - if (!connection.syn || bufferToBase64(signal.data.cellId[1]) != connection.syn.me) { - return - } - console.log('Got Signal', signal.data.payload.signal_name, signal) - switch (signal.data.payload.signal_name) { - case 'SyncReq': - connection.session.syncReq({from: signal.data.payload.signal_payload}) - break - case 'SyncResp': - const state = signal.data.payload.signal_payload - state.deltas = state.deltas.map(d=>JSON.parse(d)) - connection.session.syncResp(state) - break - case 'ChangeReq': - { - let [index, deltas] = signal.data.payload.signal_payload - deltas = deltas.map(d=>JSON.parse(d)) - connection.session.changeReq([index, deltas]) - break - } - case 'Change': - { - let [index, deltas] = signal.data.payload.signal_payload - deltas = deltas.map(d=>JSON.parse(d)) - connection.session.change(index, deltas) - break - } - case 'FolkLore': - { - let data = decodeJson(signal.data.payload.signal_payload) - connection.session.folklore(data) - break - } - case 'Heartbeat': - { - let [from, jsonData] = signal.data.payload.signal_payload - const data = decodeJson(jsonData) - connection.session.heartbeat(from, data) - break - } - case 'CommitNotice': - connection.session.commitNotice(signal.data.payload.signal_payload) - } -} diff --git a/ui/src/utils.js b/ui/src/utils.js deleted file mode 100644 index 62e3d891..00000000 --- a/ui/src/utils.js +++ /dev/null @@ -1,52 +0,0 @@ -export const bufferToBase64 = buffer => { - if (typeof window !== "undefined") { - // browser - var binary = '' - var bytes = new Uint8Array(buffer) - var len = bytes.byteLength - for (var i = 0; i < len; i++) { - binary += String.fromCharCode(bytes[i]) - } - return window.btoa(binary) - } else { - // nodejs - return buffer.toString('base64') - } -} - -export const base64ToBuffer = base64 => { - if (typeof window !== "undefined") { - return Uint8Array.from(window.atob(base64), c => c.charCodeAt(0)) - } else { - return Buffer.from(base64, 'base64'); - } -} - -export function decodeJson(jsonStr) { - return JSON.parse( jsonStr, function( key, value ){ - // the receiver function looks for the typed array flag - try{ - if (key == "pubKey") { - return base64ToBuffer(value) - } - }catch(e){ - console.log("decodeJson Error:", e) - } - - // if flag not found no conversion is done - return value - }) -} - -export function encodeJson(obj) { - return JSON.stringify( obj , function( key, value ){ - if (key == "pubKey") { - if (typeof window !== "undefined") { - return bufferToBase64(value) // In the browser it's the actual array - } else { - return bufferToBase64(Buffer.from(value.data)) // In node it's an object - } - } - return value - }) -} diff --git a/zomes/syn/src/error.rs b/zomes/syn/src/error.rs index fc7a85a3..fd203869 100644 --- a/zomes/syn/src/error.rs +++ b/zomes/syn/src/error.rs @@ -1,4 +1,6 @@ use hdk::prelude::*; + + use std::convert::Infallible; #[derive(thiserror::Error, Debug)] diff --git a/zomes/syn/src/lib.rs b/zomes/syn/src/lib.rs index 40c250f5..b70171da 100644 --- a/zomes/syn/src/lib.rs +++ b/zomes/syn/src/lib.rs @@ -179,6 +179,7 @@ fn get_snapshot_info_for_session( let (snapshot_hash, _) = element.header().entry_data().unwrap(); // should always have entry data let commits = get_links_and_load_type::<ContentChange>(snapshot_hash.clone(), None)?; + debug!("get_snapshot_info_for_session|commits|debug|1|{:?}", &commits); // build hash map from commits vec, with keys as previous_change let tuples = commits .into_iter() @@ -274,6 +275,7 @@ pub fn get_sessions(_: ()) -> ExternResult<SessionList> { let path = get_sessions_path(); let links = get_links(path.hash()?, None)?.into_inner(); let sessions = links.into_iter().map(|l| l.target).collect(); + debug!("get_sessions: sessions: {:?}", sessions); Ok(SessionList(sessions)) }