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/files/dist
|
||||
packages/models/dist
|
||||
packages/services/dist
|
||||
|
||||
**/.pnp.*
|
||||
**/.yarn/*
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -41,7 +41,7 @@
|
||||
"@standardnotes/common": "^1.23.1",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "^1.6.39",
|
||||
"@standardnotes/services": "^1.13.23",
|
||||
"@standardnotes/services": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||
"@standardnotes/utils": "^1.6.12",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.23.1",
|
||||
"@standardnotes/services": "^1.13.23",
|
||||
"@standardnotes/services": "workspace:*",
|
||||
"@standardnotes/utils": "^1.6.12",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"@standardnotes/filepicker": "workspace:*",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "^1.6.39",
|
||||
"@standardnotes/services": "^1.13.23",
|
||||
"@standardnotes/services": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||
"@standardnotes/utils": "^1.6.12",
|
||||
"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/filepicker": "workspace:*",
|
||||
"@standardnotes/icons": "workspace:*",
|
||||
"@standardnotes/services": "^1.13.23",
|
||||
"@standardnotes/services": "workspace:*",
|
||||
"@standardnotes/sncrypto-web": "1.10.1",
|
||||
"@standardnotes/snjs": "^2.118.3",
|
||||
"@standardnotes/styles": "workspace:*",
|
||||
|
||||
41
yarn.lock
41
yarn.lock
@@ -6549,7 +6549,7 @@ __metadata:
|
||||
"@standardnotes/config": 2.4.3
|
||||
"@standardnotes/models": "workspace:*"
|
||||
"@standardnotes/responses": ^1.6.39
|
||||
"@standardnotes/services": ^1.13.23
|
||||
"@standardnotes/services": "workspace:*"
|
||||
"@standardnotes/sncrypto-common": ^1.9.0
|
||||
"@standardnotes/utils": ^1.6.12
|
||||
"@types/jest": ^27.4.1
|
||||
@@ -6604,7 +6604,7 @@ __metadata:
|
||||
resolution: "@standardnotes/filepicker@workspace:packages/filepicker"
|
||||
dependencies:
|
||||
"@standardnotes/common": ^1.23.1
|
||||
"@standardnotes/services": ^1.13.23
|
||||
"@standardnotes/services": "workspace:*"
|
||||
"@standardnotes/utils": ^1.6.12
|
||||
"@types/jest": ^27.4.1
|
||||
"@types/wicg-file-system-access": ^2020.9.5
|
||||
@@ -6626,7 +6626,7 @@ __metadata:
|
||||
"@standardnotes/filepicker": "workspace:*"
|
||||
"@standardnotes/models": "workspace:*"
|
||||
"@standardnotes/responses": ^1.6.39
|
||||
"@standardnotes/services": ^1.13.23
|
||||
"@standardnotes/services": "workspace:*"
|
||||
"@standardnotes/sncrypto-common": ^1.9.0
|
||||
"@standardnotes/utils": ^1.6.12
|
||||
"@types/jest": ^27.4.1
|
||||
@@ -7122,31 +7122,24 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standardnotes/services@npm:^1.13.22":
|
||||
version: 1.13.22
|
||||
resolution: "@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: 0.0.0-use.local
|
||||
resolution: "@standardnotes/services@workspace:packages/services"
|
||||
dependencies:
|
||||
"@standardnotes/auth": ^3.19.4
|
||||
"@standardnotes/common": ^1.23.1
|
||||
"@standardnotes/models": ^1.11.12
|
||||
"@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/models": "workspace:*"
|
||||
"@standardnotes/responses": ^1.6.39
|
||||
"@standardnotes/utils": ^1.6.12
|
||||
checksum: 7e67af13c4eb845c6bcbbac46897b94fe4754a728dba04605ccfbd96da49a0b305299fd3db9778a29488c5b29cf1ac4db08c28844e3ed95a2a872595376e1dc6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
"@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
|
||||
reflect-metadata: ^0.1.13
|
||||
ts-jest: ^27.1.3
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@standardnotes/settings@npm:^1.15.0":
|
||||
version: 1.15.0
|
||||
@@ -7419,7 +7412,7 @@ __metadata:
|
||||
"@standardnotes/encryption": "workspace:*"
|
||||
"@standardnotes/filepicker": "workspace:*"
|
||||
"@standardnotes/icons": "workspace:*"
|
||||
"@standardnotes/services": ^1.13.23
|
||||
"@standardnotes/services": "workspace:*"
|
||||
"@standardnotes/sncrypto-web": 1.10.1
|
||||
"@standardnotes/snjs": ^2.118.3
|
||||
"@standardnotes/styles": "workspace:*"
|
||||
|
||||
Reference in New Issue
Block a user