From 73211238e3cd6f51f896db4f250ed08eabeb5192 Mon Sep 17 00:00:00 2001 From: "guillem.cordoba" Date: Thu, 4 Jul 2024 18:28:07 +0200 Subject: [PATCH] Eventual consistency fixes --- flake.lock | 30 +- pnpm-lock.yaml | 551 ++++++++++------------ tests/package.json | 2 +- tests/src/assign-role-lifecycle.test.ts | 127 +++-- tests/src/setup.ts | 41 +- tests/vitest.config.ts | 41 +- ui/custom-elements.json | 159 ++++--- ui/package.json | 2 +- ui/src/elements/role-detail.ts | 10 +- ui/src/roles-client.ts | 15 +- ui/src/roles-store.ts | 365 +++++++++----- zomes/coordinator/roles/src/all_roles.rs | 8 - zomes/coordinator/roles/src/assignees.rs | 11 +- zomes/coordinator/roles/src/lib.rs | 2 +- zomes/coordinator/roles/src/role_claim.rs | 11 +- zomes/coordinator/roles/src/utils.rs | 68 +++ zomes/integrity/roles/src/roles.rs | 10 +- 17 files changed, 858 insertions(+), 595 deletions(-) delete mode 100644 zomes/coordinator/roles/src/all_roles.rs create mode 100644 zomes/coordinator/roles/src/utils.rs diff --git a/flake.lock b/flake.lock index d633d36b2..a0352e8c2 100644 --- a/flake.lock +++ b/flake.lock @@ -2888,11 +2888,11 @@ ] }, "locked": { - "lastModified": 1720039884, - "narHash": "sha256-tcxWXrHUetNDwzMGPUPIkdKnKBRz6sRpTpHngOXVKMQ=", + "lastModified": 1720108358, + "narHash": "sha256-gXhphrxVUfIIht9iR0DD1UWu/XwMrC+3LLsTvBBA5pQ=", "owner": "holochain", "repo": "holochain", - "rev": "ee7d3f23c3681c27e1340dbb2056f45f7866c3c4", + "rev": "01570f0e096fa18f0cca9db63b618edf7e566293", "type": "github" }, "original": { @@ -4414,11 +4414,11 @@ "versions": "versions_7" }, "locked": { - "lastModified": 1720090319, - "narHash": "sha256-NeI8bNpohFTk8r0f8nh1P7M4p1OPUX4kL3+27f9cfX4=", + "lastModified": 1720109703, + "narHash": "sha256-ufw0whMyhMmKkjTzON5OjOpKEHKfVuCzPfltrQYZpnA=", "owner": "darksoil-studio", "repo": "notifications", - "rev": "58b44bd9e23dcafa728b6f9dd932c75c07657af0", + "rev": "d590188a83aea4e9d081ca811d4f354e4ec930d7", "type": "github" }, "original": { @@ -5804,11 +5804,11 @@ }, "locked": { "dir": "versions/0_3", - "lastModified": 1720039884, - "narHash": "sha256-tcxWXrHUetNDwzMGPUPIkdKnKBRz6sRpTpHngOXVKMQ=", + "lastModified": 1720108358, + "narHash": "sha256-gXhphrxVUfIIht9iR0DD1UWu/XwMrC+3LLsTvBBA5pQ=", "owner": "holochain", "repo": "holochain", - "rev": "ee7d3f23c3681c27e1340dbb2056f45f7866c3c4", + "rev": "01570f0e096fa18f0cca9db63b618edf7e566293", "type": "github" }, "original": { @@ -5850,11 +5850,11 @@ }, "locked": { "dir": "versions/0_3", - "lastModified": 1720039884, - "narHash": "sha256-tcxWXrHUetNDwzMGPUPIkdKnKBRz6sRpTpHngOXVKMQ=", + "lastModified": 1720108358, + "narHash": "sha256-gXhphrxVUfIIht9iR0DD1UWu/XwMrC+3LLsTvBBA5pQ=", "owner": "holochain", "repo": "holochain", - "rev": "ee7d3f23c3681c27e1340dbb2056f45f7866c3c4", + "rev": "01570f0e096fa18f0cca9db63b618edf7e566293", "type": "github" }, "original": { @@ -5942,11 +5942,11 @@ }, "locked": { "dir": "versions/0_3", - "lastModified": 1720039884, - "narHash": "sha256-tcxWXrHUetNDwzMGPUPIkdKnKBRz6sRpTpHngOXVKMQ=", + "lastModified": 1720108358, + "narHash": "sha256-gXhphrxVUfIIht9iR0DD1UWu/XwMrC+3LLsTvBBA5pQ=", "owner": "holochain", "repo": "holochain", - "rev": "ee7d3f23c3681c27e1340dbb2056f45f7866c3c4", + "rev": "01570f0e096fa18f0cca9db63b618edf7e566293", "type": "github" }, "original": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c8f2be540..2d8adf1c3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: version: 0.300.0-rc.0 '@trivago/prettier-plugin-sort-imports': specifier: ^4.3.0 - version: 4.3.0(@vue/compiler-sfc@3.4.30)(prettier@3.3.2) + version: 4.3.0(@vue/compiler-sfc@3.4.31)(prettier@3.3.2) concurrently: specifier: ^6.2.1 version: 6.5.1 @@ -31,10 +31,10 @@ importers: version: 3.3.2 typescript: specifier: ^5.4.5 - version: 5.5.2 + version: 5.5.3 typescript-eslint: specifier: ^7.7.0 - version: 7.14.1(eslint@8.57.0)(typescript@5.5.2) + version: 7.15.0(eslint@8.57.0)(typescript@5.5.3) docs: devDependencies: @@ -49,7 +49,7 @@ importers: version: link:../ui '@holochain-open-dev/profiles': specifier: github:holochain-open-dev/profiles#nixify&path:ui - version: https://codeload.github.com/holochain-open-dev/profiles/tar.gz/d7ea5ffc05312b00acd6c9446cba81d7e7852752#path:ui(@types/react@18.3.3) + version: https://codeload.github.com/holochain-open-dev/profiles/tar.gz/748bb4879e247eff44a4a23a8d2aaa02a585578a#path:ui(@types/react@18.3.3) '@holochain/client': specifier: ^0.17.0 version: 0.17.0 @@ -64,10 +64,10 @@ importers: version: 3.1.4 vitepress: specifier: ^1.0.1 - version: 1.2.3(@algolia/client-search@4.24.0)(@types/react@18.3.3)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.5.2) + version: 1.2.3(@algolia/client-search@4.24.0)(@types/react@18.3.3)(postcss@8.4.39)(search-insights@2.14.0)(typescript@5.5.3) vitepress-plugin-mermaid: specifier: ^2.0.16 - version: 2.0.16(mermaid@10.9.1)(vitepress@1.2.3(@algolia/client-search@4.24.0)(@types/react@18.3.3)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.5.2)) + version: 2.0.16(mermaid@10.9.1)(vitepress@1.2.3(@algolia/client-search@4.24.0)(@types/react@18.3.3)(postcss@8.4.39)(search-insights@2.14.0)(typescript@5.5.3)) tests: dependencies: @@ -91,7 +91,7 @@ importers: version: 0.8.2 typescript: specifier: ^5.4.5 - version: 5.5.2 + version: 5.5.3 vitest: specifier: ^1.4.0 version: 1.6.0 @@ -99,8 +99,8 @@ importers: ui: dependencies: '@darksoil-studio/notifications': - specifier: github:darksoil-studio/notifications#58b44bd9e23dcafa728b6f9dd932c75c07657af0&path:ui - version: https://codeload.github.com/darksoil-studio/notifications/tar.gz/58b44bd9e23dcafa728b6f9dd932c75c07657af0#path:ui(@types/react@18.3.3) + specifier: github:darksoil-studio/notifications#d590188a83aea4e9d081ca811d4f354e4ec930d7&path:ui + version: https://codeload.github.com/darksoil-studio/notifications/tar.gz/d590188a83aea4e9d081ca811d4f354e4ec930d7#path:ui(@types/react@18.3.3) '@holochain-open-dev/elements': specifier: ^0.300.0 version: 0.300.0(@types/react@18.3.3) @@ -146,7 +146,7 @@ importers: version: 2.6.3 typescript: specifier: ^5.4.5 - version: 5.5.2 + version: 5.5.3 vite: specifier: ^4.0.0 version: 4.5.3 @@ -322,8 +322,8 @@ packages: '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} - '@darksoil-studio/notifications@https://codeload.github.com/darksoil-studio/notifications/tar.gz/58b44bd9e23dcafa728b6f9dd932c75c07657af0#path:ui': - resolution: {path: ui, tarball: https://codeload.github.com/darksoil-studio/notifications/tar.gz/58b44bd9e23dcafa728b6f9dd932c75c07657af0} + '@darksoil-studio/notifications@https://codeload.github.com/darksoil-studio/notifications/tar.gz/d590188a83aea4e9d081ca811d4f354e4ec930d7#path:ui': + resolution: {path: ui, tarball: https://codeload.github.com/darksoil-studio/notifications/tar.gz/d590188a83aea4e9d081ca811d4f354e4ec930d7} version: 0.1.0 '@docsearch/css@3.6.0': @@ -625,8 +625,8 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.10.1': - resolution: {integrity: sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==} + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/eslintrc@2.1.4': @@ -637,14 +637,14 @@ packages: resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@floating-ui/core@1.6.3': - resolution: {integrity: sha512-1ZpCvYf788/ZXOhRQGFxnYQOVgeU+pi0i+d0Ow34La7qjIXETi6RNswGVKkA6KcDO8/+Ysu2E/CeUmmeEBDvTg==} + '@floating-ui/core@1.6.4': + resolution: {integrity: sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==} - '@floating-ui/dom@1.6.6': - resolution: {integrity: sha512-qiTYajAnh3P+38kECeffMSQgbvXty2VB6rS+42iWR4FPIlZjLK84E9qtLnMTLIpPz2znD/TaFqaiavMUrS+Hcw==} + '@floating-ui/dom@1.6.7': + resolution: {integrity: sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==} - '@floating-ui/utils@0.2.3': - resolution: {integrity: sha512-XGndio0l5/Gvd6CLIABvsav9HHezgDFFhDfHk1bvLfr9ni8dojqLSvBbotJEjmIwNHL7vK4QzBJTdBRoB+c1ww==} + '@floating-ui/utils@0.2.4': + resolution: {integrity: sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==} '@github/catalyst@1.6.0': resolution: {integrity: sha512-u8A+DameixqpeyHzvnJWTGj+wfiskQOYHzSiJscCWVfMkIT3rxnbHMtGh3lMthaRY21nbUOK71WcsCnCrXhBJQ==} @@ -659,13 +659,6 @@ packages: resolution: {path: ui, tarball: https://codeload.github.com/holochain-open-dev/profiles/tar.gz/748bb4879e247eff44a4a23a8d2aaa02a585578a} version: 0.300.0 - '@holochain-open-dev/profiles@https://codeload.github.com/holochain-open-dev/profiles/tar.gz/d7ea5ffc05312b00acd6c9446cba81d7e7852752#path:ui': - resolution: {path: ui, tarball: https://codeload.github.com/holochain-open-dev/profiles/tar.gz/d7ea5ffc05312b00acd6c9446cba81d7e7852752} - version: 0.300.0 - - '@holochain-open-dev/signals@0.300.0': - resolution: {integrity: sha512-3qk/vsV1rUaL6mkaCP/MxeKWXx3isnqBzWA0Qe2nqBkXRs4b4w9wVdBZunhN5CKJEDz7/2hcE3fa7k0H8whMDA==} - '@holochain-open-dev/signals@0.300.4': resolution: {integrity: sha512-4MX3tCOFP0vbe0odwOAcX2xURObZFche18pcndA5AlJB9TXq60OKLXXEMKwDQqkC9W1QAv825iopcWUIk9y7nQ==} @@ -857,11 +850,11 @@ packages: cpu: [x64] os: [win32] - '@shikijs/core@1.9.1': - resolution: {integrity: sha512-EmUful2MQtY8KgCF1OkBtOuMcvaZEvmdubhW0UHCGXi21O9dRLeADVCj+k6ZS+de7Mz9d2qixOXJ+GLhcK3pXg==} + '@shikijs/core@1.10.1': + resolution: {integrity: sha512-qdiJS5a/QGCff7VUFIqd0hDdWly9rDp8lhVmXVrS11aazX8LOTRLHAXkkEeONNsS43EcCd7gax9LLoOz4vlFQA==} - '@shikijs/transformers@1.9.1': - resolution: {integrity: sha512-wPrGTpBURQ95IKPIhPQE3bGsANpPPtea1+aVHZp0aYtgxfL5UM3QbJ5rNdCuhcyjz/JNp5ZvSItOr+ayJxebJQ==} + '@shikijs/transformers@1.10.1': + resolution: {integrity: sha512-0gLtcFyi6R6zcUkFajUEp1Qiv7lHBSFgOz4tQvS8nFsYCQSLI1/9pM+Me8jEIPXv7XLKAoUjw6InL+Sv+BHw/A==} '@shoelace-style/animations@1.1.0': resolution: {integrity: sha512-Be+cahtZyI2dPKRm8EZSx3YJQ+jLvEcn3xzRP7tM4tqBnvd/eW/64Xh0iOf0t2w5P8iJKfdBbpVNE9naCaOf2g==} @@ -939,8 +932,8 @@ packages: '@types/web-bluetooth@0.0.20': resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} - '@typescript-eslint/eslint-plugin@7.14.1': - resolution: {integrity: sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==} + '@typescript-eslint/eslint-plugin@7.15.0': + resolution: {integrity: sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: '@typescript-eslint/parser': ^7.0.0 @@ -950,8 +943,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@7.14.1': - resolution: {integrity: sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA==} + '@typescript-eslint/parser@7.15.0': + resolution: {integrity: sha512-k9fYuQNnypLFcqORNClRykkGOMOj+pV6V91R4GO/l1FDGwpqmSwoOQrOHo3cGaH63e+D3ZiCAOsuS/D2c99j/A==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -960,12 +953,12 @@ packages: typescript: optional: true - '@typescript-eslint/scope-manager@7.14.1': - resolution: {integrity: sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==} + '@typescript-eslint/scope-manager@7.15.0': + resolution: {integrity: sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/type-utils@7.14.1': - resolution: {integrity: sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==} + '@typescript-eslint/type-utils@7.15.0': + resolution: {integrity: sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -974,12 +967,12 @@ packages: typescript: optional: true - '@typescript-eslint/types@7.14.1': - resolution: {integrity: sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==} + '@typescript-eslint/types@7.15.0': + resolution: {integrity: sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/typescript-estree@7.14.1': - resolution: {integrity: sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==} + '@typescript-eslint/typescript-estree@7.15.0': + resolution: {integrity: sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: typescript: '*' @@ -987,14 +980,14 @@ packages: typescript: optional: true - '@typescript-eslint/utils@7.14.1': - resolution: {integrity: sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==} + '@typescript-eslint/utils@7.15.0': + resolution: {integrity: sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 - '@typescript-eslint/visitor-keys@7.14.1': - resolution: {integrity: sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==} + '@typescript-eslint/visitor-keys@7.15.0': + resolution: {integrity: sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==} engines: {node: ^18.18.0 || >=20.0.0} '@ungap/structured-clone@1.2.0': @@ -1022,43 +1015,43 @@ packages: '@vitest/utils@1.6.0': resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} - '@vue/compiler-core@3.4.30': - resolution: {integrity: sha512-ZL8y4Xxdh8O6PSwfdZ1IpQ24PjTAieOz3jXb/MDTfDtANcKBMxg1KLm6OX2jofsaQGYfIVzd3BAG22i56/cF1w==} + '@vue/compiler-core@3.4.31': + resolution: {integrity: sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==} - '@vue/compiler-dom@3.4.30': - resolution: {integrity: sha512-+16Sd8lYr5j/owCbr9dowcNfrHd+pz+w2/b5Lt26Oz/kB90C9yNbxQ3bYOvt7rI2bxk0nqda39hVcwDFw85c2Q==} + '@vue/compiler-dom@3.4.31': + resolution: {integrity: sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==} - '@vue/compiler-sfc@3.4.30': - resolution: {integrity: sha512-8vElKklHn/UY8+FgUFlQrYAPbtiSB2zcgeRKW7HkpSRn/JjMRmZvuOtwDx036D1aqKNSTtXkWRfqx53Qb+HmMg==} + '@vue/compiler-sfc@3.4.31': + resolution: {integrity: sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==} - '@vue/compiler-ssr@3.4.30': - resolution: {integrity: sha512-ZJ56YZGXJDd6jky4mmM0rNaNP6kIbQu9LTKZDhcpddGe/3QIalB1WHHmZ6iZfFNyj5mSypTa4+qDJa5VIuxMSg==} + '@vue/compiler-ssr@3.4.31': + resolution: {integrity: sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==} - '@vue/devtools-api@7.3.4': - resolution: {integrity: sha512-E5dJlLW+NgGb+WS33y99ioOJL2OXpVhje6VwXGJ/q5fNizJDpe67Ml0GBSrlYOKNSjZs2mwcZd7B3e12th3Q0g==} + '@vue/devtools-api@7.3.5': + resolution: {integrity: sha512-BSdBBu5hOIv+gBJC9jzYMh5bC27FQwjWLSb8fVAniqlL9gvsqvK27xTgczMf+hgctlszMYQnRm3bpY/j8vhPqw==} - '@vue/devtools-kit@7.3.4': - resolution: {integrity: sha512-DalQZWaFLRyA4qfKT0WT7e+q2AwvYoTwd0pWqswHqcpviXw+oU6FlSJHMrEACB3lBHjN1KBS9Kh527sWIe1vcg==} + '@vue/devtools-kit@7.3.5': + resolution: {integrity: sha512-wwfi10gJ1HMtjzcd8aIOnzBHlIRqsYDgcDyrKvkeyc0Gbcoe7UrkXRVHZUOtcxxoplHA0PwpT6wFg0uUCmi8Ww==} - '@vue/devtools-shared@7.3.4': - resolution: {integrity: sha512-5S5cHh7oWLZdboujnLteR3rT8UGfKHfA34aGLyFRB/B5TqBxmeLW1Rq32xW6TCDEy4isoYsYHGwJVp6DQcpiDA==} + '@vue/devtools-shared@7.3.5': + resolution: {integrity: sha512-Rqii3VazmWTi67a86rYopi61n5Ved05EybJCwyrfoO9Ok3MaS/4yRFl706ouoISMlyrASJFEzM0/AiDA6w4f9A==} - '@vue/reactivity@3.4.30': - resolution: {integrity: sha512-bVJurnCe3LS0JII8PPoAA63Zd2MBzcKrEzwdQl92eHCcxtIbxD2fhNwJpa+KkM3Y/A4T5FUnmdhgKwOf6BfbcA==} + '@vue/reactivity@3.4.31': + resolution: {integrity: sha512-VGkTani8SOoVkZNds1PfJ/T1SlAIOf8E58PGAhIOUDYPC4GAmFA2u/E14TDAFcf3vVDKunc4QqCe/SHr8xC65Q==} - '@vue/runtime-core@3.4.30': - resolution: {integrity: sha512-qaFEbnNpGz+tlnkaualomogzN8vBLkgzK55uuWjYXbYn039eOBZrWxyXWq/7qh9Bz2FPifZqGjVDl/FXiq9L2g==} + '@vue/runtime-core@3.4.31': + resolution: {integrity: sha512-LDkztxeUPazxG/p8c5JDDKPfkCDBkkiNLVNf7XZIUnJ+66GVGkP+TIh34+8LtPisZ+HMWl2zqhIw0xN5MwU1cw==} - '@vue/runtime-dom@3.4.30': - resolution: {integrity: sha512-tV6B4YiZRj5QsaJgw2THCy5C1H+2UeywO9tqgWEc21tn85qHEERndHN/CxlyXvSBFrpmlexCIdnqPuR9RM9thw==} + '@vue/runtime-dom@3.4.31': + resolution: {integrity: sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==} - '@vue/server-renderer@3.4.30': - resolution: {integrity: sha512-TBD3eqR1DeDc0cMrXS/vEs/PWzq1uXxnvjoqQuDGFIEHFIwuDTX/KWAQKIBjyMWLFHEeTDGYVsYci85z2UbTDg==} + '@vue/server-renderer@3.4.31': + resolution: {integrity: sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==} peerDependencies: - vue: 3.4.30 + vue: 3.4.31 - '@vue/shared@3.4.30': - resolution: {integrity: sha512-CLg+f8RQCHQnKvuHY9adMsMaQOcqclh6Z5V9TaoMgy0ut0tz848joZ7/CYFFyF/yZ5i2yaw7Fn498C+CNZVHIg==} + '@vue/shared@3.4.31': + resolution: {integrity: sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==} '@vueuse/core@10.11.0': resolution: {integrity: sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==} @@ -1127,8 +1120,8 @@ packages: resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} engines: {node: '>=0.4.0'} - acorn@8.12.0: - resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==} + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} engines: {node: '>=0.4.0'} hasBin: true @@ -1329,8 +1322,8 @@ packages: peerDependencies: cytoscape: ^3.2.0 - cytoscape@3.29.2: - resolution: {integrity: sha512-2G1ycU28Nh7OHT9rkXRLpCDP30MKH1dXJORZuBhtEhEW7pKwgPi77ImqlCWinouyE1PNepIOGZBOrE84DG7LyQ==} + cytoscape@3.30.0: + resolution: {integrity: sha512-l590mjTHT6/Cbxp13dGPC2Y7VXdgc+rUeF8AnF/JPzhjNevbDJfObnJgaSjlldOgBQZbue+X6IUZ7r5GAgvauQ==} engines: {node: '>=0.10'} d3-array@2.12.1: @@ -1871,8 +1864,8 @@ packages: jsonschema@1.4.1: resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==} - katex@0.16.10: - resolution: {integrity: sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==} + katex@0.16.11: + resolution: {integrity: sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==} hasBin: true keyv@4.5.4: @@ -2176,15 +2169,15 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - pkg-types@1.1.1: - resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} + pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} - postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} - preact@10.22.0: - resolution: {integrity: sha512-RRurnSjJPj4rp5K6XoP45Ui33ncb7e4H7WiOHVpjbkvqvA3U+N8Z6Qbo0AE6leGYBV66n8EhEaFixvIu3SkxFw==} + preact@10.22.1: + resolution: {integrity: sha512-jRYbDDgMpIb5LHq3hkI0bbl+l/TQ9UnkdQ0ww+lp+4MMOdqaUYdFc5qeyP+IV8FAd/2Em7drVPeKdQxsiWCf/A==} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -2314,8 +2307,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shiki@1.9.1: - resolution: {integrity: sha512-8PDkgb5ja3nfujTjvC4VytL6wGOGCtFAClUb2r3QROevYXxcq+/shVJK5s6gy0HZnjaJgFxd6BpPqpRfqne5rA==} + shiki@1.10.1: + resolution: {integrity: sha512-uafV7WCgN4YYrccH6yxpnps6k38sSTlFRrwc4jycWmhWxJIm9dPrk+XkY1hZ2t0I7jmacMNb15Lf2fspa/Y3lg==} siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -2474,8 +2467,8 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} - typescript-eslint@7.14.1: - resolution: {integrity: sha512-Eo1X+Y0JgGPspcANKjeR6nIqXl4VL5ldXLc15k4m9upq+eY5fhU2IueiEZL6jmHrKH8aCfbIvM/v3IrX5Hg99w==} + typescript-eslint@7.15.0: + resolution: {integrity: sha512-Ta40FhMXBCwHura4X4fncaCVkVcnJ9jnOq5+Lp4lN8F4DzHZtOwZdRvVBiNUGznUDHPwdGnrnwxmUOU2fFQqFA==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -2494,8 +2487,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - typescript@5.5.2: - resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} + typescript@5.5.3: + resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==} engines: {node: '>=14.17'} hasBin: true @@ -2565,8 +2558,8 @@ packages: terser: optional: true - vite@5.3.1: - resolution: {integrity: sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==} + vite@5.3.3: + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2647,8 +2640,8 @@ packages: '@vue/composition-api': optional: true - vue@3.4.30: - resolution: {integrity: sha512-NcxtKCwkdf1zPsr7Y8+QlDBCGqxvjLXF2EX+yi76rV5rrz90Y6gK1cq0olIhdWGgrlhs9ElHuhi9t3+W5sG5Xw==} + vue@3.4.31: + resolution: {integrity: sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -2690,8 +2683,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2718,8 +2711,8 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.0.0: - resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} engines: {node: '>=12.20'} snapshots: @@ -2981,7 +2974,7 @@ snapshots: enabled: 2.0.0 kuler: 2.0.0 - '@darksoil-studio/notifications@https://codeload.github.com/darksoil-studio/notifications/tar.gz/58b44bd9e23dcafa728b6f9dd932c75c07657af0#path:ui(@types/react@18.3.3)': + '@darksoil-studio/notifications@https://codeload.github.com/darksoil-studio/notifications/tar.gz/d590188a83aea4e9d081ca811d4f354e4ec930d7#path:ui(@types/react@18.3.3)': dependencies: '@holochain-open-dev/elements': 0.300.0(@types/react@18.3.3) '@holochain-open-dev/profiles': https://codeload.github.com/holochain-open-dev/profiles/tar.gz/748bb4879e247eff44a4a23a8d2aaa02a585578a#path:ui(@types/react@18.3.3) @@ -3004,7 +2997,7 @@ snapshots: '@docsearch/js@3.6.0(@algolia/client-search@4.24.0)(@types/react@18.3.3)(search-insights@2.14.0)': dependencies: '@docsearch/react': 3.6.0(@algolia/client-search@4.24.0)(@types/react@18.3.3)(search-insights@2.14.0) - preact: 10.22.0 + preact: 10.22.1 transitivePeerDependencies: - '@algolia/client-search' - '@types/react' @@ -3164,7 +3157,7 @@ snapshots: eslint: 8.57.0 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.10.1': {} + '@eslint-community/regexpp@4.11.0': {} '@eslint/eslintrc@2.1.4': dependencies: @@ -3182,16 +3175,16 @@ snapshots: '@eslint/js@8.57.0': {} - '@floating-ui/core@1.6.3': + '@floating-ui/core@1.6.4': dependencies: - '@floating-ui/utils': 0.2.3 + '@floating-ui/utils': 0.2.4 - '@floating-ui/dom@1.6.6': + '@floating-ui/dom@1.6.7': dependencies: - '@floating-ui/core': 1.6.3 - '@floating-ui/utils': 0.2.3 + '@floating-ui/core': 1.6.4 + '@floating-ui/utils': 0.2.4 - '@floating-ui/utils@0.2.3': {} + '@floating-ui/utils@0.2.4': {} '@github/catalyst@1.6.0': {} @@ -3232,36 +3225,6 @@ snapshots: - bufferutil - utf-8-validate - '@holochain-open-dev/profiles@https://codeload.github.com/holochain-open-dev/profiles/tar.gz/d7ea5ffc05312b00acd6c9446cba81d7e7852752#path:ui(@types/react@18.3.3)': - dependencies: - '@holochain-open-dev/elements': 0.300.0(@types/react@18.3.3) - '@holochain-open-dev/signals': 0.300.0(@types/react@18.3.3) - '@holochain-open-dev/utils': 0.300.1 - '@holochain/client': 0.17.0 - '@lit/context': 1.1.2 - '@lit/localize': 0.12.1 - '@mdi/js': 7.4.47 - '@msgpack/msgpack': 2.8.0 - '@shoelace-style/shoelace': 2.15.1(@types/react@18.3.3) - lit: 3.1.4 - transitivePeerDependencies: - - '@types/react' - - bufferutil - - utf-8-validate - - '@holochain-open-dev/signals@0.300.0(@types/react@18.3.3)': - dependencies: - '@holochain-open-dev/utils': 0.300.1 - '@holochain/client': 0.17.0 - '@shoelace-style/shoelace': 2.15.1(@types/react@18.3.3) - async-signals: 0.1.11 - lit-signal-watcher: 0.1.1 - signal-polyfill: 0.1.1 - transitivePeerDependencies: - - '@types/react' - - bufferutil - - utf-8-validate - '@holochain-open-dev/signals@0.300.4(@types/react@18.3.3)': dependencies: '@holochain-open-dev/utils': 0.300.1 @@ -3295,11 +3258,11 @@ snapshots: '@holochain/serialization': 0.1.0-beta-rc.3 '@msgpack/msgpack': 2.8.0 emittery: 1.0.3 - isomorphic-ws: 5.0.0(ws@8.17.1) + isomorphic-ws: 5.0.0(ws@8.18.0) js-base64: 3.7.7 libsodium-wrappers: 0.7.13 lodash-es: 4.17.21 - ws: 8.17.1 + ws: 8.18.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -3313,7 +3276,7 @@ snapshots: lodash: 4.17.21 uuid: 8.3.2 winston: 3.13.0 - ws: 8.17.1 + ws: 8.18.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -3401,9 +3364,9 @@ snapshots: '@mermaid-js/mermaid-mindmap@9.3.0': dependencies: '@braintree/sanitize-url': 6.0.4 - cytoscape: 3.29.2 - cytoscape-cose-bilkent: 4.1.0(cytoscape@3.29.2) - cytoscape-fcose: 2.2.0(cytoscape@3.29.2) + cytoscape: 3.30.0 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.30.0) + cytoscape-fcose: 2.2.0(cytoscape@3.30.0) d3: 7.9.0 khroma: 2.1.0 non-layered-tidy-tree-layout: 2.0.2 @@ -3475,11 +3438,11 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.18.0': optional: true - '@shikijs/core@1.9.1': {} + '@shikijs/core@1.10.1': {} - '@shikijs/transformers@1.9.1': + '@shikijs/transformers@1.10.1': dependencies: - shiki: 1.9.1 + shiki: 1.10.1 '@shoelace-style/animations@1.1.0': {} @@ -3488,7 +3451,7 @@ snapshots: '@shoelace-style/shoelace@2.15.1(@types/react@18.3.3)': dependencies: '@ctrl/tinycolor': 4.1.0 - '@floating-ui/dom': 1.6.6 + '@floating-ui/dom': 1.6.7 '@lit/react': 1.0.5(@types/react@18.3.3) '@shoelace-style/animations': 1.1.0 '@shoelace-style/localize': 3.1.2 @@ -3500,7 +3463,7 @@ snapshots: '@sinclair/typebox@0.27.8': {} - '@trivago/prettier-plugin-sort-imports@4.3.0(@vue/compiler-sfc@3.4.30)(prettier@3.3.2)': + '@trivago/prettier-plugin-sort-imports@4.3.0(@vue/compiler-sfc@3.4.31)(prettier@3.3.2)': dependencies: '@babel/generator': 7.17.7 '@babel/parser': 7.24.7 @@ -3510,7 +3473,7 @@ snapshots: lodash: 4.17.21 prettier: 3.3.2 optionalDependencies: - '@vue/compiler-sfc': 3.4.30 + '@vue/compiler-sfc': 3.4.31 transitivePeerDependencies: - supports-color @@ -3564,93 +3527,93 @@ snapshots: '@types/web-bluetooth@0.0.20': {} - '@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)': dependencies: - '@eslint-community/regexpp': 4.10.1 - '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/scope-manager': 7.14.1 - '@typescript-eslint/type-utils': 7.14.1(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/utils': 7.14.1(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/visitor-keys': 7.14.1 + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 7.15.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/scope-manager': 7.15.0 + '@typescript-eslint/type-utils': 7.15.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/utils': 7.15.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/visitor-keys': 7.15.0 eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.5.2) + ts-api-utils: 1.3.0(typescript@5.5.3) optionalDependencies: - typescript: 5.5.2 + typescript: 5.5.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/parser@7.15.0(eslint@8.57.0)(typescript@5.5.3)': dependencies: - '@typescript-eslint/scope-manager': 7.14.1 - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) - '@typescript-eslint/visitor-keys': 7.14.1 + '@typescript-eslint/scope-manager': 7.15.0 + '@typescript-eslint/types': 7.15.0 + '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3) + '@typescript-eslint/visitor-keys': 7.15.0 debug: 4.3.5 eslint: 8.57.0 optionalDependencies: - typescript: 5.5.2 + typescript: 5.5.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@7.14.1': + '@typescript-eslint/scope-manager@7.15.0': dependencies: - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/visitor-keys': 7.14.1 + '@typescript-eslint/types': 7.15.0 + '@typescript-eslint/visitor-keys': 7.15.0 - '@typescript-eslint/type-utils@7.14.1(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/type-utils@7.15.0(eslint@8.57.0)(typescript@5.5.3)': dependencies: - '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) - '@typescript-eslint/utils': 7.14.1(eslint@8.57.0)(typescript@5.5.2) + '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3) + '@typescript-eslint/utils': 7.15.0(eslint@8.57.0)(typescript@5.5.3) debug: 4.3.5 eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.5.2) + ts-api-utils: 1.3.0(typescript@5.5.3) optionalDependencies: - typescript: 5.5.2 + typescript: 5.5.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@7.14.1': {} + '@typescript-eslint/types@7.15.0': {} - '@typescript-eslint/typescript-estree@7.14.1(typescript@5.5.2)': + '@typescript-eslint/typescript-estree@7.15.0(typescript@5.5.3)': dependencies: - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/visitor-keys': 7.14.1 + '@typescript-eslint/types': 7.15.0 + '@typescript-eslint/visitor-keys': 7.15.0 debug: 4.3.5 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.2 - ts-api-utils: 1.3.0(typescript@5.5.2) + ts-api-utils: 1.3.0(typescript@5.5.3) optionalDependencies: - typescript: 5.5.2 + typescript: 5.5.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.14.1(eslint@8.57.0)(typescript@5.5.2)': + '@typescript-eslint/utils@7.15.0(eslint@8.57.0)(typescript@5.5.3)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 7.14.1 - '@typescript-eslint/types': 7.14.1 - '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2) + '@typescript-eslint/scope-manager': 7.15.0 + '@typescript-eslint/types': 7.15.0 + '@typescript-eslint/typescript-estree': 7.15.0(typescript@5.5.3) eslint: 8.57.0 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/visitor-keys@7.14.1': + '@typescript-eslint/visitor-keys@7.15.0': dependencies: - '@typescript-eslint/types': 7.14.1 + '@typescript-eslint/types': 7.15.0 eslint-visitor-keys: 3.4.3 '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-vue@5.0.5(vite@5.3.1)(vue@3.4.30(typescript@5.5.2))': + '@vitejs/plugin-vue@5.0.5(vite@5.3.3)(vue@3.4.31(typescript@5.5.3))': dependencies: - vite: 5.3.1 - vue: 3.4.30(typescript@5.5.2) + vite: 5.3.3 + vue: 3.4.31(typescript@5.5.3) '@vitest/expect@1.6.0': dependencies: @@ -3681,43 +3644,43 @@ snapshots: loupe: 2.3.7 pretty-format: 29.7.0 - '@vue/compiler-core@3.4.30': + '@vue/compiler-core@3.4.31': dependencies: '@babel/parser': 7.24.7 - '@vue/shared': 3.4.30 + '@vue/shared': 3.4.31 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.0 - '@vue/compiler-dom@3.4.30': + '@vue/compiler-dom@3.4.31': dependencies: - '@vue/compiler-core': 3.4.30 - '@vue/shared': 3.4.30 + '@vue/compiler-core': 3.4.31 + '@vue/shared': 3.4.31 - '@vue/compiler-sfc@3.4.30': + '@vue/compiler-sfc@3.4.31': dependencies: '@babel/parser': 7.24.7 - '@vue/compiler-core': 3.4.30 - '@vue/compiler-dom': 3.4.30 - '@vue/compiler-ssr': 3.4.30 - '@vue/shared': 3.4.30 + '@vue/compiler-core': 3.4.31 + '@vue/compiler-dom': 3.4.31 + '@vue/compiler-ssr': 3.4.31 + '@vue/shared': 3.4.31 estree-walker: 2.0.2 magic-string: 0.30.10 - postcss: 8.4.38 + postcss: 8.4.39 source-map-js: 1.2.0 - '@vue/compiler-ssr@3.4.30': + '@vue/compiler-ssr@3.4.31': dependencies: - '@vue/compiler-dom': 3.4.30 - '@vue/shared': 3.4.30 + '@vue/compiler-dom': 3.4.31 + '@vue/shared': 3.4.31 - '@vue/devtools-api@7.3.4': + '@vue/devtools-api@7.3.5': dependencies: - '@vue/devtools-kit': 7.3.4 + '@vue/devtools-kit': 7.3.5 - '@vue/devtools-kit@7.3.4': + '@vue/devtools-kit@7.3.5': dependencies: - '@vue/devtools-shared': 7.3.4 + '@vue/devtools-shared': 7.3.5 birpc: 0.2.17 hookable: 5.5.3 mitt: 3.0.1 @@ -3725,49 +3688,49 @@ snapshots: speakingurl: 14.0.1 superjson: 2.2.1 - '@vue/devtools-shared@7.3.4': + '@vue/devtools-shared@7.3.5': dependencies: rfdc: 1.4.1 - '@vue/reactivity@3.4.30': + '@vue/reactivity@3.4.31': dependencies: - '@vue/shared': 3.4.30 + '@vue/shared': 3.4.31 - '@vue/runtime-core@3.4.30': + '@vue/runtime-core@3.4.31': dependencies: - '@vue/reactivity': 3.4.30 - '@vue/shared': 3.4.30 + '@vue/reactivity': 3.4.31 + '@vue/shared': 3.4.31 - '@vue/runtime-dom@3.4.30': + '@vue/runtime-dom@3.4.31': dependencies: - '@vue/reactivity': 3.4.30 - '@vue/runtime-core': 3.4.30 - '@vue/shared': 3.4.30 + '@vue/reactivity': 3.4.31 + '@vue/runtime-core': 3.4.31 + '@vue/shared': 3.4.31 csstype: 3.1.3 - '@vue/server-renderer@3.4.30(vue@3.4.30(typescript@5.5.2))': + '@vue/server-renderer@3.4.31(vue@3.4.31(typescript@5.5.3))': dependencies: - '@vue/compiler-ssr': 3.4.30 - '@vue/shared': 3.4.30 - vue: 3.4.30(typescript@5.5.2) + '@vue/compiler-ssr': 3.4.31 + '@vue/shared': 3.4.31 + vue: 3.4.31(typescript@5.5.3) - '@vue/shared@3.4.30': {} + '@vue/shared@3.4.31': {} - '@vueuse/core@10.11.0(vue@3.4.30(typescript@5.5.2))': + '@vueuse/core@10.11.0(vue@3.4.31(typescript@5.5.3))': dependencies: '@types/web-bluetooth': 0.0.20 '@vueuse/metadata': 10.11.0 - '@vueuse/shared': 10.11.0(vue@3.4.30(typescript@5.5.2)) - vue-demi: 0.14.8(vue@3.4.30(typescript@5.5.2)) + '@vueuse/shared': 10.11.0(vue@3.4.31(typescript@5.5.3)) + vue-demi: 0.14.8(vue@3.4.31(typescript@5.5.3)) transitivePeerDependencies: - '@vue/composition-api' - vue - '@vueuse/integrations@10.11.0(focus-trap@7.5.4)(vue@3.4.30(typescript@5.5.2))': + '@vueuse/integrations@10.11.0(focus-trap@7.5.4)(vue@3.4.31(typescript@5.5.3))': dependencies: - '@vueuse/core': 10.11.0(vue@3.4.30(typescript@5.5.2)) - '@vueuse/shared': 10.11.0(vue@3.4.30(typescript@5.5.2)) - vue-demi: 0.14.8(vue@3.4.30(typescript@5.5.2)) + '@vueuse/core': 10.11.0(vue@3.4.31(typescript@5.5.3)) + '@vueuse/shared': 10.11.0(vue@3.4.31(typescript@5.5.3)) + vue-demi: 0.14.8(vue@3.4.31(typescript@5.5.3)) optionalDependencies: focus-trap: 7.5.4 transitivePeerDependencies: @@ -3776,9 +3739,9 @@ snapshots: '@vueuse/metadata@10.11.0': {} - '@vueuse/shared@10.11.0(vue@3.4.30(typescript@5.5.2))': + '@vueuse/shared@10.11.0(vue@3.4.31(typescript@5.5.3))': dependencies: - vue-demi: 0.14.8(vue@3.4.30(typescript@5.5.2)) + vue-demi: 0.14.8(vue@3.4.31(typescript@5.5.3)) transitivePeerDependencies: - '@vue/composition-api' - vue @@ -3789,15 +3752,15 @@ snapshots: '@xmldom/xmldom@0.8.10': {} - acorn-jsx@5.3.2(acorn@8.12.0): + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: - acorn: 8.12.0 + acorn: 8.12.1 acorn-walk@8.3.3: dependencies: - acorn: 8.12.0 + acorn: 8.12.1 - acorn@8.12.0: {} + acorn@8.12.1: {} ajv@6.12.6: dependencies: @@ -4020,18 +3983,18 @@ snapshots: custom-elements-manifest@2.1.0: {} - cytoscape-cose-bilkent@4.1.0(cytoscape@3.29.2): + cytoscape-cose-bilkent@4.1.0(cytoscape@3.30.0): dependencies: cose-base: 1.0.3 - cytoscape: 3.29.2 + cytoscape: 3.30.0 - cytoscape-fcose@2.2.0(cytoscape@3.29.2): + cytoscape-fcose@2.2.0(cytoscape@3.30.0): dependencies: cose-base: 2.2.0 - cytoscape: 3.29.2 + cytoscape: 3.30.0 optional: true - cytoscape@3.29.2: {} + cytoscape@3.30.0: {} d3-array@2.12.1: dependencies: @@ -4332,7 +4295,7 @@ snapshots: eslint@8.57.0: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.10.1 + '@eslint-community/regexpp': 4.11.0 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.57.0 '@humanwhocodes/config-array': 0.11.14 @@ -4374,8 +4337,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.12.0 - acorn-jsx: 5.3.2(acorn@8.12.0) + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 3.4.3 esquery@1.5.0: @@ -4589,9 +4552,9 @@ snapshots: isexe@2.0.0: {} - isomorphic-ws@5.0.0(ws@8.17.1): + isomorphic-ws@5.0.0(ws@8.18.0): dependencies: - ws: 8.17.1 + ws: 8.18.0 javascript-natural-sort@0.7.1: {} @@ -4621,7 +4584,7 @@ snapshots: jsonschema@1.4.1: {} - katex@0.16.10: + katex@0.16.11: dependencies: commander: 8.3.0 @@ -4691,7 +4654,7 @@ snapshots: local-pkg@0.5.0: dependencies: mlly: 1.7.1 - pkg-types: 1.1.1 + pkg-types: 1.1.3 locate-path@6.0.0: dependencies: @@ -4756,15 +4719,15 @@ snapshots: '@braintree/sanitize-url': 6.0.4 '@types/d3-scale': 4.0.8 '@types/d3-scale-chromatic': 3.0.3 - cytoscape: 3.29.2 - cytoscape-cose-bilkent: 4.1.0(cytoscape@3.29.2) + cytoscape: 3.30.0 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.30.0) d3: 7.9.0 d3-sankey: 0.12.3 dagre-d3-es: 7.0.10 dayjs: 1.11.11 dompurify: 3.1.5 elkjs: 0.9.3 - katex: 0.16.10 + katex: 0.16.11 khroma: 2.1.0 lodash-es: 4.17.21 mdast-util-from-markdown: 1.3.1 @@ -4932,9 +4895,9 @@ snapshots: mlly@1.7.1: dependencies: - acorn: 8.12.0 + acorn: 8.12.1 pathe: 1.1.2 - pkg-types: 1.1.1 + pkg-types: 1.1.3 ufo: 1.5.3 mri@1.2.0: {} @@ -4988,7 +4951,7 @@ snapshots: p-limit@5.0.0: dependencies: - yocto-queue: 1.0.0 + yocto-queue: 1.1.1 p-locate@5.0.0: dependencies: @@ -5022,19 +4985,19 @@ snapshots: picomatch@2.3.1: {} - pkg-types@1.1.1: + pkg-types@1.1.3: dependencies: confbox: 0.1.7 mlly: 1.7.1 pathe: 1.1.2 - postcss@8.4.38: + postcss@8.4.39: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 - preact@10.22.0: {} + preact@10.22.1: {} prelude-ls@1.2.1: {} @@ -5167,9 +5130,9 @@ snapshots: shebang-regex@3.0.0: {} - shiki@1.9.1: + shiki@1.10.1: dependencies: - '@shikijs/core': 1.9.1 + '@shikijs/core': 1.10.1 siginfo@2.0.0: {} @@ -5270,9 +5233,9 @@ snapshots: triple-beam@1.4.1: {} - ts-api-utils@1.3.0(typescript@5.5.2): + ts-api-utils@1.3.0(typescript@5.5.3): dependencies: - typescript: 5.5.2 + typescript: 5.5.3 ts-dedent@2.2.0: {} @@ -5288,14 +5251,14 @@ snapshots: type-fest@0.20.2: {} - typescript-eslint@7.14.1(eslint@8.57.0)(typescript@5.5.2): + typescript-eslint@7.15.0(eslint@8.57.0)(typescript@5.5.3): dependencies: - '@typescript-eslint/eslint-plugin': 7.14.1(@typescript-eslint/parser@7.14.1(eslint@8.57.0)(typescript@5.5.2))(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/parser': 7.14.1(eslint@8.57.0)(typescript@5.5.2) - '@typescript-eslint/utils': 7.14.1(eslint@8.57.0)(typescript@5.5.2) + '@typescript-eslint/eslint-plugin': 7.15.0(@typescript-eslint/parser@7.15.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/parser': 7.15.0(eslint@8.57.0)(typescript@5.5.3) + '@typescript-eslint/utils': 7.15.0(eslint@8.57.0)(typescript@5.5.3) eslint: 8.57.0 optionalDependencies: - typescript: 5.5.2 + typescript: 5.5.3 transitivePeerDependencies: - supports-color @@ -5303,7 +5266,7 @@ snapshots: typescript@5.4.5: {} - typescript@5.5.2: {} + typescript@5.5.3: {} typical@4.0.0: {} @@ -5338,7 +5301,7 @@ snapshots: debug: 4.3.5 pathe: 1.1.2 picocolors: 1.0.1 - vite: 5.3.1 + vite: 5.3.3 transitivePeerDependencies: - '@types/node' - less @@ -5352,46 +5315,46 @@ snapshots: vite@4.5.3: dependencies: esbuild: 0.18.20 - postcss: 8.4.38 + postcss: 8.4.39 rollup: 3.29.4 optionalDependencies: fsevents: 2.3.3 - vite@5.3.1: + vite@5.3.3: dependencies: esbuild: 0.21.5 - postcss: 8.4.38 + postcss: 8.4.39 rollup: 4.18.0 optionalDependencies: fsevents: 2.3.3 - vitepress-plugin-mermaid@2.0.16(mermaid@10.9.1)(vitepress@1.2.3(@algolia/client-search@4.24.0)(@types/react@18.3.3)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.5.2)): + vitepress-plugin-mermaid@2.0.16(mermaid@10.9.1)(vitepress@1.2.3(@algolia/client-search@4.24.0)(@types/react@18.3.3)(postcss@8.4.39)(search-insights@2.14.0)(typescript@5.5.3)): dependencies: mermaid: 10.9.1 - vitepress: 1.2.3(@algolia/client-search@4.24.0)(@types/react@18.3.3)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.5.2) + vitepress: 1.2.3(@algolia/client-search@4.24.0)(@types/react@18.3.3)(postcss@8.4.39)(search-insights@2.14.0)(typescript@5.5.3) optionalDependencies: '@mermaid-js/mermaid-mindmap': 9.3.0 - vitepress@1.2.3(@algolia/client-search@4.24.0)(@types/react@18.3.3)(postcss@8.4.38)(search-insights@2.14.0)(typescript@5.5.2): + vitepress@1.2.3(@algolia/client-search@4.24.0)(@types/react@18.3.3)(postcss@8.4.39)(search-insights@2.14.0)(typescript@5.5.3): dependencies: '@docsearch/css': 3.6.0 '@docsearch/js': 3.6.0(@algolia/client-search@4.24.0)(@types/react@18.3.3)(search-insights@2.14.0) - '@shikijs/core': 1.9.1 - '@shikijs/transformers': 1.9.1 + '@shikijs/core': 1.10.1 + '@shikijs/transformers': 1.10.1 '@types/markdown-it': 14.1.1 - '@vitejs/plugin-vue': 5.0.5(vite@5.3.1)(vue@3.4.30(typescript@5.5.2)) - '@vue/devtools-api': 7.3.4 - '@vue/shared': 3.4.30 - '@vueuse/core': 10.11.0(vue@3.4.30(typescript@5.5.2)) - '@vueuse/integrations': 10.11.0(focus-trap@7.5.4)(vue@3.4.30(typescript@5.5.2)) + '@vitejs/plugin-vue': 5.0.5(vite@5.3.3)(vue@3.4.31(typescript@5.5.3)) + '@vue/devtools-api': 7.3.5 + '@vue/shared': 3.4.31 + '@vueuse/core': 10.11.0(vue@3.4.31(typescript@5.5.3)) + '@vueuse/integrations': 10.11.0(focus-trap@7.5.4)(vue@3.4.31(typescript@5.5.3)) focus-trap: 7.5.4 mark.js: 8.11.1 minisearch: 6.3.0 - shiki: 1.9.1 - vite: 5.3.1 - vue: 3.4.30(typescript@5.5.2) + shiki: 1.10.1 + vite: 5.3.3 + vue: 3.4.31(typescript@5.5.3) optionalDependencies: - postcss: 8.4.38 + postcss: 8.4.39 transitivePeerDependencies: - '@algolia/client-search' - '@types/node' @@ -5438,7 +5401,7 @@ snapshots: strip-literal: 2.1.0 tinybench: 2.8.0 tinypool: 0.8.4 - vite: 5.3.1 + vite: 5.3.3 vite-node: 1.6.0 why-is-node-running: 2.2.2 transitivePeerDependencies: @@ -5450,19 +5413,19 @@ snapshots: - supports-color - terser - vue-demi@0.14.8(vue@3.4.30(typescript@5.5.2)): + vue-demi@0.14.8(vue@3.4.31(typescript@5.5.3)): dependencies: - vue: 3.4.30(typescript@5.5.2) + vue: 3.4.31(typescript@5.5.3) - vue@3.4.30(typescript@5.5.2): + vue@3.4.31(typescript@5.5.3): dependencies: - '@vue/compiler-dom': 3.4.30 - '@vue/compiler-sfc': 3.4.30 - '@vue/runtime-dom': 3.4.30 - '@vue/server-renderer': 3.4.30(vue@3.4.30(typescript@5.5.2)) - '@vue/shared': 3.4.30 + '@vue/compiler-dom': 3.4.31 + '@vue/compiler-sfc': 3.4.31 + '@vue/runtime-dom': 3.4.31 + '@vue/server-renderer': 3.4.31(vue@3.4.31(typescript@5.5.3)) + '@vue/shared': 3.4.31 optionalDependencies: - typescript: 5.5.2 + typescript: 5.5.3 w3c-keyname@2.2.8: {} @@ -5507,7 +5470,7 @@ snapshots: wrappy@1.0.2: {} - ws@8.17.1: {} + ws@8.18.0: {} y18n@5.0.8: {} @@ -5525,4 +5488,4 @@ snapshots: yocto-queue@0.1.0: {} - yocto-queue@1.0.0: {} + yocto-queue@1.1.1: {} diff --git a/tests/package.json b/tests/package.json index c26ced5c6..90facd128 100644 --- a/tests/package.json +++ b/tests/package.json @@ -2,7 +2,7 @@ "name": "tests", "private": true, "scripts": { - "test": "vitest run" + "test": "RUST_LOG=error vitest run" }, "dependencies": { "@holochain-open-dev/signals": "^0.300.4", diff --git a/tests/src/assign-role-lifecycle.test.ts b/tests/src/assign-role-lifecycle.test.ts index 8e263acfb..675c186d7 100644 --- a/tests/src/assign-role-lifecycle.test.ts +++ b/tests/src/assign-role-lifecycle.test.ts @@ -1,6 +1,6 @@ import { toPromise } from '@holochain-open-dev/signals'; import { HashType, retype } from '@holochain-open-dev/utils'; -import { dhtSync, runScenario } from '@holochain/tryorama'; +import { dhtSync, pause, runScenario } from '@holochain/tryorama'; import { assert, expect, test } from 'vitest'; import { RolesStore } from '../../ui/src/roles-store.js'; @@ -19,18 +19,23 @@ test('Assign role lifecycle', async () => { await runScenario(async scenario => { const { alice, bob } = await setup(scenario); + let roles = alice.store.allRoles; + assert.equal(roles.length, 2); + assert.equal(roles[0], 'admin'); + assert.equal(roles[1], 'editor'); + // Wait for the created entry to be propagated to the other node. await dhtSync([alice.player, bob.player], alice.player.cells[0].cell_id[0]); - let roles = await toPromise(alice.store.allRolesWithAssignees); - assert.equal(roles.length, 1); - assert.equal(roles[0], 'admin'); await expect(() => createExampleEntryThatOnlyEditorsCanCreate(alice.store), ).rejects.toThrowError(); - // Wait for the created entry to be propagated to the other node. - await dhtSync([alice.player, bob.player], alice.player.cells[0].cell_id[0]); + await waitUntil( + async () => + (await toPromise(bob.store.assignees.get('admin'))).length === 1, + 10000, + ); let admins = await toPromise(bob.store.assignees.get('admin')); assert.equal(admins.length, 1); @@ -51,10 +56,11 @@ test('Assign role lifecycle', async () => { // Wait for the created entry to be propagated to the other node. await dhtSync([alice.player, bob.player], alice.player.cells[0].cell_id[0]); - roles = await toPromise(bob.store.allRolesWithAssignees); - assert.equal(roles.length, 2); - assert.ok(roles.includes('admin')); - assert.ok(roles.includes('editor')); + await waitUntil( + async () => + (await toPromise(bob.store.assignees.get('editor'))).length === 1, + 10_000, + ); let editors = await toPromise(bob.store.assignees.get('editor')); assert.equal(editors.length, 1); @@ -63,6 +69,13 @@ test('Assign role lifecycle', async () => { new Uint8Array(bob.player.agentPubKey).toString(), ); + await waitUntil( + async () => + (await bob.store.client.queryUndeletedRoleClaimsForRole('editor')) + .length === 1, + 40_000, + ); + let roleClaims = await bob.store.client.queryUndeletedRoleClaimsForRole('editor'); assert.equal(roleClaims.length, 1); @@ -74,36 +87,39 @@ test('Assign role lifecycle', async () => { bob.store.client.requestUnassignRole('editor', bob.player.agentPubKey), ).rejects.toThrowError(); - let pendingUnassigments = await toPromise(bob.store.pendingUnassigments); + let pendingUnassigments = await toPromise(bob.store.pendingUnassignments); assert.equal(pendingUnassigments.length, 0); await alice.store.client.requestUnassignRole( 'editor', bob.player.agentPubKey, ); - - // Wait for the created entry to be propagated to the other node. - await dhtSync([alice.player, bob.player], alice.player.cells[0].cell_id[0]); - - pendingUnassigments = await toPromise(bob.store.pendingUnassigments); + await pause(100); + pendingUnassigments = await toPromise(alice.store.pendingUnassignments); assert.equal(pendingUnassigments.length, 1); - assert.equal( - retype(pendingUnassigments[0].target, HashType.AGENT).toString(), - new Uint8Array(bob.player.agentPubKey).toString(), - ); - // Alice can't unassign bob's role + // Alice can't unassign Bob's role await expect(() => alice.store.client.unassignMyRole( pendingUnassigments[0].create_link_hash, ), ).rejects.toThrowError(); - await dhtSync([alice.player, bob.player], alice.player.cells[0].cell_id[0]); - // Bob will delete their role claim automatically when getting assignees + await waitUntil( + async () => + (await toPromise(bob.store.assignees.get('editor'))).length === 0, + 10_000, + ); + editors = await toPromise(bob.store.assignees.get('editor')); assert.equal(editors.length, 0); + await waitUntil(async () => { + const roleClaims = + await bob.store.client.queryUndeletedRoleClaimsForRole('editor'); + return roleClaims.length === 0; + }, 20_000); + roleClaims = await bob.store.client.queryUndeletedRoleClaimsForRole('editor'); assert.equal(roleClaims.length, 0); @@ -114,13 +130,23 @@ test('Assign role lifecycle', async () => { }); }); +async function waitUntil(condition: () => Promise, timeout: number) { + const start = Date.now(); + const isDone = await condition(); + if (isDone) return; + if (timeout <= 0) throw new Error('timeout'); + await pause(1000); + return waitUntil(condition, timeout - (Date.now() - start)); +} + test('Admin can assign admin that assigns a role', async () => { await runScenario(async scenario => { const { alice, bob, carol } = await setup(scenario); - let roles = await toPromise(alice.store.allRolesWithAssignees); - assert.equal(roles.length, 1); + let roles = alice.store.allRoles; + assert.equal(roles.length, 2); assert.equal(roles[0], 'admin'); + assert.equal(roles[1], 'editor'); // Wait for the created entry to be propagated to the other node. await dhtSync( @@ -128,6 +154,12 @@ test('Admin can assign admin that assigns a role', async () => { alice.player.cells[0].cell_id[0], ); + await waitUntil( + async () => + (await toPromise(bob.store.assignees.get('admin'))).length === 1, + 30_000, + ); + let admins = await toPromise(bob.store.assignees.get('admin')); assert.equal(admins.length, 1); assert.equal( @@ -147,10 +179,16 @@ test('Admin can assign admin that assigns a role', async () => { alice.player.cells[0].cell_id[0], ); + await waitUntil( + async () => + (await toPromise(bob.store.assignees.get('admin'))).length === 2, + 30_000, + ); + admins = await toPromise(bob.store.assignees.get('admin')); assert.equal(admins.length, 2); - let pendingUnassigments = await toPromise(bob.store.pendingUnassigments); + let pendingUnassigments = await toPromise(bob.store.pendingUnassignments); assert.equal(pendingUnassigments.length, 0); await expect(() => @@ -165,9 +203,16 @@ test('Admin can assign admin that assigns a role', async () => { alice.player.cells[0].cell_id[0], ); - pendingUnassigments = await toPromise(carol.store.pendingUnassigments); + pendingUnassigments = await toPromise(carol.store.pendingUnassignments); assert.equal(pendingUnassigments.length, 0); + await waitUntil( + async () => + (await carol.store.client.queryUndeletedRoleClaimsForRole('editor')) + .length === 1, + 20_000, + ); + let editors = await toPromise(carol.store.assignees.get('editor')); assert.equal(editors.length, 1); assert.equal( @@ -183,29 +228,31 @@ test('Admin can assign admin that assigns a role', async () => { 'editor', carol.player.agentPubKey, ); - - // Wait for the created entry to be propagated to the other node. - await dhtSync( - [alice.player, bob.player, carol.player], - alice.player.cells[0].cell_id[0], - ); - - pendingUnassigments = await toPromise(carol.store.pendingUnassigments); + await pause(100); + pendingUnassigments = await toPromise(bob.store.pendingUnassignments); assert.equal(pendingUnassigments.length, 1); - assert.equal( - retype(pendingUnassigments[0].target, HashType.AGENT).toString(), - new Uint8Array(carol.player.agentPubKey).toString(), - ); // Bob can't unassign Carol's role await expect(() => bob.store.client.unassignMyRole(pendingUnassigments[0].create_link_hash), ).rejects.toThrowError(); - // Carol will delete their role claim automatically when getting assignees + await waitUntil( + async () => + (await toPromise(carol.store.assignees.get('editor'))).length === 0, + 10_000, + ); + + // Carol will filter out their role claim automatically when getting assignees editors = await toPromise(carol.store.assignees.get('editor')); assert.equal(editors.length, 0); + await waitUntil(async () => { + const roleClaims = + await carol.store.client.queryUndeletedRoleClaimsForRole('editor'); + return roleClaims.length === 0; + }, 30_000); + roleClaims = await bob.store.client.queryUndeletedRoleClaimsForRole('editor'); assert.equal(roleClaims.length, 0); diff --git a/tests/src/setup.ts b/tests/src/setup.ts index 9debb07f5..6de907614 100644 --- a/tests/src/setup.ts +++ b/tests/src/setup.ts @@ -1,13 +1,18 @@ import { AppBundle, encodeHashToBase64 } from '@holochain/client'; -import { AgentApp, Scenario, enableAndGetAgentApp } from '@holochain/tryorama'; -import { decode, encode } from '@msgpack/msgpack'; -import { decompressSync, unzipSync } from 'fflate'; +import { + AgentApp, + Scenario, + enableAndGetAgentApp, + pause, +} from '@holochain/tryorama'; +import { decode } from '@msgpack/msgpack'; +import { decompressSync } from 'fflate'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import { RolesClient } from '../../ui/src/roles-client.js'; -import { RolesStore } from '../../ui/src/roles-store.js'; +import { RolesStore, RolesStoreConfig } from '../../ui/src/roles-store.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -59,16 +64,44 @@ export async function setup(scenario: Scenario) { { appBundleSource }, ]); + await aliceConductor + .adminWs() + .authorizeSigningCredentials( + (Object.values(appInfo.cell_info)[0][0] as any).provisioned.cell_id, + ); + + await bob.conductor + .adminWs() + .authorizeSigningCredentials(bob.cells[0].cell_id); + + await carol.conductor + .adminWs() + .authorizeSigningCredentials(carol.cells[0].cell_id); + + const config: RolesStoreConfig = { + roles_config: [ + { + role: 'editor', + description: 'An editor role can create special entries', + plural_name: 'editors', + singular_name: 'editor', + }, + ], + }; + const aliceStore = new RolesStore( new RolesClient(appWs as any, 'roles_test', 'roles'), + config, ); const bobStore = new RolesStore( new RolesClient(bob.appWs as any, 'roles_test', 'roles'), + config, ); const carolStore = new RolesStore( new RolesClient(carol.appWs as any, 'roles_test', 'roles'), + config, ); // Shortcut peer discovery through gossip and register all agents in every diff --git a/tests/vitest.config.ts b/tests/vitest.config.ts index 7ecd61a9f..d8b8e93e5 100644 --- a/tests/vitest.config.ts +++ b/tests/vitest.config.ts @@ -1,24 +1,25 @@ -import { defineConfig } from "vitest/config"; +import { defineConfig } from 'vitest/config'; + //@ts-ignore -import pkg from "./package.json"; +import pkg from './package.json'; export default defineConfig({ - test: { - poolOptions: { - threads: { - singleThread: true, - }, - }, - testTimeout: 60 * 1000 * 3, // 3 mins - deps: { - optimizer: { - ssr: { - enabled: true, - //@ts-ignore - include: Object.keys(pkg.dependencies), - exclude: ["@holochain/client"], - }, - }, - }, - }, + test: { + poolOptions: { + threads: { + singleThread: true, + }, + }, + testTimeout: 60 * 1000 * 3, // 3 mins + deps: { + optimizer: { + ssr: { + enabled: true, + //@ts-ignore + include: Object.keys(pkg.dependencies), + exclude: ['@holochain/client', 'fflate'], + }, + }, + }, + }, }); diff --git a/ui/custom-elements.json b/ui/custom-elements.json index 8e14f23c1..cbbecbb23 100644 --- a/ui/custom-elements.json +++ b/ui/custom-elements.json @@ -2,62 +2,6 @@ "schemaVersion": "1.0.0", "readme": "", "modules": [ - { - "kind": "javascript-module", - "path": "locales/locales.js", - "declarations": [ - { - "kind": "variable", - "name": "sourceLocale", - "default": "`en`", - "description": "The locale code that templates in this source code are written in." - }, - { - "kind": "variable", - "name": "targetLocales", - "type": { - "text": "array" - }, - "default": "[\n ,\n]", - "description": "The other locale codes that this application is localized into. Sorted\nlexicographically." - }, - { - "kind": "variable", - "name": "allLocales", - "type": { - "text": "array" - }, - "default": "[\n `en`,\n]", - "description": "All valid project locale codes. Sorted lexicographically." - } - ], - "exports": [ - { - "kind": "js", - "name": "sourceLocale", - "declaration": { - "name": "sourceLocale", - "module": "locales/locales.js" - } - }, - { - "kind": "js", - "name": "targetLocales", - "declaration": { - "name": "targetLocales", - "module": "locales/locales.js" - } - }, - { - "kind": "js", - "name": "allLocales", - "declaration": { - "name": "allLocales", - "module": "locales/locales.js" - } - } - ] - }, { "kind": "javascript-module", "path": "src/context.ts", @@ -346,7 +290,7 @@ "name": "createRoleClaim", "return": { "type": { - "text": "Promise>" + "text": "Promise" } }, "parameters": [ @@ -410,16 +354,6 @@ } ] }, - { - "kind": "method", - "name": "getAllRoles", - "return": { - "type": { - "text": "Promise>" - } - }, - "description": "All Roles" - }, { "kind": "method", "name": "roleBaseAddress", @@ -435,7 +369,8 @@ "text": "string" } } - ] + ], + "description": "All Roles" }, { "kind": "method", @@ -555,6 +490,24 @@ "description": "", "name": "RolesStore", "members": [ + { + "kind": "field", + "name": "_unassigningRoleCreateLinkHash", + "type": { + "text": "ActionHash[]" + }, + "privacy": "private", + "default": "[]" + }, + { + "kind": "field", + "name": "_claimingRoles", + "type": { + "text": "ActionHash[]" + }, + "privacy": "private", + "default": "[]" + }, { "kind": "field", "name": "myRoleClaims", @@ -572,27 +525,29 @@ }, { "kind": "field", - "name": "allRolesWithAssignees" + "name": "roleBaseAddress", + "privacy": "private", + "default": "new LazyMap((role: string) =>\n\t\tfromPromise(() => this.client.roleBaseAddress(role)),\n\t)" }, { "kind": "field", - "name": "roleBasedAddress", + "name": "roleToAssigneeLinks", "privacy": "private", - "default": "new LazyMap((role: string) =>\n\t\tfromPromise(() => this.client.roleBaseAddress(role)),\n\t)" + "default": "new LazyMap((role: string) =>\n\t\tpipe(this.roleBaseAddress.get(role), roleBaseAddress =>\n\t\t\tliveLinksSignal(\n\t\t\t\tthis.client,\n\t\t\t\troleBaseAddress,\n\t\t\t\t() => this.client.getAssigneesForRole(role),\n\t\t\t\t'RoleToAssignee',\n\t\t\t\t4000,\n\t\t\t),\n\t\t),\n\t)" }, { "kind": "field", "name": "assignees", - "default": "new LazyMap((role: string) =>\n\t\tpipe(\n\t\t\tthis.roleBasedAddress.get(role),\n\t\t\troleBaseAddress =>\n\t\t\t\tliveLinksSignal(\n\t\t\t\t\tthis.client,\n\t\t\t\t\troleBaseAddress,\n\t\t\t\t\t() => this.client.getAssigneesForRole(role),\n\t\t\t\t\t'RoleToAssignee',\n\t\t\t\t\t4000,\n\t\t\t\t),\n\t\t\t() =>\n\t\t\t\tjoinAsync([\n\t\t\t\t\tthis.pendingUnassigments.get(),\n\t\t\t\t\tthis.myRoleClaims.get(role).get(),\n\t\t\t\t]),\n\t\t\tasync ([pendingUnassignments, myRoleClaims], assigneesLinks) => {\n\t\t\t\tlet assignees = uniquify(\n\t\t\t\t\tassigneesLinks.map(a => retype(a.target, HashType.AGENT)),\n\t\t\t\t);\n\n\t\t\t\tconst myPendingUnassignmentsForThisRole = pendingUnassignments.filter(\n\t\t\t\t\tpendingUnassigment =>\n\t\t\t\t\t\tretype(pendingUnassigment.target, HashType.AGENT).toString() ===\n\t\t\t\t\t\t\tnew Uint8Array(this.client.client.myPubKey).toString() &&\n\t\t\t\t\t\tnew TextDecoder().decode(pendingUnassigment.tag) === role,\n\t\t\t\t);\n\n\t\t\t\t/** If I am assigned to the role but haven't claimed it, do so */\n\n\t\t\t\tconst myAssigneeLink = assigneesLinks.find(\n\t\t\t\t\ta =>\n\t\t\t\t\t\tretype(a.target, HashType.AGENT).toString() ===\n\t\t\t\t\t\tnew Uint8Array(this.client.client.myPubKey).toString(),\n\t\t\t\t);\n\n\t\t\t\tif (\n\t\t\t\t\tmyPendingUnassignmentsForThisRole.length === 0 &&\n\t\t\t\t\tmyAssigneeLink &&\n\t\t\t\t\tmyRoleClaims.length === 0\n\t\t\t\t) {\n\t\t\t\t\tawait this.client.createRoleClaim({\n\t\t\t\t\t\trole,\n\t\t\t\t\t\tassign_role_create_link_hash: myAssigneeLink.create_link_hash,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\treturn assignees;\n\t\t\t},\n\t\t),\n\t)" + "default": "new LazyMap(\n\t\t(role: string) =>\n\t\t\tnew AsyncComputed(() => {\n\t\t\t\tconst assigneesLinks = this.roleToAssigneeLinks.get(role).get();\n\t\t\t\tconst pendingUnassignments = this.pendingUnassignments.get();\n\t\t\t\tconst myRoleClaims = this.myRoleClaims.get(role).get();\n\n\t\t\t\tif (assigneesLinks.status !== 'completed') return assigneesLinks;\n\t\t\t\tif (pendingUnassignments.status !== 'completed')\n\t\t\t\t\treturn pendingUnassignments;\n\t\t\t\tif (myRoleClaims.status !== 'completed') return myRoleClaims;\n\n\t\t\t\tlet assignees = uniquify(\n\t\t\t\t\tassigneesLinks.value.map(a => retype(a.target, HashType.AGENT)),\n\t\t\t\t);\n\n\t\t\t\tconst myPendingUnassignmentsForThisRole =\n\t\t\t\t\tpendingUnassignments.value.filter(\n\t\t\t\t\t\tpendingUnassignment =>\n\t\t\t\t\t\t\tencodeHashToBase64(\n\t\t\t\t\t\t\t\tretype(pendingUnassignment.target, HashType.AGENT),\n\t\t\t\t\t\t\t) === encodeHashToBase64(this.client.client.myPubKey) &&\n\t\t\t\t\t\t\tnew TextDecoder().decode(pendingUnassignment.tag) === role,\n\t\t\t\t\t);\n\t\t\t\tif (myPendingUnassignmentsForThisRole.length > 0) {\n\t\t\t\t\tassignees = assignees.filter(\n\t\t\t\t\t\ta =>\n\t\t\t\t\t\t\tencodeHashToBase64(a) !==\n\t\t\t\t\t\t\tencodeHashToBase64(this.client.client.myPubKey),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\tvalue: assignees,\n\t\t\t\t};\n\t\t\t}),\n\t)" }, { "kind": "field", "name": "rolesForAgent", - "default": "new LazyHoloHashMap(assignee =>\n\t\tpipe(\n\t\t\tthis.allRolesWithAssignees,\n\t\t\troles =>\n\t\t\t\tjoinAsyncMap(mapValues(slice(this.assignees, roles), r => r.get())),\n\t\t\tassigneesByRoles => {\n\t\t\t\tconst assigneeRoles: string[] = [];\n\n\t\t\t\tfor (const [role, assignees] of Array.from(\n\t\t\t\t\tassigneesByRoles.entries(),\n\t\t\t\t)) {\n\t\t\t\t\tif (assignees.find(a => assignee.toString() === a.toString())) {\n\t\t\t\t\t\tassigneeRoles.push(role);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn assigneeRoles;\n\t\t\t},\n\t\t),\n\t)" + "default": "new LazyHoloHashMap(\n\t\tassignee =>\n\t\t\tnew AsyncComputed(() => {\n\t\t\t\tconst assigneesByRoles = joinAsyncMap(\n\t\t\t\t\tmapValues(slice(this.assignees, this.allRoles), r => r.get()),\n\t\t\t\t);\n\t\t\t\tif (assigneesByRoles.status !== 'completed') return assigneesByRoles;\n\n\t\t\t\tconst assigneeRoles: string[] = [];\n\n\t\t\t\tfor (const [role, assignees] of Array.from(\n\t\t\t\t\tassigneesByRoles.value.entries(),\n\t\t\t\t)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tassignees.find(\n\t\t\t\t\t\t\ta => encodeHashToBase64(assignee) === encodeHashToBase64(a),\n\t\t\t\t\t\t)\n\t\t\t\t\t) {\n\t\t\t\t\t\tassigneeRoles.push(role);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\tvalue: assigneeRoles,\n\t\t\t\t};\n\t\t\t}),\n\t)" }, { "kind": "field", - "name": "pendingUnassigments" + "name": "pendingUnassignments" } ] } @@ -760,6 +715,62 @@ "declarations": [], "exports": [] }, + { + "kind": "javascript-module", + "path": "locales/locales.js", + "declarations": [ + { + "kind": "variable", + "name": "sourceLocale", + "default": "`en`", + "description": "The locale code that templates in this source code are written in." + }, + { + "kind": "variable", + "name": "targetLocales", + "type": { + "text": "array" + }, + "default": "[\n ,\n]", + "description": "The other locale codes that this application is localized into. Sorted\nlexicographically." + }, + { + "kind": "variable", + "name": "allLocales", + "type": { + "text": "array" + }, + "default": "[\n `en`,\n]", + "description": "All valid project locale codes. Sorted lexicographically." + } + ], + "exports": [ + { + "kind": "js", + "name": "sourceLocale", + "declaration": { + "name": "sourceLocale", + "module": "locales/locales.js" + } + }, + { + "kind": "js", + "name": "targetLocales", + "declaration": { + "name": "targetLocales", + "module": "locales/locales.js" + } + }, + { + "kind": "js", + "name": "allLocales", + "declaration": { + "name": "allLocales", + "module": "locales/locales.js" + } + } + ] + }, { "kind": "javascript-module", "path": "src/elements/all-roles.ts", diff --git a/ui/package.json b/ui/package.json index 01d85a74c..16c173861 100644 --- a/ui/package.json +++ b/ui/package.json @@ -32,7 +32,7 @@ "@holochain/client": "^0.17.0", "@holochain-open-dev/elements": "^0.300.0", "@holochain-open-dev/profiles": "github:holochain-open-dev/profiles#748bb4879e247eff44a4a23a8d2aaa02a585578a&path:ui", - "@darksoil-studio/notifications": "github:darksoil-studio/notifications#58b44bd9e23dcafa728b6f9dd932c75c07657af0&path:ui", + "@darksoil-studio/notifications": "github:darksoil-studio/notifications#d590188a83aea4e9d081ca811d4f354e4ec930d7&path:ui", "@holochain-open-dev/signals": "^0.300.4", "@holochain-open-dev/utils": "^0.300.1", "@lit/context": "^1.0.0", diff --git a/ui/src/elements/role-detail.ts b/ui/src/elements/role-detail.ts index b8691ae21..287c572dd 100644 --- a/ui/src/elements/role-detail.ts +++ b/ui/src/elements/role-detail.ts @@ -8,6 +8,7 @@ import '@holochain-open-dev/profiles/dist/elements/profile-list-item.js'; import { SearchAgents } from '@holochain-open-dev/profiles/dist/elements/search-agents.js'; import '@holochain-open-dev/profiles/dist/elements/search-agents.js'; import { SignalWatcher } from '@holochain-open-dev/signals'; +import { HashType, retype } from '@holochain-open-dev/utils'; import { AgentPubKey, encodeHashToBase64 } from '@holochain/client'; import { consume } from '@lit/context'; import { msg, str } from '@lit/localize'; @@ -112,7 +113,7 @@ export class RoleDetail extends SignalWatcher(LitElement) { assignee: AgentPubKey, assigneesCount: number, ) { - const pendingUnassignments = this.rolesStore.pendingUnassigments.get(); + const pendingUnassignments = this.rolesStore.pendingUnassignments.get(); switch (pendingUnassignments.status) { case 'pending': return html``; @@ -125,7 +126,8 @@ export class RoleDetail extends SignalWatcher(LitElement) { case 'completed': const pendingUnassignment = !!pendingUnassignments.value.find( link => - link.target.toString() === assignee.toString() && + retype(link.target, HashType.AGENT).toString() === + assignee.toString() && new TextDecoder().decode(link.tag) === roleConfig.role, ); if (pendingUnassignment) { @@ -293,9 +295,7 @@ export class RoleDetail extends SignalWatcher(LitElement) { assigneesForRoleAndIAmAdmin() { const assignees = this.rolesStore.assignees.get(this.role).get(); - const myRoles = this.rolesStore.rolesForAgent - .get(this.rolesStore.client.client.myPubKey) - .get(); + const myRoles = this.rolesStore.myRoles.get(); if (assignees.status !== 'completed') return assignees; if (myRoles.status !== 'completed') return myRoles; const iAmAdmin = myRoles.value.includes(adminRoleConfig.role); diff --git a/ui/src/roles-client.ts b/ui/src/roles-client.ts index 4b94a2ef0..21a1ce807 100644 --- a/ui/src/roles-client.ts +++ b/ui/src/roles-client.ts @@ -1,4 +1,8 @@ -import { EntryRecord, ZomeClient } from '@holochain-open-dev/utils'; +import { + EntryRecord, + ZomeClient, + utf32Decode, +} from '@holochain-open-dev/utils'; import { ActionHash, AgentPubKey, @@ -25,9 +29,8 @@ export class RolesClient extends ZomeClient { } /** Role Claim */ - async createRoleClaim(roleClaim: RoleClaim): Promise> { - const record: Record = await this.callZome('create_role_claim', roleClaim); - return new EntryRecord(record); + async createRoleClaim(roleClaim: RoleClaim): Promise { + await this.callZome('create_role_claim', roleClaim); } async getRoleClaim( @@ -58,10 +61,6 @@ export class RolesClient extends ZomeClient { /** All Roles */ - async getAllRoles(): Promise> { - return this.callZome('get_all_roles', undefined); - } - async roleBaseAddress(role: string): Promise { return this.callZome('role_base_address', role); } diff --git a/ui/src/roles-store.ts b/ui/src/roles-store.ts index cae7845de..fe85ebf8a 100644 --- a/ui/src/roles-store.ts +++ b/ui/src/roles-store.ts @@ -1,23 +1,25 @@ import { NotificationsStore } from '@darksoil-studio/notifications'; -import { wrapPathInSvg } from '@holochain-open-dev/elements'; +import { wrapPathInSvg } from '@holochain-open-dev/elements/dist/icon.js'; import { + AsyncComputed, AsyncState, + Signal, collectionSignal, fromPromise, joinAsync, liveLinksSignal, pipe, + toPromise, uniquify, - watch, } from '@holochain-open-dev/signals'; import { HashType, LazyHoloHashMap, LazyMap, - decodeComponent, + hashEntry, retype, } from '@holochain-open-dev/utils'; -import { Link, encodeHashToBase64 } from '@holochain/client'; +import { ActionHash, Link, encodeHashToBase64 } from '@holochain/client'; import { msg, str } from '@lit/localize'; import { mdiSmartCard, mdiSmartCardOff } from '@mdi/js'; import { decode, encode } from '@msgpack/msgpack'; @@ -37,6 +39,8 @@ export interface RolesStoreConfig { } export class RolesStore { + private _unassigningRoleCreateLinkHash: ActionHash[] = []; + private _claimingRoles: ActionHash[] = []; constructor( public client: RolesClient, public config: RolesStoreConfig, @@ -44,7 +48,111 @@ export class RolesStore { ) { // Always watch the unassignments to automatically unassign a role whenever // we receive a unassign role request - watch(this.pendingUnassigments, pendingUnassigments => {}); + effect(async () => { + const pendingUnassignments = this.pendingUnassignments.get(); + if (pendingUnassignments.status !== 'completed') return; + + const isUnassignmentLinkForMe = (pendingUnassignment: Link) => + encodeHashToBase64( + retype(pendingUnassignment.target, HashType.AGENT), + ) === encodeHashToBase64(this.client.client.myPubKey); + + const myPendingUnassignmentsForThisRole = + pendingUnassignments.value.filter(link => + isUnassignmentLinkForMe(link), + ); + + if (myPendingUnassignmentsForThisRole.length > 0) { + for (const link of myPendingUnassignmentsForThisRole) { + if ( + !this._unassigningRoleCreateLinkHash.find( + linkHash => + encodeHashToBase64(linkHash) === + encodeHashToBase64(link.create_link_hash), + ) + ) { + const myRoleClaimsForThisRole = await toPromise( + this.myRoleClaims.get(new TextDecoder().decode(link.tag)), + ); + if (myRoleClaimsForThisRole.length === 0) return; // Wait for the RoleClaim to actually be committed by the other effect, then go ahead and unassign the role + + this._unassigningRoleCreateLinkHash.push(link.create_link_hash); + this.client.unassignMyRole(link.create_link_hash).finally(() => { + this._unassigningRoleCreateLinkHash = + this._unassigningRoleCreateLinkHash.filter( + linkHash => + encodeHashToBase64(linkHash) !== + encodeHashToBase64(link.create_link_hash), + ); + }); + } + } + } + }); + + effect(async () => { + const myRoles = this.rolesForAgent.get(this.client.client.myPubKey).get(); + const pendingUnassignments = this.pendingUnassignments.get(); + if (myRoles.status !== 'completed') return; + if (pendingUnassignments.status !== 'completed') return; + + const myRoleClaims = joinAsync( + myRoles.value.map(role => this.myRoleClaims.get(role).get()), + ); + + if (myRoleClaims.status !== 'completed') return; + + /** If I am assigned to the role but haven't claimed it, do so */ + + for (let i = 0; i < myRoles.value.length; i++) { + const role = myRoles.value[i]; + const myRoleClaimsForThisRole = myRoleClaims.value[i]; + + const myPendingUnassignmentsForThisRole = + pendingUnassignments.value.filter( + pendingUnassignment => + encodeHashToBase64( + retype(pendingUnassignment.target, HashType.AGENT), + ) === encodeHashToBase64(this.client.client.myPubKey) && + new TextDecoder().decode(pendingUnassignment.tag) === role, + ); + if ( + myPendingUnassignmentsForThisRole.length === 0 && + myRoleClaimsForThisRole.length === 0 + ) { + const assigneesLinks = this.roleToAssigneeLinks.get(role).get(); + if (assigneesLinks.status !== 'completed') return; + const link = assigneesLinks.value.find( + l => + encodeHashToBase64(retype(l.target, HashType.AGENT)) === + encodeHashToBase64(this.client.client.myPubKey), + ); + if (link) { + if ( + !this._claimingRoles.find( + actionHash => + encodeHashToBase64(actionHash) === + encodeHashToBase64(link.create_link_hash), + ) + ) { + this._claimingRoles.push(link.create_link_hash); + await this.client + .createRoleClaim({ + role, + assign_role_create_link_hash: link.create_link_hash, + }) + .finally(() => { + this._claimingRoles = this._claimingRoles.filter( + actionHash => + encodeHashToBase64(actionHash) === + encodeHashToBase64(link.create_link_hash), + ); + }); + } + } + } + } + }); if (notificationsStore) { notificationsStore.addTypes({ @@ -71,7 +179,7 @@ export class RolesStore { status: 'completed', value: { body: msg( - str`You were assigned the ${roleConfig?.singular_name} role.`, + str`You have been assigned the ${roleConfig?.singular_name} role.`, ), iconSrc: wrapPathInSvg(mdiSmartCard), }, @@ -120,6 +228,15 @@ export class RolesStore { signal.type === 'LinkCreated' && signal.link_type === 'RoleToAssignee' ) { + const recipient = retype( + signal.action.hashed.content.target_address, + HashType.AGENT, + ); + if ( + encodeHashToBase64(recipient) === + encodeHashToBase64(this.client.client.myPubKey) + ) + return; // We just assigned a role: notify the assignee await notificationsStore.client.createNotification({ content: encode({ @@ -128,18 +245,22 @@ export class RolesStore { notification_type: NOTIFICATIONS_TYPES.ASSIGNED_ROLE, persistent: false, notification_group: encodeHashToBase64(signal.action.hashed.hash), - recipients: [ - retype( - signal.action.hashed.content.target_address, - HashType.AGENT, - ), - ], + recipients: [recipient], }); } if ( signal.type === 'LinkCreated' && signal.link_type === 'PendingUnassignments' ) { + const recipient = retype( + signal.action.hashed.content.target_address, + HashType.AGENT, + ); + if ( + encodeHashToBase64(recipient) === + encodeHashToBase64(this.client.client.myPubKey) + ) + return; // We just assigned a role: notify the assignee await notificationsStore.client.createNotification({ content: encode({ @@ -148,12 +269,7 @@ export class RolesStore { notification_type: NOTIFICATIONS_TYPES.UNASSIGNED_ROLE, persistent: false, notification_group: encodeHashToBase64(signal.action.hashed.hash), - recipients: [ - retype( - signal.action.hashed.content.target_address, - HashType.AGENT, - ), - ], + recipients: [recipient], }); } }); @@ -175,118 +291,149 @@ export class RolesStore { return ['admin', ...this.config.roles_config.map(r => r.role)]; } - allRolesWithAssignees = pipe( - collectionSignal(this.client, () => this.client.getAllRoles(), 'RolesPath'), - allRoles => allRoles.map(l => decodeComponent(l.tag)), + private roleBaseAddress = new LazyMap((role: string) => + fromPromise(() => this.client.roleBaseAddress(role)), ); - private roleBasedAddress = new LazyMap((role: string) => - fromPromise(() => this.client.roleBaseAddress(role)), + private roleToAssigneeLinks = new LazyMap((role: string) => + pipe(this.roleBaseAddress.get(role), roleBaseAddress => + liveLinksSignal( + this.client, + roleBaseAddress, + () => this.client.getAssigneesForRole(role), + 'RoleToAssignee', + 4000, + ), + ), ); - assignees = new LazyMap((role: string) => - pipe( - this.roleBasedAddress.get(role), - roleBaseAddress => - liveLinksSignal( - this.client, - roleBaseAddress, - () => this.client.getAssigneesForRole(role), - 'RoleToAssignee', - 4000, - ), - () => - joinAsync([ - this.pendingUnassigments.get(), - this.myRoleClaims.get(role).get(), - ]), - async ([pendingUnassignments, myRoleClaims], assigneesLinks) => { - let assignees = uniquify( - assigneesLinks.map(a => retype(a.target, HashType.AGENT)), - ); + assignees = new LazyMap( + (role: string) => + new AsyncComputed(() => { + const assigneesLinks = this.roleToAssigneeLinks.get(role).get(); + const pendingUnassignments = this.pendingUnassignments.get(); + const myRoleClaims = this.myRoleClaims.get(role).get(); - const myPendingUnassignmentsForThisRole = pendingUnassignments.filter( - pendingUnassigment => - retype(pendingUnassigment.target, HashType.AGENT).toString() === - new Uint8Array(this.client.client.myPubKey).toString() && - new TextDecoder().decode(pendingUnassigment.tag) === role, - ); + if (assigneesLinks.status !== 'completed') return assigneesLinks; + if (pendingUnassignments.status !== 'completed') + return pendingUnassignments; + if (myRoleClaims.status !== 'completed') return myRoleClaims; - /** If I am assigned to the role but haven't claimed it, do so */ - - const myAssigneeLink = assigneesLinks.find( - a => - retype(a.target, HashType.AGENT).toString() === - new Uint8Array(this.client.client.myPubKey).toString(), + let assignees = uniquify( + assigneesLinks.value.map(a => retype(a.target, HashType.AGENT)), ); - if ( - myPendingUnassignmentsForThisRole.length === 0 && - myAssigneeLink && - myRoleClaims.length === 0 - ) { - await this.client.createRoleClaim({ - role, - assign_role_create_link_hash: myAssigneeLink.create_link_hash, - }); + const myPendingUnassignmentsForThisRole = + pendingUnassignments.value.filter( + pendingUnassignment => + encodeHashToBase64( + retype(pendingUnassignment.target, HashType.AGENT), + ) === encodeHashToBase64(this.client.client.myPubKey) && + new TextDecoder().decode(pendingUnassignment.tag) === role, + ); + if (myPendingUnassignmentsForThisRole.length > 0) { + assignees = assignees.filter( + a => + encodeHashToBase64(a) !== + encodeHashToBase64(this.client.client.myPubKey), + ); } - return assignees; - }, - ), + return { + status: 'completed', + value: assignees, + }; + }), ); - rolesForAgent = new LazyHoloHashMap(assignee => - pipe( - this.allRolesWithAssignees, - roles => - joinAsyncMap(mapValues(slice(this.assignees, roles), r => r.get())), - assigneesByRoles => { + rolesForAgent = new LazyHoloHashMap( + assignee => + new AsyncComputed(() => { + const assigneesByRoles = joinAsyncMap( + mapValues(slice(this.assignees, this.allRoles), r => r.get()), + ); + if (assigneesByRoles.status !== 'completed') return assigneesByRoles; + const assigneeRoles: string[] = []; for (const [role, assignees] of Array.from( - assigneesByRoles.entries(), + assigneesByRoles.value.entries(), )) { - if (assignees.find(a => assignee.toString() === a.toString())) { + if ( + assignees.find( + a => encodeHashToBase64(assignee) === encodeHashToBase64(a), + ) + ) { assigneeRoles.push(role); } } - return assigneeRoles; - }, - ), + return { + status: 'completed', + value: assigneeRoles, + }; + }), ); - pendingUnassigments = pipe( - collectionSignal( - this.client, - () => this.client.getPendingUnassignments(), - 'PendingUnassignments', - ), - links => - links.map(link => ({ - ...link, - target: retype(link.target, HashType.AGENT), - })) as Link[], - async pendingUnassignments => { - /** If I have been requested to unassign a role and I still have it, unassign it */ - const isUnassignmentLinkForMe = (pendingUnassigment: Link) => - retype(pendingUnassigment.target, HashType.AGENT).toString() === - new Uint8Array(this.client.client.myPubKey).toString(); - - const myPendingUnassignmentsForThisRole = pendingUnassignments.filter( - isUnassignmentLinkForMe, - ); - if (myPendingUnassignmentsForThisRole.length > 0) { - for (const link of myPendingUnassignmentsForThisRole) { - await this.client.unassignMyRole(link.create_link_hash); - } - - pendingUnassignments = pendingUnassignments.filter( - link => !isUnassignmentLinkForMe(link), - ); - } - return pendingUnassignments; - }, + pendingUnassignments = collectionSignal( + this.client, + () => this.client.getPendingUnassignments(), + 'PendingUnassignments', + 4000, ); + + myRoles = new AsyncComputed(() => { + const myRoles = this.rolesForAgent.get(this.client.client.myPubKey).get(); + if (myRoles.status !== 'completed') return myRoles; + + const myRoleClaims = joinAsync( + myRoles.value.map(role => this.myRoleClaims.get(role).get()), + ); + if (myRoleClaims.status !== 'completed') return myRoleClaims; + + const myClaimedRoles = myRoles.value.filter( + (_, i) => myRoleClaims.value[i].length > 0, + ); + + return { + status: 'completed', + value: myClaimedRoles, + }; + }); +} + +// NOTE: This scheduling logic is too basic to be useful. Do not copy/paste. +// This function would usually live in a library/framework, not application code +let pending = false; + +let w = new Signal.subtle.Watcher(() => { + if (!pending) { + pending = true; + queueMicrotask(() => { + pending = false; + for (let s of w.getPending()) s.get(); + w.watch(); + }); + } +}); + +// TODO: why do we need to use this complicated effect method? +// An effect effect Signal which evaluates to cb, which schedules a read of +// itself on the microtask queue whenever one of its dependencies might change +function effect(cb: any) { + let destructor: any; + let c = new Signal.Computed(() => { + if (typeof destructor === 'function') { + destructor(); + } + destructor = cb(); + }); + w.watch(c); + c.get(); + return () => { + if (typeof destructor === 'function') { + destructor(); + } + w.unwatch(c); + }; } diff --git a/zomes/coordinator/roles/src/all_roles.rs b/zomes/coordinator/roles/src/all_roles.rs deleted file mode 100644 index 654c60bbc..000000000 --- a/zomes/coordinator/roles/src/all_roles.rs +++ /dev/null @@ -1,8 +0,0 @@ -use hdk::prelude::*; -use roles_integrity::*; - -#[hdk_extern] -pub fn get_all_roles() -> ExternResult> { - let path = all_roles_path()?; - get_links(GetLinksInputBuilder::try_new(path.path_entry_hash()?, LinkTypes::RolesPath)?.build()) -} diff --git a/zomes/coordinator/roles/src/assignees.rs b/zomes/coordinator/roles/src/assignees.rs index 6de55682e..c8986240b 100644 --- a/zomes/coordinator/roles/src/assignees.rs +++ b/zomes/coordinator/roles/src/assignees.rs @@ -1,7 +1,10 @@ use hdk::prelude::*; use roles_integrity::*; -use crate::role_claim::query_undeleted_role_claims_for_role; +use crate::{ + role_claim::query_undeleted_role_claims_for_role, + utils::{delete_link_relaxed, delete_relaxed}, +}; #[derive(Serialize, Deserialize, Debug)] pub struct AssignRoleInput { @@ -105,10 +108,10 @@ pub fn unassign_my_role(pending_unassignment_link: ActionHash) -> ExternResult<( }?; let role_claim = RoleClaim::try_from(role_claim_record.clone())?; - delete_entry(role_claim_record.action_address().clone())?; - delete_link(role_claim.assign_role_create_link_hash)?; + delete_relaxed(role_claim_record.action_address().clone())?; + delete_link_relaxed(role_claim.assign_role_create_link_hash)?; - delete_link(pending_unassignment_link)?; + delete_link_relaxed(pending_unassignment_link)?; Ok(()) } diff --git a/zomes/coordinator/roles/src/lib.rs b/zomes/coordinator/roles/src/lib.rs index cb348def2..86233ab67 100644 --- a/zomes/coordinator/roles/src/lib.rs +++ b/zomes/coordinator/roles/src/lib.rs @@ -3,9 +3,9 @@ use hdk::prelude::*; use role_claim::create_role_claim; use roles_integrity::*; -pub mod all_roles; pub mod assignees; pub mod role_claim; +pub mod utils; #[hdk_extern] pub fn init(_: ()) -> ExternResult { diff --git a/zomes/coordinator/roles/src/role_claim.rs b/zomes/coordinator/roles/src/role_claim.rs index 40165e621..ce50a3ae3 100644 --- a/zomes/coordinator/roles/src/role_claim.rs +++ b/zomes/coordinator/roles/src/role_claim.rs @@ -1,13 +1,12 @@ use hdk::prelude::*; use roles_integrity::*; +use crate::utils::create_relaxed; + #[hdk_extern] -pub fn create_role_claim(role_claim: RoleClaim) -> ExternResult { - let role_claim_hash = create_entry(&EntryTypes::RoleClaim(role_claim.clone()))?; - let record = get(role_claim_hash.clone(), GetOptions::default())?.ok_or(wasm_error!( - WasmErrorInner::Guest("Could not find the newly created RoleClaim".to_string()) - ))?; - Ok(record) +pub fn create_role_claim(role_claim: RoleClaim) -> ExternResult<()> { + create_relaxed(EntryTypes::RoleClaim(role_claim.clone()))?; + Ok(()) } #[hdk_extern] diff --git a/zomes/coordinator/roles/src/utils.rs b/zomes/coordinator/roles/src/utils.rs new file mode 100644 index 000000000..dc2e26374 --- /dev/null +++ b/zomes/coordinator/roles/src/utils.rs @@ -0,0 +1,68 @@ +use hdk::prelude::*; +use roles_integrity::*; + +pub fn create_relaxed(entry_type: EntryTypes) -> ExternResult<()> { + HDK.with(|h| { + let index = ScopedEntryDefIndex::try_from(&entry_type)?; + let vis = EntryVisibility::from(&entry_type); + let entry = Entry::try_from(entry_type)?; + + h.borrow().create(CreateInput::new( + index, + vis, + entry, + // This is used to test many conductors thrashing creates between + // each other so we want to avoid retries that make the test take + // a long time. + ChainTopOrdering::Relaxed, + )) + })?; + + Ok(()) +} + +pub fn create_link_relaxed( + base_address: impl Into, + target_address: impl Into, + link_type: T, + tag: impl Into, +) -> ExternResult<()> +where + ScopedLinkType: TryFrom, + WasmError: From, +{ + let ScopedLinkType { + zome_index, + zome_type: link_type, + } = link_type.try_into()?; + HDK.with(|h| { + h.borrow().create_link(CreateLinkInput::new( + base_address.into(), + target_address.into(), + zome_index, + link_type, + tag.into(), + ChainTopOrdering::Relaxed, + )) + })?; + + Ok(()) +} + +pub fn delete_link_relaxed(address: ActionHash) -> ExternResult<()> { + HDK.with(|h| { + h.borrow() + .delete_link(DeleteLinkInput::new(address, ChainTopOrdering::Relaxed)) + })?; + + Ok(()) +} + +pub fn delete_relaxed(address: ActionHash) -> ExternResult<()> { + HDK.with(|h| { + h.borrow() + .delete(DeleteInput::new(address, ChainTopOrdering::Relaxed)) + })?; + + Ok(()) +} diff --git a/zomes/integrity/roles/src/roles.rs b/zomes/integrity/roles/src/roles.rs index 248f85636..523da8968 100644 --- a/zomes/integrity/roles/src/roles.rs +++ b/zomes/integrity/roles/src/roles.rs @@ -6,11 +6,6 @@ pub fn all_roles_path() -> ExternResult { Path::from("all_roles").typed(LinkTypes::RolesPath) } -#[hdk_extern] -pub fn role_base_address(role: String) -> ExternResult { - role_path(&role)?.path_entry_hash() -} - pub fn role_path(role: &String) -> ExternResult { let all_roles = all_roles_path()?; let mut components = all_roles.path.0; @@ -18,6 +13,11 @@ pub fn role_path(role: &String) -> ExternResult { Path::from(components).typed(LinkTypes::RolesPath) } +#[hdk_extern] +pub fn role_base_address(role: String) -> ExternResult { + role_path(&role)?.path_entry_hash() +} + pub fn validate_create_link_roles_path( _action: CreateLink, base_address: AnyLinkableHash,