feat: add services package
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,6 +20,7 @@ packages/features/dist
|
|||||||
packages/encryption/dist
|
packages/encryption/dist
|
||||||
packages/files/dist
|
packages/files/dist
|
||||||
packages/models/dist
|
packages/models/dist
|
||||||
|
packages/services/dist
|
||||||
|
|
||||||
**/.pnp.*
|
**/.pnp.*
|
||||||
**/.yarn/*
|
**/.yarn/*
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -41,7 +41,7 @@
|
|||||||
"@standardnotes/common": "^1.23.1",
|
"@standardnotes/common": "^1.23.1",
|
||||||
"@standardnotes/models": "workspace:*",
|
"@standardnotes/models": "workspace:*",
|
||||||
"@standardnotes/responses": "^1.6.39",
|
"@standardnotes/responses": "^1.6.39",
|
||||||
"@standardnotes/services": "^1.13.23",
|
"@standardnotes/services": "workspace:*",
|
||||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||||
"@standardnotes/utils": "^1.6.12",
|
"@standardnotes/utils": "^1.6.12",
|
||||||
"reflect-metadata": "^0.1.13"
|
"reflect-metadata": "^0.1.13"
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@standardnotes/common": "^1.23.1",
|
"@standardnotes/common": "^1.23.1",
|
||||||
"@standardnotes/services": "^1.13.23",
|
"@standardnotes/services": "workspace:*",
|
||||||
"@standardnotes/utils": "^1.6.12",
|
"@standardnotes/utils": "^1.6.12",
|
||||||
"reflect-metadata": "^0.1.13"
|
"reflect-metadata": "^0.1.13"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
"@standardnotes/filepicker": "workspace:*",
|
"@standardnotes/filepicker": "workspace:*",
|
||||||
"@standardnotes/models": "workspace:*",
|
"@standardnotes/models": "workspace:*",
|
||||||
"@standardnotes/responses": "^1.6.39",
|
"@standardnotes/responses": "^1.6.39",
|
||||||
"@standardnotes/services": "^1.13.23",
|
"@standardnotes/services": "workspace:*",
|
||||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||||
"@standardnotes/utils": "^1.6.12",
|
"@standardnotes/utils": "^1.6.12",
|
||||||
"reflect-metadata": "^0.1.13"
|
"reflect-metadata": "^0.1.13"
|
||||||
|
|||||||
3
packages/services/.eslintignore
Normal file
3
packages/services/.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
coverage
|
||||||
9
packages/services/.eslintrc
Normal file
9
packages/services/.eslintrc
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../.eslintrc",
|
||||||
|
"parserOptions": {
|
||||||
|
"project": "./linter.tsconfig.json"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-explicit-any": ["warn", { "ignoreRestArgs": true }]
|
||||||
|
}
|
||||||
|
}
|
||||||
500
packages/services/CHANGELOG.md
Normal file
500
packages/services/CHANGELOG.md
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
# Change Log
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.13.25](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.24...@standardnotes/services@1.13.25) (2022-07-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.24](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.23...@standardnotes/services@1.13.24) (2022-07-04)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add missing reflect-metadata package to all packages ([ce3a5bb](https://github.com/standardnotes/snjs/commit/ce3a5bbf3f1d2276ac4abc3eec3c6a44c8c3ba9b))
|
||||||
|
|
||||||
|
## [1.13.23](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.22...@standardnotes/services@1.13.23) (2022-06-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.22](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.21...@standardnotes/services@1.13.22) (2022-06-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.21](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.20...@standardnotes/services@1.13.21) (2022-06-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.20](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.19...@standardnotes/services@1.13.20) (2022-06-22)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* mobile keychain types ([#769](https://github.com/standardnotes/snjs/issues/769)) ([1fa6fb5](https://github.com/standardnotes/snjs/commit/1fa6fb57e398e60c27041b826540b6a1a6de5e91))
|
||||||
|
|
||||||
|
## [1.13.19](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.18...@standardnotes/services@1.13.19) (2022-06-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.18](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.17...@standardnotes/services@1.13.18) (2022-06-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.17](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.16...@standardnotes/services@1.13.17) (2022-06-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.16](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.15...@standardnotes/services@1.13.16) (2022-06-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.15](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.14...@standardnotes/services@1.13.15) (2022-06-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.14](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.13...@standardnotes/services@1.13.14) (2022-06-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.13](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.12...@standardnotes/services@1.13.13) (2022-06-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.12](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.11...@standardnotes/services@1.13.12) (2022-06-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.11](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.10...@standardnotes/services@1.13.11) (2022-06-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.10](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.9...@standardnotes/services@1.13.10) (2022-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.9](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.8...@standardnotes/services@1.13.9) (2022-06-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.8](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.7...@standardnotes/services@1.13.8) (2022-06-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.7](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.6...@standardnotes/services@1.13.7) (2022-06-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.6](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.5...@standardnotes/services@1.13.6) (2022-05-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.5](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.4...@standardnotes/services@1.13.5) (2022-05-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.4](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.3...@standardnotes/services@1.13.4) (2022-05-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.3](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.2...@standardnotes/services@1.13.3) (2022-05-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.2](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.1...@standardnotes/services@1.13.2) (2022-05-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.13.1](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.13.0...@standardnotes/services@1.13.1) (2022-05-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
# [1.13.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.12.2...@standardnotes/services@1.13.0) (2022-05-21)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* display controllers ([#743](https://github.com/standardnotes/snjs/issues/743)) ([5fadce3](https://github.com/standardnotes/snjs/commit/5fadce37f1b3f2f51b8a90c257bc666ac7710074))
|
||||||
|
|
||||||
|
## [1.12.2](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.12.1...@standardnotes/services@1.12.2) (2022-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.12.1](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.12.0...@standardnotes/services@1.12.1) (2022-05-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
# [1.12.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.11.9...@standardnotes/services@1.12.0) (2022-05-20)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* authentication with PKCE mechanism ([#719](https://github.com/standardnotes/snjs/issues/719)) ([1bc19b7](https://github.com/standardnotes/snjs/commit/1bc19b79decf83a563d1cf095ee2e56f738152d1))
|
||||||
|
|
||||||
|
## [1.11.9](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.11.8...@standardnotes/services@1.11.9) (2022-05-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.11.8](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.11.7...@standardnotes/services@1.11.8) (2022-05-17)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* workspace signout all ([0ac4501](https://github.com/standardnotes/snjs/commit/0ac45019428946016ef02384b07b8190378008fc))
|
||||||
|
|
||||||
|
## [1.11.7](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.11.6...@standardnotes/services@1.11.7) (2022-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.11.6](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.11.5...@standardnotes/services@1.11.6) (2022-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.11.5](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.11.4...@standardnotes/services@1.11.5) (2022-05-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.11.4](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.11.3...@standardnotes/services@1.11.4) (2022-05-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.11.3](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.11.2...@standardnotes/services@1.11.3) (2022-05-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.11.2](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.11.1...@standardnotes/services@1.11.2) (2022-05-13)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.11.1](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.11.0...@standardnotes/services@1.11.1) (2022-05-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
# [1.11.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.11...@standardnotes/services@1.11.0) (2022-05-12)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* file desktop backups ([#731](https://github.com/standardnotes/snjs/issues/731)) ([0dbce7d](https://github.com/standardnotes/snjs/commit/0dbce7dc9712fde848445b951079c81479c8bc11))
|
||||||
|
|
||||||
|
## [1.10.11](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.10...@standardnotes/services@1.10.11) (2022-05-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.10.10](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.9...@standardnotes/services@1.10.10) (2022-05-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.10.9](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.8...@standardnotes/services@1.10.9) (2022-05-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.10.8](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.7...@standardnotes/services@1.10.8) (2022-05-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.10.7](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.6...@standardnotes/services@1.10.7) (2022-05-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.10.6](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.5...@standardnotes/services@1.10.6) (2022-05-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.10.5](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.4...@standardnotes/services@1.10.5) (2022-05-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.10.4](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.2...@standardnotes/services@1.10.4) (2022-05-04)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* config package missing dependencies ([3dec12f](https://github.com/standardnotes/snjs/commit/3dec12fa4a83a8aed8419819eafb7c34795cb09f))
|
||||||
|
|
||||||
|
## [1.10.3](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.2...@standardnotes/services@1.10.3) (2022-05-04)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* config package missing dependencies ([3dec12f](https://github.com/standardnotes/snjs/commit/3dec12fa4a83a8aed8419819eafb7c34795cb09f))
|
||||||
|
|
||||||
|
## [1.10.2](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.1...@standardnotes/services@1.10.2) (2022-05-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.10.1](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.10.0...@standardnotes/services@1.10.1) (2022-05-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
# [1.10.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.15...@standardnotes/services@1.10.0) (2022-04-29)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* service diagnostics ([#718](https://github.com/standardnotes/snjs/issues/718)) ([17cf40f](https://github.com/standardnotes/snjs/commit/17cf40f4489c8f1915b19c0318d252cf83bc050d))
|
||||||
|
|
||||||
|
## [1.9.15](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.14...@standardnotes/services@1.9.15) (2022-04-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.14](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.13...@standardnotes/services@1.9.14) (2022-04-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.13](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.12...@standardnotes/services@1.9.13) (2022-04-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.12](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.11...@standardnotes/services@1.9.12) (2022-04-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.11](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.10...@standardnotes/services@1.9.11) (2022-04-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.10](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.9...@standardnotes/services@1.9.10) (2022-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.9](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.8...@standardnotes/services@1.9.9) (2022-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.8](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.7...@standardnotes/services@1.9.8) (2022-04-21)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* abort key recovery after aborted challenge ([#703](https://github.com/standardnotes/snjs/issues/703)) ([a67fb7e](https://github.com/standardnotes/snjs/commit/a67fb7e8cde41a5c9fadf545933e35d525faeaf0))
|
||||||
|
|
||||||
|
## [1.9.7](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.6...@standardnotes/services@1.9.7) (2022-04-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.6](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.5...@standardnotes/services@1.9.6) (2022-04-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.5](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.4...@standardnotes/services@1.9.5) (2022-04-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.4](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.3...@standardnotes/services@1.9.4) (2022-04-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.3](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.2...@standardnotes/services@1.9.3) (2022-04-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.9.2](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.1...@standardnotes/services@1.9.2) (2022-04-18)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* make timestamps required in payload construction ([#695](https://github.com/standardnotes/snjs/issues/695)) ([b3326c0](https://github.com/standardnotes/snjs/commit/b3326c0a998cd9d44a76afc377f182885ef48275))
|
||||||
|
|
||||||
|
## [1.9.1](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.9.0...@standardnotes/services@1.9.1) (2022-04-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
# [1.9.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.8.6...@standardnotes/services@1.9.0) (2022-04-15)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* no merge payloads in payload manager ([#693](https://github.com/standardnotes/snjs/issues/693)) ([68a577c](https://github.com/standardnotes/snjs/commit/68a577cb887fd2d5556dc9ddec461f6ae665fcb6))
|
||||||
|
|
||||||
|
## [1.8.6](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.8.5...@standardnotes/services@1.8.6) (2022-04-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.8.5](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.8.4...@standardnotes/services@1.8.5) (2022-04-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.8.4](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.8.3...@standardnotes/services@1.8.4) (2022-04-13)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.8.3](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.8.2...@standardnotes/services@1.8.3) (2022-04-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.8.2](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.8.1...@standardnotes/services@1.8.2) (2022-04-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.8.1](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.8.0...@standardnotes/services@1.8.1) (2022-04-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
# [1.8.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.7.2...@standardnotes/services@1.8.0) (2022-04-01)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* content interfaces and model type strictness ([#685](https://github.com/standardnotes/snjs/issues/685)) ([e2450c5](https://github.com/standardnotes/snjs/commit/e2450c59e8309d7080efaa03905b2abc728d9403))
|
||||||
|
|
||||||
|
## [1.7.2](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.7.1...@standardnotes/services@1.7.2) (2022-04-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.7.1](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.7.0...@standardnotes/services@1.7.1) (2022-03-31)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
# [1.7.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.14...@standardnotes/services@1.7.0) (2022-03-31)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* encryption and models packages ([#679](https://github.com/standardnotes/snjs/issues/679)) ([5e03d48](https://github.com/standardnotes/snjs/commit/5e03d48aba7e3dd266117201139ab869b1f70cc9))
|
||||||
|
|
||||||
|
## [1.6.14](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.13...@standardnotes/services@1.6.14) (2022-03-31)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.13](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.12...@standardnotes/services@1.6.13) (2022-03-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.12](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.11...@standardnotes/services@1.6.12) (2022-03-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.11](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.10...@standardnotes/services@1.6.11) (2022-03-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.10](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.9...@standardnotes/services@1.6.10) (2022-03-23)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.9](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.8...@standardnotes/services@1.6.9) (2022-03-23)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.8](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.7...@standardnotes/services@1.6.8) (2022-03-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.7](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.6...@standardnotes/services@1.6.7) (2022-03-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.6](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.5...@standardnotes/services@1.6.6) (2022-03-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.5](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.4...@standardnotes/services@1.6.5) (2022-03-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.4](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.3...@standardnotes/services@1.6.4) (2022-03-18)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.3](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.2...@standardnotes/services@1.6.3) (2022-03-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.2](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.0...@standardnotes/services@1.6.2) (2022-03-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.6.1](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.6.0...@standardnotes/services@1.6.1) (2022-03-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
# [1.6.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.12...@standardnotes/services@1.6.0) (2022-03-14)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* move vault into applications package ([#653](https://github.com/standardnotes/snjs/issues/653)) ([3d320eb](https://github.com/standardnotes/snjs/commit/3d320eb51ac74729ab8864f1c4c4f24d8fb794d5))
|
||||||
|
|
||||||
|
## [1.5.12](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.11...@standardnotes/services@1.5.12) (2022-03-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.5.11](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.10...@standardnotes/services@1.5.11) (2022-03-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.5.10](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.9...@standardnotes/services@1.5.10) (2022-03-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.5.9](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.8...@standardnotes/services@1.5.9) (2022-03-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.5.8](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.7...@standardnotes/services@1.5.8) (2022-03-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.5.7](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.6...@standardnotes/services@1.5.7) (2022-03-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.5.6](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.5...@standardnotes/services@1.5.6) (2022-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.5.5](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.4...@standardnotes/services@1.5.5) (2022-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.5.4](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.3...@standardnotes/services@1.5.4) (2022-03-08)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.5.3](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.2...@standardnotes/services@1.5.3) (2022-03-08)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.5.2](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.1...@standardnotes/services@1.5.2) (2022-03-08)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
## [1.5.1](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.5.0...@standardnotes/services@1.5.1) (2022-03-07)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
# [1.5.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.4.0...@standardnotes/services@1.5.0) (2022-03-07)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* integrity service ([#626](https://github.com/standardnotes/snjs/issues/626)) ([c5854fb](https://github.com/standardnotes/snjs/commit/c5854fb912dbe585516eeac3dde73573586c4e67))
|
||||||
|
|
||||||
|
# [1.4.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.3.0...@standardnotes/services@1.4.0) (2022-03-02)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* inject internal event bus to services for seamless event publishing ([#624](https://github.com/standardnotes/snjs/issues/624)) ([24b1e5c](https://github.com/standardnotes/snjs/commit/24b1e5c3e5ffe3c8ff228b97e91b83cb6c4077a5))
|
||||||
|
|
||||||
|
# [1.3.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.2.3...@standardnotes/services@1.3.0) (2022-03-02)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add internal events handling between services ([#620](https://github.com/standardnotes/snjs/issues/620)) ([d982e36](https://github.com/standardnotes/snjs/commit/d982e365eda5268b6df339e9e0fe926a4808d86f))
|
||||||
|
|
||||||
|
## [1.2.3](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.2.1...@standardnotes/services@1.2.3) (2022-02-28)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add pseudo change to get lerna to trigger ([41e6817](https://github.com/standardnotes/snjs/commit/41e6817bbf726b0932cdf16f58622328b9e42803))
|
||||||
|
|
||||||
|
## [1.2.2](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.2.1...@standardnotes/services@1.2.2) (2022-02-28)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add pseudo change to get lerna to trigger ([41e6817](https://github.com/standardnotes/snjs/commit/41e6817bbf726b0932cdf16f58622328b9e42803))
|
||||||
|
|
||||||
|
## [1.2.1](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.2.0...@standardnotes/services@1.2.1) (2022-02-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
# [1.2.0](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.1.1...@standardnotes/services@1.2.0) (2022-02-25)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* extract core functionalities to separate packages ([#610](https://github.com/standardnotes/snjs/issues/610)) ([801547a](https://github.com/standardnotes/snjs/commit/801547a71614ad51a92fb249eaa184ed46a44aac))
|
||||||
|
|
||||||
|
## [1.1.1](https://github.com/standardnotes/snjs/compare/@standardnotes/services@1.1.0...@standardnotes/services@1.1.1) (2022-02-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @standardnotes/services
|
||||||
|
|
||||||
|
# 1.1.0 (2022-02-22)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* extract services package ([#605](https://github.com/standardnotes/snjs/issues/605)) ([3966b10](https://github.com/standardnotes/snjs/commit/3966b10745c10ef5bb92871abb13ceb4ea631362))
|
||||||
11
packages/services/jest.config.js
Normal file
11
packages/services/jest.config.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const base = require('../../node_modules/@standardnotes/config/src/jest.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...base,
|
||||||
|
globals: {
|
||||||
|
'ts-jest': {
|
||||||
|
tsconfig: 'tsconfig.json',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
4
packages/services/linter.tsconfig.json
Normal file
4
packages/services/linter.tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": ["dist"]
|
||||||
|
}
|
||||||
43
packages/services/package.json
Normal file
43
packages/services/package.json
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "@standardnotes/services",
|
||||||
|
"version": "1.14.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0 <17.0.0"
|
||||||
|
},
|
||||||
|
"description": "Services for Standard Notes SNJS library",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"author": "Standard Notes",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rm -fr dist",
|
||||||
|
"prestart": "yarn clean",
|
||||||
|
"start": "tsc -p tsconfig.json --watch",
|
||||||
|
"prebuild": "yarn clean",
|
||||||
|
"build": "tsc -p tsconfig.json",
|
||||||
|
"lint": "eslint . --ext .ts",
|
||||||
|
"test:unit": "jest spec --coverage"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@standardnotes/auth": "^3.19.4",
|
||||||
|
"@standardnotes/common": "^1.23.1",
|
||||||
|
"@standardnotes/models": "workspace:*",
|
||||||
|
"@standardnotes/responses": "^1.6.39",
|
||||||
|
"@standardnotes/utils": "^1.6.12",
|
||||||
|
"reflect-metadata": "^0.1.13"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^27.4.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
||||||
|
"@typescript-eslint/parser": "^5.12.1",
|
||||||
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"jest": "^27.5.1",
|
||||||
|
"ts-jest": "^27.1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
28
packages/services/src/Domain/Alert/AlertService.ts
Normal file
28
packages/services/src/Domain/Alert/AlertService.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum ButtonType {
|
||||||
|
Info = 0,
|
||||||
|
Danger = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DismissBlockingDialog = () => void
|
||||||
|
|
||||||
|
export abstract class AlertService {
|
||||||
|
abstract confirm(
|
||||||
|
text: string,
|
||||||
|
title?: string,
|
||||||
|
confirmButtonText?: string,
|
||||||
|
confirmButtonType?: ButtonType,
|
||||||
|
cancelButtonText?: string,
|
||||||
|
): Promise<boolean>
|
||||||
|
|
||||||
|
abstract alert(text: string, title?: string, closeButtonText?: string): Promise<void>
|
||||||
|
|
||||||
|
abstract blockingDialog(text: string, title?: string): DismissBlockingDialog | Promise<DismissBlockingDialog>
|
||||||
|
|
||||||
|
showErrorAlert(error: ClientDisplayableError): Promise<void> {
|
||||||
|
return this.alert(error.text, error.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
19
packages/services/src/Domain/Api/ApiServiceInterface.ts
Normal file
19
packages/services/src/Domain/Api/ApiServiceInterface.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { AbstractService } from '../Service/AbstractService'
|
||||||
|
import { Uuid } from '@standardnotes/common'
|
||||||
|
import { Role } from '@standardnotes/auth'
|
||||||
|
import { FilesApiInterface } from '../Files/FilesApiInterface'
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum ApiServiceEvent {
|
||||||
|
MetaReceived = 'MetaReceived',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MetaReceivedData = {
|
||||||
|
userUuid: Uuid
|
||||||
|
userRoles: Role[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiServiceInterface
|
||||||
|
extends AbstractService<ApiServiceEvent.MetaReceived, MetaReceivedData>,
|
||||||
|
FilesApiInterface {}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ApplicationIdentifier } from '@standardnotes/common'
|
||||||
|
|
||||||
|
import { DeinitCallback } from './DeinitCallback'
|
||||||
|
import { DeinitMode } from './DeinitMode'
|
||||||
|
import { DeinitSource } from './DeinitSource'
|
||||||
|
import { UserClientInterface } from './UserClientInterface'
|
||||||
|
|
||||||
|
export interface ApplicationInterface {
|
||||||
|
deinit(mode: DeinitMode, source: DeinitSource): void
|
||||||
|
|
||||||
|
getDeinitMode(): DeinitMode
|
||||||
|
|
||||||
|
get user(): UserClientInterface
|
||||||
|
|
||||||
|
readonly identifier: ApplicationIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppGroupManagedApplication extends ApplicationInterface {
|
||||||
|
onDeinit: DeinitCallback
|
||||||
|
|
||||||
|
setOnDeinit(onDeinit: DeinitCallback): void
|
||||||
|
}
|
||||||
11
packages/services/src/Domain/Application/ApplicationStage.ts
Normal file
11
packages/services/src/Domain/Application/ApplicationStage.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
export enum ApplicationStage {
|
||||||
|
PreparingForLaunch_0 = 0.0,
|
||||||
|
ReadyForLaunch_05 = 0.5,
|
||||||
|
StorageDecrypted_09 = 0.9,
|
||||||
|
Launched_10 = 1.0,
|
||||||
|
LoadingDatabase_11 = 1.1,
|
||||||
|
LoadedDatabase_12 = 1.2,
|
||||||
|
FullSyncCompleted_13 = 1.3,
|
||||||
|
SignedIn_30 = 3.0,
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { DeinitSource } from './DeinitSource'
|
||||||
|
import { DeinitMode } from './DeinitMode'
|
||||||
|
import { AppGroupManagedApplication } from './ApplicationInterface'
|
||||||
|
|
||||||
|
export type DeinitCallback = (application: AppGroupManagedApplication, mode: DeinitMode, source: DeinitSource) => void
|
||||||
6
packages/services/src/Domain/Application/DeinitMode.ts
Normal file
6
packages/services/src/Domain/Application/DeinitMode.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum DeinitMode {
|
||||||
|
Soft = 'Soft',
|
||||||
|
Hard = 'Hard',
|
||||||
|
}
|
||||||
8
packages/services/src/Domain/Application/DeinitSource.ts
Normal file
8
packages/services/src/Domain/Application/DeinitSource.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum DeinitSource {
|
||||||
|
SignOut = 1,
|
||||||
|
Lock,
|
||||||
|
SwitchWorkspace,
|
||||||
|
SignOutAll,
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { DeinitSource } from './DeinitSource'
|
||||||
|
export interface UserClientInterface {
|
||||||
|
deleteAccount(): Promise<{
|
||||||
|
error: boolean
|
||||||
|
message?: string
|
||||||
|
}>
|
||||||
|
|
||||||
|
signOut(force?: boolean, source?: DeinitSource): Promise<void>
|
||||||
|
}
|
||||||
21
packages/services/src/Domain/Challenge/ChallengeInterface.ts
Normal file
21
packages/services/src/Domain/Challenge/ChallengeInterface.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { ChallengePromptInterface } from './Prompt/ChallengePromptInterface'
|
||||||
|
import { ChallengeReason } from './Types/ChallengeReason'
|
||||||
|
import { ChallengeValidation } from './Types/ChallengeValidation'
|
||||||
|
|
||||||
|
export interface ChallengeInterface {
|
||||||
|
readonly id: number
|
||||||
|
readonly prompts: ChallengePromptInterface[]
|
||||||
|
readonly reason: ChallengeReason
|
||||||
|
readonly cancelable: boolean
|
||||||
|
|
||||||
|
/** Outside of the modal, this is the title of the modal itself */
|
||||||
|
get modalTitle(): string
|
||||||
|
|
||||||
|
/** Inside of the modal, this is the H1 */
|
||||||
|
get heading(): string | undefined
|
||||||
|
|
||||||
|
/** Inside of the modal, this is the H2 */
|
||||||
|
get subheading(): string | undefined
|
||||||
|
|
||||||
|
hasPromptForValidationType(type: ChallengeValidation): boolean
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { ChallengeInterface } from './ChallengeInterface'
|
||||||
|
import { ChallengeArtifacts } from './Types/ChallengeArtifacts'
|
||||||
|
import { ChallengeValidation } from './Types/ChallengeValidation'
|
||||||
|
import { ChallengeValue } from './Types/ChallengeValue'
|
||||||
|
|
||||||
|
export interface ChallengeResponseInterface {
|
||||||
|
readonly challenge: ChallengeInterface
|
||||||
|
readonly values: ChallengeValue[]
|
||||||
|
readonly artifacts?: ChallengeArtifacts
|
||||||
|
|
||||||
|
getValueForType(type: ChallengeValidation): ChallengeValue
|
||||||
|
getDefaultValue(): ChallengeValue
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { AbstractService } from '../Service/AbstractService'
|
||||||
|
import { ChallengeInterface } from './ChallengeInterface'
|
||||||
|
import { ChallengePromptInterface } from './Prompt/ChallengePromptInterface'
|
||||||
|
import { ChallengeResponseInterface } from './ChallengeResponseInterface'
|
||||||
|
import { ChallengeReason } from './Types/ChallengeReason'
|
||||||
|
|
||||||
|
export interface ChallengeServiceInterface extends AbstractService {
|
||||||
|
/**
|
||||||
|
* Resolves when the challenge has been completed.
|
||||||
|
* For non-validated challenges, will resolve when the first value is submitted.
|
||||||
|
*/
|
||||||
|
promptForChallengeResponse(challenge: ChallengeInterface): Promise<ChallengeResponseInterface | undefined>
|
||||||
|
|
||||||
|
createChallenge(
|
||||||
|
prompts: ChallengePromptInterface[],
|
||||||
|
reason: ChallengeReason,
|
||||||
|
cancelable: boolean,
|
||||||
|
heading?: string,
|
||||||
|
subheading?: string,
|
||||||
|
): ChallengeInterface
|
||||||
|
|
||||||
|
completeChallenge(challenge: ChallengeInterface): void
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { assertUnreachable } from '@standardnotes/utils'
|
||||||
|
import { ChallengeKeyboardType } from '../Types/ChallengeKeyboardType'
|
||||||
|
import { ChallengeRawValue } from '../Types/ChallengeRawValue'
|
||||||
|
import { ChallengeValidation } from '../Types/ChallengeValidation'
|
||||||
|
import { ChallengePromptInterface } from './ChallengePromptInterface'
|
||||||
|
import { ChallengePromptTitle } from './PromptTitles'
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export class ChallengePrompt implements ChallengePromptInterface {
|
||||||
|
public readonly id = Math.random()
|
||||||
|
public readonly placeholder: string
|
||||||
|
public readonly title: string
|
||||||
|
public readonly validates: boolean
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly validation: ChallengeValidation,
|
||||||
|
title?: string,
|
||||||
|
placeholder?: string,
|
||||||
|
public readonly secureTextEntry = true,
|
||||||
|
public readonly keyboardType?: ChallengeKeyboardType,
|
||||||
|
public readonly initialValue?: ChallengeRawValue,
|
||||||
|
) {
|
||||||
|
switch (this.validation) {
|
||||||
|
case ChallengeValidation.AccountPassword:
|
||||||
|
this.title = title ?? ChallengePromptTitle.AccountPassword
|
||||||
|
this.placeholder = placeholder ?? ChallengePromptTitle.AccountPassword
|
||||||
|
this.validates = true
|
||||||
|
break
|
||||||
|
case ChallengeValidation.LocalPasscode:
|
||||||
|
this.title = title ?? ChallengePromptTitle.LocalPasscode
|
||||||
|
this.placeholder = placeholder ?? ChallengePromptTitle.LocalPasscode
|
||||||
|
this.validates = true
|
||||||
|
break
|
||||||
|
case ChallengeValidation.Biometric:
|
||||||
|
this.title = title ?? ChallengePromptTitle.Biometrics
|
||||||
|
this.placeholder = placeholder ?? ''
|
||||||
|
this.validates = true
|
||||||
|
break
|
||||||
|
case ChallengeValidation.ProtectionSessionDuration:
|
||||||
|
this.title = title ?? ChallengePromptTitle.RememberFor
|
||||||
|
this.placeholder = placeholder ?? ''
|
||||||
|
this.validates = true
|
||||||
|
break
|
||||||
|
case ChallengeValidation.None:
|
||||||
|
this.title = title ?? ''
|
||||||
|
this.placeholder = placeholder ?? ''
|
||||||
|
this.validates = false
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
assertUnreachable(this.validation)
|
||||||
|
}
|
||||||
|
Object.freeze(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { ChallengeKeyboardType } from '../Types/ChallengeKeyboardType'
|
||||||
|
import { ChallengeRawValue } from '../Types/ChallengeRawValue'
|
||||||
|
import { ChallengeValidation } from '../Types/ChallengeValidation'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Challenge can have many prompts. Each prompt represents a unique input,
|
||||||
|
* such as a text field, or biometric scanner.
|
||||||
|
*/
|
||||||
|
export interface ChallengePromptInterface {
|
||||||
|
readonly id: number
|
||||||
|
readonly placeholder: string
|
||||||
|
readonly title: string
|
||||||
|
readonly validates: boolean
|
||||||
|
|
||||||
|
readonly validation: ChallengeValidation
|
||||||
|
readonly secureTextEntry: boolean
|
||||||
|
readonly keyboardType?: ChallengeKeyboardType
|
||||||
|
readonly initialValue?: ChallengeRawValue
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export const ChallengePromptTitle = {
|
||||||
|
AccountPassword: 'Account Password',
|
||||||
|
LocalPasscode: 'Application Passcode',
|
||||||
|
Biometrics: 'Biometrics',
|
||||||
|
RememberFor: 'Remember For',
|
||||||
|
Mfa: 'Two-factor Authentication Code',
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { RootKeyInterface } from '@standardnotes/models'
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export type ChallengeArtifacts = {
|
||||||
|
wrappingKey?: RootKeyInterface
|
||||||
|
rootKey?: RootKeyInterface
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
/** For mobile */
|
||||||
|
export enum ChallengeKeyboardType {
|
||||||
|
Alphanumeric = 'default',
|
||||||
|
Numeric = 'numeric',
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export type ChallengeRawValue = number | string | boolean
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum ChallengeReason {
|
||||||
|
AccessProtectedFile,
|
||||||
|
AccessProtectedNote,
|
||||||
|
AddPasscode,
|
||||||
|
ApplicationUnlock,
|
||||||
|
ChangeAutolockInterval,
|
||||||
|
ChangePasscode,
|
||||||
|
CreateDecryptedBackupWithProtectedItems,
|
||||||
|
Custom,
|
||||||
|
DecryptEncryptedFile,
|
||||||
|
DisableBiometrics,
|
||||||
|
DisableMfa,
|
||||||
|
ExportBackup,
|
||||||
|
ImportFile,
|
||||||
|
Migration,
|
||||||
|
ProtocolUpgrade,
|
||||||
|
RemovePasscode,
|
||||||
|
ResaveRootKey,
|
||||||
|
RevokeSession,
|
||||||
|
SearchProtectedNotesText,
|
||||||
|
SelectProtectedNote,
|
||||||
|
UnprotectFile,
|
||||||
|
UnprotectNote,
|
||||||
|
DeleteAccount,
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum ChallengeValidation {
|
||||||
|
None = 0,
|
||||||
|
LocalPasscode = 1,
|
||||||
|
AccountPassword = 2,
|
||||||
|
Biometric = 3,
|
||||||
|
ProtectionSessionDuration = 4,
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { ChallengePromptInterface } from '../Prompt/ChallengePromptInterface'
|
||||||
|
import { ChallengeRawValue } from './ChallengeRawValue'
|
||||||
|
|
||||||
|
export interface ChallengeValue {
|
||||||
|
readonly prompt: ChallengePromptInterface
|
||||||
|
readonly value: ChallengeRawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export function CreateChallengeValue(prompt: ChallengePromptInterface, value: ChallengeRawValue): ChallengeValue {
|
||||||
|
return { prompt, value }
|
||||||
|
}
|
||||||
12
packages/services/src/Domain/Challenge/index.ts
Normal file
12
packages/services/src/Domain/Challenge/index.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export * from './ChallengeInterface'
|
||||||
|
export * from './ChallengeResponseInterface'
|
||||||
|
export * from './ChallengeServiceInterface'
|
||||||
|
export * from './Prompt/ChallengePrompt'
|
||||||
|
export * from './Prompt/ChallengePromptInterface'
|
||||||
|
export * from './Prompt/PromptTitles'
|
||||||
|
export * from './Types/ChallengeArtifacts'
|
||||||
|
export * from './Types/ChallengeKeyboardType'
|
||||||
|
export * from './Types/ChallengeRawValue'
|
||||||
|
export * from './Types/ChallengeReason'
|
||||||
|
export * from './Types/ChallengeValidation'
|
||||||
|
export * from './Types/ChallengeValue'
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { WebClientRequiresDesktopMethods } from './DesktopWebCommunication'
|
||||||
|
import { DeviceInterface } from './DeviceInterface'
|
||||||
|
import { Environment } from './Environments'
|
||||||
|
import { WebOrDesktopDeviceInterface } from './WebOrDesktopDeviceInterface'
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export function isDesktopDevice(x: DeviceInterface): x is DesktopDeviceInterface {
|
||||||
|
return x.environment === Environment.Desktop
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DesktopDeviceInterface extends WebOrDesktopDeviceInterface, WebClientRequiresDesktopMethods {
|
||||||
|
environment: Environment.Desktop
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { DecryptedTransferPayload } from '@standardnotes/models'
|
||||||
|
import { FileBackupsDevice } from './FileBackupsDevice'
|
||||||
|
|
||||||
|
export interface WebClientRequiresDesktopMethods extends FileBackupsDevice {
|
||||||
|
localBackupsCount(): Promise<number>
|
||||||
|
|
||||||
|
viewlocalBackups(): void
|
||||||
|
|
||||||
|
deleteLocalBackups(): Promise<void>
|
||||||
|
|
||||||
|
syncComponents(payloads: unknown[]): void
|
||||||
|
|
||||||
|
onMajorDataChange(): void
|
||||||
|
|
||||||
|
onInitialDataLoad(): void
|
||||||
|
|
||||||
|
onSearch(text?: string): void
|
||||||
|
|
||||||
|
downloadBackup(): void | Promise<void>
|
||||||
|
|
||||||
|
get extensionsServerHost(): string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DesktopClientRequiresWebMethods {
|
||||||
|
updateAvailable(): void
|
||||||
|
|
||||||
|
windowGainedFocus(): void
|
||||||
|
|
||||||
|
windowLostFocus(): void
|
||||||
|
|
||||||
|
onComponentInstallationComplete(componentData: DecryptedTransferPayload, error: unknown): Promise<void>
|
||||||
|
|
||||||
|
requestBackupFile(): Promise<string | undefined>
|
||||||
|
|
||||||
|
didBeginBackup(): void
|
||||||
|
|
||||||
|
didFinishBackup(success: boolean): void
|
||||||
|
}
|
||||||
84
packages/services/src/Domain/Device/DeviceInterface.ts
Normal file
84
packages/services/src/Domain/Device/DeviceInterface.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { Environment } from './Environments'
|
||||||
|
import { ApplicationIdentifier } from '@standardnotes/common'
|
||||||
|
import {
|
||||||
|
FullyFormedTransferPayload,
|
||||||
|
TransferPayload,
|
||||||
|
LegacyRawKeychainValue,
|
||||||
|
NamespacedRootKeyInKeychain,
|
||||||
|
} from '@standardnotes/models'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Platforms must override this class to provide platform specific utilities
|
||||||
|
* and access to the migration service, such as exposing an interface to read
|
||||||
|
* raw values from the database or value storage.
|
||||||
|
*/
|
||||||
|
export interface DeviceInterface {
|
||||||
|
environment: Environment
|
||||||
|
|
||||||
|
deinit(): void
|
||||||
|
|
||||||
|
getRawStorageValue(key: string): Promise<string | undefined>
|
||||||
|
|
||||||
|
getJsonParsedRawStorageValue(key: string): Promise<unknown | undefined>
|
||||||
|
|
||||||
|
getAllRawStorageKeyValues(): Promise<{ key: string; value: unknown }[]>
|
||||||
|
|
||||||
|
setRawStorageValue(key: string, value: string): Promise<void>
|
||||||
|
|
||||||
|
removeRawStorageValue(key: string): Promise<void>
|
||||||
|
|
||||||
|
removeAllRawStorageValues(): Promise<void>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On web platforms, databased created may be new.
|
||||||
|
* New databases can be because of new sessions, or if the browser deleted it.
|
||||||
|
* In this case, callers should orchestrate with the server to redownload all items
|
||||||
|
* from scratch.
|
||||||
|
* @returns { isNewDatabase } - True if the database was newly created
|
||||||
|
*/
|
||||||
|
openDatabase(identifier: ApplicationIdentifier): Promise<{ isNewDatabase?: boolean } | undefined>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In a key/value database, this function returns just the keys.
|
||||||
|
*/
|
||||||
|
getDatabaseKeys(): Promise<string[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all keychain and database data from device.
|
||||||
|
* @param workspaceIdentifiers An array of identifiers present during time of function call. Used in case
|
||||||
|
* caller needs to reference the identifiers. This param should not be used to selectively clear workspaces.
|
||||||
|
* @returns true for killsApplication if the clear data operation kills the application process completely.
|
||||||
|
* This tends to be the case for the desktop application.
|
||||||
|
*/
|
||||||
|
clearAllDataFromDevice(workspaceIdentifiers: ApplicationIdentifier[]): Promise<{ killsApplication: boolean }>
|
||||||
|
|
||||||
|
getAllRawDatabasePayloads<T extends FullyFormedTransferPayload = FullyFormedTransferPayload>(
|
||||||
|
identifier: ApplicationIdentifier,
|
||||||
|
): Promise<T[]>
|
||||||
|
|
||||||
|
saveRawDatabasePayload(payload: TransferPayload, identifier: ApplicationIdentifier): Promise<void>
|
||||||
|
|
||||||
|
saveRawDatabasePayloads(payloads: TransferPayload[], identifier: ApplicationIdentifier): Promise<void>
|
||||||
|
|
||||||
|
removeRawDatabasePayloadWithId(id: string, identifier: ApplicationIdentifier): Promise<void>
|
||||||
|
|
||||||
|
removeAllRawDatabasePayloads(identifier: ApplicationIdentifier): Promise<void>
|
||||||
|
|
||||||
|
getNamespacedKeychainValue(identifier: ApplicationIdentifier): Promise<NamespacedRootKeyInKeychain | undefined>
|
||||||
|
|
||||||
|
setNamespacedKeychainValue(value: NamespacedRootKeyInKeychain, identifier: ApplicationIdentifier): Promise<void>
|
||||||
|
|
||||||
|
clearNamespacedKeychainValue(identifier: ApplicationIdentifier): Promise<void>
|
||||||
|
|
||||||
|
setLegacyRawKeychainValue(value: LegacyRawKeychainValue): Promise<void>
|
||||||
|
|
||||||
|
clearRawKeychainValue(): Promise<void>
|
||||||
|
|
||||||
|
openUrl(url: string): void
|
||||||
|
|
||||||
|
performSoftReset(): void
|
||||||
|
|
||||||
|
performHardReset(): void
|
||||||
|
|
||||||
|
isDeviceDestroyed(): boolean
|
||||||
|
}
|
||||||
16
packages/services/src/Domain/Device/Environments.ts
Normal file
16
packages/services/src/Domain/Device/Environments.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export enum Environment {
|
||||||
|
Web = 1,
|
||||||
|
Desktop = 2,
|
||||||
|
Mobile = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Platform {
|
||||||
|
Ios = 1,
|
||||||
|
Android = 2,
|
||||||
|
MacWeb = 3,
|
||||||
|
MacDesktop = 4,
|
||||||
|
WindowsWeb = 5,
|
||||||
|
WindowsDesktop = 6,
|
||||||
|
LinuxWeb = 7,
|
||||||
|
LinuxDesktop = 8,
|
||||||
|
}
|
||||||
51
packages/services/src/Domain/Device/FileBackupsDevice.ts
Normal file
51
packages/services/src/Domain/Device/FileBackupsDevice.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { Uuid } from '@standardnotes/common'
|
||||||
|
import { BackupFileEncryptedContextualPayload } from '@standardnotes/models'
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export const FileBackupsConstantsV1 = {
|
||||||
|
Version: '1.0.0',
|
||||||
|
MetadataFileName: 'metadata.sn.json',
|
||||||
|
BinaryFileName: 'file.encrypted',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileBackupMetadataFile {
|
||||||
|
info: Record<string, string>
|
||||||
|
file: BackupFileEncryptedContextualPayload
|
||||||
|
itemsKey: BackupFileEncryptedContextualPayload
|
||||||
|
version: '1.0.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileBackupsMapping {
|
||||||
|
version: typeof FileBackupsConstantsV1.Version
|
||||||
|
files: Record<
|
||||||
|
Uuid,
|
||||||
|
{
|
||||||
|
backedUpOn: Date
|
||||||
|
absolutePath: string
|
||||||
|
relativePath: string
|
||||||
|
metadataFileName: typeof FileBackupsConstantsV1.MetadataFileName
|
||||||
|
binaryFileName: typeof FileBackupsConstantsV1.BinaryFileName
|
||||||
|
version: typeof FileBackupsConstantsV1.Version
|
||||||
|
}
|
||||||
|
>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileBackupsDevice {
|
||||||
|
getFilesBackupsMappingFile(): Promise<FileBackupsMapping>
|
||||||
|
saveFilesBackupsFile(
|
||||||
|
uuid: Uuid,
|
||||||
|
metaFile: string,
|
||||||
|
downloadRequest: {
|
||||||
|
chunkSizes: number[]
|
||||||
|
valetToken: string
|
||||||
|
url: string
|
||||||
|
},
|
||||||
|
): Promise<'success' | 'failed'>
|
||||||
|
isFilesBackupsEnabled(): Promise<boolean>
|
||||||
|
enableFilesBackups(): Promise<void>
|
||||||
|
disableFilesBackups(): Promise<void>
|
||||||
|
changeFilesBackupsLocation(): Promise<string | undefined>
|
||||||
|
getFilesBackupsLocation(): Promise<string>
|
||||||
|
openFilesBackupsLocation(): Promise<void>
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { DeviceInterface } from './DeviceInterface'
|
||||||
|
import { Environment } from './Environments'
|
||||||
|
import { RawKeychainValue } from '@standardnotes/models'
|
||||||
|
|
||||||
|
export interface MobileDeviceInterface extends DeviceInterface {
|
||||||
|
environment: Environment.Mobile
|
||||||
|
|
||||||
|
getRawKeychainValue(): Promise<RawKeychainValue | undefined>
|
||||||
|
}
|
||||||
18
packages/services/src/Domain/Device/TypeCheck.spec.ts
Normal file
18
packages/services/src/Domain/Device/TypeCheck.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { DeviceInterface } from './DeviceInterface'
|
||||||
|
import { Environment } from './Environments'
|
||||||
|
import { MobileDeviceInterface } from './MobileDeviceInterface'
|
||||||
|
import { isMobileDevice } from './TypeCheck'
|
||||||
|
|
||||||
|
describe('device type check', () => {
|
||||||
|
it('should return true for mobile devices', () => {
|
||||||
|
const device = { environment: Environment.Mobile } as jest.Mocked<MobileDeviceInterface>
|
||||||
|
|
||||||
|
expect(isMobileDevice(device)).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non mobile devices', () => {
|
||||||
|
const device = { environment: Environment.Web } as jest.Mocked<DeviceInterface>
|
||||||
|
|
||||||
|
expect(isMobileDevice(device)).toBeFalsy()
|
||||||
|
})
|
||||||
|
})
|
||||||
9
packages/services/src/Domain/Device/TypeCheck.ts
Normal file
9
packages/services/src/Domain/Device/TypeCheck.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Environment } from './Environments'
|
||||||
|
import { MobileDeviceInterface } from './MobileDeviceInterface'
|
||||||
|
import { DeviceInterface } from './DeviceInterface'
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export function isMobileDevice(x: DeviceInterface): x is MobileDeviceInterface {
|
||||||
|
return x.environment === Environment.Mobile
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { DeviceInterface } from './DeviceInterface'
|
||||||
|
import { RawKeychainValue } from '@standardnotes/models'
|
||||||
|
|
||||||
|
export interface WebOrDesktopDeviceInterface extends DeviceInterface {
|
||||||
|
readonly appVersion: string
|
||||||
|
|
||||||
|
getKeychainValue(): Promise<RawKeychainValue>
|
||||||
|
|
||||||
|
setKeychainValue(value: RawKeychainValue): Promise<void>
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
type DiagnosticValue =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| Date
|
||||||
|
| boolean
|
||||||
|
| null
|
||||||
|
| undefined
|
||||||
|
| DiagnosticValue[]
|
||||||
|
| { [key: string]: DiagnosticValue }
|
||||||
|
|
||||||
|
export type DiagnosticInfo = {
|
||||||
|
[key: string]: Record<string, DiagnosticValue>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServiceDiagnostics {
|
||||||
|
getDiagnostics(): Promise<DiagnosticInfo | undefined>
|
||||||
|
}
|
||||||
1
packages/services/src/Domain/Event/EventObserver.ts
Normal file
1
packages/services/src/Domain/Event/EventObserver.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export type EventObserver<E, D> = (eventName: E, data?: D) => Promise<void> | void
|
||||||
25
packages/services/src/Domain/Event/SyncEvent.ts
Normal file
25
packages/services/src/Domain/Event/SyncEvent.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
export enum SyncEvent {
|
||||||
|
/**
|
||||||
|
* A potentially multi-round trip that keeps syncing until all items have been uploaded.
|
||||||
|
* However, this event will still trigger if there are more items waiting to be downloaded on the
|
||||||
|
* server
|
||||||
|
*/
|
||||||
|
SyncCompletedWithAllItemsUploaded = 'SyncCompletedWithAllItemsUploaded',
|
||||||
|
SyncCompletedWithAllItemsUploadedAndDownloaded = 'SyncCompletedWithAllItemsUploadedAndDownloaded',
|
||||||
|
SingleRoundTripSyncCompleted = 'SingleRoundTripSyncCompleted',
|
||||||
|
SyncWillBegin = 'sync:will-begin',
|
||||||
|
DownloadFirstSyncCompleted = 'sync:download-first-completed',
|
||||||
|
SyncTakingTooLong = 'sync:taking-too-long',
|
||||||
|
SyncError = 'sync:error',
|
||||||
|
InvalidSession = 'sync:invalid-session',
|
||||||
|
MajorDataChange = 'major-data-change',
|
||||||
|
LocalDataIncrementalLoad = 'local-data-incremental-load',
|
||||||
|
LocalDataLoaded = 'local-data-loaded',
|
||||||
|
EnterOutOfSync = 'enter-out-of-sync',
|
||||||
|
ExitOutOfSync = 'exit-out-of-sync',
|
||||||
|
StatusChanged = 'status-changed',
|
||||||
|
DatabaseWriteError = 'database-write-error',
|
||||||
|
DatabaseReadError = 'database-read-error',
|
||||||
|
SyncRequestsIntegrityCheck = 'sync:requests-integrity-check',
|
||||||
|
}
|
||||||
3
packages/services/src/Domain/Event/SyncEventReceiver.ts
Normal file
3
packages/services/src/Domain/Event/SyncEventReceiver.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { SyncEvent } from './SyncEvent'
|
||||||
|
|
||||||
|
export type SyncEventReceiver = (event: SyncEvent) => void
|
||||||
27
packages/services/src/Domain/FileSystem/FileSystemApi.ts
Normal file
27
packages/services/src/Domain/FileSystem/FileSystemApi.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
export interface DirectoryHandle {
|
||||||
|
nativeHandle: unknown
|
||||||
|
}
|
||||||
|
export interface FileHandleReadWrite {
|
||||||
|
nativeHandle: unknown
|
||||||
|
writableStream: unknown
|
||||||
|
}
|
||||||
|
export interface FileHandleRead {
|
||||||
|
nativeHandle: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FileSystemResult = 'aborted' | 'success' | 'failed'
|
||||||
|
export type FileSystemNoSelection = 'aborted' | 'failed'
|
||||||
|
|
||||||
|
export interface FileSystemApi {
|
||||||
|
selectDirectory(): Promise<DirectoryHandle | FileSystemNoSelection>
|
||||||
|
selectFile(): Promise<FileHandleRead | FileSystemNoSelection>
|
||||||
|
readFile(
|
||||||
|
file: FileHandleRead,
|
||||||
|
onBytes: (bytes: Uint8Array, isLast: boolean) => Promise<void>,
|
||||||
|
): Promise<FileSystemResult>
|
||||||
|
createDirectory(parentDirectory: DirectoryHandle, name: string): Promise<DirectoryHandle | FileSystemNoSelection>
|
||||||
|
createFile(directory: DirectoryHandle, name: string): Promise<FileHandleReadWrite | FileSystemNoSelection>
|
||||||
|
saveBytes(file: FileHandleReadWrite, bytes: Uint8Array): Promise<'success' | 'failed'>
|
||||||
|
saveString(file: FileHandleReadWrite, contents: string): Promise<'success' | 'failed'>
|
||||||
|
closeFileWriteStream(file: FileHandleReadWrite): Promise<'success' | 'failed'>
|
||||||
|
}
|
||||||
28
packages/services/src/Domain/Files/FilesApiInterface.ts
Normal file
28
packages/services/src/Domain/Files/FilesApiInterface.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { StartUploadSessionResponse, MinimalHttpResponse, ClientDisplayableError } from '@standardnotes/responses'
|
||||||
|
import { FileContent } from '@standardnotes/models'
|
||||||
|
|
||||||
|
export interface FilesApiInterface {
|
||||||
|
startUploadSession(apiToken: string): Promise<StartUploadSessionResponse>
|
||||||
|
|
||||||
|
uploadFileBytes(apiToken: string, chunkId: number, encryptedBytes: Uint8Array): Promise<boolean>
|
||||||
|
|
||||||
|
closeUploadSession(apiToken: string): Promise<boolean>
|
||||||
|
|
||||||
|
downloadFile(
|
||||||
|
file: { encryptedChunkSizes: FileContent['encryptedChunkSizes'] },
|
||||||
|
chunkIndex: number,
|
||||||
|
apiToken: string,
|
||||||
|
contentRangeStart: number,
|
||||||
|
onBytesReceived: (bytes: Uint8Array) => Promise<void>,
|
||||||
|
): Promise<ClientDisplayableError | undefined>
|
||||||
|
|
||||||
|
deleteFile(apiToken: string): Promise<MinimalHttpResponse>
|
||||||
|
|
||||||
|
createFileValetToken(
|
||||||
|
remoteIdentifier: string,
|
||||||
|
operation: 'write' | 'read' | 'delete',
|
||||||
|
unencryptedFileSize?: number,
|
||||||
|
): Promise<string | ClientDisplayableError>
|
||||||
|
|
||||||
|
getFilesDownloadUrl(): string
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { CheckIntegrityResponse, IntegrityPayload } from '@standardnotes/responses'
|
||||||
|
|
||||||
|
export interface IntegrityApiInterface {
|
||||||
|
checkIntegrity(integrityPayloads: IntegrityPayload[]): Promise<CheckIntegrityResponse>
|
||||||
|
}
|
||||||
4
packages/services/src/Domain/Integrity/IntegrityEvent.ts
Normal file
4
packages/services/src/Domain/Integrity/IntegrityEvent.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
export enum IntegrityEvent {
|
||||||
|
IntegrityCheckCompleted = 'IntegrityCheckCompleted',
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { ServerItemResponse } from '@standardnotes/responses'
|
||||||
|
import { SyncSource } from '../Sync/SyncSource'
|
||||||
|
|
||||||
|
export type IntegrityEventPayload = {
|
||||||
|
rawPayloads: ServerItemResponse[]
|
||||||
|
source: SyncSource
|
||||||
|
}
|
||||||
160
packages/services/src/Domain/Integrity/IntegrityService.spec.ts
Normal file
160
packages/services/src/Domain/Integrity/IntegrityService.spec.ts
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
import { TransferPayload } from '@standardnotes/models'
|
||||||
|
import { SyncEvent } from '../Event/SyncEvent'
|
||||||
|
|
||||||
|
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
|
||||||
|
import { ItemsServerInterface } from '../Item/ItemsServerInterface'
|
||||||
|
import { SyncSource } from '../Sync/SyncSource'
|
||||||
|
import { IntegrityApiInterface } from './IntegrityApiInterface'
|
||||||
|
import { IntegrityService } from './IntegrityService'
|
||||||
|
import { PayloadManagerInterface } from '../Payloads/PayloadManagerInterface'
|
||||||
|
import { IntegrityPayload } from '@standardnotes/responses'
|
||||||
|
|
||||||
|
describe('IntegrityService', () => {
|
||||||
|
let integrityApi: IntegrityApiInterface
|
||||||
|
let itemApi: ItemsServerInterface
|
||||||
|
let payloadManager: PayloadManagerInterface
|
||||||
|
let internalEventBus: InternalEventBusInterface
|
||||||
|
|
||||||
|
const createService = () => new IntegrityService(integrityApi, itemApi, payloadManager, internalEventBus)
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
integrityApi = {} as jest.Mocked<IntegrityApiInterface>
|
||||||
|
integrityApi.checkIntegrity = jest.fn()
|
||||||
|
|
||||||
|
itemApi = {} as jest.Mocked<ItemsServerInterface>
|
||||||
|
itemApi.getSingleItem = jest.fn()
|
||||||
|
|
||||||
|
payloadManager = {} as jest.Mocked<PayloadManagerInterface>
|
||||||
|
payloadManager.integrityPayloads = []
|
||||||
|
|
||||||
|
internalEventBus = {} as jest.Mocked<InternalEventBusInterface>
|
||||||
|
internalEventBus.publishSync = jest.fn()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should check integrity of payloads and publish mismatches', async () => {
|
||||||
|
integrityApi.checkIntegrity = jest.fn().mockReturnValue({
|
||||||
|
data: {
|
||||||
|
mismatches: [{ uuid: '1-2-3', updated_at_timestamp: 234 } as IntegrityPayload],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
itemApi.getSingleItem = jest.fn().mockReturnValue({
|
||||||
|
data: {
|
||||||
|
item: {
|
||||||
|
uuid: '1-2-3',
|
||||||
|
content: 'foobar',
|
||||||
|
} as Partial<TransferPayload>,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await createService().handleEvent({
|
||||||
|
type: SyncEvent.SyncRequestsIntegrityCheck,
|
||||||
|
payload: {
|
||||||
|
integrityPayloads: [{ uuid: '1-2-3', updated_at_timestamp: 123 } as IntegrityPayload],
|
||||||
|
source: SyncSource.AfterDownloadFirst,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(internalEventBus.publishSync).toHaveBeenCalledWith(
|
||||||
|
{
|
||||||
|
payload: {
|
||||||
|
rawPayloads: [
|
||||||
|
{
|
||||||
|
content: 'foobar',
|
||||||
|
uuid: '1-2-3',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
source: 5,
|
||||||
|
},
|
||||||
|
type: 'IntegrityCheckCompleted',
|
||||||
|
},
|
||||||
|
'SEQUENCE',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should publish empty mismatches if everything is in sync', async () => {
|
||||||
|
integrityApi.checkIntegrity = jest.fn().mockReturnValue({
|
||||||
|
data: {
|
||||||
|
mismatches: [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await createService().handleEvent({
|
||||||
|
type: SyncEvent.SyncRequestsIntegrityCheck,
|
||||||
|
payload: {
|
||||||
|
integrityPayloads: [{ uuid: '1-2-3', updated_at_timestamp: 123 } as IntegrityPayload],
|
||||||
|
source: SyncSource.AfterDownloadFirst,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(internalEventBus.publishSync).toHaveBeenCalledWith(
|
||||||
|
{
|
||||||
|
payload: {
|
||||||
|
rawPayloads: [],
|
||||||
|
source: 5,
|
||||||
|
},
|
||||||
|
type: 'IntegrityCheckCompleted',
|
||||||
|
},
|
||||||
|
'SEQUENCE',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not publish mismatches if checking integrity fails', async () => {
|
||||||
|
integrityApi.checkIntegrity = jest.fn().mockReturnValue({
|
||||||
|
error: 'Ooops',
|
||||||
|
})
|
||||||
|
|
||||||
|
await createService().handleEvent({
|
||||||
|
type: SyncEvent.SyncRequestsIntegrityCheck,
|
||||||
|
payload: {
|
||||||
|
integrityPayloads: [{ uuid: '1-2-3', updated_at_timestamp: 123 } as IntegrityPayload],
|
||||||
|
source: SyncSource.AfterDownloadFirst,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(internalEventBus.publishSync).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should publish empty mismatches if fetching items fails', async () => {
|
||||||
|
integrityApi.checkIntegrity = jest.fn().mockReturnValue({
|
||||||
|
data: {
|
||||||
|
mismatches: [{ uuid: '1-2-3', updated_at_timestamp: 234 } as IntegrityPayload],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
itemApi.getSingleItem = jest.fn().mockReturnValue({
|
||||||
|
error: 'Ooops',
|
||||||
|
})
|
||||||
|
|
||||||
|
await createService().handleEvent({
|
||||||
|
type: SyncEvent.SyncRequestsIntegrityCheck,
|
||||||
|
payload: {
|
||||||
|
integrityPayloads: [{ uuid: '1-2-3', updated_at_timestamp: 123 } as IntegrityPayload],
|
||||||
|
source: SyncSource.AfterDownloadFirst,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(internalEventBus.publishSync).toHaveBeenCalledWith(
|
||||||
|
{
|
||||||
|
payload: {
|
||||||
|
rawPayloads: [],
|
||||||
|
source: 5,
|
||||||
|
},
|
||||||
|
type: 'IntegrityCheckCompleted',
|
||||||
|
},
|
||||||
|
'SEQUENCE',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not handle different event types', async () => {
|
||||||
|
await createService().handleEvent({
|
||||||
|
type: SyncEvent.SyncCompletedWithAllItemsUploaded,
|
||||||
|
payload: {
|
||||||
|
integrityPayloads: [{ uuid: '1-2-3', updated_at_timestamp: 123 } as IntegrityPayload],
|
||||||
|
source: SyncSource.AfterDownloadFirst,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(integrityApi.checkIntegrity).not.toHaveBeenCalled()
|
||||||
|
expect(itemApi.getSingleItem).not.toHaveBeenCalled()
|
||||||
|
expect(internalEventBus.publishSync).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
62
packages/services/src/Domain/Integrity/IntegrityService.ts
Normal file
62
packages/services/src/Domain/Integrity/IntegrityService.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { IntegrityEvent } from './IntegrityEvent'
|
||||||
|
import { AbstractService } from '../Service/AbstractService'
|
||||||
|
import { ItemsServerInterface } from '../Item/ItemsServerInterface'
|
||||||
|
import { IntegrityApiInterface } from './IntegrityApiInterface'
|
||||||
|
import { GetSingleItemResponse } from '@standardnotes/responses'
|
||||||
|
import { InternalEventHandlerInterface } from '../Internal/InternalEventHandlerInterface'
|
||||||
|
import { InternalEventInterface } from '../Internal/InternalEventInterface'
|
||||||
|
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
|
||||||
|
import { SyncEvent } from '../Event/SyncEvent'
|
||||||
|
import { IntegrityEventPayload } from './IntegrityEventPayload'
|
||||||
|
import { SyncSource } from '../Sync/SyncSource'
|
||||||
|
import { PayloadManagerInterface } from '../Payloads/PayloadManagerInterface'
|
||||||
|
|
||||||
|
export class IntegrityService
|
||||||
|
extends AbstractService<IntegrityEvent, IntegrityEventPayload>
|
||||||
|
implements InternalEventHandlerInterface
|
||||||
|
{
|
||||||
|
constructor(
|
||||||
|
private integrityApi: IntegrityApiInterface,
|
||||||
|
private itemApi: ItemsServerInterface,
|
||||||
|
private payloadManager: PayloadManagerInterface,
|
||||||
|
protected override internalEventBus: InternalEventBusInterface,
|
||||||
|
) {
|
||||||
|
super(internalEventBus)
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||||
|
if (event.type !== SyncEvent.SyncRequestsIntegrityCheck) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const integrityCheckResponse = await this.integrityApi.checkIntegrity(this.payloadManager.integrityPayloads)
|
||||||
|
if (integrityCheckResponse.error !== undefined) {
|
||||||
|
this.log(`Could not obtain integrity check: ${integrityCheckResponse.error}`)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverItemResponsePromises: Promise<GetSingleItemResponse>[] = []
|
||||||
|
for (const mismatch of integrityCheckResponse.data.mismatches) {
|
||||||
|
serverItemResponsePromises.push(this.itemApi.getSingleItem(mismatch.uuid))
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverItemResponses = await Promise.all(serverItemResponsePromises)
|
||||||
|
|
||||||
|
const rawPayloads = []
|
||||||
|
for (const serverItemResponse of serverItemResponses) {
|
||||||
|
if (serverItemResponse.data === undefined || serverItemResponse.error || !('item' in serverItemResponse.data)) {
|
||||||
|
this.log(`Could not obtain item for integrity adjustments: ${serverItemResponse.error}`)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rawPayloads.push(serverItemResponse.data.item)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.notifyEventSync(IntegrityEvent.IntegrityCheckCompleted, {
|
||||||
|
rawPayloads: rawPayloads,
|
||||||
|
source: (event.payload as { source: SyncSource }).source,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
117
packages/services/src/Domain/Internal/InternalEventBus.spec.ts
Normal file
117
packages/services/src/Domain/Internal/InternalEventBus.spec.ts
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import { InternalEventHandlerInterface } from './InternalEventHandlerInterface'
|
||||||
|
import { InternalEventBus } from './InternalEventBus'
|
||||||
|
import { InternalEventPublishStrategy } from './InternalEventPublishStrategy'
|
||||||
|
|
||||||
|
describe('InternalEventBus', () => {
|
||||||
|
let eventHandler1: InternalEventHandlerInterface
|
||||||
|
let eventHandler2: InternalEventHandlerInterface
|
||||||
|
let eventHandler3: InternalEventHandlerInterface
|
||||||
|
|
||||||
|
const createEventBus = () => new InternalEventBus()
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
eventHandler1 = {} as jest.Mocked<InternalEventHandlerInterface>
|
||||||
|
eventHandler1.handleEvent = jest.fn()
|
||||||
|
|
||||||
|
eventHandler2 = {} as jest.Mocked<InternalEventHandlerInterface>
|
||||||
|
eventHandler2.handleEvent = jest.fn()
|
||||||
|
|
||||||
|
eventHandler3 = {} as jest.Mocked<InternalEventHandlerInterface>
|
||||||
|
eventHandler3.handleEvent = jest.fn()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should trigger appropriate event handlers upon event publishing', () => {
|
||||||
|
const eventBus = createEventBus()
|
||||||
|
eventBus.addEventHandler(eventHandler1, 'test_event_1')
|
||||||
|
eventBus.addEventHandler(eventHandler2, 'test_event_2')
|
||||||
|
eventBus.addEventHandler(eventHandler1, 'test_event_3')
|
||||||
|
eventBus.addEventHandler(eventHandler3, 'test_event_2')
|
||||||
|
|
||||||
|
eventBus.publish({ type: 'test_event_2', payload: { foo: 'bar' } })
|
||||||
|
|
||||||
|
expect(eventHandler1.handleEvent).not.toHaveBeenCalled()
|
||||||
|
expect(eventHandler2.handleEvent).toHaveBeenCalledWith({
|
||||||
|
type: 'test_event_2',
|
||||||
|
payload: { foo: 'bar' },
|
||||||
|
})
|
||||||
|
expect(eventHandler3.handleEvent).toHaveBeenCalledWith({
|
||||||
|
type: 'test_event_2',
|
||||||
|
payload: { foo: 'bar' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should do nothing if there are no appropriate event handlers', () => {
|
||||||
|
const eventBus = createEventBus()
|
||||||
|
eventBus.addEventHandler(eventHandler1, 'test_event_1')
|
||||||
|
eventBus.addEventHandler(eventHandler2, 'test_event_2')
|
||||||
|
eventBus.addEventHandler(eventHandler1, 'test_event_3')
|
||||||
|
eventBus.addEventHandler(eventHandler3, 'test_event_2')
|
||||||
|
|
||||||
|
eventBus.publish({ type: 'test_event_4', payload: { foo: 'bar' } })
|
||||||
|
|
||||||
|
expect(eventHandler1.handleEvent).not.toHaveBeenCalled()
|
||||||
|
expect(eventHandler2.handleEvent).not.toHaveBeenCalled()
|
||||||
|
expect(eventHandler3.handleEvent).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle event synchronously in a sequential order', async () => {
|
||||||
|
const eventBus = createEventBus()
|
||||||
|
eventBus.addEventHandler(eventHandler1, 'test_event_1')
|
||||||
|
eventBus.addEventHandler(eventHandler2, 'test_event_2')
|
||||||
|
eventBus.addEventHandler(eventHandler1, 'test_event_3')
|
||||||
|
eventBus.addEventHandler(eventHandler3, 'test_event_2')
|
||||||
|
|
||||||
|
await eventBus.publishSync({ type: 'test_event_2', payload: { foo: 'bar' } }, InternalEventPublishStrategy.SEQUENCE)
|
||||||
|
|
||||||
|
expect(eventHandler1.handleEvent).not.toHaveBeenCalled()
|
||||||
|
expect(eventHandler2.handleEvent).toHaveBeenCalledWith({
|
||||||
|
type: 'test_event_2',
|
||||||
|
payload: { foo: 'bar' },
|
||||||
|
})
|
||||||
|
expect(eventHandler3.handleEvent).toHaveBeenCalledWith({
|
||||||
|
type: 'test_event_2',
|
||||||
|
payload: { foo: 'bar' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle event synchronously in a random order', async () => {
|
||||||
|
const eventBus = createEventBus()
|
||||||
|
eventBus.addEventHandler(eventHandler1, 'test_event_1')
|
||||||
|
eventBus.addEventHandler(eventHandler2, 'test_event_2')
|
||||||
|
eventBus.addEventHandler(eventHandler1, 'test_event_3')
|
||||||
|
eventBus.addEventHandler(eventHandler3, 'test_event_2')
|
||||||
|
|
||||||
|
await eventBus.publishSync({ type: 'test_event_2', payload: { foo: 'bar' } }, InternalEventPublishStrategy.ASYNC)
|
||||||
|
|
||||||
|
expect(eventHandler1.handleEvent).not.toHaveBeenCalled()
|
||||||
|
expect(eventHandler2.handleEvent).toHaveBeenCalledWith({
|
||||||
|
type: 'test_event_2',
|
||||||
|
payload: { foo: 'bar' },
|
||||||
|
})
|
||||||
|
expect(eventHandler3.handleEvent).toHaveBeenCalledWith({
|
||||||
|
type: 'test_event_2',
|
||||||
|
payload: { foo: 'bar' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should do nothing if there are no appropriate event handlers for synchronous handling', async () => {
|
||||||
|
const eventBus = createEventBus()
|
||||||
|
eventBus.addEventHandler(eventHandler1, 'test_event_1')
|
||||||
|
eventBus.addEventHandler(eventHandler2, 'test_event_2')
|
||||||
|
eventBus.addEventHandler(eventHandler1, 'test_event_3')
|
||||||
|
eventBus.addEventHandler(eventHandler3, 'test_event_2')
|
||||||
|
|
||||||
|
await eventBus.publishSync({ type: 'test_event_4', payload: { foo: 'bar' } }, InternalEventPublishStrategy.ASYNC)
|
||||||
|
|
||||||
|
expect(eventHandler1.handleEvent).not.toHaveBeenCalled()
|
||||||
|
expect(eventHandler2.handleEvent).not.toHaveBeenCalled()
|
||||||
|
expect(eventHandler3.handleEvent).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should clear event observers on deinit', async () => {
|
||||||
|
const eventBus = createEventBus()
|
||||||
|
eventBus.deinit()
|
||||||
|
|
||||||
|
expect(eventBus['eventHandlers']).toBeUndefined
|
||||||
|
})
|
||||||
|
})
|
||||||
61
packages/services/src/Domain/Internal/InternalEventBus.ts
Normal file
61
packages/services/src/Domain/Internal/InternalEventBus.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { InternalEventBusInterface } from './InternalEventBusInterface'
|
||||||
|
import { InternalEventHandlerInterface } from './InternalEventHandlerInterface'
|
||||||
|
import { InternalEventInterface } from './InternalEventInterface'
|
||||||
|
import { InternalEventPublishStrategy } from './InternalEventPublishStrategy'
|
||||||
|
import { InternalEventType } from './InternalEventType'
|
||||||
|
|
||||||
|
export class InternalEventBus implements InternalEventBusInterface {
|
||||||
|
private eventHandlers: Map<InternalEventType, InternalEventHandlerInterface[]>
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.eventHandlers = new Map<InternalEventType, InternalEventHandlerInterface[]>()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit(): void {
|
||||||
|
;(this.eventHandlers as unknown) = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventHandler(handler: InternalEventHandlerInterface, eventType: string): void {
|
||||||
|
let handlersForEventType = this.eventHandlers.get(eventType)
|
||||||
|
if (handlersForEventType === undefined) {
|
||||||
|
handlersForEventType = []
|
||||||
|
}
|
||||||
|
|
||||||
|
handlersForEventType.push(handler)
|
||||||
|
|
||||||
|
this.eventHandlers.set(eventType, handlersForEventType)
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(event: InternalEventInterface): void {
|
||||||
|
const handlersForEventType = this.eventHandlers.get(event.type)
|
||||||
|
if (handlersForEventType === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const handlerForEventType of handlersForEventType) {
|
||||||
|
void handlerForEventType.handleEvent(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async publishSync(event: InternalEventInterface, strategy: InternalEventPublishStrategy): Promise<void> {
|
||||||
|
const handlersForEventType = this.eventHandlers.get(event.type)
|
||||||
|
if (handlersForEventType === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strategy === InternalEventPublishStrategy.SEQUENCE) {
|
||||||
|
for (const handlerForEventType of handlersForEventType) {
|
||||||
|
await handlerForEventType.handleEvent(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strategy === InternalEventPublishStrategy.ASYNC) {
|
||||||
|
const handlerPromises = []
|
||||||
|
for (const handlerForEventType of handlersForEventType) {
|
||||||
|
handlerPromises.push(handlerForEventType.handleEvent(event))
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(handlerPromises)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { InternalEventInterface } from './InternalEventInterface'
|
||||||
|
import { InternalEventType } from './InternalEventType'
|
||||||
|
import { InternalEventHandlerInterface } from './InternalEventHandlerInterface'
|
||||||
|
import { InternalEventPublishStrategy } from '..'
|
||||||
|
|
||||||
|
export interface InternalEventBusInterface {
|
||||||
|
/**
|
||||||
|
* Associate an event handler with a certain event type
|
||||||
|
* @param handler event handler instance
|
||||||
|
* @param eventType event type to associate with
|
||||||
|
*/
|
||||||
|
addEventHandler(handler: InternalEventHandlerInterface, eventType: InternalEventType): void
|
||||||
|
/**
|
||||||
|
* Asynchronously publish an event for handling
|
||||||
|
* @param event internal event object
|
||||||
|
*/
|
||||||
|
publish(event: InternalEventInterface): void
|
||||||
|
/**
|
||||||
|
* Synchronously publish an event for handling.
|
||||||
|
* This will await for all handlers to finish processing the event.
|
||||||
|
* @param event internal event object
|
||||||
|
* @param strategy strategy with which the handlers will process the event.
|
||||||
|
* Either all handlers will start at once or they will do it sequentially.
|
||||||
|
*/
|
||||||
|
publishSync(event: InternalEventInterface, strategy: InternalEventPublishStrategy): Promise<void>
|
||||||
|
|
||||||
|
deinit(): void
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { InternalEventInterface } from './InternalEventInterface'
|
||||||
|
|
||||||
|
export interface InternalEventHandlerInterface {
|
||||||
|
handleEvent(event: InternalEventInterface): Promise<void>
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { InternalEventType } from './InternalEventType'
|
||||||
|
|
||||||
|
export interface InternalEventInterface {
|
||||||
|
type: InternalEventType
|
||||||
|
payload: unknown
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export enum InternalEventPublishStrategy {
|
||||||
|
ASYNC = 'ASYNC',
|
||||||
|
SEQUENCE = 'SEQUENCE',
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export type InternalEventType = string
|
||||||
133
packages/services/src/Domain/Item/ItemManagerInterface.ts
Normal file
133
packages/services/src/Domain/Item/ItemManagerInterface.ts
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import { ContentType } from '@standardnotes/common'
|
||||||
|
import {
|
||||||
|
MutationType,
|
||||||
|
ItemsKeyInterface,
|
||||||
|
ItemsKeyMutatorInterface,
|
||||||
|
DecryptedItemInterface,
|
||||||
|
DecryptedItemMutator,
|
||||||
|
DecryptedPayloadInterface,
|
||||||
|
PayloadEmitSource,
|
||||||
|
EncryptedItemInterface,
|
||||||
|
DeletedItemInterface,
|
||||||
|
ItemContent,
|
||||||
|
PredicateInterface,
|
||||||
|
} from '@standardnotes/models'
|
||||||
|
import { AbstractService } from '../Service/AbstractService'
|
||||||
|
|
||||||
|
export type ItemManagerChangeData<I extends DecryptedItemInterface = DecryptedItemInterface> = {
|
||||||
|
/** The items are pre-existing but have been changed */
|
||||||
|
changed: I[]
|
||||||
|
|
||||||
|
/** The items have been newly inserted */
|
||||||
|
inserted: I[]
|
||||||
|
|
||||||
|
/** The items should no longer be displayed in the interface, either due to being deleted, or becoming error-encrypted */
|
||||||
|
removed: (EncryptedItemInterface | DeletedItemInterface)[]
|
||||||
|
|
||||||
|
/** Items for which encrypted overwrite protection is enabled and enacted */
|
||||||
|
ignored: EncryptedItemInterface[]
|
||||||
|
|
||||||
|
/** Items which were previously error decrypting but now successfully decrypted */
|
||||||
|
unerrored: I[]
|
||||||
|
|
||||||
|
source: PayloadEmitSource
|
||||||
|
sourceKey?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ItemManagerChangeObserverCallback<I extends DecryptedItemInterface = DecryptedItemInterface> = (
|
||||||
|
data: ItemManagerChangeData<I>,
|
||||||
|
) => void
|
||||||
|
|
||||||
|
export interface ItemManagerInterface extends AbstractService {
|
||||||
|
addObserver<I extends DecryptedItemInterface = DecryptedItemInterface>(
|
||||||
|
contentType: ContentType | ContentType[],
|
||||||
|
callback: ItemManagerChangeObserverCallback<I>,
|
||||||
|
): () => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the item as deleted and needing sync.
|
||||||
|
*/
|
||||||
|
setItemToBeDeleted(itemToLookupUuidFor: DecryptedItemInterface, source?: PayloadEmitSource): Promise<void>
|
||||||
|
|
||||||
|
setItemsToBeDeleted(itemsToLookupUuidsFor: DecryptedItemInterface[]): Promise<void>
|
||||||
|
|
||||||
|
setItemsDirty(
|
||||||
|
itemsToLookupUuidsFor: DecryptedItemInterface[],
|
||||||
|
isUserModified?: boolean,
|
||||||
|
): Promise<DecryptedItemInterface[]>
|
||||||
|
|
||||||
|
get items(): DecryptedItemInterface[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the item as-is by reading its payload value. This function will not
|
||||||
|
* modify item in any way (such as marking it as dirty). It is up to the caller
|
||||||
|
* to pass in a dirtied item if that is their intention.
|
||||||
|
*/
|
||||||
|
insertItem(item: DecryptedItemInterface): Promise<DecryptedItemInterface>
|
||||||
|
|
||||||
|
emitItemFromPayload(payload: DecryptedPayloadInterface, source: PayloadEmitSource): Promise<DecryptedItemInterface>
|
||||||
|
|
||||||
|
getItems<T extends DecryptedItemInterface>(contentType: ContentType | ContentType[]): T[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all non-deleted items keys
|
||||||
|
*/
|
||||||
|
getDisplayableItemsKeys(): ItemsKeyInterface[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an item and conditionally maps it and marks it as dirty.
|
||||||
|
* @param needsSync - Whether to mark the item as needing sync
|
||||||
|
*/
|
||||||
|
createItem<T extends DecryptedItemInterface, C extends ItemContent = ItemContent>(
|
||||||
|
contentType: ContentType,
|
||||||
|
content: C,
|
||||||
|
needsSync?: boolean,
|
||||||
|
): Promise<T>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an unmanaged item that can later be inserted via `insertItem`
|
||||||
|
*/
|
||||||
|
createTemplateItem<
|
||||||
|
C extends ItemContent = ItemContent,
|
||||||
|
I extends DecryptedItemInterface<C> = DecryptedItemInterface<C>,
|
||||||
|
>(
|
||||||
|
contentType: ContentType,
|
||||||
|
content?: C,
|
||||||
|
): I
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumers wanting to modify an item should run it through this block,
|
||||||
|
* so that data is properly mapped through our function, and latest state
|
||||||
|
* is properly reconciled.
|
||||||
|
*/
|
||||||
|
changeItem<
|
||||||
|
M extends DecryptedItemMutator = DecryptedItemMutator,
|
||||||
|
I extends DecryptedItemInterface = DecryptedItemInterface,
|
||||||
|
>(
|
||||||
|
itemToLookupUuidFor: I,
|
||||||
|
mutate?: (mutator: M) => void,
|
||||||
|
mutationType?: MutationType,
|
||||||
|
emitSource?: PayloadEmitSource,
|
||||||
|
payloadSourceKey?: string,
|
||||||
|
): Promise<I>
|
||||||
|
|
||||||
|
changeItemsKey(
|
||||||
|
itemToLookupUuidFor: ItemsKeyInterface,
|
||||||
|
mutate: (mutator: ItemsKeyMutatorInterface) => void,
|
||||||
|
mutationType?: MutationType,
|
||||||
|
emitSource?: PayloadEmitSource,
|
||||||
|
payloadSourceKey?: string,
|
||||||
|
): Promise<ItemsKeyInterface>
|
||||||
|
|
||||||
|
itemsMatchingPredicate<T extends DecryptedItemInterface>(
|
||||||
|
contentType: ContentType,
|
||||||
|
predicate: PredicateInterface<T>,
|
||||||
|
): T[]
|
||||||
|
|
||||||
|
itemsMatchingPredicates<T extends DecryptedItemInterface>(
|
||||||
|
contentType: ContentType,
|
||||||
|
predicates: PredicateInterface<T>[],
|
||||||
|
): T[]
|
||||||
|
|
||||||
|
subItemsMatchingPredicates<T extends DecryptedItemInterface>(items: T[], predicates: PredicateInterface<T>[]): T[]
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { Uuid } from '@standardnotes/common'
|
||||||
|
import { GetSingleItemResponse } from '@standardnotes/responses'
|
||||||
|
|
||||||
|
export interface ItemsServerInterface {
|
||||||
|
getSingleItem(itemUuid: Uuid): Promise<GetSingleItemResponse>
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
PayloadInterface,
|
||||||
|
EncryptedPayloadInterface,
|
||||||
|
FullyFormedPayloadInterface,
|
||||||
|
PayloadEmitSource,
|
||||||
|
} from '@standardnotes/models'
|
||||||
|
import { IntegrityPayload } from '@standardnotes/responses'
|
||||||
|
|
||||||
|
export interface PayloadManagerInterface {
|
||||||
|
emitPayloads(
|
||||||
|
payloads: PayloadInterface[],
|
||||||
|
emitSource: PayloadEmitSource,
|
||||||
|
sourceKey?: string,
|
||||||
|
): Promise<PayloadInterface[]>
|
||||||
|
|
||||||
|
integrityPayloads: IntegrityPayload[]
|
||||||
|
|
||||||
|
get invalidPayloads(): EncryptedPayloadInterface[]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a detached array of all items which are not deleted
|
||||||
|
*/
|
||||||
|
get nonDeletedItems(): FullyFormedPayloadInterface[]
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { PrefKey, PrefValue } from '@standardnotes/models'
|
||||||
|
import { AbstractService } from '../Service/AbstractService'
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum PreferencesServiceEvent {
|
||||||
|
PreferencesChanged = 'PreferencesChanged',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PreferenceServiceInterface extends AbstractService<PreferencesServiceEvent> {
|
||||||
|
getValue<K extends PrefKey>(key: K, defaultValue: PrefValue[K] | undefined): PrefValue[K] | undefined
|
||||||
|
getValue<K extends PrefKey>(key: K, defaultValue: PrefValue[K]): PrefValue[K]
|
||||||
|
getValue<K extends PrefKey>(key: K, defaultValue?: PrefValue[K]): PrefValue[K] | undefined
|
||||||
|
setValue<K extends PrefKey>(key: K, value: PrefValue[K]): Promise<void>
|
||||||
|
}
|
||||||
108
packages/services/src/Domain/Service/AbstractService.ts
Normal file
108
packages/services/src/Domain/Service/AbstractService.ts
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
import { log, removeFromArray } from '@standardnotes/utils'
|
||||||
|
import { EventObserver } from '../Event/EventObserver'
|
||||||
|
import { ServiceInterface } from './ServiceInterface'
|
||||||
|
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
|
||||||
|
import { ApplicationStage } from '../Application/ApplicationStage'
|
||||||
|
import { InternalEventPublishStrategy } from '../Internal/InternalEventPublishStrategy'
|
||||||
|
import { DiagnosticInfo } from '../Diagnostics/ServiceDiagnostics'
|
||||||
|
|
||||||
|
export abstract class AbstractService<EventName = string, EventData = undefined>
|
||||||
|
implements ServiceInterface<EventName, EventData>
|
||||||
|
{
|
||||||
|
private eventObservers: EventObserver<EventName, EventData>[] = []
|
||||||
|
public loggingEnabled = false
|
||||||
|
private criticalPromises: Promise<unknown>[] = []
|
||||||
|
|
||||||
|
constructor(protected internalEventBus: InternalEventBusInterface) {}
|
||||||
|
|
||||||
|
public addEventObserver(observer: EventObserver<EventName, EventData>): () => void {
|
||||||
|
this.eventObservers.push(observer)
|
||||||
|
|
||||||
|
const thislessEventObservers = this.eventObservers
|
||||||
|
return () => {
|
||||||
|
removeFromArray(thislessEventObservers, observer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async notifyEvent(eventName: EventName, data?: EventData): Promise<void> {
|
||||||
|
for (const observer of this.eventObservers) {
|
||||||
|
await observer(eventName, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.internalEventBus?.publish({
|
||||||
|
type: eventName as unknown as string,
|
||||||
|
payload: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async notifyEventSync(eventName: EventName, data?: EventData): Promise<void> {
|
||||||
|
for (const observer of this.eventObservers) {
|
||||||
|
await observer(eventName, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.internalEventBus?.publishSync(
|
||||||
|
{
|
||||||
|
type: eventName as unknown as string,
|
||||||
|
payload: data,
|
||||||
|
},
|
||||||
|
InternalEventPublishStrategy.SEQUENCE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
getDiagnostics(): Promise<DiagnosticInfo | undefined> {
|
||||||
|
return Promise.resolve(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by application to allow services to momentarily block deinit until
|
||||||
|
* sensitive operations complete.
|
||||||
|
*/
|
||||||
|
public async blockDeinit(): Promise<void> {
|
||||||
|
await Promise.all(this.criticalPromises)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by application before restart.
|
||||||
|
* Subclasses should deregister any observers/timers
|
||||||
|
*/
|
||||||
|
public deinit(): void {
|
||||||
|
this.eventObservers.length = 0
|
||||||
|
;(this.internalEventBus as unknown) = undefined
|
||||||
|
;(this.criticalPromises as unknown) = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A critical function is one that should block signing out or destroying application
|
||||||
|
* session until the crticial function has completed. For example, persisting keys to
|
||||||
|
* disk is a critical operation, and should be wrapped in this function call. The
|
||||||
|
* parent application instance will await all criticial functions via the `blockDeinit`
|
||||||
|
* function before signing out and deiniting.
|
||||||
|
*/
|
||||||
|
protected async executeCriticalFunction<T = void>(func: () => Promise<T>): Promise<T> {
|
||||||
|
const promise = func()
|
||||||
|
this.criticalPromises.push(promise)
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application instances will call this function directly when they arrive
|
||||||
|
* at a certain migratory state.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
public async handleApplicationStage(_stage: ApplicationStage): Promise<void> {
|
||||||
|
// optional override
|
||||||
|
}
|
||||||
|
|
||||||
|
getServiceName(): string {
|
||||||
|
return this.constructor.name
|
||||||
|
}
|
||||||
|
|
||||||
|
log(..._args: unknown[]): void {
|
||||||
|
if (this.loggingEnabled) {
|
||||||
|
// eslint-disable-next-line prefer-rest-params
|
||||||
|
log(this.getServiceName(), ...arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
packages/services/src/Domain/Service/ServiceInterface.ts
Normal file
12
packages/services/src/Domain/Service/ServiceInterface.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { ApplicationStage } from '../Application/ApplicationStage'
|
||||||
|
import { ServiceDiagnostics } from '../Diagnostics/ServiceDiagnostics'
|
||||||
|
import { EventObserver } from '../Event/EventObserver'
|
||||||
|
|
||||||
|
export interface ServiceInterface<E, D> extends ServiceDiagnostics {
|
||||||
|
loggingEnabled: boolean
|
||||||
|
addEventObserver(observer: EventObserver<E, D>): () => void
|
||||||
|
blockDeinit(): Promise<void>
|
||||||
|
deinit(): void
|
||||||
|
handleApplicationStage(stage: ApplicationStage): Promise<void>
|
||||||
|
log(message: string, ...args: unknown[]): void
|
||||||
|
}
|
||||||
62
packages/services/src/Domain/Status/StatusService.ts
Normal file
62
packages/services/src/Domain/Status/StatusService.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { removeFromArray } from '@standardnotes/utils'
|
||||||
|
import { AbstractService } from '../Service/AbstractService'
|
||||||
|
import { StatusServiceEvent, StatusServiceInterface, StatusMessageIdentifier } from './StatusServiceInterface'
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export class StatusService extends AbstractService<StatusServiceEvent, string> implements StatusServiceInterface {
|
||||||
|
private _message = ''
|
||||||
|
private directSetMessage?: string
|
||||||
|
private dynamicMessages: string[] = []
|
||||||
|
|
||||||
|
get message(): string {
|
||||||
|
return this._message
|
||||||
|
}
|
||||||
|
|
||||||
|
setMessage(message: string | undefined): void {
|
||||||
|
this.directSetMessage = message
|
||||||
|
this.recomputeMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
addMessage(message: string): StatusMessageIdentifier {
|
||||||
|
this.dynamicMessages.push(message)
|
||||||
|
|
||||||
|
this.recomputeMessage()
|
||||||
|
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
removeMessage(message: StatusMessageIdentifier): void {
|
||||||
|
removeFromArray(this.dynamicMessages, message)
|
||||||
|
|
||||||
|
this.recomputeMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
private recomputeMessage(): void {
|
||||||
|
const messages = [...this.dynamicMessages]
|
||||||
|
|
||||||
|
if (this.directSetMessage) {
|
||||||
|
messages.unshift(this.directSetMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
this._message = this.messageFromArray(messages)
|
||||||
|
|
||||||
|
void this.notifyEvent(StatusServiceEvent.MessageChanged, this._message)
|
||||||
|
}
|
||||||
|
|
||||||
|
private messageFromArray(messages: string[]): string {
|
||||||
|
let message = ''
|
||||||
|
|
||||||
|
messages.forEach((value, index) => {
|
||||||
|
const isLast = index === messages.length - 1
|
||||||
|
|
||||||
|
message += value
|
||||||
|
|
||||||
|
if (!isLast) {
|
||||||
|
message += ', '
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { AbstractService } from '../Service/AbstractService'
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum StatusServiceEvent {
|
||||||
|
MessageChanged = 'MessageChanged',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StatusMessageIdentifier = string
|
||||||
|
|
||||||
|
export interface StatusServiceInterface extends AbstractService<StatusServiceEvent, string> {
|
||||||
|
get message(): string
|
||||||
|
setMessage(message: string | undefined): void
|
||||||
|
addMessage(message: string): StatusMessageIdentifier
|
||||||
|
removeMessage(message: StatusMessageIdentifier): void
|
||||||
|
}
|
||||||
24
packages/services/src/Domain/Storage/InMemoryStore.spec.ts
Normal file
24
packages/services/src/Domain/Storage/InMemoryStore.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { InMemoryStore } from './InMemoryStore'
|
||||||
|
import { StorageKey } from './StorageKeys'
|
||||||
|
|
||||||
|
describe('InMemoryStore', () => {
|
||||||
|
const createStore = () => new InMemoryStore()
|
||||||
|
|
||||||
|
it('should set and retrieve a value', () => {
|
||||||
|
const store = createStore()
|
||||||
|
|
||||||
|
store.setValue(StorageKey.CodeVerifier, 'test')
|
||||||
|
|
||||||
|
expect(store.getValue(StorageKey.CodeVerifier)).toEqual('test')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should remove a value', () => {
|
||||||
|
const store = createStore()
|
||||||
|
|
||||||
|
store.setValue(StorageKey.CodeVerifier, 'test')
|
||||||
|
|
||||||
|
store.removeValue(StorageKey.CodeVerifier)
|
||||||
|
|
||||||
|
expect(store.getValue(StorageKey.CodeVerifier)).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
22
packages/services/src/Domain/Storage/InMemoryStore.ts
Normal file
22
packages/services/src/Domain/Storage/InMemoryStore.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { KeyValueStoreInterface } from './KeyValueStoreInterface'
|
||||||
|
import { StorageKey } from './StorageKeys'
|
||||||
|
|
||||||
|
export class InMemoryStore implements KeyValueStoreInterface<string> {
|
||||||
|
private values: Map<StorageKey, string>
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.values = new Map<StorageKey, string>()
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(key: StorageKey, value: string): void {
|
||||||
|
this.values.set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
getValue(key: StorageKey): string | undefined {
|
||||||
|
return this.values.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
removeValue(key: StorageKey): void {
|
||||||
|
this.values.delete(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { StorageKey } from './StorageKeys'
|
||||||
|
|
||||||
|
export interface KeyValueStoreInterface<T> {
|
||||||
|
setValue(key: StorageKey, value: T): void
|
||||||
|
getValue(key: StorageKey): T | undefined
|
||||||
|
removeValue(key: StorageKey): void
|
||||||
|
}
|
||||||
7
packages/services/src/Domain/Storage/StorageKeys.spec.ts
Normal file
7
packages/services/src/Domain/Storage/StorageKeys.spec.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { namespacedKey } from './StorageKeys'
|
||||||
|
|
||||||
|
describe('StorageKeys', () => {
|
||||||
|
it('namespacedKey', () => {
|
||||||
|
expect(namespacedKey('namespace', 'key')).toEqual('namespace-key')
|
||||||
|
})
|
||||||
|
})
|
||||||
66
packages/services/src/Domain/Storage/StorageKeys.ts
Normal file
66
packages/services/src/Domain/Storage/StorageKeys.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* Unmanaged keys stored in root storage.
|
||||||
|
* Raw storage keys exist outside of StorageManager domain
|
||||||
|
*/
|
||||||
|
export enum RawStorageKey {
|
||||||
|
StorageObject = 'storage',
|
||||||
|
DescriptorRecord = 'descriptors',
|
||||||
|
SnjsVersion = 'snjs_version',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys used for retrieving and saving simple key/value pairs.
|
||||||
|
* These keys are managed and are embedded inside RawStorageKey.StorageObject
|
||||||
|
*/
|
||||||
|
export enum StorageKey {
|
||||||
|
RootKeyParams = 'ROOT_KEY_PARAMS',
|
||||||
|
WrappedRootKey = 'WRAPPED_ROOT_KEY',
|
||||||
|
RootKeyWrapperKeyParams = 'ROOT_KEY_WRAPPER_KEY_PARAMS',
|
||||||
|
Session = 'session',
|
||||||
|
User = 'user',
|
||||||
|
ServerHost = 'server',
|
||||||
|
LegacyUuid = 'uuid',
|
||||||
|
LastSyncToken = 'syncToken',
|
||||||
|
PaginationToken = 'cursorToken',
|
||||||
|
BiometricsState = 'biometrics_state',
|
||||||
|
MobilePasscodeTiming = 'passcode_timing',
|
||||||
|
MobileBiometricsTiming = 'biometrics_timing',
|
||||||
|
MobilePasscodeKeyboardType = 'passcodeKeyboardType',
|
||||||
|
MobilePreferences = 'preferences',
|
||||||
|
MobileScreenshotPrivacyEnabled = 'screenshotPrivacy_enabled',
|
||||||
|
ProtectionExpirey = 'SessionExpiresAtKey',
|
||||||
|
ProtectionSessionLength = 'SessionLengthKey',
|
||||||
|
KeyRecoveryUndecryptableItems = 'key_recovery_undecryptable',
|
||||||
|
StorageEncryptionPolicy = 'storage_policy',
|
||||||
|
WebSocketUrl = 'webSocket_url',
|
||||||
|
UserRoles = 'user_roles',
|
||||||
|
UserFeatures = 'user_features',
|
||||||
|
ExperimentalFeatures = 'experimental_features',
|
||||||
|
DeinitMode = 'deinit_mode',
|
||||||
|
CodeVerifier = 'code_verifier',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum NonwrappedStorageKey {
|
||||||
|
MobileFirstRun = 'first_run',
|
||||||
|
}
|
||||||
|
|
||||||
|
export function namespacedKey(namespace: string, key: string) {
|
||||||
|
return `${namespace}-${key}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LegacyKeys1_0_0 = {
|
||||||
|
WebPasscodeParamsKey: 'offlineParams',
|
||||||
|
MobilePasscodeParamsKey: 'pc_params',
|
||||||
|
AllAccountKeyParamsKey: 'auth_params',
|
||||||
|
WebEncryptedStorageKey: 'encryptedStorage',
|
||||||
|
MobileWrappedRootKeyKey: 'encrypted_account_keys',
|
||||||
|
MobileBiometricsPrefs: 'biometrics_prefs',
|
||||||
|
AllMigrations: 'migrations',
|
||||||
|
MobileThemesCache: 'ThemePreferencesKey',
|
||||||
|
MobileLightTheme: 'lightTheme',
|
||||||
|
MobileDarkTheme: 'darkTheme',
|
||||||
|
MobileLastExportDate: 'LastExportDateKey',
|
||||||
|
MobileDoNotWarnUnsupportedEditors: 'DoNotShowAgainUnsupportedEditorsKey',
|
||||||
|
MobileOptionsState: 'options',
|
||||||
|
MobilePasscodeKeyboardType: 'passcodeKeyboardType',
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { PayloadInterface, RootKeyInterface } from '@standardnotes/models'
|
||||||
|
import { StorageValueModes } from './StorageTypes'
|
||||||
|
|
||||||
|
export interface StorageServiceInterface {
|
||||||
|
getValue<T>(key: string, mode?: StorageValueModes, defaultValue?: T): T
|
||||||
|
|
||||||
|
canDecryptWithKey(key: RootKeyInterface): Promise<boolean>
|
||||||
|
|
||||||
|
savePayload(payload: PayloadInterface): Promise<void>
|
||||||
|
|
||||||
|
savePayloads(decryptedPayloads: PayloadInterface[]): Promise<void>
|
||||||
|
|
||||||
|
setValue(key: string, value: unknown, mode?: StorageValueModes): void
|
||||||
|
|
||||||
|
removeValue(key: string, mode?: StorageValueModes): Promise<void>
|
||||||
|
}
|
||||||
39
packages/services/src/Domain/Storage/StorageTypes.ts
Normal file
39
packages/services/src/Domain/Storage/StorageTypes.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { LocalStorageEncryptedContextualPayload, LocalStorageDecryptedContextualPayload } from '@standardnotes/models'
|
||||||
|
|
||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum StoragePersistencePolicies {
|
||||||
|
Default = 1,
|
||||||
|
Ephemeral = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum StorageEncryptionPolicy {
|
||||||
|
Default = 1,
|
||||||
|
Disabled = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum StorageValueModes {
|
||||||
|
/** Stored inside wrapped encrpyed storage object */
|
||||||
|
Default = 1,
|
||||||
|
/** Stored outside storage object, unencrypted */
|
||||||
|
Nonwrapped = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ValueModesKeys {
|
||||||
|
/* Is encrypted */
|
||||||
|
Wrapped = 'wrapped',
|
||||||
|
/* Is decrypted */
|
||||||
|
Unwrapped = 'unwrapped',
|
||||||
|
/* Lives outside of wrapped/unwrapped */
|
||||||
|
Nonwrapped = 'nonwrapped',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ValuesObjectRecord = Record<string, unknown>
|
||||||
|
|
||||||
|
export type WrappedStorageValue = LocalStorageEncryptedContextualPayload | LocalStorageDecryptedContextualPayload
|
||||||
|
|
||||||
|
export type StorageValuesObject = {
|
||||||
|
[ValueModesKeys.Wrapped]: WrappedStorageValue
|
||||||
|
[ValueModesKeys.Unwrapped]: ValuesObjectRecord
|
||||||
|
[ValueModesKeys.Nonwrapped]: ValuesObjectRecord
|
||||||
|
}
|
||||||
14
packages/services/src/Domain/Sync/SyncMode.ts
Normal file
14
packages/services/src/Domain/Sync/SyncMode.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum SyncMode {
|
||||||
|
/**
|
||||||
|
* Performs a standard sync, uploading any dirty items and retrieving items.
|
||||||
|
*/
|
||||||
|
Default = 1,
|
||||||
|
/**
|
||||||
|
* The first sync for an account, where we first want to download all remote items first
|
||||||
|
* before uploading any dirty items. This allows a consumer, for example, to download
|
||||||
|
* all data to see if user has an items key, and if not, only then create a new one.
|
||||||
|
*/
|
||||||
|
DownloadFirst = 2,
|
||||||
|
}
|
||||||
21
packages/services/src/Domain/Sync/SyncOptions.ts
Normal file
21
packages/services/src/Domain/Sync/SyncOptions.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
import { SyncMode } from './SyncMode'
|
||||||
|
import { SyncQueueStrategy } from './SyncQueueStrategy'
|
||||||
|
import { SyncSource } from './SyncSource'
|
||||||
|
|
||||||
|
export type SyncOptions = {
|
||||||
|
queueStrategy?: SyncQueueStrategy
|
||||||
|
mode?: SyncMode
|
||||||
|
/** Whether the server should compute and return an integrity hash. */
|
||||||
|
checkIntegrity?: boolean
|
||||||
|
/** Internally used to keep track of how sync requests were spawned. */
|
||||||
|
source: SyncSource
|
||||||
|
/** Whether to await any sync requests that may be queued from this call. */
|
||||||
|
awaitAll?: boolean
|
||||||
|
/**
|
||||||
|
* A callback that is triggered after pre-sync save completes,
|
||||||
|
* and before the sync request is network dispatched
|
||||||
|
*/
|
||||||
|
onPresyncSave?: () => void
|
||||||
|
}
|
||||||
14
packages/services/src/Domain/Sync/SyncQueueStrategy.ts
Normal file
14
packages/services/src/Domain/Sync/SyncQueueStrategy.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum SyncQueueStrategy {
|
||||||
|
/**
|
||||||
|
* Promise will be resolved on the next sync request after the current one completes.
|
||||||
|
* If there is no scheduled sync request, one will be scheduled.
|
||||||
|
*/
|
||||||
|
ResolveOnNext = 1,
|
||||||
|
/**
|
||||||
|
* A new sync request is guarenteed to be generated for your request, no matter how long it takes.
|
||||||
|
* Promise will be resolved whenever this sync request is processed in the serial queue.
|
||||||
|
*/
|
||||||
|
ForceSpawnNew = 2,
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
import { SyncOptions } from './SyncOptions'
|
||||||
|
|
||||||
|
export interface SyncServiceInterface {
|
||||||
|
sync(options?: Partial<SyncOptions>): Promise<unknown>
|
||||||
|
}
|
||||||
11
packages/services/src/Domain/Sync/SyncSource.ts
Normal file
11
packages/services/src/Domain/Sync/SyncSource.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
export enum SyncSource {
|
||||||
|
External = 1,
|
||||||
|
SpawnQueue = 2,
|
||||||
|
ResolveQueue = 3,
|
||||||
|
MoreDirtyItems = 4,
|
||||||
|
AfterDownloadFirst = 5,
|
||||||
|
IntegrityCheck = 6,
|
||||||
|
ResolveOutOfSync = 7,
|
||||||
|
}
|
||||||
51
packages/services/src/Domain/index.ts
Normal file
51
packages/services/src/Domain/index.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
export * from './Alert/AlertService'
|
||||||
|
export * from './Api/ApiServiceInterface'
|
||||||
|
export * from './Application/ApplicationStage'
|
||||||
|
export * from './Application/DeinitCallback'
|
||||||
|
export * from './Application/DeinitSource'
|
||||||
|
export * from './Application/DeinitMode'
|
||||||
|
export * from './Application/UserClientInterface'
|
||||||
|
export * from './Application/ApplicationInterface'
|
||||||
|
export * from './Challenge'
|
||||||
|
export * from './Device/DesktopDeviceInterface'
|
||||||
|
export * from './Device/DesktopWebCommunication'
|
||||||
|
export * from './Device/DeviceInterface'
|
||||||
|
export * from './Device/Environments'
|
||||||
|
export * from './Device/FileBackupsDevice'
|
||||||
|
export * from './Device/MobileDeviceInterface'
|
||||||
|
export * from './Device/TypeCheck'
|
||||||
|
export * from './Device/WebOrDesktopDeviceInterface'
|
||||||
|
export * from './Diagnostics/ServiceDiagnostics'
|
||||||
|
export * from './Event/EventObserver'
|
||||||
|
export * from './Event/SyncEvent'
|
||||||
|
export * from './Event/SyncEventReceiver'
|
||||||
|
export * from './Files/FilesApiInterface'
|
||||||
|
export * from './FileSystem/FileSystemApi'
|
||||||
|
export * from './Integrity/IntegrityApiInterface'
|
||||||
|
export * from './Integrity/IntegrityEvent'
|
||||||
|
export * from './Integrity/IntegrityEventPayload'
|
||||||
|
export * from './Integrity/IntegrityService'
|
||||||
|
export * from './Internal/InternalEventBus'
|
||||||
|
export * from './Internal/InternalEventBusInterface'
|
||||||
|
export * from './Internal/InternalEventHandlerInterface'
|
||||||
|
export * from './Internal/InternalEventInterface'
|
||||||
|
export * from './Internal/InternalEventPublishStrategy'
|
||||||
|
export * from './Internal/InternalEventType'
|
||||||
|
export * from './Item/ItemManagerInterface'
|
||||||
|
export * from './Item/ItemsServerInterface'
|
||||||
|
export * from './Payloads/PayloadManagerInterface'
|
||||||
|
export * from './Preferences/PreferenceServiceInterface'
|
||||||
|
export * from './Service/AbstractService'
|
||||||
|
export * from './Service/ServiceInterface'
|
||||||
|
export * from './Status/StatusService'
|
||||||
|
export * from './Status/StatusServiceInterface'
|
||||||
|
export * from './Storage/StorageKeys'
|
||||||
|
export * from './Storage/InMemoryStore'
|
||||||
|
export * from './Storage/KeyValueStoreInterface'
|
||||||
|
export * from './Storage/StorageServiceInterface'
|
||||||
|
export * from './Storage/StorageTypes'
|
||||||
|
export * from './Sync/SyncMode'
|
||||||
|
export * from './Sync/SyncOptions'
|
||||||
|
export * from './Sync/SyncQueueStrategy'
|
||||||
|
export * from './Sync/SyncServiceInterface'
|
||||||
|
export * from './Sync/SyncSource'
|
||||||
1
packages/services/src/index.ts
Normal file
1
packages/services/src/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './Domain'
|
||||||
13
packages/services/tsconfig.json
Normal file
13
packages/services/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../node_modules/@standardnotes/config/src/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./dist",
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*"
|
||||||
|
],
|
||||||
|
"references": [],
|
||||||
|
"exclude": ["**/*.spec.ts", "dist", "node_modules"]
|
||||||
|
}
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
"@standardnotes/encryption": "workspace:*",
|
"@standardnotes/encryption": "workspace:*",
|
||||||
"@standardnotes/filepicker": "workspace:*",
|
"@standardnotes/filepicker": "workspace:*",
|
||||||
"@standardnotes/icons": "workspace:*",
|
"@standardnotes/icons": "workspace:*",
|
||||||
"@standardnotes/services": "^1.13.23",
|
"@standardnotes/services": "workspace:*",
|
||||||
"@standardnotes/sncrypto-web": "1.10.1",
|
"@standardnotes/sncrypto-web": "1.10.1",
|
||||||
"@standardnotes/snjs": "^2.118.3",
|
"@standardnotes/snjs": "^2.118.3",
|
||||||
"@standardnotes/styles": "workspace:*",
|
"@standardnotes/styles": "workspace:*",
|
||||||
|
|||||||
41
yarn.lock
41
yarn.lock
@@ -6549,7 +6549,7 @@ __metadata:
|
|||||||
"@standardnotes/config": 2.4.3
|
"@standardnotes/config": 2.4.3
|
||||||
"@standardnotes/models": "workspace:*"
|
"@standardnotes/models": "workspace:*"
|
||||||
"@standardnotes/responses": ^1.6.39
|
"@standardnotes/responses": ^1.6.39
|
||||||
"@standardnotes/services": ^1.13.23
|
"@standardnotes/services": "workspace:*"
|
||||||
"@standardnotes/sncrypto-common": ^1.9.0
|
"@standardnotes/sncrypto-common": ^1.9.0
|
||||||
"@standardnotes/utils": ^1.6.12
|
"@standardnotes/utils": ^1.6.12
|
||||||
"@types/jest": ^27.4.1
|
"@types/jest": ^27.4.1
|
||||||
@@ -6604,7 +6604,7 @@ __metadata:
|
|||||||
resolution: "@standardnotes/filepicker@workspace:packages/filepicker"
|
resolution: "@standardnotes/filepicker@workspace:packages/filepicker"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@standardnotes/common": ^1.23.1
|
"@standardnotes/common": ^1.23.1
|
||||||
"@standardnotes/services": ^1.13.23
|
"@standardnotes/services": "workspace:*"
|
||||||
"@standardnotes/utils": ^1.6.12
|
"@standardnotes/utils": ^1.6.12
|
||||||
"@types/jest": ^27.4.1
|
"@types/jest": ^27.4.1
|
||||||
"@types/wicg-file-system-access": ^2020.9.5
|
"@types/wicg-file-system-access": ^2020.9.5
|
||||||
@@ -6626,7 +6626,7 @@ __metadata:
|
|||||||
"@standardnotes/filepicker": "workspace:*"
|
"@standardnotes/filepicker": "workspace:*"
|
||||||
"@standardnotes/models": "workspace:*"
|
"@standardnotes/models": "workspace:*"
|
||||||
"@standardnotes/responses": ^1.6.39
|
"@standardnotes/responses": ^1.6.39
|
||||||
"@standardnotes/services": ^1.13.23
|
"@standardnotes/services": "workspace:*"
|
||||||
"@standardnotes/sncrypto-common": ^1.9.0
|
"@standardnotes/sncrypto-common": ^1.9.0
|
||||||
"@standardnotes/utils": ^1.6.12
|
"@standardnotes/utils": ^1.6.12
|
||||||
"@types/jest": ^27.4.1
|
"@types/jest": ^27.4.1
|
||||||
@@ -7122,31 +7122,24 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@standardnotes/services@npm:^1.13.22":
|
"@standardnotes/services@^1.13.22, @standardnotes/services@^1.13.23, @standardnotes/services@workspace:*, @standardnotes/services@workspace:packages/services":
|
||||||
version: 1.13.22
|
version: 0.0.0-use.local
|
||||||
resolution: "@standardnotes/services@npm:1.13.22"
|
resolution: "@standardnotes/services@workspace:packages/services"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@standardnotes/auth": ^3.19.4
|
"@standardnotes/auth": ^3.19.4
|
||||||
"@standardnotes/common": ^1.23.1
|
"@standardnotes/common": ^1.23.1
|
||||||
"@standardnotes/models": ^1.11.12
|
"@standardnotes/models": "workspace:*"
|
||||||
"@standardnotes/responses": ^1.6.38
|
|
||||||
"@standardnotes/utils": ^1.6.12
|
|
||||||
checksum: e84f4e43d49c42b1f99b4e54380f1539ca3ff3451290b7f290fc1e480f2207f8567035015f8788c7b9f961f88eb81b43a4cca591328390dbd0622c9e4891063b
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@standardnotes/services@npm:^1.13.23":
|
|
||||||
version: 1.13.23
|
|
||||||
resolution: "@standardnotes/services@npm:1.13.23"
|
|
||||||
dependencies:
|
|
||||||
"@standardnotes/auth": ^3.19.4
|
|
||||||
"@standardnotes/common": ^1.23.1
|
|
||||||
"@standardnotes/models": ^1.11.13
|
|
||||||
"@standardnotes/responses": ^1.6.39
|
"@standardnotes/responses": ^1.6.39
|
||||||
"@standardnotes/utils": ^1.6.12
|
"@standardnotes/utils": ^1.6.12
|
||||||
checksum: 7e67af13c4eb845c6bcbbac46897b94fe4754a728dba04605ccfbd96da49a0b305299fd3db9778a29488c5b29cf1ac4db08c28844e3ed95a2a872595376e1dc6
|
"@types/jest": ^27.4.1
|
||||||
languageName: node
|
"@typescript-eslint/eslint-plugin": ^5.30.0
|
||||||
linkType: hard
|
"@typescript-eslint/parser": ^5.12.1
|
||||||
|
eslint-plugin-prettier: ^4.2.1
|
||||||
|
jest: ^27.5.1
|
||||||
|
reflect-metadata: ^0.1.13
|
||||||
|
ts-jest: ^27.1.3
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
"@standardnotes/settings@npm:^1.15.0":
|
"@standardnotes/settings@npm:^1.15.0":
|
||||||
version: 1.15.0
|
version: 1.15.0
|
||||||
@@ -7419,7 +7412,7 @@ __metadata:
|
|||||||
"@standardnotes/encryption": "workspace:*"
|
"@standardnotes/encryption": "workspace:*"
|
||||||
"@standardnotes/filepicker": "workspace:*"
|
"@standardnotes/filepicker": "workspace:*"
|
||||||
"@standardnotes/icons": "workspace:*"
|
"@standardnotes/icons": "workspace:*"
|
||||||
"@standardnotes/services": ^1.13.23
|
"@standardnotes/services": "workspace:*"
|
||||||
"@standardnotes/sncrypto-web": 1.10.1
|
"@standardnotes/sncrypto-web": 1.10.1
|
||||||
"@standardnotes/snjs": ^2.118.3
|
"@standardnotes/snjs": ^2.118.3
|
||||||
"@standardnotes/styles": "workspace:*"
|
"@standardnotes/styles": "workspace:*"
|
||||||
|
|||||||
Reference in New Issue
Block a user