新增构建OpenSSL镜像相关文件

This commit is contained in:
2024-03-15 14:52:38 +08:00
committed by huty
parent 43337c1a0b
commit 132c17af2d
10119 changed files with 1581963 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
{
"extends": "../../.eslintrc",
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./linter.tsconfig.json"
}
}

View File

@@ -0,0 +1,418 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.41.0...@standardnotes/domain-core@1.41.1) (2023-11-27)
### Bug Fixes
* repository config in package.json files ([ed1bf37](https://github.com/standardnotes/server/commit/ed1bf37287af23a25b8388ada95f0acdec8f71ea))
# [1.41.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.40.0...@standardnotes/domain-core@1.41.0) (2023-11-27)
### Features
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
# [1.40.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.39.0...@standardnotes/domain-core@1.40.0) (2023-10-26)
### Features
* extract setting name to domain-core package ([0e43bc0](https://github.com/standardnotes/server/commit/0e43bc00427113f421b0c4b67c067f0de96caf52))
# [1.39.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.38.0...@standardnotes/domain-core@1.39.0) (2023-10-26)
### Features
* refactor settings ([#890](https://github.com/standardnotes/server/issues/890)) ([1b5078e](https://github.com/standardnotes/server/commit/1b5078eb9629397822f5403643c60fbf4182df92))
# [1.38.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.37.0...@standardnotes/domain-core@1.38.0) (2023-10-19)
### Features
* remove transition state ([#882](https://github.com/standardnotes/server/issues/882)) ([a2c1ebe](https://github.com/standardnotes/server/commit/a2c1ebe675cd5678c923715056a6966f465a15d6))
# [1.37.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.36.0...@standardnotes/domain-core@1.37.0) (2023-10-11)
### Features
* **domain-core:** add email scheduled task service identifier ([af5ebb2](https://github.com/standardnotes/server/commit/af5ebb25e7fd05390089e0f7c00af50648ad49d9))
# [1.36.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.35.0...@standardnotes/domain-core@1.36.0) (2023-10-11)
### Features
* add opentelemetry for scheduled tasks ([443235a](https://github.com/standardnotes/server/commit/443235a861181acf708d98fba25ce6d79f198b56))
* **domain-core:** add email bounce processor service identifier ([6356fca](https://github.com/standardnotes/server/commit/6356fcaeed012e7ea5d174b47aaebb94a4040d29))
# [1.35.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.34.2...@standardnotes/domain-core@1.35.0) (2023-10-06)
### Features
* add xray to analytics scheduler and websockets ([f0531d6](https://github.com/standardnotes/server/commit/f0531d68cb77036222f2a34602819f11e6a2697d))
## [1.34.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.34.1...@standardnotes/domain-core@1.34.2) (2023-10-04)
### Bug Fixes
* identifying services in workers ([eab78b3](https://github.com/standardnotes/server/commit/eab78b3a95ec82cb779d069d172169bfa92368c9))
## [1.34.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.34.0...@standardnotes/domain-core@1.34.1) (2023-09-27)
### Bug Fixes
* removing items in a vault and notifying about designated survivor ([#855](https://github.com/standardnotes/server/issues/855)) ([1d06ffe](https://github.com/standardnotes/server/commit/1d06ffe9d51722ada7baa040e1d5ed351fc28f39))
# [1.34.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.2...@standardnotes/domain-core@1.34.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.33.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.1...@standardnotes/domain-core@1.33.2) (2023-09-25)
### Bug Fixes
* **domain-core:** notification paylod to string casting ([a02a287](https://github.com/standardnotes/server/commit/a02a28774b6d500200043faefb9ebac3719e7661))
## [1.33.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.0...@standardnotes/domain-core@1.33.1) (2023-09-25)
### Bug Fixes
* refactor the structure of notifications ([#853](https://github.com/standardnotes/server/issues/853)) ([cebab59](https://github.com/standardnotes/server/commit/cebab59a026c6868886e0945787a8ddb0442fbc3))
# [1.33.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.32.0...@standardnotes/domain-core@1.33.0) (2023-09-21)
### Features
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/server/issues/841)) ([230c96d](https://github.com/standardnotes/server/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
# [1.32.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.31.0...@standardnotes/domain-core@1.32.0) (2023-09-20)
### Features
* **syncing-server:** distinct notifications upon user removal from shared vault ([#840](https://github.com/standardnotes/server/issues/840)) ([41e2136](https://github.com/standardnotes/server/commit/41e2136bc07312974701a70652528d304105e0f9))
# [1.31.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.30.1...@standardnotes/domain-core@1.31.0) (2023-09-20)
### Features
* **syncing-server:** add notification for user upon declined shared vault invitation ([#837](https://github.com/standardnotes/server/issues/837)) ([31e7aaf](https://github.com/standardnotes/server/commit/31e7aaf253029a951d8b943d6cffd655cd5ca765))
## [1.30.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.30.0...@standardnotes/domain-core@1.30.1) (2023-09-19)
### Bug Fixes
* **domain-core:** allow any version and variant of the UUID format ([de081fe](https://github.com/standardnotes/server/commit/de081fe78658bdd36c8c5d86b70a16a2880aa3d1))
# [1.30.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.29.0...@standardnotes/domain-core@1.30.0) (2023-09-18)
### Features
* add publishing notifications for users when a user is added to vault ([#834](https://github.com/standardnotes/server/issues/834)) ([547a79e](https://github.com/standardnotes/server/commit/547a79e23174dab0a756e4e5bee218e4859b3b42))
# [1.29.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.28.1...@standardnotes/domain-core@1.29.0) (2023-09-15)
### Features
* refactor transition to minimize status changes ([#828](https://github.com/standardnotes/server/issues/828)) ([36f07c6](https://github.com/standardnotes/server/commit/36f07c691afc213ecf817d6e98f885ddb19a6ed6))
## [1.28.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.28.0...@standardnotes/domain-core@1.28.1) (2023-09-12)
### Bug Fixes
* comparing uuids ([0a1d162](https://github.com/standardnotes/server/commit/0a1d1624e818000f2e951f29323a88e6e233c755))
# [1.28.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.27.0...@standardnotes/domain-core@1.28.0) (2023-09-07)
### Features
* add VaultsUser role name ([#809](https://github.com/standardnotes/server/issues/809)) ([d4830de](https://github.com/standardnotes/server/commit/d4830dec018139ffaf93d0e91d6655c5dcc6be9c))
# [1.27.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.26.3...@standardnotes/domain-core@1.27.0) (2023-09-06)
### Features
* should be able to access shared item revisions as third party user ([#807](https://github.com/standardnotes/server/issues/807)) ([794cd87](https://github.com/standardnotes/server/commit/794cd8734acf89fb29f09dfb169a3f08f252bb6a))
## [1.26.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.26.2...@standardnotes/domain-core@1.26.3) (2023-09-04)
**Note:** Version bump only for package @standardnotes/domain-core
## [1.26.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.26.1...@standardnotes/domain-core@1.26.2) (2023-09-01)
**Note:** Version bump only for package @standardnotes/domain-core
## [1.26.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.26.0...@standardnotes/domain-core@1.26.1) (2023-08-30)
**Note:** Version bump only for package @standardnotes/domain-core
# [1.26.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.25.2...@standardnotes/domain-core@1.26.0) (2023-08-18)
### Features
* add mechanism for determining if a user should use the primary or secondary items database ([#700](https://github.com/standardnotes/server/issues/700)) ([302b624](https://github.com/standardnotes/server/commit/302b624504f4c87fd7c3ddfee77cbdc14a61018b))
## [1.25.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.25.1...@standardnotes/domain-core@1.25.2) (2023-08-09)
### Reverts
* Revert "Revert "feat(syncing-server): notify shared vault users upon file uploads or removals (#692)"" ([1c3ff52](https://github.com/standardnotes/server/commit/1c3ff526b7c4885f71f019f6c01142f522a6f8ad)), closes [#692](https://github.com/standardnotes/server/issues/692)
## [1.25.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.25.0...@standardnotes/domain-core@1.25.1) (2023-08-09)
### Reverts
* Revert "feat(syncing-server): notify shared vault users upon file uploads or removals (#692)" ([d261c81](https://github.com/standardnotes/server/commit/d261c81cd0bdbb9001c8589224f007ed2d338903)), closes [#692](https://github.com/standardnotes/server/issues/692)
# [1.25.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.24.2...@standardnotes/domain-core@1.25.0) (2023-08-09)
### Features
* **syncing-server:** notify shared vault users upon file uploads or removals ([#692](https://github.com/standardnotes/server/issues/692)) ([46867c1](https://github.com/standardnotes/server/commit/46867c1a4dd310c1971ff37e1bdf380c10e478fd))
## [1.24.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.24.1...@standardnotes/domain-core@1.24.2) (2023-08-02)
### Bug Fixes
* **domain-core:** remove unused content types ([71624f1](https://github.com/standardnotes/server/commit/71624f18979ed9254fafeeced733e598cd66cbeb))
## [1.24.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.24.0...@standardnotes/domain-core@1.24.1) (2023-07-27)
### Bug Fixes
* setting env vars on home server in e2e environment ([f87036e](https://github.com/standardnotes/server/commit/f87036e3a8dc6b7784e74e5f32ffd220033724f5))
# [1.24.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.4...@standardnotes/domain-core@1.24.0) (2023-07-26)
### Features
* extract shared vault user permission to domain-core ([e215ac4](https://github.com/standardnotes/server/commit/e215ac4343e9f8818f40004d31390d6ac23e369d))
## [1.23.4](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.3...@standardnotes/domain-core@1.23.4) (2023-07-26)
### Bug Fixes
* **syncing-server:** persisting aggregate changes from root ([#674](https://github.com/standardnotes/server/issues/674)) ([c34f548](https://github.com/standardnotes/server/commit/c34f548e45bbd8defb8d490936e90755fd284e78))
## [1.23.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.2...@standardnotes/domain-core@1.23.3) (2023-07-21)
### Bug Fixes
* **domain-core:** notification payload creation from string ([1708c3f](https://github.com/standardnotes/server/commit/1708c3f8a00897369585e1ef8022950a7c3c610e))
## [1.23.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.1...@standardnotes/domain-core@1.23.2) (2023-07-21)
### Bug Fixes
* user notifications structure ([#667](https://github.com/standardnotes/server/issues/667)) ([1bbb639](https://github.com/standardnotes/server/commit/1bbb639c83922ec09e3778f85419d76669d36ae3))
## [1.23.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.0...@standardnotes/domain-core@1.23.1) (2023-07-19)
### Bug Fixes
* **syncing-server:** add missing messages and key system identifier sql representations ([#663](https://github.com/standardnotes/server/issues/663)) ([d026152](https://github.com/standardnotes/server/commit/d026152ac8cb2ecda2eee8d3f7385d655b210938))
# [1.23.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.22.0...@standardnotes/domain-core@1.23.0) (2023-07-17)
### Features
* **syncing-server:** refactor syncing to decouple getting and saving items ([#659](https://github.com/standardnotes/server/issues/659)) ([cb74b23](https://github.com/standardnotes/server/commit/cb74b23e45b207136e299ce8a3db2c04dc87e21e))
# [1.22.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.21.1...@standardnotes/domain-core@1.22.0) (2023-07-12)
### Features
* domain items ([#655](https://github.com/standardnotes/server/issues/655)) ([a0af8f0](https://github.com/standardnotes/server/commit/a0af8f00252e1219e58cb7e066c11a8e71692e9d))
## [1.21.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.21.0...@standardnotes/domain-core@1.21.1) (2023-07-07)
### Bug Fixes
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/server/issues/648)) ([c288e5d](https://github.com/standardnotes/server/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.20.0...@standardnotes/domain-core@1.21.0) (2023-07-06)
### Features
* getting shared vault users and removing shared vault user ([#642](https://github.com/standardnotes/server/issues/642)) ([e905128](https://github.com/standardnotes/server/commit/e905128d45eaadb34d3465d4480dfb3a2c5f3f79))
# [1.20.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.19.0...@standardnotes/domain-core@1.20.0) (2023-07-05)
### Features
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
# [1.19.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.18.0...@standardnotes/domain-core@1.19.0) (2023-06-30)
### Features
* add shared vaults model. ([#631](https://github.com/standardnotes/server/issues/631)) ([38e77f0](https://github.com/standardnotes/server/commit/38e77f04be441b7506c3390fb0d9894b34119c3e))
# [1.18.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.17.0...@standardnotes/domain-core@1.18.0) (2023-06-02)
### Features
* **home-server:** add overriding environment variables in underlying services ([#621](https://github.com/standardnotes/server/issues/621)) ([f0cbec0](https://github.com/standardnotes/server/commit/f0cbec07b87d60dfad92072944553f76e0bea164))
# [1.17.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.16.2...@standardnotes/domain-core@1.17.0) (2023-05-31)
### Features
* **home-server:** add custom home server logs ([#619](https://github.com/standardnotes/server/issues/619)) ([bc63d0a](https://github.com/standardnotes/server/commit/bc63d0aeea86abbb4a144b2682b7070d7bdfe878))
## [1.16.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.16.0...@standardnotes/domain-core@1.16.2) (2023-05-30)
### Bug Fixes
* bump version manually to publish packages ([b0d01df](https://github.com/standardnotes/server/commit/b0d01dffd91557c67eac2940d9270bca208c1128))
# [1.16.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.15.0...@standardnotes/domain-core@1.16.0) (2023-05-29)
### Features
* add files server as a service to home-server ([#614](https://github.com/standardnotes/server/issues/614)) ([c7d575a](https://github.com/standardnotes/server/commit/c7d575a0ffc7eb3e8799c3835da5727584f4f67b))
# [1.15.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.14.2...@standardnotes/domain-core@1.15.0) (2023-05-16)
### Features
* home-server package initial setup with Api Gateway and Auth services ([#605](https://github.com/standardnotes/server/issues/605)) ([dc71e67](https://github.com/standardnotes/server/commit/dc71e6777fc4c51234b79f6fb409f9f3111cc6a5))
## [1.14.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.14.1...@standardnotes/domain-core@1.14.2) (2023-05-09)
### Bug Fixes
* node engine version requirement in package.json files ([62a0e89](https://github.com/standardnotes/server/commit/62a0e89748ab306566c4aa10b9dc0385fb0f1684))
## [1.14.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.14.0...@standardnotes/domain-core@1.14.1) (2023-05-05)
**Note:** Version bump only for package @standardnotes/domain-core
# [1.14.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.13.0...@standardnotes/domain-core@1.14.0) (2023-05-02)
### Features
* extract cache entry model to domain-core ([#581](https://github.com/standardnotes/server/issues/581)) ([c71f7ff](https://github.com/standardnotes/server/commit/c71f7ff8ad4ffbd7151e8397b5816e383b178eb4))
# [1.13.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.12.0...@standardnotes/domain-core@1.13.0) (2023-04-27)
### Features
* sqlite driver for auth service ([#572](https://github.com/standardnotes/server/issues/572)) ([3aef599](https://github.com/standardnotes/server/commit/3aef5998df2b4cf96c597ffa11a47dfc250d1647))
# [1.12.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.11.3...@standardnotes/domain-core@1.12.0) (2023-03-08)
### Features
* **domain-core:** add internal team user role ([#473](https://github.com/standardnotes/server/issues/473)) ([979a320](https://github.com/standardnotes/server/commit/979a320ca666991ad2b023436f58c59ae168c768))
## [1.11.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.11.2...@standardnotes/domain-core@1.11.3) (2023-02-15)
### Bug Fixes
* **domain-core:** remove unnecessary dependencies ([16043a7](https://github.com/standardnotes/server/commit/16043a7d6881378ed3286e08dc9e21e5e6b89171))
## [1.11.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.11.1...@standardnotes/domain-core@1.11.2) (2023-01-20)
**Note:** Version bump only for package @standardnotes/domain-core
## [1.11.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.11.0...@standardnotes/domain-core@1.11.1) (2023-01-16)
### Bug Fixes
* **revisions:** add required role to revisions list response ([e7beee2](https://github.com/standardnotes/server/commit/e7beee278871d2939b058d842404fd6980d7f48a))
# [1.11.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.10.0...@standardnotes/domain-core@1.11.0) (2022-12-15)
### Features
* **domain-core:** add legacy session model ([4084f2f](https://github.com/standardnotes/server/commit/4084f2f5ecf8379ff69d619d3d12495c010c3980))
# [1.10.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.9.0...@standardnotes/domain-core@1.10.0) (2022-12-15)
### Features
* **domain-core:** add session model ([1c4d4c5](https://github.com/standardnotes/server/commit/1c4d4c57dea1187dc130f1ae8b7dc8ede332320f))
# [1.9.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.8.0...@standardnotes/domain-core@1.9.0) (2022-12-07)
### Features
* **domain-core:** rename email subscription rejection level to email level ([c87561f](https://github.com/standardnotes/server/commit/c87561fca782883b84f58b4f0b9f85ecc279ca50))
# [1.8.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.7.0...@standardnotes/domain-core@1.8.0) (2022-12-05)
### Features
* **domain-core:** add email subscription rejection levels ([02f3c85](https://github.com/standardnotes/server/commit/02f3c85796ade7cb69edbdda2367c0d91ac1bdf0))
# [1.7.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.6.0...@standardnotes/domain-core@1.7.0) (2022-12-05)
### Features
* **domain-core:** distinguish between username and email ([06fd404](https://github.com/standardnotes/server/commit/06fd404d44b44a53733f889aabd4da63f21e2f36))
# [1.6.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.5.2...@standardnotes/domain-core@1.6.0) (2022-12-02)
### Features
* **domain-core:** add subscription plan name value object ([800fe9e](https://github.com/standardnotes/server/commit/800fe9e4c80c33f2da8097b5a153f470a23b7984))
## [1.5.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.5.1...@standardnotes/domain-core@1.5.2) (2022-12-02)
### Bug Fixes
* **domain-core:** rename timestamps to dates ([dd86c5b](https://github.com/standardnotes/server/commit/dd86c5bcdf3a1a37d684f6416d4cc6f24497fe5e))
## [1.5.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.5.0...@standardnotes/domain-core@1.5.1) (2022-11-25)
**Note:** Version bump only for package @standardnotes/domain-core
# [1.5.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.4.0...@standardnotes/domain-core@1.5.0) (2022-11-24)
### Features
* **domain-core:** add methods to check role power ([9d90f27](https://github.com/standardnotes/server/commit/9d90f276de8915d91d009909154036ba128687e0))
# [1.4.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.3.0...@standardnotes/domain-core@1.4.0) (2022-11-24)
### Features
* **domain-core:** add role name collection value object ([ae2f8f0](https://github.com/standardnotes/server/commit/ae2f8f086b9f647bb98c59f32375b45243cb0af9))
# [1.3.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.2.2...@standardnotes/domain-core@1.3.0) (2022-11-24)
### Features
* **domain-core:** add role name value object ([748630e](https://github.com/standardnotes/server/commit/748630e1f1ed1dfae2e743cd2b3d3fd91967088c))
## [1.2.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.2.1...@standardnotes/domain-core@1.2.2) (2022-11-22)
**Note:** Version bump only for package @standardnotes/domain-core
## [1.2.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.2.0...@standardnotes/domain-core@1.2.1) (2022-11-21)
### Bug Fixes
* **domain-core:** remove revisions related models to revisions microservice ([a6542dd](https://github.com/standardnotes/server/commit/a6542dd63870a8ada5fd8143d8e2133a570d9329))
# [1.2.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.1.1...@standardnotes/domain-core@1.2.0) (2022-11-18)
### Features
* **domain-core:** add revision definition to domain core ([c8f3a0c](https://github.com/standardnotes/server/commit/c8f3a0ce7b589a6fbc47941fc5d1a44b6cf04fe3))
* **revisions:** add revisions microservice ([d5c06bf](https://github.com/standardnotes/server/commit/d5c06bfa58a987685fbd8fbab0d22df3fcff3377))
## [1.1.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.1.0...@standardnotes/domain-core@1.1.1) (2022-11-14)
### Bug Fixes
* **syncing-server:** retrieving revisions ([50f7ae3](https://github.com/standardnotes/server/commit/50f7ae338ad66d3465fa16c31e7c47c57b1e0c3c))
# 1.1.0 (2022-11-14)
### Features
* **analytics:** extract domain core into a separate package ([0f94e2a](https://github.com/standardnotes/server/commit/0f94e2ad0c8927733eac31f130cbe649dce765f9))

View File

@@ -0,0 +1,10 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const base = require('../../jest.config')
const { defaults: tsjPreset } = require('ts-jest/presets')
module.exports = {
...base,
transform: {
...tsjPreset.transform,
},
}

View File

@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["dist"]
}

View File

@@ -0,0 +1,48 @@
{
"name": "@standardnotes/domain-core",
"version": "1.41.1",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
"description": "Domain Core SDK used in SN projects",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"files": [
"dist/src/**/*.js",
"dist/src/**/*.d.ts"
],
"publishConfig": {
"access": "public",
"provenance": true
},
"repository": {
"type": "git",
"url": "git@github.com:standardnotes/server.git",
"directory": "packages/domain-core"
},
"author": "Standard Notes",
"license": "AGPL-3.0-or-later",
"scripts": {
"clean": "rm -fr dist",
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"test": "jest --coverage --no-cache --passWithNoTests"
},
"dependencies": {
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/jest": "^29.5.1",
"@types/node": "^20.5.7",
"@types/uuid": "^9.0.3",
"@typescript-eslint/eslint-plugin": "^6.5.0",
"@typescript-eslint/parser": "^6.5.0",
"eslint": "^8.39.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"prettier": "^3.0.3",
"ts-jest": "^29.1.0",
"typescript": "^5.0.4"
}
}

View File

@@ -0,0 +1,16 @@
import { LegacySession } from './LegacySession'
describe('LegacySession', () => {
it('should create a value object', () => {
const valueOrError = LegacySession.create('foobar')
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().accessToken).toEqual('foobar')
})
it('should not create an invalid value object', () => {
const valueOrError = LegacySession.create('')
expect(valueOrError.isFailed()).toBeTruthy()
})
})

View File

@@ -0,0 +1,22 @@
import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result'
import { LegacySessionProps } from './LegacySessionProps'
import { Validator } from '../Core/Validator'
export class LegacySession extends ValueObject<LegacySessionProps> {
get accessToken(): string {
return this.props.token
}
private constructor(props: LegacySessionProps) {
super(props)
}
static create(token: string): Result<LegacySession> {
if (Validator.isNotEmpty(token).isFailed()) {
return Result.fail<LegacySession>('Could not create legacy session. Token value is empty')
}
return Result.ok<LegacySession>(new LegacySession({ token }))
}
}

View File

@@ -0,0 +1,3 @@
export interface LegacySessionProps {
token: string
}

View File

@@ -0,0 +1,26 @@
import { Session } from './Session'
import { SessionToken } from './SessionToken'
describe('Session', () => {
it('should create a session value object', () => {
const accessToken = SessionToken.create('foobar1', 1234567890).getValue()
const refreshToken = SessionToken.create('foobar2', 1234567890).getValue()
const valueOrError = Session.create(accessToken, refreshToken)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().accessToken.value).toEqual('foobar1')
expect(valueOrError.getValue().refreshToken.value).toEqual('foobar2')
expect(valueOrError.getValue().isReadOnly()).toEqual(false)
})
it('should create a session reado-only value object', () => {
const accessToken = SessionToken.create('foobar', 1234567890).getValue()
const refreshToken = SessionToken.create('foobar', 1234567890).getValue()
const valueOrError = Session.create(accessToken, refreshToken, true)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().isReadOnly()).toEqual(true)
})
})

View File

@@ -0,0 +1,26 @@
import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result'
import { SessionProps } from './SessionProps'
import { SessionToken } from './SessionToken'
export class Session extends ValueObject<SessionProps> {
get accessToken(): SessionToken {
return this.props.accessToken
}
get refreshToken(): SessionToken {
return this.props.refreshToken
}
isReadOnly(): boolean {
return this.props.readonlyAccess || false
}
private constructor(props: SessionProps) {
super(props)
}
static create(accessToken: SessionToken, refreshToken: SessionToken, readonlyAccess?: boolean): Result<Session> {
return Result.ok<Session>(new Session({ accessToken, refreshToken, readonlyAccess }))
}
}

View File

@@ -0,0 +1,7 @@
import { SessionToken } from './SessionToken'
export interface SessionProps {
accessToken: SessionToken
refreshToken: SessionToken
readonlyAccess?: boolean
}

View File

@@ -0,0 +1,21 @@
import { SessionToken } from './SessionToken'
describe('SessionToken', () => {
it('should create a value object', () => {
const valueOrError = SessionToken.create('foobar', 1234567890)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('foobar')
expect(valueOrError.getValue().expiresAt).toEqual(1234567890)
})
it('should not create an invalid value object', () => {
let valueOrError = SessionToken.create('', 1234567890)
expect(valueOrError.isFailed()).toBeTruthy()
valueOrError = SessionToken.create('foobar', undefined as unknown as number)
expect(valueOrError.isFailed()).toBeTruthy()
})
})

View File

@@ -0,0 +1,29 @@
import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result'
import { SessionTokenProps } from './SessionTokenProps'
import { Validator } from '../Core/Validator'
export class SessionToken extends ValueObject<SessionTokenProps> {
get value(): string {
return this.props.value
}
get expiresAt(): number {
return this.props.expiresAt
}
private constructor(props: SessionTokenProps) {
super(props)
}
static create(value: string, expiresAt: number): Result<SessionToken> {
if (Validator.isNotEmpty(value).isFailed()) {
return Result.fail<SessionToken>('Could not create session token. Token value is empty')
}
if (Validator.isNotEmpty(expiresAt).isFailed()) {
return Result.fail<SessionToken>('Could not create session token. Token expiration is empty')
}
return Result.ok<SessionToken>(new SessionToken({ value, expiresAt }))
}
}

View File

@@ -0,0 +1,4 @@
export interface SessionTokenProps {
value: string
expiresAt: number
}

View File

@@ -0,0 +1,14 @@
import { Entity } from '../Core/Entity'
import { Result } from '../Core/Result'
import { UniqueEntityId } from '../Core/UniqueEntityId'
import { CacheEntryProps } from './CacheEntryProps'
export class CacheEntry extends Entity<CacheEntryProps> {
private constructor(props: CacheEntryProps, id?: UniqueEntityId) {
super(props, id)
}
static create(props: CacheEntryProps, id?: UniqueEntityId): Result<CacheEntry> {
return Result.ok<CacheEntry>(new CacheEntry(props, id))
}
}

View File

@@ -0,0 +1,5 @@
export interface CacheEntryProps {
key: string
value: string
expiresAt: Date | null
}

View File

@@ -0,0 +1,7 @@
import { CacheEntry } from './CacheEntry'
export interface CacheEntryRepositoryInterface {
save(cacheEntry: CacheEntry): Promise<void>
findUnexpiredOneByKey(key: string): Promise<CacheEntry | null>
removeByKey(key: string): Promise<void>
}

View File

@@ -0,0 +1,39 @@
import { ContentType } from './ContentType'
describe('ContentType', () => {
it('should create a value object', () => {
const valueOrError = ContentType.create(ContentType.TYPES.Component)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('SN|Component')
})
it('should not create an invalid value object', () => {
for (const value of ['', undefined, 0, 'FOOBAR']) {
const valueOrError = ContentType.create(value as string)
expect(valueOrError.isFailed()).toBeTruthy()
}
})
it('should return a display name', () => {
const valueOrError = ContentType.create(ContentType.TYPES.FilesafeFileMetadata)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().getDisplayName()).toEqual('FileSafe file')
})
it('should return null for a display name if the value is null', () => {
const valueOrError = ContentType.create(null)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().getDisplayName()).toBeNull()
})
it('should fallback to the value if the display name is not found', () => {
const valueOrError = ContentType.create(ContentType.TYPES.EncryptedStorage)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().getDisplayName()).toEqual('SN|EncryptedStorage')
})
})

View File

@@ -0,0 +1,77 @@
import { Result } from '../Core/Result'
import { ValueObject } from '../Core/ValueObject'
import { ContentTypeProps } from './ContentTypeProps'
export class ContentType extends ValueObject<ContentTypeProps> {
static readonly TYPES = {
Any: '*',
Item: 'SF|Item',
KeySystemItemsKey: 'SN|KeySystemItemsKey',
KeySystemRootKey: 'SN|KeySystemRootKey',
TrustedContact: 'SN|TrustedContact',
VaultListing: 'SN|VaultListing',
RootKey: 'SN|RootKey|NoSync',
ItemsKey: 'SN|ItemsKey',
EncryptedStorage: 'SN|EncryptedStorage',
Note: 'Note',
Tag: 'Tag',
SmartView: 'SN|SmartTag',
Component: 'SN|Component',
Editor: 'SN|Editor',
ActionsExtension: 'Extension',
UserPrefs: 'SN|UserPreferences',
HistorySession: 'SN|HistorySession',
Theme: 'SN|Theme',
File: 'SN|File',
FilesafeCredentials: 'SN|FileSafe|Credentials',
FilesafeFileMetadata: 'SN|FileSafe|FileMetadata',
FilesafeIntegration: 'SN|FileSafe|Integration',
ExtensionRepo: 'SN|ExtensionRepo',
}
private readonly displayNamesMap: Partial<Record<string, string>> = {
[ContentType.TYPES.ActionsExtension]: 'action-based extension',
[ContentType.TYPES.Component]: 'component',
[ContentType.TYPES.Editor]: 'editor',
[ContentType.TYPES.File]: 'file',
[ContentType.TYPES.FilesafeCredentials]: 'FileSafe credential',
[ContentType.TYPES.FilesafeFileMetadata]: 'FileSafe file',
[ContentType.TYPES.FilesafeIntegration]: 'FileSafe integration',
[ContentType.TYPES.ItemsKey]: 'encryption key',
[ContentType.TYPES.Note]: 'note',
[ContentType.TYPES.SmartView]: 'smart view',
[ContentType.TYPES.Tag]: 'tag',
[ContentType.TYPES.Theme]: 'theme',
[ContentType.TYPES.UserPrefs]: 'user preferences',
}
get value(): string | null {
return this.props.value
}
private constructor(props: ContentTypeProps) {
super(props)
}
static create(type: string | null): Result<ContentType> {
if (type === null) {
return Result.ok<ContentType>(new ContentType({ value: null }))
}
const isValidType = Object.values(this.TYPES).includes(type)
if (!isValidType) {
return Result.fail<ContentType>(`Invalid content type: ${type}`)
} else {
return Result.ok<ContentType>(new ContentType({ value: type }))
}
}
getDisplayName(): string | null {
if (!this.value) {
return null
}
return this.displayNamesMap[this.value] || this.value
}
}

View File

@@ -0,0 +1,3 @@
export interface ContentTypeProps {
value: string | null
}

View File

@@ -0,0 +1,21 @@
import { Dates } from './Dates'
describe('Dates', () => {
it('should create a value object', () => {
const valueOrError = Dates.create(new Date(1), new Date(2))
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().createdAt).toEqual(new Date(1))
expect(valueOrError.getValue().updatedAt).toEqual(new Date(2))
})
it('should not create an invalid value object', () => {
let valueOrError = Dates.create(null as unknown as Date, '2' as unknown as Date)
expect(valueOrError.isFailed()).toBeTruthy()
valueOrError = Dates.create(new Date(2), '2' as unknown as Date)
expect(valueOrError.isFailed()).toBeTruthy()
})
})

View File

@@ -0,0 +1,28 @@
import { Result } from '../Core/Result'
import { ValueObject } from '../Core/ValueObject'
import { DatesProps } from './DatesProps'
export class Dates extends ValueObject<DatesProps> {
get createdAt(): Date {
return this.props.createdAt
}
get updatedAt(): Date {
return this.props.updatedAt
}
private constructor(props: DatesProps) {
super(props)
}
static create(createdAt: Date, updatedAt: Date): Result<Dates> {
if (!(createdAt instanceof Date)) {
return Result.fail<Dates>(`Could not create Dates. Creation date should be a date object, given: ${createdAt}`)
}
if (!(updatedAt instanceof Date)) {
return Result.fail<Dates>(`Could not create Dates. Update date should be a date object, given: ${updatedAt}`)
}
return Result.ok<Dates>(new Dates({ createdAt, updatedAt }))
}
}

View File

@@ -0,0 +1,4 @@
export interface DatesProps {
createdAt: Date
updatedAt: Date
}

View File

@@ -0,0 +1,22 @@
import { Email } from './Email'
describe('Email', () => {
it('should create a value object', () => {
const valueOrError = Email.create('test@test.te')
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('test@test.te')
})
it('should not create an invalid value object', () => {
const valueOrError = Email.create('foobar')
expect(valueOrError.isFailed()).toBeTruthy()
})
it('should not create an invalid type object', () => {
const valueOrError = Email.create(undefined as unknown as string)
expect(valueOrError.isFailed()).toBeTruthy()
})
})

View File

@@ -0,0 +1,29 @@
import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result'
import { EmailProps } from './EmailProps'
import { Validator } from '../Core/Validator'
export class Email extends ValueObject<EmailProps> {
get value(): string {
return this.props.value
}
private constructor(props: EmailProps) {
super(props)
}
static create(email: string): Result<Email> {
if (Validator.isString(email).isFailed()) {
return Result.fail<Email>('Email must be a string')
}
const trimmedAndLowerCasedEmail = email.trim().toLowerCase()
const emailValidation = Validator.isValidEmail(trimmedAndLowerCasedEmail)
if (emailValidation.isFailed()) {
return Result.fail<Email>(emailValidation.getError())
}
return Result.ok<Email>(new Email({ value: trimmedAndLowerCasedEmail }))
}
}

View File

@@ -0,0 +1,3 @@
export interface EmailProps {
value: string
}

View File

@@ -0,0 +1,45 @@
import { RoleName } from './RoleName'
describe('RoleName', () => {
it('should create a value object', () => {
const valueOrError = RoleName.create(RoleName.NAMES.ProUser)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('PRO_USER')
})
it('should not create an invalid value object', () => {
for (const value of ['', undefined, null, 0, 'SOME_USER']) {
const valueOrError = RoleName.create(value as string)
expect(valueOrError.isFailed()).toBeTruthy()
}
})
it('should say if a role has more power or equal power to another role', () => {
const proUserRole = RoleName.create(RoleName.NAMES.ProUser).getValue()
const plusUserRole = RoleName.create(RoleName.NAMES.PlusUser).getValue()
const coreUser = RoleName.create(RoleName.NAMES.CoreUser).getValue()
const internalTeamUser = RoleName.create(RoleName.NAMES.InternalTeamUser).getValue()
expect(internalTeamUser.hasMoreOrEqualPowerTo(proUserRole)).toBeTruthy()
expect(internalTeamUser.hasMoreOrEqualPowerTo(proUserRole)).toBeTruthy()
expect(internalTeamUser.hasMoreOrEqualPowerTo(plusUserRole)).toBeTruthy()
expect(internalTeamUser.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
expect(proUserRole.hasMoreOrEqualPowerTo(internalTeamUser)).toBeFalsy()
expect(proUserRole.hasMoreOrEqualPowerTo(proUserRole)).toBeTruthy()
expect(proUserRole.hasMoreOrEqualPowerTo(plusUserRole)).toBeTruthy()
expect(proUserRole.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
expect(plusUserRole.hasMoreOrEqualPowerTo(internalTeamUser)).toBeFalsy()
expect(plusUserRole.hasMoreOrEqualPowerTo(proUserRole)).toBeFalsy()
expect(plusUserRole.hasMoreOrEqualPowerTo(plusUserRole)).toBeTruthy()
expect(plusUserRole.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
expect(coreUser.hasMoreOrEqualPowerTo(internalTeamUser)).toBeFalsy()
expect(coreUser.hasMoreOrEqualPowerTo(proUserRole)).toBeFalsy()
expect(coreUser.hasMoreOrEqualPowerTo(plusUserRole)).toBeFalsy()
expect(coreUser.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
})
})

View File

@@ -0,0 +1,47 @@
import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result'
import { RoleNameProps } from './RoleNameProps'
export class RoleName extends ValueObject<RoleNameProps> {
static readonly NAMES = {
CoreUser: 'CORE_USER',
PlusUser: 'PLUS_USER',
ProUser: 'PRO_USER',
InternalTeamUser: 'INTERNAL_TEAM_USER',
VaultsUser: 'VAULTS_USER',
}
get value(): string {
return this.props.value
}
hasMoreOrEqualPowerTo(roleName: RoleName): boolean {
switch (this.value) {
case RoleName.NAMES.InternalTeamUser:
return true
case RoleName.NAMES.ProUser:
return [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser].includes(roleName.value)
case RoleName.NAMES.PlusUser:
return [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser].includes(roleName.value)
case RoleName.NAMES.CoreUser:
case RoleName.NAMES.VaultsUser:
return [RoleName.NAMES.CoreUser].includes(roleName.value)
/*istanbul ignore next*/
default:
throw new Error(`Invalid role name: ${this.value}`)
}
}
private constructor(props: RoleNameProps) {
super(props)
}
static create(name: string): Result<RoleName> {
const isValidName = Object.values(this.NAMES).includes(name)
if (!isValidName) {
return Result.fail<RoleName>(`Invalid role name: ${name}`)
} else {
return Result.ok<RoleName>(new RoleName({ value: name }))
}
}
}

View File

@@ -0,0 +1,111 @@
import { RoleName } from './RoleName'
import { RoleNameCollection } from './RoleNameCollection'
describe('RoleNameCollection', () => {
it('should create a value object', () => {
const valueOrError = RoleNameCollection.create([RoleName.NAMES.ProUser])
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value[0].value).toEqual('PRO_USER')
})
it('should tell if collections are not equal', () => {
const roles1 = [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]
let roles2 = RoleNameCollection.create([RoleName.NAMES.ProUser, RoleName.NAMES.CoreUser]).getValue()
let valueOrError = RoleNameCollection.create(roles1)
expect(valueOrError.getValue().equals(roles2)).toBeFalsy()
roles2 = RoleNameCollection.create([
RoleName.NAMES.ProUser,
RoleName.NAMES.PlusUser,
RoleName.NAMES.CoreUser,
]).getValue()
valueOrError = RoleNameCollection.create(roles1)
expect(valueOrError.getValue().equals(roles2)).toBeFalsy()
})
it('should tell if collections are equal', () => {
const roles1 = [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]
const roles2 = RoleNameCollection.create([RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]).getValue()
const valueOrError = RoleNameCollection.create(roles1)
expect(valueOrError.getValue().equals(roles2)).toBeTruthy()
})
it('should tell if collection includes element', () => {
const roles1 = [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]
const valueOrError = RoleNameCollection.create(roles1)
expect(valueOrError.getValue().includes(RoleName.create(RoleName.NAMES.ProUser).getValue())).toBeTruthy()
})
it('should tell if collection does not includes element', () => {
const roles1 = [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]
const valueOrError = RoleNameCollection.create(roles1)
expect(valueOrError.getValue().includes(RoleName.create(RoleName.NAMES.CoreUser).getValue())).toBeFalsy()
})
it('should tell if collection has a role with more or equal power to', () => {
let roles = [RoleName.NAMES.VaultsUser]
let valueOrError = RoleNameCollection.create(roles)
let roleNames = valueOrError.getValue()
expect(
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.PlusUser).getValue()),
).toBeFalsy()
expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.ProUser).getValue())).toBeFalsy()
expect(
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.CoreUser).getValue()),
).toBeTruthy()
roles = [RoleName.NAMES.CoreUser]
valueOrError = RoleNameCollection.create(roles)
roleNames = valueOrError.getValue()
expect(
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.PlusUser).getValue()),
).toBeFalsy()
expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.ProUser).getValue())).toBeFalsy()
expect(
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.CoreUser).getValue()),
).toBeTruthy()
roles = [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser]
valueOrError = RoleNameCollection.create(roles)
roleNames = valueOrError.getValue()
expect(
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.PlusUser).getValue()),
).toBeTruthy()
expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.ProUser).getValue())).toBeFalsy()
expect(
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.CoreUser).getValue()),
).toBeTruthy()
roles = [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]
valueOrError = RoleNameCollection.create(roles)
roleNames = valueOrError.getValue()
expect(
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.PlusUser).getValue()),
).toBeTruthy()
expect(
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.ProUser).getValue()),
).toBeTruthy()
expect(
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.CoreUser).getValue()),
).toBeTruthy()
})
it('should fail to create a collection if a role name is invalid', () => {
const valueOrError = RoleNameCollection.create(['invalid-role-name'])
expect(valueOrError.isFailed()).toBeTruthy()
expect(valueOrError.getError()).toEqual('Invalid role name: invalid-role-name')
})
})

View File

@@ -0,0 +1,61 @@
import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result'
import { RoleNameCollectionProps } from './RoleNameCollectionProps'
import { RoleName } from './RoleName'
export class RoleNameCollection extends ValueObject<RoleNameCollectionProps> {
get value(): RoleName[] {
return this.props.value
}
includes(roleName: RoleName): boolean {
for (const existingRoleName of this.props.value) {
if (existingRoleName.value === roleName.value) {
return true
}
}
return false
}
hasARoleNameWithMoreOrEqualPowerTo(roleName: RoleName): boolean {
for (const existingRoleName of this.props.value) {
if (existingRoleName.hasMoreOrEqualPowerTo(roleName)) {
return true
}
}
return false
}
override equals(roleNameCollection: RoleNameCollection): boolean {
if (this.props.value.length !== roleNameCollection.value.length) {
return false
}
for (const roleName of roleNameCollection.value) {
if (!this.includes(roleName)) {
return false
}
}
return true
}
private constructor(props: RoleNameCollectionProps) {
super(props)
}
static create(roleNameStrings: string[]): Result<RoleNameCollection> {
const roleNames: RoleName[] = []
for (const roleNameString of roleNameStrings) {
const roleNameOrError = RoleName.create(roleNameString)
if (roleNameOrError.isFailed()) {
return Result.fail<RoleNameCollection>(roleNameOrError.getError())
}
roleNames.push(roleNameOrError.getValue())
}
return Result.ok<RoleNameCollection>(new RoleNameCollection({ value: roleNames }))
}
}

View File

@@ -0,0 +1,5 @@
import { RoleName } from './RoleName'
export interface RoleNameCollectionProps {
value: RoleName[]
}

View File

@@ -0,0 +1,3 @@
export interface RoleNameProps {
value: string
}

View File

@@ -0,0 +1,23 @@
import { Timestamps } from './Timestamps'
describe('Timestamps', () => {
it('should create a value object', () => {
const valueOrError = Timestamps.create(123, 234)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().createdAt).toEqual(123)
expect(valueOrError.getValue().updatedAt).toEqual(234)
})
it('should not create an invalid value object', () => {
const valueOrError = Timestamps.create('' as unknown as number, 123)
expect(valueOrError.isFailed()).toBeTruthy()
})
it('should not create an invalid value object', () => {
const valueOrError = Timestamps.create(123, '' as unknown as number)
expect(valueOrError.isFailed()).toBeTruthy()
})
})

View File

@@ -0,0 +1,30 @@
import { Result } from '../Core/Result'
import { ValueObject } from '../Core/ValueObject'
import { TimestampsProps } from './TimestampsProps'
export class Timestamps extends ValueObject<TimestampsProps> {
get createdAt(): number {
return this.props.createdAt
}
get updatedAt(): number {
return this.props.updatedAt
}
private constructor(props: TimestampsProps) {
super(props)
}
static create(createdAt: number, updatedAt: number): Result<Timestamps> {
if (isNaN(createdAt) || typeof createdAt !== 'number') {
return Result.fail<Timestamps>(
`Could not create Timestamps. Creation date should be a number, given: ${createdAt}`,
)
}
if (isNaN(updatedAt) || typeof updatedAt !== 'number') {
return Result.fail<Timestamps>(`Could not create Timestamps. Update date should be a number, given: ${updatedAt}`)
}
return Result.ok<Timestamps>(new Timestamps({ createdAt, updatedAt }))
}
}

View File

@@ -0,0 +1,4 @@
export interface TimestampsProps {
createdAt: number
updatedAt: number
}

View File

@@ -0,0 +1,34 @@
import { Username } from './Username'
describe('Username', () => {
it('should create a value object', () => {
const valueOrError = Username.create('test@test.te')
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('test@test.te')
})
it('should not create an invalid value object', () => {
const valueOrError = Username.create('')
expect(valueOrError.isFailed()).toBeTruthy()
})
it('should not create an invalid type object', () => {
const valueOrError = Username.create(undefined as unknown as string)
expect(valueOrError.isFailed()).toBeTruthy()
})
it('should indicate if the username is potentially a vault account', () => {
const value = Username.create('a75a31ce95365904ef0e0a8e6cefc1f5e99adfef81bbdb6d4499eeb10ae0ff67').getValue()
expect(value.isPotentiallyAPrivateUsernameAccount()).toBeTruthy()
})
it('should indicate if the user is not a vault account', () => {
const value = Username.create('test@test.te').getValue()
expect(value.isPotentiallyAPrivateUsernameAccount()).toBeFalsy()
})
})

View File

@@ -0,0 +1,32 @@
import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result'
import { UsernameProps } from './UsernameProps'
import { Validator } from '../Core/Validator'
export class Username extends ValueObject<UsernameProps> {
get value(): string {
return this.props.value
}
private constructor(props: UsernameProps) {
super(props)
}
static create(username: string): Result<Username> {
if (Validator.isString(username).isFailed()) {
return Result.fail<Username>('Username must be a string')
}
const trimmedAndLowerCasedUsername = username.trim().toLowerCase()
if (Validator.isNotEmpty(trimmedAndLowerCasedUsername).isFailed()) {
return Result.fail<Username>('Username cannot be empty')
}
return Result.ok<Username>(new Username({ value: trimmedAndLowerCasedUsername }))
}
isPotentiallyAPrivateUsernameAccount(): boolean {
return this.value.length === 64 && !this.value.includes('@')
}
}

View File

@@ -0,0 +1,3 @@
export interface UsernameProps {
value: string
}

View File

@@ -0,0 +1,44 @@
import { Uuid } from './Uuid'
describe('Uuid', () => {
it('should create a value object', () => {
const valueOrError = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d')
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('84c0f8e8-544a-4c7e-9adf-26209303bc1d')
})
it('should create a value object on upper case', () => {
const valueOrError = Uuid.create('00B57455-B563-4B50-A2AA-B19762102219')
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('00B57455-B563-4B50-A2AA-B19762102219')
})
it('should not create an invalid value object', () => {
const valueOrError = Uuid.create('1-2-3')
expect(valueOrError.isFailed()).toBeTruthy()
})
it('should check equality between two value objects', () => {
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
expect(uuid1.equals(uuid2)).toBeTruthy()
})
it('should check inequality between two value objects', () => {
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1e').getValue()
expect(uuid1.equals(uuid2)).toBeFalsy()
})
it('should check inequality between two value objects of different types', () => {
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
expect(uuid1.equals(null as unknown as Uuid)).toBeFalsy()
expect(uuid1.equals(undefined as unknown as Uuid)).toBeFalsy()
})
})

View File

@@ -0,0 +1,23 @@
import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result'
import { UuidProps } from './UuidProps'
import { Validator } from '../Core/Validator'
export class Uuid extends ValueObject<UuidProps> {
get value(): string {
return this.props.value
}
private constructor(props: UuidProps) {
super(props)
}
static create(uuid: string): Result<Uuid> {
const validUuidOrError = Validator.isValidUuid(uuid)
if (validUuidOrError.isFailed()) {
return Result.fail<Uuid>(validUuidOrError.getError())
} else {
return Result.ok<Uuid>(new Uuid({ value: uuid }))
}
}
}

View File

@@ -0,0 +1,3 @@
export interface UuidProps {
value: string
}

View File

@@ -0,0 +1,20 @@
/* istanbul ignore file */
import { Change } from './Change'
import { Entity } from './Entity'
export abstract class Aggregate<T> extends Entity<T> {
private changesOnAggregateRoot: Change[] = []
addChange(change: Change): void {
this.changesOnAggregateRoot.push(change)
}
flushChanges(): void {
this.changesOnAggregateRoot = []
}
getChanges(): Change[] {
return this.changesOnAggregateRoot
}
}

View File

@@ -0,0 +1,26 @@
/* istanbul ignore file */
import { ChangeProps } from './ChangeProps'
import { Result } from './Result'
export class Change {
static readonly TYPES = {
Add: 'add',
Remove: 'remove',
Modify: 'modify',
}
public readonly props: ChangeProps
constructor(props: ChangeProps) {
this.props = Object.freeze(props)
}
static create(props: ChangeProps): Result<Change> {
if (!Object.values(Change.TYPES).includes(props.changeType)) {
return Result.fail('Invalid change type')
}
return Result.ok(new Change(props))
}
}

View File

@@ -0,0 +1,9 @@
/* istanbul ignore file */
import { Entity } from './Entity'
export interface ChangeProps {
aggregateRootUuid: string
changeType: string
changeData: Entity<unknown>
}

View File

@@ -0,0 +1,34 @@
/* istanbul ignore file */
import { UniqueEntityId } from './UniqueEntityId'
export abstract class Entity<T> {
protected readonly _id: UniqueEntityId
constructor(
public readonly props: T,
id?: UniqueEntityId,
) {
this._id = id ? id : new UniqueEntityId()
}
get id(): UniqueEntityId {
return this._id
}
public equals(object?: Entity<T>): boolean {
if (object == null || object == undefined) {
return false
}
if (this === object) {
return true
}
if (!(object instanceof Entity)) {
return false
}
return this._id.equals(object._id)
}
}

View File

@@ -0,0 +1,24 @@
/* istanbul ignore file */
export class Id<T> {
constructor(private value: T) {}
equals(id?: Id<T>): boolean {
if (id === null || id === undefined) {
return false
}
if (!(id instanceof this.constructor)) {
return false
}
return id.toValue() === this.value
}
toString() {
return String(this.value)
}
toValue(): T {
return this.value
}
}

View File

@@ -0,0 +1,39 @@
/* istanbul ignore file */
export class Result<T> {
constructor(
private isSuccess: boolean,
private error?: string,
private value?: T,
) {
Object.freeze(this)
}
isFailed(): boolean {
return !this.isSuccess
}
getValue(): T {
if (!this.isSuccess) {
throw new Error(`Cannot get value of an unsuccessfull result: ${this.error}`)
}
return this.value as T
}
getError(): string {
if (this.isSuccess || this.error === undefined) {
throw new Error('Cannot get an error of a successfull result')
}
return this.error
}
static ok<U>(value?: U): Result<U> {
return new Result<U>(true, undefined, value)
}
static fail<U>(error: string): Result<U> {
return new Result<U>(false, error)
}
}

View File

@@ -0,0 +1,10 @@
/* istanbul ignore file */
import { v4 as uuid } from 'uuid'
import { Id } from './Id'
export class UniqueEntityId extends Id<string | number> {
constructor(id?: string | number) {
super(id ? id : uuid())
}
}

View File

@@ -0,0 +1,96 @@
import { Validator } from './Validator'
describe('Validator', () => {
const validUuids = [
'2221101c-1da9-4d2b-9b32-b8be2a8d1c82',
'c08f2f29-a74b-42b4-aefd-98af9832391c',
'b453fa64-1493-443b-b5bb-bca7b9c696c7',
'fa7350b3-77cf-8c0c-40b2-6046b13254fe',
]
const invalidUuids = [
123,
'someone@127.0.0.1',
'',
null,
'b453fa64-1493-443b-b5bb-ca7b9c696c7',
'c08f*f29-a74b-42b4-aefd-98af9832391c',
'c08f*f29-a74b-42b4-aefd-98af9832391c',
'../../escaped.sh',
]
const validEmails = [
'something@something.com',
'someone@localhost.localdomain',
'a/b@domain.com',
'{}@domain.com',
'karol+test@standardnotes.com',
"m*'!%@something.sa",
'tu!!7n7.ad##0!!!@company.ca',
'%@com.com',
"!#$%&'*+/=?^_`{|}~.-@com.com",
'someone@do-ma-in.com',
'""testlah""@example.com',
]
const invalidEmails = [
'someone@127.0.0.1',
'a@b.b',
'',
null,
'.wooly@example.com',
'wo..oly@example.com',
'somebody@example',
'a @p.com',
]
it('should validate proper uuids', () => {
for (const validUuid of validUuids) {
expect(Validator.isValidUuid(validUuid).isFailed()).toBeFalsy()
}
})
it('should not validate invalid uuids', () => {
for (const invalidUuid of invalidUuids) {
expect(Validator.isValidUuid(invalidUuid as string).isFailed()).toBeTruthy()
}
})
it('should validate proper emails', () => {
for (const validEmail of validEmails) {
expect(Validator.isValidEmail(validEmail).isFailed()).toBeFalsy()
}
})
it('should not validate invalid emails', () => {
for (const invalidEmail of invalidEmails) {
expect(Validator.isValidEmail(invalidEmail as string).isFailed()).toBeTruthy()
}
})
it('should validate value if not empty', () => {
for (const value of [1, 'foobar', {}, 0]) {
expect(Validator.isNotEmpty(value).isFailed()).toBeFalsy()
}
})
it('should not validate value if empty', () => {
for (const value of [null, undefined, '', []]) {
expect(Validator.isNotEmpty(value).isFailed()).toBeTruthy()
}
})
describe('is not empty string', () => {
it('should not validate invalid string', () => {
expect(Validator.isNotEmptyString(123 as unknown as string).isFailed()).toBeTruthy()
})
it('should not validate an empty string', () => {
expect(Validator.isNotEmptyString('').isFailed()).toBeTruthy()
})
it('should validate a string', () => {
expect(Validator.isNotEmptyString('foo').isFailed()).toBeFalsy()
})
})
})

View File

@@ -0,0 +1,60 @@
import { Result } from './Result'
export class Validator {
private static readonly UUID_ANY_VERSION_AND_VARIANT_REGEX =
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
private static readonly EMAIL_REGEX =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
static isValidUuid(value: string): Result<string> {
const matchesUuidRegex = String(value).toLowerCase().match(Validator.UUID_ANY_VERSION_AND_VARIANT_REGEX) !== null
if (matchesUuidRegex) {
return Result.ok()
}
return Result.fail(`Given value is not a valid uuid: ${value}`)
}
static isValidEmail(value: string): Result<string> {
const matchesUuidRegex = String(value).toLowerCase().match(Validator.EMAIL_REGEX) !== null
if (matchesUuidRegex) {
return Result.ok()
}
return Result.fail(`Given value is not a valid email address: ${value}`)
}
static isString(value: unknown): Result<string> {
if (typeof value === 'string') {
return Result.ok()
}
return Result.fail(`Given value is not a string: ${typeof value}`)
}
static isNotEmpty(value: unknown): Result<string> {
if (value instanceof Array && value.length === 0) {
return Result.fail(`Given value is empty: ${value}`)
}
if (value === null || value === undefined || value === '') {
return Result.fail(`Given value is empty: ${value}`)
}
return Result.ok()
}
static isNotEmptyString(value: unknown): Result<string> {
const isStringResult = Validator.isString(value)
if (isStringResult.isFailed()) {
return isStringResult
}
const isNotEmptyResult = Validator.isNotEmpty(value)
if (isNotEmptyResult.isFailed()) {
return isNotEmptyResult
}
return Result.ok()
}
}

View File

@@ -0,0 +1,18 @@
/* istanbul ignore file */
import { ValueObjectProps } from './ValueObjectProps'
export abstract class ValueObject<T extends ValueObjectProps> {
public readonly props: T
constructor(props: T) {
this.props = Object.freeze(props)
}
public equals(vo?: ValueObject<T>): boolean {
if (vo === null || vo === undefined) {
return false
}
return JSON.stringify(this.props) === JSON.stringify(vo.props)
}
}

View File

@@ -0,0 +1,4 @@
export interface ValueObjectProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[index: string]: any
}

View File

@@ -0,0 +1,13 @@
import { ControllerContainerInterface } from './ControllerContainerInterface'
export class ControllerContainer implements ControllerContainerInterface {
private controllerMethodsMap: Map<string, (request: never, response: never) => Promise<unknown>> = new Map()
register(methodIdentifier: string, binding: (request: never, response: never) => Promise<unknown>): void {
this.controllerMethodsMap.set(methodIdentifier, binding)
}
get(methodIdentifier: string): ((request: never, response: never) => Promise<unknown>) | undefined {
return this.controllerMethodsMap.get(methodIdentifier)
}
}

View File

@@ -0,0 +1,4 @@
export interface ControllerContainerInterface {
register(methodIdentifier: string, binding: (request: never, response: never) => Promise<unknown>): void
get(methodIdentifier: string): ((request: never, response: never) => Promise<unknown>) | undefined
}

View File

@@ -0,0 +1,18 @@
import { EmailLevel } from './EmailLevel'
describe('EmailLevel', () => {
it('should create a value object', () => {
const valueOrError = EmailLevel.create(EmailLevel.LEVELS.SignIn)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('SIGN_IN')
})
it('should not create an invalid value object', () => {
for (const value of ['', undefined, null, 0, 'FOOBAR']) {
const valueOrError = EmailLevel.create(value as string)
expect(valueOrError.isFailed()).toBeTruthy()
}
})
})

View File

@@ -0,0 +1,31 @@
import { Result } from '../Core/Result'
import { ValueObject } from '../Core/ValueObject'
import { EmailLevelProps } from './EmailLevelProps'
export class EmailLevel extends ValueObject<EmailLevelProps> {
static readonly LEVELS = {
System: 'SYSTEM',
SignIn: 'SIGN_IN',
Marketing: 'MARKETING',
FailedCloudBackup: 'FAILED_CLOUD_BACKUP',
FailedEmailBackup: 'FAILED_EMAIL_BACKUP',
}
get value(): string {
return this.props.value
}
private constructor(props: EmailLevelProps) {
super(props)
}
static create(name: string): Result<EmailLevel> {
const isValidName = Object.values(this.LEVELS).includes(name)
if (!isValidName) {
return Result.fail<EmailLevel>(`Invalid subscription rejection level: ${name}`)
} else {
return Result.ok<EmailLevel>(new EmailLevel({ value: name }))
}
}
}

View File

@@ -0,0 +1,3 @@
export interface EmailLevelProps {
value: string
}

View File

@@ -0,0 +1,34 @@
export abstract class AbstractEnv {
protected env?: { [key: string]: string } = {}
protected overrides: { [key: string]: string }
constructor(overrides: { [key: string]: string } = {}) {
this.overrides = overrides
}
abstract load(): void
get(key: string, optional = false): string {
if (!this.env) {
this.load()
}
if (this.overrides[key]) {
return this.overrides[key]
}
if (!process.env[key] && !optional) {
throw new Error(`Environment variable ${key} not set`)
}
return <string>process.env[key]
}
getAll(): { [key: string]: string } {
if (!this.env) {
this.load()
}
return this.env as { [key: string]: string }
}
}

View File

@@ -0,0 +1,4 @@
export interface MapperInterface<T, U> {
toDomain(projection: U): T
toProjection(domain: T): U
}

View File

@@ -0,0 +1,90 @@
import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result'
import { NotificationPayloadProps } from './NotificationPayloadProps'
import { NotificationType } from './NotificationType'
import { Uuid } from '../Common/Uuid'
import { NotificationPayloadIdentifierType } from './NotificationPayloadIdentifierType'
export class NotificationPayload extends ValueObject<NotificationPayloadProps> {
private constructor(props: NotificationPayloadProps) {
super(props)
}
override toString(): string {
return JSON.stringify({
version: this.props.version,
type: this.props.type.value,
primaryIdentifier: this.props.primaryIdentifier.value,
primaryIndentifierType: this.props.primaryIndentifierType.value,
secondaryIdentifier: this.props.secondaryIdentifier?.value,
secondaryIdentifierType: this.props.secondaryIdentifierType?.value,
})
}
static createFromString(jsonPayload: string): Result<NotificationPayload> {
try {
const props = JSON.parse(jsonPayload)
const typeOrError = NotificationType.create(props.type)
if (typeOrError.isFailed()) {
return Result.fail<NotificationPayload>(typeOrError.getError())
}
const type = typeOrError.getValue()
const primaryIdentifierOrError = Uuid.create(props.primaryIdentifier)
if (primaryIdentifierOrError.isFailed()) {
return Result.fail<NotificationPayload>(primaryIdentifierOrError.getError())
}
const primaryIdentifier = primaryIdentifierOrError.getValue()
const primaryIndentifierTypeOrError = NotificationPayloadIdentifierType.create(props.primaryIndentifierType)
if (primaryIndentifierTypeOrError.isFailed()) {
return Result.fail<NotificationPayload>(primaryIndentifierTypeOrError.getError())
}
const primaryIndentifierType = primaryIndentifierTypeOrError.getValue()
let secondaryIdentifier: Uuid | undefined
if (props.secondaryIdentifier) {
const secondaryIdentifierOrError = Uuid.create(props.secondaryIdentifier)
if (secondaryIdentifierOrError.isFailed()) {
return Result.fail<NotificationPayload>(secondaryIdentifierOrError.getError())
}
secondaryIdentifier = secondaryIdentifierOrError.getValue()
}
let secondaryIdentifierType: NotificationPayloadIdentifierType | undefined
if (props.secondaryIdentifierType) {
const secondaryIdentifierTypeOrError = NotificationPayloadIdentifierType.create(props.secondaryIdentifierType)
if (secondaryIdentifierTypeOrError.isFailed()) {
return Result.fail<NotificationPayload>(secondaryIdentifierTypeOrError.getError())
}
secondaryIdentifierType = secondaryIdentifierTypeOrError.getValue()
}
return NotificationPayload.create({
version: props.version,
type,
primaryIdentifier,
primaryIndentifierType,
secondaryIdentifier,
secondaryIdentifierType,
})
} catch (error) {
return Result.fail<NotificationPayload>((error as Error).message)
}
}
static create(props: NotificationPayloadProps): Result<NotificationPayload> {
if (
props.secondaryIdentifier === undefined &&
props.type.equals(NotificationType.create(NotificationType.TYPES.SharedVaultItemRemoved).getValue())
) {
return Result.fail<NotificationPayload>(
`Item uuid is required for ${NotificationType.TYPES.SharedVaultItemRemoved} notification type`,
)
}
return Result.ok<NotificationPayload>(new NotificationPayload(props))
}
}

View File

@@ -0,0 +1,28 @@
import { Result } from '../Core/Result'
import { ValueObject } from '../Core/ValueObject'
import { NotificationPayloadIdentifierTypeProps } from './NotificationPayloadIdentifierTypeProps'
export class NotificationPayloadIdentifierType extends ValueObject<NotificationPayloadIdentifierTypeProps> {
static readonly TYPES = {
SharedVaultUuid: 'shared_vault_uuid',
UserUuid: 'user_uuid',
SharedVaultInviteUuid: 'shared_vault_invite_uuid',
ItemUuid: 'item_uuid',
}
private constructor(props: NotificationPayloadIdentifierTypeProps) {
super(props)
}
get value(): string {
return this.props.value
}
static create(type: string): Result<NotificationPayloadIdentifierType> {
if (!Object.values(this.TYPES).includes(type)) {
return Result.fail<NotificationPayloadIdentifierType>(`Invalid notification payload identifier type: ${type}`)
}
return Result.ok<NotificationPayloadIdentifierType>(new NotificationPayloadIdentifierType({ value: type }))
}
}

View File

@@ -0,0 +1,3 @@
export interface NotificationPayloadIdentifierTypeProps {
value: string
}

View File

@@ -0,0 +1,12 @@
import { Uuid } from '../Common/Uuid'
import { NotificationPayloadIdentifierType } from './NotificationPayloadIdentifierType'
import { NotificationType } from './NotificationType'
export interface NotificationPayloadProps {
type: NotificationType
primaryIdentifier: Uuid
primaryIndentifierType: NotificationPayloadIdentifierType
secondaryIdentifier?: Uuid
secondaryIdentifierType?: NotificationPayloadIdentifierType
version: string
}

View File

@@ -0,0 +1,16 @@
import { NotificationType } from './NotificationType'
describe('NotificationType', () => {
it('should create a value object', () => {
const valueOrError = NotificationType.create(NotificationType.TYPES.SharedVaultItemRemoved)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('shared_vault_item_removed')
})
it('should not create an invalid value object', () => {
const valueOrError = NotificationType.create('TEST')
expect(valueOrError.isFailed()).toBeTruthy()
})
})

View File

@@ -0,0 +1,33 @@
import { Result } from '../Core/Result'
import { ValueObject } from '../Core/ValueObject'
import { NotificationTypeProps } from './NotificationTypeProps'
export class NotificationType extends ValueObject<NotificationTypeProps> {
static readonly TYPES = {
SharedVaultItemRemoved: 'shared_vault_item_removed',
SelfRemovedFromSharedVault: 'self_removed_from_shared_vault',
UserRemovedFromSharedVault: 'user_removed_from_shared_vault',
UserDesignatedAsSurvivor: 'user_designated_as_survivor',
UserAddedToSharedVault: 'user_added_to_shared_vault',
SharedVaultInviteCanceled: 'shared_vault_invite_canceled',
SharedVaultFileUploaded: 'shared_vault_file_uploaded',
SharedVaultFileRemoved: 'shared_vault_file_removed',
}
get value(): string {
return this.props.value
}
private constructor(props: NotificationTypeProps) {
super(props)
}
static create(notificationType: string): Result<NotificationType> {
const isValidType = Object.values(this.TYPES).includes(notificationType)
if (!isValidType) {
return Result.fail<NotificationType>(`Invalid notification type: ${notificationType}`)
} else {
return Result.ok<NotificationType>(new NotificationType({ value: notificationType }))
}
}
}

View File

@@ -0,0 +1,3 @@
export interface NotificationTypeProps {
value: string
}

View File

@@ -0,0 +1,6 @@
import { Transform } from 'stream'
export interface ServiceConfiguration {
logger?: Transform
environmentOverrides?: { [name: string]: string }
}

View File

@@ -0,0 +1,15 @@
import { ServiceContainerInterface } from './ServiceContainerInterface'
import { ServiceIdentifier } from './ServiceIdentifier'
import { ServiceInterface } from './ServiceInterface'
export class ServiceContainer implements ServiceContainerInterface {
private serviceMap: Map<string, unknown> = new Map()
register(serviceIdentifer: ServiceIdentifier, service: ServiceInterface): void {
this.serviceMap.set(serviceIdentifer.value, service)
}
get(serviceIdentifer: ServiceIdentifier): ServiceInterface | undefined {
return this.serviceMap.get(serviceIdentifer.value) as ServiceInterface
}
}

View File

@@ -0,0 +1,7 @@
import { ServiceIdentifier } from './ServiceIdentifier'
import { ServiceInterface } from './ServiceInterface'
export interface ServiceContainerInterface {
register(serviceIdentifer: ServiceIdentifier, service: ServiceInterface): void
get(serviceIdentifer: ServiceIdentifier): ServiceInterface | undefined
}

View File

@@ -0,0 +1,18 @@
import { ServiceIdentifier } from './ServiceIdentifier'
describe('ServiceIdentifier', () => {
it('should create a value object', () => {
const valueOrError = ServiceIdentifier.create(ServiceIdentifier.NAMES.Auth)
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('Auth')
})
it('should not create an invalid value object', () => {
for (const value of ['', undefined, null, 0, 'SOME_SERVICE']) {
const valueOrError = ServiceIdentifier.create(value as string)
expect(valueOrError.isFailed()).toBeTruthy()
}
})
})

View File

@@ -0,0 +1,45 @@
import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result'
import { ServiceIdentifierProps } from './ServiceIdentifierProps'
export class ServiceIdentifier extends ValueObject<ServiceIdentifierProps> {
static readonly NAMES = {
AnalyticsWorker: 'AnalyticsWorker',
AnalyticsScheduledTask: 'AnalyticsScheduledTask',
ApiGateway: 'ApiGateway',
Auth: 'Auth',
AuthWorker: 'AuthWorker',
AuthScheduledTask: 'AuthScheduledTask',
SyncingServer: 'SyncingServer',
SyncingServerWorker: 'SyncingServerWorker',
Revisions: 'Revisions',
RevisionsWorker: 'RevisionsWorker',
Files: 'Files',
FilesWorker: 'FilesWorker',
SchedulerWorker: 'SchedulerWorker',
SchedulerScheduledTask: 'SchedulerScheduledTask',
Email: 'Email',
EmailWorker: 'EmailWorker',
EmailBounceProcessor: 'EmailBounceProcessor',
EmailScheduledTask: 'EmailScheduledTask',
Websockets: 'Websockets',
WebsocketsWorker: 'WebsocketsWorker',
}
get value(): string {
return this.props.value
}
private constructor(props: ServiceIdentifierProps) {
super(props)
}
static create(name: string): Result<ServiceIdentifier> {
const isValidName = Object.values(this.NAMES).includes(name)
if (!isValidName) {
return Result.fail<ServiceIdentifier>(`Invalid subscription plan name: ${name}`)
} else {
return Result.ok<ServiceIdentifier>(new ServiceIdentifier({ value: name }))
}
}
}

View File

@@ -0,0 +1,3 @@
export interface ServiceIdentifierProps {
value: string
}

View File

@@ -0,0 +1,8 @@
import { ServiceConfiguration } from './ServiceConfiguration'
import { ServiceIdentifier } from './ServiceIdentifier'
export interface ServiceInterface {
getContainer(configuration?: ServiceConfiguration): Promise<unknown>
getId(): ServiceIdentifier
handleRequest(request: never, response: never, endpointOrMethodIdentifier: string): Promise<unknown>
}

View File

@@ -0,0 +1,64 @@
import { Result } from '../Core/Result'
import { ValueObject } from '../Core/ValueObject'
import { SettingNameProps } from './SettingNameProps'
export class SettingName extends ValueObject<SettingNameProps> {
static readonly NAMES = {
MfaSecret: 'MFA_SECRET',
ExtensionKey: 'EXTENSION_KEY',
EmailBackupFrequency: 'EMAIL_BACKUP_FREQUENCY',
DropboxBackupFrequency: 'DROPBOX_BACKUP_FREQUENCY',
DropboxBackupToken: 'DROPBOX_BACKUP_TOKEN',
OneDriveBackupFrequency: 'ONE_DRIVE_BACKUP_FREQUENCY',
OneDriveBackupToken: 'ONE_DRIVE_BACKUP_TOKEN',
GoogleDriveBackupFrequency: 'GOOGLE_DRIVE_BACKUP_FREQUENCY',
GoogleDriveBackupToken: 'GOOGLE_DRIVE_BACKUP_TOKEN',
MuteFailedBackupsEmails: 'MUTE_FAILED_BACKUPS_EMAILS',
MuteFailedCloudBackupsEmails: 'MUTE_FAILED_CLOUD_BACKUPS_EMAILS',
MuteSignInEmails: 'MUTE_SIGN_IN_EMAILS',
MuteMarketingEmails: 'MUTE_MARKETING_EMAILS',
ListedAuthorSecrets: 'LISTED_AUTHOR_SECRETS',
LogSessionUserAgent: 'LOG_SESSION_USER_AGENT',
RecoveryCodes: 'RECOVERY_CODES',
FileUploadBytesLimit: 'FILE_UPLOAD_BYTES_LIMIT',
FileUploadBytesUsed: 'FILE_UPLOAD_BYTES_USED',
}
get value(): string {
return this.props.value
}
isSensitive(): boolean {
return [SettingName.NAMES.MfaSecret, SettingName.NAMES.ExtensionKey].includes(this.props.value)
}
isASubscriptionSetting(): boolean {
return [
SettingName.NAMES.FileUploadBytesLimit,
SettingName.NAMES.FileUploadBytesUsed,
SettingName.NAMES.MuteSignInEmails,
].includes(this.props.value)
}
isARegularOnlySubscriptionSetting(): boolean {
return [SettingName.NAMES.FileUploadBytesLimit, SettingName.NAMES.FileUploadBytesUsed].includes(this.props.value)
}
isASharedAndRegularOnlySubscriptionSetting(): boolean {
return [SettingName.NAMES.MuteSignInEmails].includes(this.props.value)
}
private constructor(props: SettingNameProps) {
super(props)
}
static create(name: string): Result<SettingName> {
const isValidName = Object.values(this.NAMES).includes(name)
if (!isValidName) {
return Result.fail<SettingName>(`Invalid setting name: ${name}`)
} else {
return Result.ok<SettingName>(new SettingName({ value: name }))
}
}
}

View File

@@ -0,0 +1,3 @@
export interface SettingNameProps {
value: string
}

View File

@@ -0,0 +1,19 @@
import { SharedVaultUserPermission } from './SharedVaultUserPermission'
import { SharedVaultUser } from './SharedVaultUser'
import { Uuid } from '../Common/Uuid'
import { Timestamps } from '../Common/Timestamps'
describe('SharedVaultUser', () => {
it('should create an entity', () => {
const entityOrError = SharedVaultUser.create({
permission: SharedVaultUserPermission.create('read').getValue(),
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
timestamps: Timestamps.create(123456789, 123456789).getValue(),
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
isDesignatedSurvivor: false,
})
expect(entityOrError.isFailed()).toBeFalsy()
expect(entityOrError.getValue().id).not.toBeNull()
})
})

View File

@@ -0,0 +1,14 @@
import { Entity } from '../Core/Entity'
import { Result } from '../Core/Result'
import { UniqueEntityId } from '../Core/UniqueEntityId'
import { SharedVaultUserProps } from './SharedVaultUserProps'
export class SharedVaultUser extends Entity<SharedVaultUserProps> {
private constructor(props: SharedVaultUserProps, id?: UniqueEntityId) {
super(props, id)
}
static create(props: SharedVaultUserProps, id?: UniqueEntityId): Result<SharedVaultUser> {
return Result.ok<SharedVaultUser>(new SharedVaultUser(props, id))
}
}

View File

@@ -0,0 +1,16 @@
import { SharedVaultUserPermission } from './SharedVaultUserPermission'
describe('SharedVaultUserPermission', () => {
it('should create a value object', () => {
const valueOrError = SharedVaultUserPermission.create('read')
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('read')
})
it('should not create an invalid value object', () => {
const valueOrError = SharedVaultUserPermission.create('TEST')
expect(valueOrError.isFailed()).toBeTruthy()
})
})

View File

@@ -0,0 +1,29 @@
import { Result } from '../Core/Result'
import { ValueObject } from '../Core/ValueObject'
import { SharedVaultUserPermissionProps } from './SharedVaultUserPermissionProps'
export class SharedVaultUserPermission extends ValueObject<SharedVaultUserPermissionProps> {
static readonly PERMISSIONS = {
Read: 'read',
Write: 'write',
Admin: 'admin',
}
get value(): string {
return this.props.value
}
private constructor(props: SharedVaultUserPermissionProps) {
super(props)
}
static create(sharedVaultUserPermission: string): Result<SharedVaultUserPermission> {
const isValidPermission = Object.values(this.PERMISSIONS).includes(sharedVaultUserPermission)
if (!isValidPermission) {
return Result.fail<SharedVaultUserPermission>(`Invalid shared vault user permission ${sharedVaultUserPermission}`)
} else {
return Result.ok<SharedVaultUserPermission>(new SharedVaultUserPermission({ value: sharedVaultUserPermission }))
}
}
}

View File

@@ -0,0 +1,3 @@
export interface SharedVaultUserPermissionProps {
value: string
}

View File

@@ -0,0 +1,11 @@
import { Timestamps } from '../Common/Timestamps'
import { Uuid } from '../Common/Uuid'
import { SharedVaultUserPermission } from './SharedVaultUserPermission'
export interface SharedVaultUserProps {
sharedVaultUuid: Uuid
userUuid: Uuid
permission: SharedVaultUserPermission
isDesignatedSurvivor: boolean
timestamps: Timestamps
}

View File

@@ -0,0 +1,18 @@
import { SubscriptionPlanName } from './SubscriptionPlanName'
describe('SubscriptionPlanName', () => {
it('should create a value object', () => {
const valueOrError = SubscriptionPlanName.create('PRO_PLAN')
expect(valueOrError.isFailed()).toBeFalsy()
expect(valueOrError.getValue().value).toEqual('PRO_PLAN')
})
it('should not create an invalid value object', () => {
for (const value of ['', undefined, null, 0, 'SOME_PLAN']) {
const valueOrError = SubscriptionPlanName.create(value as string)
expect(valueOrError.isFailed()).toBeTruthy()
}
})
})

View File

@@ -0,0 +1,27 @@
import { ValueObject } from '../Core/ValueObject'
import { Result } from '../Core/Result'
import { SubscriptionPlanNameProps } from './SubscriptionPlanNameProps'
export class SubscriptionPlanName extends ValueObject<SubscriptionPlanNameProps> {
static readonly NAMES = {
PlusPlan: 'PLUS_PLAN',
ProPlan: 'PRO_PLAN',
}
get value(): string {
return this.props.value
}
private constructor(props: SubscriptionPlanNameProps) {
super(props)
}
static create(name: string): Result<SubscriptionPlanName> {
const isValidName = Object.values(this.NAMES).includes(name)
if (!isValidName) {
return Result.fail<SubscriptionPlanName>(`Invalid subscription plan name: ${name}`)
} else {
return Result.ok<SubscriptionPlanName>(new SubscriptionPlanName({ value: name }))
}
}
}

View File

@@ -0,0 +1,3 @@
export interface SubscriptionPlanNameProps {
value: string
}

View File

@@ -0,0 +1,5 @@
import { Result } from '../Core/Result'
export interface SyncUseCaseInterface<T> {
execute(...args: any[]): Result<T>
}

View File

@@ -0,0 +1,5 @@
import { Result } from '../Core/Result'
export interface UseCaseInterface<T> {
execute(...args: any[]): Promise<Result<T>>
}

View File

@@ -0,0 +1,76 @@
export * from './Auth/LegacySession'
export * from './Auth/LegacySessionProps'
export * from './Auth/Session'
export * from './Auth/SessionProps'
export * from './Auth/SessionToken'
export * from './Auth/SessionTokenProps'
export * from './Cache/CacheEntry'
export * from './Cache/CacheEntryProps'
export * from './Cache/CacheEntryRepositoryInterface'
export * from './Common/ContentType'
export * from './Common/ContentTypeProps'
export * from './Common/Dates'
export * from './Common/DatesProps'
export * from './Common/Email'
export * from './Common/EmailProps'
export * from './Common/RoleName'
export * from './Common/RoleNameProps'
export * from './Common/RoleNameCollection'
export * from './Common/RoleNameCollectionProps'
export * from './Common/Timestamps'
export * from './Common/TimestampsProps'
export * from './Common/Username'
export * from './Common/UsernameProps'
export * from './Common/Uuid'
export * from './Common/UuidProps'
export * from './Core/Aggregate'
export * from './Core/Change'
export * from './Core/ChangeProps'
export * from './Core/Entity'
export * from './Core/Id'
export * from './Core/Result'
export * from './Core/UniqueEntityId'
export * from './Core/Validator'
export * from './Core/ValueObject'
export * from './Core/ValueObjectProps'
export * from './DI/ControllerContainer'
export * from './DI/ControllerContainerInterface'
export * from './Email/EmailLevel'
export * from './Email/EmailLevelProps'
export * from './Env/AbstractEnv'
export * from './Mapping/MapperInterface'
export * from './Notification/NotificationPayload'
export * from './Notification/NotificationPayloadIdentifierType'
export * from './Notification/NotificationPayloadIdentifierTypeProps'
export * from './Notification/NotificationPayloadProps'
export * from './Notification/NotificationType'
export * from './Notification/NotificationTypeProps'
export * from './Service/ServiceConfiguration'
export * from './Service/ServiceContainer'
export * from './Service/ServiceContainerInterface'
export * from './Service/ServiceIdentifier'
export * from './Service/ServiceIdentifierProps'
export * from './Service/ServiceInterface'
export * from './Setting/SettingName'
export * from './Setting/SettingNameProps'
export * from './SharedVault/SharedVaultUser'
export * from './SharedVault/SharedVaultUserPermission'
export * from './SharedVault/SharedVaultUserPermissionProps'
export * from './SharedVault/SharedVaultUserProps'
export * from './Subscription/SubscriptionPlanName'
export * from './Subscription/SubscriptionPlanNameProps'
export * from './UseCase/SyncUseCaseInterface'
export * from './UseCase/UseCaseInterface'

View File

@@ -0,0 +1 @@
export * from './Domain'

View File

@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"composite": true,
"outDir": "./dist",
},
"include": [
"src/**/*"
],
"references": []
}