Browse Source

feat: add storing item saving and modifying metrics (#1013)

* feat: add storing item saving and modifying metrics

* fix: missing region in cloudwatch client

* fix: metric store binding
Karol Sójko 1 year ago
parent
commit
efd816a627
43 changed files with 1171 additions and 11 deletions
  1. 407 0
      .pnp.cjs
  2. BIN
      .yarn/cache/@aws-sdk-client-cloudwatch-npm-3.485.0-afe4ac001f-0e02739ef1.zip
  3. BIN
      .yarn/cache/@aws-sdk-client-sso-npm-3.485.0-5f6733bd23-635de0e310.zip
  4. BIN
      .yarn/cache/@aws-sdk-client-sts-npm-3.485.0-cc69ab3505-98c7f4d722.zip
  5. BIN
      .yarn/cache/@aws-sdk-core-npm-3.485.0-77ed30ee18-b84dafb213.zip
  6. BIN
      .yarn/cache/@aws-sdk-credential-provider-env-npm-3.485.0-0fda7f74e0-b8346ea6f5.zip
  7. BIN
      .yarn/cache/@aws-sdk-credential-provider-ini-npm-3.485.0-bec3aaa989-3176b03ee1.zip
  8. BIN
      .yarn/cache/@aws-sdk-credential-provider-node-npm-3.485.0-9f40e4a3cf-d31e5a95ea.zip
  9. BIN
      .yarn/cache/@aws-sdk-credential-provider-process-npm-3.485.0-62d3460338-e740fb949e.zip
  10. BIN
      .yarn/cache/@aws-sdk-credential-provider-sso-npm-3.485.0-42db25db09-7269315797.zip
  11. BIN
      .yarn/cache/@aws-sdk-credential-provider-web-identity-npm-3.485.0-420b04bcce-33125ce0b7.zip
  12. BIN
      .yarn/cache/@aws-sdk-middleware-host-header-npm-3.485.0-2e625f9614-9ca3da2a26.zip
  13. BIN
      .yarn/cache/@aws-sdk-middleware-logger-npm-3.485.0-3ff7eeabbb-2fcb731794.zip
  14. BIN
      .yarn/cache/@aws-sdk-middleware-recursion-detection-npm-3.485.0-af05ed4810-afdea18930.zip
  15. BIN
      .yarn/cache/@aws-sdk-middleware-signing-npm-3.485.0-3117db6053-f9dbb39d8d.zip
  16. BIN
      .yarn/cache/@aws-sdk-middleware-user-agent-npm-3.485.0-983204fccf-a8fc812aff.zip
  17. BIN
      .yarn/cache/@aws-sdk-region-config-resolver-npm-3.485.0-1a69e46754-55bc5128b8.zip
  18. BIN
      .yarn/cache/@aws-sdk-token-providers-npm-3.485.0-1fb5ab9dfb-aed270b625.zip
  19. BIN
      .yarn/cache/@aws-sdk-types-npm-3.485.0-6aa8cab069-588aae4b49.zip
  20. BIN
      .yarn/cache/@aws-sdk-util-endpoints-npm-3.485.0-5e0fad395e-c1844fed8b.zip
  21. BIN
      .yarn/cache/@aws-sdk-util-user-agent-browser-npm-3.485.0-23925a5581-d1e4d4c635.zip
  22. BIN
      .yarn/cache/@aws-sdk-util-user-agent-node-npm-3.485.0-7991a74cb3-e2805ef37b.zip
  23. 3 0
      packages/syncing-server/.env.sample
  24. 82 0
      packages/syncing-server/bin/statistics.ts
  25. 11 0
      packages/syncing-server/docker/entrypoint-statistics.js
  26. 4 2
      packages/syncing-server/docker/entrypoint.sh
  27. 1 0
      packages/syncing-server/package.json
  28. 30 0
      packages/syncing-server/src/Bootstrap/Container.ts
  29. 1 0
      packages/syncing-server/src/Bootstrap/Types.ts
  30. 16 0
      packages/syncing-server/src/Domain/Metrics/Metric.spec.ts
  31. 19 0
      packages/syncing-server/src/Domain/Metrics/Metric.ts
  32. 4 0
      packages/syncing-server/src/Domain/Metrics/MetricProps.ts
  33. 15 0
      packages/syncing-server/src/Domain/Metrics/MetricsStoreInterface.ts
  34. 7 1
      packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.spec.ts
  35. 7 0
      packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts
  36. 6 0
      packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.spec.ts
  37. 7 0
      packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts
  38. 21 0
      packages/syncing-server/src/Infra/Dummy/DummyMetricStore.ts
  39. 92 0
      packages/syncing-server/src/Infra/Redis/RedisMetricStore.ts
  40. 7 0
      packages/time/src/Domain/Time/Timer.spec.ts
  41. 4 0
      packages/time/src/Domain/Time/Timer.ts
  42. 1 0
      packages/time/src/Domain/Time/TimerInterface.ts
  43. 426 8
      yarn.lock

+ 407 - 0
.pnp.cjs

@@ -300,6 +300,57 @@ const RAW_RUNTIME_STATE =
         "linkType": "HARD"\
       }]\
     ]],\
+    ["@aws-sdk/client-cloudwatch", [\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-client-cloudwatch-npm-3.485.0-afe4ac001f-0e02739ef1.zip/node_modules/@aws-sdk/client-cloudwatch/",\
+        "packageDependencies": [\
+          ["@aws-sdk/client-cloudwatch", "npm:3.485.0"],\
+          ["@aws-crypto/sha256-browser", "npm:3.0.0"],\
+          ["@aws-crypto/sha256-js", "npm:3.0.0"],\
+          ["@aws-sdk/client-sts", "npm:3.485.0"],\
+          ["@aws-sdk/core", "npm:3.485.0"],\
+          ["@aws-sdk/credential-provider-node", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-host-header", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-logger", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-recursion-detection", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-signing", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-user-agent", "npm:3.485.0"],\
+          ["@aws-sdk/region-config-resolver", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@aws-sdk/util-endpoints", "npm:3.485.0"],\
+          ["@aws-sdk/util-user-agent-browser", "npm:3.485.0"],\
+          ["@aws-sdk/util-user-agent-node", "virtual:5f6733bd23aee10dd05576af160f1b93e0bb4a20b288e9b818dc0b69bdb08ea1a09d5836816f02bdafc9c01487816ae339c6b680c2f7849dfe249436c5f2b499#npm:3.485.0"],\
+          ["@smithy/config-resolver", "npm:2.0.23"],\
+          ["@smithy/core", "npm:1.2.2"],\
+          ["@smithy/fetch-http-handler", "npm:2.3.2"],\
+          ["@smithy/hash-node", "npm:2.0.18"],\
+          ["@smithy/invalid-dependency", "npm:2.0.16"],\
+          ["@smithy/middleware-content-length", "npm:2.0.18"],\
+          ["@smithy/middleware-endpoint", "npm:2.3.0"],\
+          ["@smithy/middleware-retry", "npm:2.0.26"],\
+          ["@smithy/middleware-serde", "npm:2.0.16"],\
+          ["@smithy/middleware-stack", "npm:2.0.10"],\
+          ["@smithy/node-config-provider", "npm:2.1.9"],\
+          ["@smithy/node-http-handler", "npm:2.2.2"],\
+          ["@smithy/protocol-http", "npm:3.0.12"],\
+          ["@smithy/smithy-client", "npm:2.2.1"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["@smithy/url-parser", "npm:2.0.16"],\
+          ["@smithy/util-base64", "npm:2.0.1"],\
+          ["@smithy/util-body-length-browser", "npm:2.0.1"],\
+          ["@smithy/util-body-length-node", "npm:2.1.0"],\
+          ["@smithy/util-defaults-mode-browser", "npm:2.0.24"],\
+          ["@smithy/util-defaults-mode-node", "npm:2.0.32"],\
+          ["@smithy/util-endpoints", "npm:1.0.8"],\
+          ["@smithy/util-retry", "npm:2.0.9"],\
+          ["@smithy/util-utf8", "npm:2.0.2"],\
+          ["@smithy/util-waiter", "npm:2.0.16"],\
+          ["fast-xml-parser", "npm:4.2.5"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
+      }]\
+    ]],\
     ["@aws-sdk/client-s3", [\
       ["npm:3.484.0", {\
         "packageLocation": "./.yarn/cache/@aws-sdk-client-s3-npm-3.484.0-681638ab7a-701523f3b3.zip/node_modules/@aws-sdk/client-s3/",\
@@ -512,6 +563,50 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-client-sso-npm-3.485.0-5f6733bd23-635de0e310.zip/node_modules/@aws-sdk/client-sso/",\
+        "packageDependencies": [\
+          ["@aws-sdk/client-sso", "npm:3.485.0"],\
+          ["@aws-crypto/sha256-browser", "npm:3.0.0"],\
+          ["@aws-crypto/sha256-js", "npm:3.0.0"],\
+          ["@aws-sdk/core", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-host-header", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-logger", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-recursion-detection", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-user-agent", "npm:3.485.0"],\
+          ["@aws-sdk/region-config-resolver", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@aws-sdk/util-endpoints", "npm:3.485.0"],\
+          ["@aws-sdk/util-user-agent-browser", "npm:3.485.0"],\
+          ["@aws-sdk/util-user-agent-node", "virtual:5f6733bd23aee10dd05576af160f1b93e0bb4a20b288e9b818dc0b69bdb08ea1a09d5836816f02bdafc9c01487816ae339c6b680c2f7849dfe249436c5f2b499#npm:3.485.0"],\
+          ["@smithy/config-resolver", "npm:2.0.23"],\
+          ["@smithy/core", "npm:1.2.2"],\
+          ["@smithy/fetch-http-handler", "npm:2.3.2"],\
+          ["@smithy/hash-node", "npm:2.0.18"],\
+          ["@smithy/invalid-dependency", "npm:2.0.16"],\
+          ["@smithy/middleware-content-length", "npm:2.0.18"],\
+          ["@smithy/middleware-endpoint", "npm:2.3.0"],\
+          ["@smithy/middleware-retry", "npm:2.0.26"],\
+          ["@smithy/middleware-serde", "npm:2.0.16"],\
+          ["@smithy/middleware-stack", "npm:2.0.10"],\
+          ["@smithy/node-config-provider", "npm:2.1.9"],\
+          ["@smithy/node-http-handler", "npm:2.2.2"],\
+          ["@smithy/protocol-http", "npm:3.0.12"],\
+          ["@smithy/smithy-client", "npm:2.2.1"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["@smithy/url-parser", "npm:2.0.16"],\
+          ["@smithy/util-base64", "npm:2.0.1"],\
+          ["@smithy/util-body-length-browser", "npm:2.0.1"],\
+          ["@smithy/util-body-length-node", "npm:2.1.0"],\
+          ["@smithy/util-defaults-mode-browser", "npm:2.0.24"],\
+          ["@smithy/util-defaults-mode-node", "npm:2.0.32"],\
+          ["@smithy/util-endpoints", "npm:1.0.8"],\
+          ["@smithy/util-retry", "npm:2.0.9"],\
+          ["@smithy/util-utf8", "npm:2.0.2"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/client-sts", [\
@@ -561,6 +656,53 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-client-sts-npm-3.485.0-cc69ab3505-98c7f4d722.zip/node_modules/@aws-sdk/client-sts/",\
+        "packageDependencies": [\
+          ["@aws-sdk/client-sts", "npm:3.485.0"],\
+          ["@aws-crypto/sha256-browser", "npm:3.0.0"],\
+          ["@aws-crypto/sha256-js", "npm:3.0.0"],\
+          ["@aws-sdk/core", "npm:3.485.0"],\
+          ["@aws-sdk/credential-provider-node", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-host-header", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-logger", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-recursion-detection", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-user-agent", "npm:3.485.0"],\
+          ["@aws-sdk/region-config-resolver", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@aws-sdk/util-endpoints", "npm:3.485.0"],\
+          ["@aws-sdk/util-user-agent-browser", "npm:3.485.0"],\
+          ["@aws-sdk/util-user-agent-node", "virtual:5f6733bd23aee10dd05576af160f1b93e0bb4a20b288e9b818dc0b69bdb08ea1a09d5836816f02bdafc9c01487816ae339c6b680c2f7849dfe249436c5f2b499#npm:3.485.0"],\
+          ["@smithy/config-resolver", "npm:2.0.23"],\
+          ["@smithy/core", "npm:1.2.2"],\
+          ["@smithy/fetch-http-handler", "npm:2.3.2"],\
+          ["@smithy/hash-node", "npm:2.0.18"],\
+          ["@smithy/invalid-dependency", "npm:2.0.16"],\
+          ["@smithy/middleware-content-length", "npm:2.0.18"],\
+          ["@smithy/middleware-endpoint", "npm:2.3.0"],\
+          ["@smithy/middleware-retry", "npm:2.0.26"],\
+          ["@smithy/middleware-serde", "npm:2.0.16"],\
+          ["@smithy/middleware-stack", "npm:2.0.10"],\
+          ["@smithy/node-config-provider", "npm:2.1.9"],\
+          ["@smithy/node-http-handler", "npm:2.2.2"],\
+          ["@smithy/protocol-http", "npm:3.0.12"],\
+          ["@smithy/smithy-client", "npm:2.2.1"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["@smithy/url-parser", "npm:2.0.16"],\
+          ["@smithy/util-base64", "npm:2.0.1"],\
+          ["@smithy/util-body-length-browser", "npm:2.0.1"],\
+          ["@smithy/util-body-length-node", "npm:2.1.0"],\
+          ["@smithy/util-defaults-mode-browser", "npm:2.0.24"],\
+          ["@smithy/util-defaults-mode-node", "npm:2.0.32"],\
+          ["@smithy/util-endpoints", "npm:1.0.8"],\
+          ["@smithy/util-middleware", "npm:2.0.9"],\
+          ["@smithy/util-retry", "npm:2.0.9"],\
+          ["@smithy/util-utf8", "npm:2.0.2"],\
+          ["fast-xml-parser", "npm:4.2.5"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/core", [\
@@ -576,6 +718,19 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-core-npm-3.485.0-77ed30ee18-b84dafb213.zip/node_modules/@aws-sdk/core/",\
+        "packageDependencies": [\
+          ["@aws-sdk/core", "npm:3.485.0"],\
+          ["@smithy/core", "npm:1.2.2"],\
+          ["@smithy/protocol-http", "npm:3.0.12"],\
+          ["@smithy/signature-v4", "npm:2.0.5"],\
+          ["@smithy/smithy-client", "npm:2.2.1"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/credential-provider-env", [\
@@ -589,6 +744,17 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-env-npm-3.485.0-0fda7f74e0-b8346ea6f5.zip/node_modules/@aws-sdk/credential-provider-env/",\
+        "packageDependencies": [\
+          ["@aws-sdk/credential-provider-env", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/property-provider", "npm:2.0.5"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/credential-provider-ini", [\
@@ -608,6 +774,23 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-ini-npm-3.485.0-bec3aaa989-3176b03ee1.zip/node_modules/@aws-sdk/credential-provider-ini/",\
+        "packageDependencies": [\
+          ["@aws-sdk/credential-provider-ini", "npm:3.485.0"],\
+          ["@aws-sdk/credential-provider-env", "npm:3.485.0"],\
+          ["@aws-sdk/credential-provider-process", "npm:3.485.0"],\
+          ["@aws-sdk/credential-provider-sso", "npm:3.485.0"],\
+          ["@aws-sdk/credential-provider-web-identity", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/credential-provider-imds", "npm:2.0.5"],\
+          ["@smithy/property-provider", "npm:2.0.5"],\
+          ["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/credential-provider-node", [\
@@ -628,6 +811,24 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-node-npm-3.485.0-9f40e4a3cf-d31e5a95ea.zip/node_modules/@aws-sdk/credential-provider-node/",\
+        "packageDependencies": [\
+          ["@aws-sdk/credential-provider-node", "npm:3.485.0"],\
+          ["@aws-sdk/credential-provider-env", "npm:3.485.0"],\
+          ["@aws-sdk/credential-provider-ini", "npm:3.485.0"],\
+          ["@aws-sdk/credential-provider-process", "npm:3.485.0"],\
+          ["@aws-sdk/credential-provider-sso", "npm:3.485.0"],\
+          ["@aws-sdk/credential-provider-web-identity", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/credential-provider-imds", "npm:2.0.5"],\
+          ["@smithy/property-provider", "npm:2.0.5"],\
+          ["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/credential-provider-process", [\
@@ -642,6 +843,18 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-process-npm-3.485.0-62d3460338-e740fb949e.zip/node_modules/@aws-sdk/credential-provider-process/",\
+        "packageDependencies": [\
+          ["@aws-sdk/credential-provider-process", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/property-provider", "npm:2.0.5"],\
+          ["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/credential-provider-sso", [\
@@ -658,6 +871,20 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-sso-npm-3.485.0-42db25db09-7269315797.zip/node_modules/@aws-sdk/credential-provider-sso/",\
+        "packageDependencies": [\
+          ["@aws-sdk/credential-provider-sso", "npm:3.485.0"],\
+          ["@aws-sdk/client-sso", "npm:3.485.0"],\
+          ["@aws-sdk/token-providers", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/property-provider", "npm:2.0.5"],\
+          ["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/credential-provider-web-identity", [\
@@ -671,6 +898,17 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-credential-provider-web-identity-npm-3.485.0-420b04bcce-33125ce0b7.zip/node_modules/@aws-sdk/credential-provider-web-identity/",\
+        "packageDependencies": [\
+          ["@aws-sdk/credential-provider-web-identity", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/property-provider", "npm:2.0.5"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/middleware-bucket-endpoint", [\
@@ -730,6 +968,17 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-middleware-host-header-npm-3.485.0-2e625f9614-9ca3da2a26.zip/node_modules/@aws-sdk/middleware-host-header/",\
+        "packageDependencies": [\
+          ["@aws-sdk/middleware-host-header", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/protocol-http", "npm:3.0.12"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/middleware-location-constraint", [\
@@ -754,6 +1003,16 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-middleware-logger-npm-3.485.0-3ff7eeabbb-2fcb731794.zip/node_modules/@aws-sdk/middleware-logger/",\
+        "packageDependencies": [\
+          ["@aws-sdk/middleware-logger", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/middleware-recursion-detection", [\
@@ -767,6 +1026,17 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-middleware-recursion-detection-npm-3.485.0-af05ed4810-afdea18930.zip/node_modules/@aws-sdk/middleware-recursion-detection/",\
+        "packageDependencies": [\
+          ["@aws-sdk/middleware-recursion-detection", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/protocol-http", "npm:3.0.12"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/middleware-sdk-s3", [\
@@ -815,6 +1085,20 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-middleware-signing-npm-3.485.0-3117db6053-f9dbb39d8d.zip/node_modules/@aws-sdk/middleware-signing/",\
+        "packageDependencies": [\
+          ["@aws-sdk/middleware-signing", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/property-provider", "npm:2.0.5"],\
+          ["@smithy/protocol-http", "npm:3.0.12"],\
+          ["@smithy/signature-v4", "npm:2.0.5"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["@smithy/util-middleware", "npm:2.0.9"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/middleware-ssec", [\
@@ -841,6 +1125,18 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-middleware-user-agent-npm-3.485.0-983204fccf-a8fc812aff.zip/node_modules/@aws-sdk/middleware-user-agent/",\
+        "packageDependencies": [\
+          ["@aws-sdk/middleware-user-agent", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@aws-sdk/util-endpoints", "npm:3.485.0"],\
+          ["@smithy/protocol-http", "npm:3.0.12"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/region-config-resolver", [\
@@ -855,6 +1151,18 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-region-config-resolver-npm-3.485.0-1a69e46754-55bc5128b8.zip/node_modules/@aws-sdk/region-config-resolver/",\
+        "packageDependencies": [\
+          ["@aws-sdk/region-config-resolver", "npm:3.485.0"],\
+          ["@smithy/node-config-provider", "npm:2.1.9"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["@smithy/util-config-provider", "npm:2.1.0"],\
+          ["@smithy/util-middleware", "npm:2.0.9"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/signature-v4-multi-region", [\
@@ -916,6 +1224,50 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-token-providers-npm-3.485.0-1fb5ab9dfb-aed270b625.zip/node_modules/@aws-sdk/token-providers/",\
+        "packageDependencies": [\
+          ["@aws-sdk/token-providers", "npm:3.485.0"],\
+          ["@aws-crypto/sha256-browser", "npm:3.0.0"],\
+          ["@aws-crypto/sha256-js", "npm:3.0.0"],\
+          ["@aws-sdk/middleware-host-header", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-logger", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-recursion-detection", "npm:3.485.0"],\
+          ["@aws-sdk/middleware-user-agent", "npm:3.485.0"],\
+          ["@aws-sdk/region-config-resolver", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@aws-sdk/util-endpoints", "npm:3.485.0"],\
+          ["@aws-sdk/util-user-agent-browser", "npm:3.485.0"],\
+          ["@aws-sdk/util-user-agent-node", "virtual:5f6733bd23aee10dd05576af160f1b93e0bb4a20b288e9b818dc0b69bdb08ea1a09d5836816f02bdafc9c01487816ae339c6b680c2f7849dfe249436c5f2b499#npm:3.485.0"],\
+          ["@smithy/config-resolver", "npm:2.0.23"],\
+          ["@smithy/fetch-http-handler", "npm:2.3.2"],\
+          ["@smithy/hash-node", "npm:2.0.18"],\
+          ["@smithy/invalid-dependency", "npm:2.0.16"],\
+          ["@smithy/middleware-content-length", "npm:2.0.18"],\
+          ["@smithy/middleware-endpoint", "npm:2.3.0"],\
+          ["@smithy/middleware-retry", "npm:2.0.26"],\
+          ["@smithy/middleware-serde", "npm:2.0.16"],\
+          ["@smithy/middleware-stack", "npm:2.0.10"],\
+          ["@smithy/node-config-provider", "npm:2.1.9"],\
+          ["@smithy/node-http-handler", "npm:2.2.2"],\
+          ["@smithy/property-provider", "npm:2.0.5"],\
+          ["@smithy/protocol-http", "npm:3.0.12"],\
+          ["@smithy/shared-ini-file-loader", "npm:2.0.6"],\
+          ["@smithy/smithy-client", "npm:2.2.1"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["@smithy/url-parser", "npm:2.0.16"],\
+          ["@smithy/util-base64", "npm:2.0.1"],\
+          ["@smithy/util-body-length-browser", "npm:2.0.1"],\
+          ["@smithy/util-body-length-node", "npm:2.1.0"],\
+          ["@smithy/util-defaults-mode-browser", "npm:2.0.24"],\
+          ["@smithy/util-defaults-mode-node", "npm:2.0.32"],\
+          ["@smithy/util-endpoints", "npm:1.0.8"],\
+          ["@smithy/util-retry", "npm:2.0.9"],\
+          ["@smithy/util-utf8", "npm:2.0.2"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/types", [\
@@ -935,6 +1287,15 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-types-npm-3.485.0-6aa8cab069-588aae4b49.zip/node_modules/@aws-sdk/types/",\
+        "packageDependencies": [\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/util-arn-parser", [\
@@ -957,6 +1318,16 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-util-endpoints-npm-3.485.0-5e0fad395e-c1844fed8b.zip/node_modules/@aws-sdk/util-endpoints/",\
+        "packageDependencies": [\
+          ["@aws-sdk/util-endpoints", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/util-endpoints", "npm:1.0.8"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/util-locate-window", [\
@@ -980,6 +1351,17 @@ const RAW_RUNTIME_STATE =
           ["tslib", "npm:2.5.2"]\
         ],\
         "linkType": "HARD"\
+      }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-util-user-agent-browser-npm-3.485.0-23925a5581-d1e4d4c635.zip/node_modules/@aws-sdk/util-user-agent-browser/",\
+        "packageDependencies": [\
+          ["@aws-sdk/util-user-agent-browser", "npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["bowser", "npm:2.11.0"],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "linkType": "HARD"\
       }]\
     ]],\
     ["@aws-sdk/util-user-agent-node", [\
@@ -990,6 +1372,30 @@ const RAW_RUNTIME_STATE =
         ],\
         "linkType": "SOFT"\
       }],\
+      ["npm:3.485.0", {\
+        "packageLocation": "./.yarn/cache/@aws-sdk-util-user-agent-node-npm-3.485.0-7991a74cb3-e2805ef37b.zip/node_modules/@aws-sdk/util-user-agent-node/",\
+        "packageDependencies": [\
+          ["@aws-sdk/util-user-agent-node", "npm:3.485.0"]\
+        ],\
+        "linkType": "SOFT"\
+      }],\
+      ["virtual:5f6733bd23aee10dd05576af160f1b93e0bb4a20b288e9b818dc0b69bdb08ea1a09d5836816f02bdafc9c01487816ae339c6b680c2f7849dfe249436c5f2b499#npm:3.485.0", {\
+        "packageLocation": "./.yarn/__virtual__/@aws-sdk-util-user-agent-node-virtual-c26ab353dd/0/cache/@aws-sdk-util-user-agent-node-npm-3.485.0-7991a74cb3-e2805ef37b.zip/node_modules/@aws-sdk/util-user-agent-node/",\
+        "packageDependencies": [\
+          ["@aws-sdk/util-user-agent-node", "virtual:5f6733bd23aee10dd05576af160f1b93e0bb4a20b288e9b818dc0b69bdb08ea1a09d5836816f02bdafc9c01487816ae339c6b680c2f7849dfe249436c5f2b499#npm:3.485.0"],\
+          ["@aws-sdk/types", "npm:3.485.0"],\
+          ["@smithy/node-config-provider", "npm:2.1.9"],\
+          ["@smithy/types", "npm:2.8.0"],\
+          ["@types/aws-crt", null],\
+          ["aws-crt", null],\
+          ["tslib", "npm:2.5.2"]\
+        ],\
+        "packagePeers": [\
+          "@types/aws-crt",\
+          "aws-crt"\
+        ],\
+        "linkType": "HARD"\
+      }],\
       ["virtual:f5e5a564ba918754c8b3b080eb29a2688af35b9c1a773de63eb148390564369a40f304a7384da1d22d51cb840254882c972d18d82a96c4804ada9a0446f624d6#npm:3.470.0", {\
         "packageLocation": "./.yarn/__virtual__/@aws-sdk-util-user-agent-node-virtual-92b1b7bd6e/0/cache/@aws-sdk-util-user-agent-node-npm-3.470.0-99b784cecc-05571ba83d.zip/node_modules/@aws-sdk/util-user-agent-node/",\
         "packageDependencies": [\
@@ -6125,6 +6531,7 @@ const RAW_RUNTIME_STATE =
         "packageLocation": "./packages/syncing-server/",\
         "packageDependencies": [\
           ["@standardnotes/syncing-server", "workspace:packages/syncing-server"],\
+          ["@aws-sdk/client-cloudwatch", "npm:3.485.0"],\
           ["@aws-sdk/client-s3", "npm:3.484.0"],\
           ["@aws-sdk/client-sns", "npm:3.484.0"],\
           ["@aws-sdk/client-sqs", "npm:3.484.0"],\

BIN
.yarn/cache/@aws-sdk-client-cloudwatch-npm-3.485.0-afe4ac001f-0e02739ef1.zip


BIN
.yarn/cache/@aws-sdk-client-sso-npm-3.485.0-5f6733bd23-635de0e310.zip


BIN
.yarn/cache/@aws-sdk-client-sts-npm-3.485.0-cc69ab3505-98c7f4d722.zip


BIN
.yarn/cache/@aws-sdk-core-npm-3.485.0-77ed30ee18-b84dafb213.zip


BIN
.yarn/cache/@aws-sdk-credential-provider-env-npm-3.485.0-0fda7f74e0-b8346ea6f5.zip


BIN
.yarn/cache/@aws-sdk-credential-provider-ini-npm-3.485.0-bec3aaa989-3176b03ee1.zip


BIN
.yarn/cache/@aws-sdk-credential-provider-node-npm-3.485.0-9f40e4a3cf-d31e5a95ea.zip


BIN
.yarn/cache/@aws-sdk-credential-provider-process-npm-3.485.0-62d3460338-e740fb949e.zip


BIN
.yarn/cache/@aws-sdk-credential-provider-sso-npm-3.485.0-42db25db09-7269315797.zip


BIN
.yarn/cache/@aws-sdk-credential-provider-web-identity-npm-3.485.0-420b04bcce-33125ce0b7.zip


BIN
.yarn/cache/@aws-sdk-middleware-host-header-npm-3.485.0-2e625f9614-9ca3da2a26.zip


BIN
.yarn/cache/@aws-sdk-middleware-logger-npm-3.485.0-3ff7eeabbb-2fcb731794.zip


BIN
.yarn/cache/@aws-sdk-middleware-recursion-detection-npm-3.485.0-af05ed4810-afdea18930.zip


BIN
.yarn/cache/@aws-sdk-middleware-signing-npm-3.485.0-3117db6053-f9dbb39d8d.zip


BIN
.yarn/cache/@aws-sdk-middleware-user-agent-npm-3.485.0-983204fccf-a8fc812aff.zip


BIN
.yarn/cache/@aws-sdk-region-config-resolver-npm-3.485.0-1a69e46754-55bc5128b8.zip


BIN
.yarn/cache/@aws-sdk-token-providers-npm-3.485.0-1fb5ab9dfb-aed270b625.zip


BIN
.yarn/cache/@aws-sdk-types-npm-3.485.0-6aa8cab069-588aae4b49.zip


BIN
.yarn/cache/@aws-sdk-util-endpoints-npm-3.485.0-5e0fad395e-c1844fed8b.zip


BIN
.yarn/cache/@aws-sdk-util-user-agent-browser-npm-3.485.0-23925a5581-d1e4d4c635.zip


BIN
.yarn/cache/@aws-sdk-util-user-agent-node-npm-3.485.0-7991a74cb3-e2805ef37b.zip


+ 3 - 0
packages/syncing-server/.env.sample

@@ -41,3 +41,6 @@ FILE_UPLOAD_PATH=
 
 VALET_TOKEN_SECRET=change-me-!
 VALET_TOKEN_TTL=7200
+
+REDIS_URL=redis://cache
+CACHE_TYPE=redis

+ 82 - 0
packages/syncing-server/bin/statistics.ts

@@ -0,0 +1,82 @@
+import 'reflect-metadata'
+
+import { Logger } from 'winston'
+import { CloudWatchClient, PutMetricDataCommand } from '@aws-sdk/client-cloudwatch'
+
+import { ContainerConfigLoader } from '../src/Bootstrap/Container'
+import TYPES from '../src/Bootstrap/Types'
+import { Env } from '../src/Bootstrap/Env'
+import { MetricsStoreInterface } from '../src/Domain/Metrics/MetricsStoreInterface'
+import { Metric } from '../src/Domain/Metrics/Metric'
+import { Time, TimerInterface } from '@standardnotes/time'
+
+const sendStatistics = async (
+  metricsStore: MetricsStoreInterface,
+  timer: TimerInterface,
+  awsRegion: string,
+): Promise<void> => {
+  const cloudwatchClient = new CloudWatchClient({
+    region: awsRegion,
+  })
+
+  const minutesToProcess = 60
+
+  const metricsToProcess = [Metric.NAMES.ItemCreated, Metric.NAMES.ItemUpdated]
+
+  for (const metricToProcess of metricsToProcess) {
+    for (let i = 0; i <= minutesToProcess; i++) {
+      const dateNMinutesAgo = timer.getUTCDateNMinutesAgo(minutesToProcess - i)
+      const timestamp = timer.convertDateToMicroseconds(dateNMinutesAgo)
+
+      const statistics = await metricsStore.getStatistics(
+        metricToProcess,
+        timestamp,
+        timestamp + Time.MicrosecondsInAMinute,
+      )
+
+      await cloudwatchClient.send(
+        new PutMetricDataCommand({
+          Namespace: 'SyncingServer',
+          MetricData: [
+            {
+              MetricName: metricToProcess,
+              Timestamp: dateNMinutesAgo,
+              StatisticValues: {
+                Maximum: statistics.max,
+                Minimum: statistics.min,
+                SampleCount: statistics.sampleCount,
+                Sum: statistics.sum,
+              },
+            },
+          ],
+        }),
+      )
+    }
+  }
+}
+
+const container = new ContainerConfigLoader('worker')
+void container.load().then((container) => {
+  const env: Env = new Env()
+  env.load()
+
+  const logger: Logger = container.get(TYPES.Sync_Logger)
+
+  logger.info('Starting statistics sending')
+
+  const metricsStore = container.get<MetricsStoreInterface>(TYPES.Sync_MetricsStore)
+  const timer = container.get<TimerInterface>(TYPES.Sync_Timer)
+  const awsRegion = env.get('SNS_AWS_REGION', true)
+
+  Promise.resolve(sendStatistics(metricsStore, timer, awsRegion))
+    .then(() => {
+      logger.info('Finished statistics sending')
+
+      process.exit(0)
+    })
+    .catch((error) => {
+      logger.error('Error while sending statistics', error)
+
+      process.exit(1)
+    })
+})

+ 11 - 0
packages/syncing-server/docker/entrypoint-statistics.js

@@ -0,0 +1,11 @@
+'use strict'
+
+const path = require('path')
+
+const pnp = require(path.normalize(path.resolve(__dirname, '../../..', '.pnp.cjs'))).setup()
+
+const index = require(path.normalize(path.resolve(__dirname, '../dist/bin/statistics.js')))
+
+Object.defineProperty(exports, '__esModule', { value: true })
+
+exports.default = index

+ 4 - 2
packages/syncing-server/docker/entrypoint.sh

@@ -5,15 +5,17 @@ COMMAND=$1 && shift 1
 
 case "$COMMAND" in
   'start-web' )
-    echo "[Docker] Starting Web..."
     exec node docker/entrypoint-server.js
     ;;
 
   'start-worker' )
-    echo "[Docker] Starting Worker..."
     exec node docker/entrypoint-worker.js
     ;;
 
+  'statistics' )
+    exec node docker/entrypoint-statistics.js
+    ;;
+
    * )
     echo "[Docker] Unknown command"
     ;;

+ 1 - 0
packages/syncing-server/package.json

@@ -32,6 +32,7 @@
     "migrate": "yarn clean && yarn build && yarn typeorm migration:run -d dist/src/Bootstrap/DataSource.js"
   },
   "dependencies": {
+    "@aws-sdk/client-cloudwatch": "^3.485.0",
     "@aws-sdk/client-s3": "^3.484.0",
     "@aws-sdk/client-sns": "^3.484.0",
     "@aws-sdk/client-sqs": "^3.484.0",

+ 30 - 0
packages/syncing-server/src/Bootstrap/Container.ts

@@ -1,4 +1,5 @@
 import * as winston from 'winston'
+import Redis from 'ioredis'
 import { Container, interfaces } from 'inversify'
 
 import { Env } from './Env'
@@ -162,6 +163,9 @@ import { SyncResponse } from '@standardnotes/grpc'
 import { SyncResponseGRPCMapper } from '../Mapping/gRPC/SyncResponseGRPCMapper'
 import { AccountDeletionVerificationRequestedEventHandler } from '../Domain/Handler/AccountDeletionVerificationRequestedEventHandler'
 import { SendEventToClients } from '../Domain/UseCase/Syncing/SendEventToClients/SendEventToClients'
+import { MetricsStoreInterface } from '../Domain/Metrics/MetricsStoreInterface'
+import { RedisMetricStore } from '../Infra/Redis/RedisMetricStore'
+import { DummyMetricStore } from '../Infra/Dummy/DummyMetricStore'
 
 export class ContainerConfigLoader {
   private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -211,6 +215,20 @@ export class ContainerConfigLoader {
     const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
     const isConfiguredForSelfHosting = env.get('MODE', true) === 'self-hosted'
     const isConfiguredForHomeServerOrSelfHosting = isConfiguredForHomeServer || isConfiguredForSelfHosting
+    const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
+
+    if (!isConfiguredForInMemoryCache) {
+      const redisUrl = env.get('REDIS_URL')
+      const isRedisInClusterMode = redisUrl.indexOf(',') > 0
+      let redis
+      if (isRedisInClusterMode) {
+        redis = new Redis.Cluster(redisUrl.split(','))
+      } else {
+        redis = new Redis(redisUrl)
+      }
+
+      container.bind(TYPES.Sync_Redis).toConstantValue(redis)
+    }
 
     container
       .bind<boolean>(TYPES.Sync_IS_CONFIGURED_FOR_HOME_SERVER_OR_SELF_HOSTING)
@@ -533,6 +551,16 @@ export class ContainerConfigLoader {
             ),
       )
 
+    if (isConfiguredForInMemoryCache) {
+      container.bind<MetricsStoreInterface>(TYPES.Sync_MetricsStore).toConstantValue(new DummyMetricStore())
+    } else {
+      container
+        .bind<MetricsStoreInterface>(TYPES.Sync_MetricsStore)
+        .toConstantValue(
+          new RedisMetricStore(container.get<Redis>(TYPES.Sync_Redis), container.get<TimerInterface>(TYPES.Sync_Timer)),
+        )
+    }
+
     // use cases
     container
       .bind<GetItems>(TYPES.Sync_GetItems)
@@ -554,6 +582,7 @@ export class ContainerConfigLoader {
           container.get(TYPES.Sync_Timer),
           container.get(TYPES.Sync_DomainEventPublisher),
           container.get(TYPES.Sync_DomainEventFactory),
+          container.get<MetricsStoreInterface>(TYPES.Sync_MetricsStore),
         ),
       )
     container
@@ -609,6 +638,7 @@ export class ContainerConfigLoader {
           container.get<DetermineSharedVaultOperationOnItem>(TYPES.Sync_DetermineSharedVaultOperationOnItem),
           container.get<AddNotificationsForUsers>(TYPES.Sync_AddNotificationsForUsers),
           container.get<RemoveNotificationsForUser>(TYPES.Sync_RemoveNotificationsForUser),
+          container.get<MetricsStoreInterface>(TYPES.Sync_MetricsStore),
         ),
       )
     container

+ 1 - 0
packages/syncing-server/src/Bootstrap/Types.ts

@@ -98,6 +98,7 @@ const TYPES = {
   Sync_SharedVaultFileMovedEventHandler: Symbol.for('Sync_SharedVaultFileMovedEventHandler'),
   Sync_SharedVaultRemovedEventHandler: Symbol.for('Sync_SharedVaultRemovedEventHandler'),
   // Services
+  Sync_MetricsStore: Symbol.for('Sync_MetricsStore'),
   Sync_ContentDecoder: Symbol.for('Sync_ContentDecoder'),
   Sync_DomainEventPublisher: Symbol.for('Sync_DomainEventPublisher'),
   Sync_DomainEventSubscriber: Symbol.for('Sync_DomainEventSubscriber'),

+ 16 - 0
packages/syncing-server/src/Domain/Metrics/Metric.spec.ts

@@ -0,0 +1,16 @@
+import { Metric } from './Metric'
+
+describe('Metric', () => {
+  it('should create a value object', () => {
+    const valueOrError = Metric.create({ name: 'ItemCreated', timestamp: 0 })
+
+    expect(valueOrError.isFailed()).toBeFalsy()
+    expect(valueOrError.getValue().props.name).toEqual('ItemCreated')
+  })
+
+  it('should not create an invalid value object', () => {
+    const valueOrError = Metric.create({ name: 'InvalidMetricName', timestamp: 0 })
+
+    expect(valueOrError.isFailed()).toBeTruthy()
+  })
+})

+ 19 - 0
packages/syncing-server/src/Domain/Metrics/Metric.ts

@@ -0,0 +1,19 @@
+import { Result, ValueObject } from '@standardnotes/domain-core'
+
+import { MetricProps } from './MetricProps'
+
+export class Metric extends ValueObject<MetricProps> {
+  static readonly NAMES = {
+    ItemCreated: 'ItemCreated',
+    ItemUpdated: 'ItemUpdated',
+  }
+
+  static create(props: MetricProps): Result<Metric> {
+    const isValidName = Object.values(this.NAMES).includes(props.name)
+    if (!isValidName) {
+      return Result.fail<Metric>(`Invalid metric name: ${props.name}`)
+    } else {
+      return Result.ok<Metric>(new Metric(props))
+    }
+  }
+}

+ 4 - 0
packages/syncing-server/src/Domain/Metrics/MetricProps.ts

@@ -0,0 +1,4 @@
+export interface MetricProps {
+  name: string
+  timestamp: number
+}

+ 15 - 0
packages/syncing-server/src/Domain/Metrics/MetricsStoreInterface.ts

@@ -0,0 +1,15 @@
+import { Metric } from './Metric'
+
+export interface MetricsStoreInterface {
+  storeMetric(metric: Metric): Promise<void>
+  getStatistics(
+    name: string,
+    from: number,
+    to: number,
+  ): Promise<{
+    sum: number
+    max: number
+    min: number
+    sampleCount: number
+  }>
+}

+ 7 - 1
packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.spec.ts

@@ -8,6 +8,7 @@ import { ContentType, Dates, Result, Timestamps, UniqueEntityId, Uuid } from '@s
 import { Item } from '../../../Item/Item'
 import { SharedVaultAssociation } from '../../../SharedVault/SharedVaultAssociation'
 import { KeySystemAssociation } from '../../../KeySystem/KeySystemAssociation'
+import { MetricsStoreInterface } from '../../../Metrics/MetricsStoreInterface'
 
 describe('SaveNewItem', () => {
   let itemRepository: ItemRepositoryInterface
@@ -16,12 +17,17 @@ describe('SaveNewItem', () => {
   let domainEventFactory: DomainEventFactoryInterface
   let itemHash1: ItemHash
   let item1: Item
+  let metricsStore: MetricsStoreInterface
 
-  const createUseCase = () => new SaveNewItem(itemRepository, timer, domainEventPublisher, domainEventFactory)
+  const createUseCase = () =>
+    new SaveNewItem(itemRepository, timer, domainEventPublisher, domainEventFactory, metricsStore)
 
   beforeEach(() => {
     const timeHelper = new Timer()
 
+    metricsStore = {} as jest.Mocked<MetricsStoreInterface>
+    metricsStore.storeMetric = jest.fn()
+
     item1 = Item.create(
       {
         userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),

+ 7 - 0
packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts

@@ -17,6 +17,8 @@ import { DomainEventFactoryInterface } from '../../../Event/DomainEventFactoryIn
 import { SharedVaultAssociation } from '../../../SharedVault/SharedVaultAssociation'
 import { KeySystemAssociation } from '../../../KeySystem/KeySystemAssociation'
 import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
+import { MetricsStoreInterface } from '../../../Metrics/MetricsStoreInterface'
+import { Metric } from '../../../Metrics/Metric'
 
 export class SaveNewItem implements UseCaseInterface<Item> {
   constructor(
@@ -24,6 +26,7 @@ export class SaveNewItem implements UseCaseInterface<Item> {
     private timer: TimerInterface,
     private domainEventPublisher: DomainEventPublisherInterface,
     private domainEventFactory: DomainEventFactoryInterface,
+    private metricsStore: MetricsStoreInterface,
   ) {}
 
   async execute(dto: SaveNewItemDTO): Promise<Result<Item>> {
@@ -135,6 +138,10 @@ export class SaveNewItem implements UseCaseInterface<Item> {
 
     await this.itemRepository.insert(newItem)
 
+    await this.metricsStore.storeMetric(
+      Metric.create({ name: Metric.NAMES.ItemCreated, timestamp: this.timer.getTimestampInMicroseconds() }).getValue(),
+    )
+
     if (contentType.value !== null && [ContentType.TYPES.Note, ContentType.TYPES.File].includes(contentType.value)) {
       await this.domainEventPublisher.publish(
         this.domainEventFactory.createItemRevisionCreationRequested({

+ 6 - 0
packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.spec.ts

@@ -20,6 +20,7 @@ import { DetermineSharedVaultOperationOnItem } from '../../SharedVaults/Determin
 import { RemoveNotificationsForUser } from '../../Messaging/RemoveNotificationsForUser/RemoveNotificationsForUser'
 import { SharedVaultOperationOnItem } from '../../../SharedVault/SharedVaultOperationOnItem'
 import { AddNotificationsForUsers } from '../../Messaging/AddNotificationsForUsers/AddNotificationsForUsers'
+import { MetricsStoreInterface } from '../../../Metrics/MetricsStoreInterface'
 
 describe('UpdateExistingItem', () => {
   let itemRepository: ItemRepositoryInterface
@@ -31,6 +32,7 @@ describe('UpdateExistingItem', () => {
   let determineSharedVaultOperationOnItem: DetermineSharedVaultOperationOnItem
   let addNotificationsForUsers: AddNotificationsForUsers
   let removeNotificationsForUser: RemoveNotificationsForUser
+  let metricsStore: MetricsStoreInterface
 
   const createUseCase = () =>
     new UpdateExistingItem(
@@ -43,11 +45,15 @@ describe('UpdateExistingItem', () => {
       determineSharedVaultOperationOnItem,
       addNotificationsForUsers,
       removeNotificationsForUser,
+      metricsStore,
     )
 
   beforeEach(() => {
     const timeHelper = new Timer()
 
+    metricsStore = {} as jest.Mocked<MetricsStoreInterface>
+    metricsStore.storeMetric = jest.fn()
+
     item1 = Item.create(
       {
         userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),

+ 7 - 0
packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts

@@ -24,6 +24,8 @@ import { RemoveNotificationsForUser } from '../../Messaging/RemoveNotificationsF
 import { ItemHash } from '../../../Item/ItemHash'
 import { AddNotificationsForUsers } from '../../Messaging/AddNotificationsForUsers/AddNotificationsForUsers'
 import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
+import { MetricsStoreInterface } from '../../../Metrics/MetricsStoreInterface'
+import { Metric } from '../../../Metrics/Metric'
 
 export class UpdateExistingItem implements UseCaseInterface<Item> {
   constructor(
@@ -36,6 +38,7 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
     private determineSharedVaultOperationOnItem: DetermineSharedVaultOperationOnItem,
     private addNotificationForUsers: AddNotificationsForUsers,
     private removeNotificationsForUser: RemoveNotificationsForUser,
+    private metricsStore: MetricsStoreInterface,
   ) {}
 
   async execute(dto: UpdateExistingItemDTO): Promise<Result<Item>> {
@@ -173,6 +176,10 @@ export class UpdateExistingItem implements UseCaseInterface<Item> {
 
     await this.itemRepository.update(dto.existingItem)
 
+    await this.metricsStore.storeMetric(
+      Metric.create({ name: Metric.NAMES.ItemUpdated, timestamp: this.timer.getTimestampInMicroseconds() }).getValue(),
+    )
+
     /* istanbul ignore next */
     const revisionsFrequency = dto.isFreeUser ? this.freeRevisionFrequency : this.premiumRevisionFrequency
 

+ 21 - 0
packages/syncing-server/src/Infra/Dummy/DummyMetricStore.ts

@@ -0,0 +1,21 @@
+import { MetricsStoreInterface } from '../../Domain/Metrics/MetricsStoreInterface'
+import { Metric } from '../../Domain/Metrics/Metric'
+
+export class DummyMetricStore implements MetricsStoreInterface {
+  async storeMetric(_metric: Metric): Promise<void> {
+    // do nothing
+  }
+
+  async getStatistics(
+    _name: string,
+    _from: number,
+    _to: number,
+  ): Promise<{ sum: number; max: number; min: number; sampleCount: number }> {
+    return {
+      sum: 0,
+      max: 0,
+      min: 0,
+      sampleCount: 0,
+    }
+  }
+}

+ 92 - 0
packages/syncing-server/src/Infra/Redis/RedisMetricStore.ts

@@ -0,0 +1,92 @@
+import * as IORedis from 'ioredis'
+import { TimerInterface } from '@standardnotes/time'
+
+import { MetricsStoreInterface } from '../../Domain/Metrics/MetricsStoreInterface'
+import { Metric } from '../../Domain/Metrics/Metric'
+
+export class RedisMetricStore implements MetricsStoreInterface {
+  private readonly METRIC_PREFIX = 'metric'
+
+  constructor(
+    private redisClient: IORedis.Redis,
+    private timer: TimerInterface,
+  ) {}
+
+  async getStatistics(
+    name: string,
+    from: number,
+    to: number,
+  ): Promise<{ sum: number; max: number; min: number; sampleCount: number }> {
+    const keysRepresentingSecondsBetweenFromAndTo = this.getKeysRepresentingSecondsBetweenFromAndTo(from, to)
+
+    let sum = 0
+    let max = 0
+    let min = 0
+    let sampleCount = 0
+
+    const values = await this.redisClient.mget(
+      keysRepresentingSecondsBetweenFromAndTo.map((key) => `${this.METRIC_PREFIX}:${name}:${key}`),
+    )
+
+    for (const value of values) {
+      if (!value) {
+        continue
+      }
+
+      const valueAsNumber = Number(value)
+
+      sum += valueAsNumber
+      sampleCount++
+
+      if (valueAsNumber > max) {
+        max = valueAsNumber
+      }
+
+      if (valueAsNumber < min) {
+        min = valueAsNumber
+      }
+    }
+
+    return {
+      sum,
+      max,
+      min,
+      sampleCount,
+    }
+  }
+
+  async storeMetric(metric: Metric): Promise<void> {
+    const date = this.timer.convertMicrosecondsToDate(metric.props.timestamp)
+    const dateToTheSecondString = this.timer.convertDateToFormattedString(date, 'YYYY-MM-DD HH:mm:ss')
+    const key = `${this.METRIC_PREFIX}:${metric.props.name}:${dateToTheSecondString}`
+
+    const pipeline = this.redisClient.pipeline()
+
+    pipeline.incr(key)
+
+    const expirationTimeIn24Hours = 60 * 60 * 24
+    pipeline.expire(key, expirationTimeIn24Hours)
+
+    await pipeline.exec()
+  }
+
+  private getKeysRepresentingSecondsBetweenFromAndTo(from: number, to: number): string[] {
+    const keys: string[] = []
+
+    const fromDate = this.timer.convertMicrosecondsToDate(from)
+
+    const secondsFrom = this.timer.convertMicrosecondsToSeconds(from)
+    const secondsTo = this.timer.convertMicrosecondsToSeconds(to)
+
+    const secondsBetweenFromAndTo = secondsTo - secondsFrom
+
+    for (let i = 0; i < secondsBetweenFromAndTo; i++) {
+      const fromDatePlusSeconds = new Date(fromDate.getTime() + i * 1000)
+      const dateToTheSecondString = this.timer.convertDateToFormattedString(fromDatePlusSeconds, 'YYYY-MM-DD HH:mm:ss')
+
+      keys.push(dateToTheSecondString)
+    }
+
+    return keys
+  }
+}

+ 7 - 0
packages/time/src/Domain/Time/Timer.spec.ts

@@ -47,6 +47,13 @@ describe('Timer', () => {
     expect(+date - +dateNHoursAgo >= 4 * 3600).toBeTruthy()
   })
 
+  it('should return a utc date n minutes ago', () => {
+    const date = createTimer().getUTCDate()
+    const dateNMinutesAgo = createTimer().getUTCDateNMinutesAgo(4)
+
+    expect(+date - +dateNMinutesAgo >= 4 * 60).toBeTruthy()
+  })
+
   it('should return a utc date n hours ahead', () => {
     const date = createTimer().getUTCDate()
     const dateNHoursAhead = createTimer().getUTCDateNHoursAhead(4)

+ 4 - 0
packages/time/src/Domain/Time/Timer.ts

@@ -19,6 +19,10 @@ export class Timer implements TimerInterface {
     return dayjs.utc().add(n, 'second').toDate()
   }
 
+  getUTCDateNMinutesAgo(n: number): Date {
+    return dayjs.utc().subtract(n, 'minute').toDate()
+  }
+
   convertMicrosecondsToTimeStructure(microseconds: number): TimeStructure {
     const days = Math.floor(microseconds / Time.MicrosecondsInADay)
 

+ 1 - 0
packages/time/src/Domain/Time/TimerInterface.ts

@@ -9,6 +9,7 @@ export interface TimerInterface {
   getUTCDateNHoursAgo(n: number): Date
   getUTCDateNHoursAhead(n: number): Date
   getUTCDateNSecondsAhead(n: number): Date
+  getUTCDateNMinutesAgo(n: number): Date
   convertDateToMilliseconds(date: Date): number
   convertDateToMicroseconds(date: Date): number
   convertDateToISOString(date: Date): string

+ 426 - 8
yarn.lock

@@ -156,6 +156,56 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/client-cloudwatch@npm:^3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/client-cloudwatch@npm:3.485.0"
+  dependencies:
+    "@aws-crypto/sha256-browser": "npm:3.0.0"
+    "@aws-crypto/sha256-js": "npm:3.0.0"
+    "@aws-sdk/client-sts": "npm:3.485.0"
+    "@aws-sdk/core": "npm:3.485.0"
+    "@aws-sdk/credential-provider-node": "npm:3.485.0"
+    "@aws-sdk/middleware-host-header": "npm:3.485.0"
+    "@aws-sdk/middleware-logger": "npm:3.485.0"
+    "@aws-sdk/middleware-recursion-detection": "npm:3.485.0"
+    "@aws-sdk/middleware-signing": "npm:3.485.0"
+    "@aws-sdk/middleware-user-agent": "npm:3.485.0"
+    "@aws-sdk/region-config-resolver": "npm:3.485.0"
+    "@aws-sdk/types": "npm:3.485.0"
+    "@aws-sdk/util-endpoints": "npm:3.485.0"
+    "@aws-sdk/util-user-agent-browser": "npm:3.485.0"
+    "@aws-sdk/util-user-agent-node": "npm:3.485.0"
+    "@smithy/config-resolver": "npm:^2.0.23"
+    "@smithy/core": "npm:^1.2.2"
+    "@smithy/fetch-http-handler": "npm:^2.3.2"
+    "@smithy/hash-node": "npm:^2.0.18"
+    "@smithy/invalid-dependency": "npm:^2.0.16"
+    "@smithy/middleware-content-length": "npm:^2.0.18"
+    "@smithy/middleware-endpoint": "npm:^2.3.0"
+    "@smithy/middleware-retry": "npm:^2.0.26"
+    "@smithy/middleware-serde": "npm:^2.0.16"
+    "@smithy/middleware-stack": "npm:^2.0.10"
+    "@smithy/node-config-provider": "npm:^2.1.9"
+    "@smithy/node-http-handler": "npm:^2.2.2"
+    "@smithy/protocol-http": "npm:^3.0.12"
+    "@smithy/smithy-client": "npm:^2.2.1"
+    "@smithy/types": "npm:^2.8.0"
+    "@smithy/url-parser": "npm:^2.0.16"
+    "@smithy/util-base64": "npm:^2.0.1"
+    "@smithy/util-body-length-browser": "npm:^2.0.1"
+    "@smithy/util-body-length-node": "npm:^2.1.0"
+    "@smithy/util-defaults-mode-browser": "npm:^2.0.24"
+    "@smithy/util-defaults-mode-node": "npm:^2.0.32"
+    "@smithy/util-endpoints": "npm:^1.0.8"
+    "@smithy/util-retry": "npm:^2.0.9"
+    "@smithy/util-utf8": "npm:^2.0.2"
+    "@smithy/util-waiter": "npm:^2.0.16"
+    fast-xml-parser: "npm:4.2.5"
+    tslib: "npm:^2.5.0"
+  checksum: 0e02739ef17b46381eb9640a8b1e444a0dc45525567b6dbc3a3ae88949423dd74688c38af9e8369d2e0793e1b1be7078557c1951ce60f97d7be01ede3f6d693b
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/client-s3@npm:^3.484.0":
   version: 3.484.0
   resolution: "@aws-sdk/client-s3@npm:3.484.0"
@@ -459,6 +509,51 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/client-sso@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/client-sso@npm:3.485.0"
+  dependencies:
+    "@aws-crypto/sha256-browser": "npm:3.0.0"
+    "@aws-crypto/sha256-js": "npm:3.0.0"
+    "@aws-sdk/core": "npm:3.485.0"
+    "@aws-sdk/middleware-host-header": "npm:3.485.0"
+    "@aws-sdk/middleware-logger": "npm:3.485.0"
+    "@aws-sdk/middleware-recursion-detection": "npm:3.485.0"
+    "@aws-sdk/middleware-user-agent": "npm:3.485.0"
+    "@aws-sdk/region-config-resolver": "npm:3.485.0"
+    "@aws-sdk/types": "npm:3.485.0"
+    "@aws-sdk/util-endpoints": "npm:3.485.0"
+    "@aws-sdk/util-user-agent-browser": "npm:3.485.0"
+    "@aws-sdk/util-user-agent-node": "npm:3.485.0"
+    "@smithy/config-resolver": "npm:^2.0.23"
+    "@smithy/core": "npm:^1.2.2"
+    "@smithy/fetch-http-handler": "npm:^2.3.2"
+    "@smithy/hash-node": "npm:^2.0.18"
+    "@smithy/invalid-dependency": "npm:^2.0.16"
+    "@smithy/middleware-content-length": "npm:^2.0.18"
+    "@smithy/middleware-endpoint": "npm:^2.3.0"
+    "@smithy/middleware-retry": "npm:^2.0.26"
+    "@smithy/middleware-serde": "npm:^2.0.16"
+    "@smithy/middleware-stack": "npm:^2.0.10"
+    "@smithy/node-config-provider": "npm:^2.1.9"
+    "@smithy/node-http-handler": "npm:^2.2.2"
+    "@smithy/protocol-http": "npm:^3.0.12"
+    "@smithy/smithy-client": "npm:^2.2.1"
+    "@smithy/types": "npm:^2.8.0"
+    "@smithy/url-parser": "npm:^2.0.16"
+    "@smithy/util-base64": "npm:^2.0.1"
+    "@smithy/util-body-length-browser": "npm:^2.0.1"
+    "@smithy/util-body-length-node": "npm:^2.1.0"
+    "@smithy/util-defaults-mode-browser": "npm:^2.0.24"
+    "@smithy/util-defaults-mode-node": "npm:^2.0.32"
+    "@smithy/util-endpoints": "npm:^1.0.8"
+    "@smithy/util-retry": "npm:^2.0.9"
+    "@smithy/util-utf8": "npm:^2.0.2"
+    tslib: "npm:^2.5.0"
+  checksum: 635de0e310c3608bd94d90766ac36b56663ae39df0164122236f5d4e5a3447ba7dd2f66d7f3481380e74f1d508053d218bd0c2872df679f11225dc46f52dba11
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/client-sts@npm:3.462.0":
   version: 3.462.0
   resolution: "@aws-sdk/client-sts@npm:3.462.0"
@@ -555,6 +650,54 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/client-sts@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/client-sts@npm:3.485.0"
+  dependencies:
+    "@aws-crypto/sha256-browser": "npm:3.0.0"
+    "@aws-crypto/sha256-js": "npm:3.0.0"
+    "@aws-sdk/core": "npm:3.485.0"
+    "@aws-sdk/credential-provider-node": "npm:3.485.0"
+    "@aws-sdk/middleware-host-header": "npm:3.485.0"
+    "@aws-sdk/middleware-logger": "npm:3.485.0"
+    "@aws-sdk/middleware-recursion-detection": "npm:3.485.0"
+    "@aws-sdk/middleware-user-agent": "npm:3.485.0"
+    "@aws-sdk/region-config-resolver": "npm:3.485.0"
+    "@aws-sdk/types": "npm:3.485.0"
+    "@aws-sdk/util-endpoints": "npm:3.485.0"
+    "@aws-sdk/util-user-agent-browser": "npm:3.485.0"
+    "@aws-sdk/util-user-agent-node": "npm:3.485.0"
+    "@smithy/config-resolver": "npm:^2.0.23"
+    "@smithy/core": "npm:^1.2.2"
+    "@smithy/fetch-http-handler": "npm:^2.3.2"
+    "@smithy/hash-node": "npm:^2.0.18"
+    "@smithy/invalid-dependency": "npm:^2.0.16"
+    "@smithy/middleware-content-length": "npm:^2.0.18"
+    "@smithy/middleware-endpoint": "npm:^2.3.0"
+    "@smithy/middleware-retry": "npm:^2.0.26"
+    "@smithy/middleware-serde": "npm:^2.0.16"
+    "@smithy/middleware-stack": "npm:^2.0.10"
+    "@smithy/node-config-provider": "npm:^2.1.9"
+    "@smithy/node-http-handler": "npm:^2.2.2"
+    "@smithy/protocol-http": "npm:^3.0.12"
+    "@smithy/smithy-client": "npm:^2.2.1"
+    "@smithy/types": "npm:^2.8.0"
+    "@smithy/url-parser": "npm:^2.0.16"
+    "@smithy/util-base64": "npm:^2.0.1"
+    "@smithy/util-body-length-browser": "npm:^2.0.1"
+    "@smithy/util-body-length-node": "npm:^2.1.0"
+    "@smithy/util-defaults-mode-browser": "npm:^2.0.24"
+    "@smithy/util-defaults-mode-node": "npm:^2.0.32"
+    "@smithy/util-endpoints": "npm:^1.0.8"
+    "@smithy/util-middleware": "npm:^2.0.9"
+    "@smithy/util-retry": "npm:^2.0.9"
+    "@smithy/util-utf8": "npm:^2.0.2"
+    fast-xml-parser: "npm:4.2.5"
+    tslib: "npm:^2.5.0"
+  checksum: 98c7f4d7223cd32a1e8ded3f4d8f2fdb228c3a717a42859fbbf8afbadec56826f1e41a33d126ef2855e1e7b6d411abf25d83cd6baf9afcd65b682436570e81bd
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/core@npm:3.451.0":
   version: 3.451.0
   resolution: "@aws-sdk/core@npm:3.451.0"
@@ -579,6 +722,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/core@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/core@npm:3.485.0"
+  dependencies:
+    "@smithy/core": "npm:^1.2.2"
+    "@smithy/protocol-http": "npm:^3.0.12"
+    "@smithy/signature-v4": "npm:^2.0.0"
+    "@smithy/smithy-client": "npm:^2.2.1"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: b84dafb213d97ebbab5d03b473e032c0e7f85872a8745fdb3274b628aa5c6e300c87a4df0747ebd29ac812c14566c295994642525210bfc81ded083d7559dbc9
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/credential-provider-env@npm:3.460.0":
   version: 3.460.0
   resolution: "@aws-sdk/credential-provider-env@npm:3.460.0"
@@ -603,6 +760,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/credential-provider-env@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/credential-provider-env@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/property-provider": "npm:^2.0.0"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: b8346ea6f54cdce2302d266e76cff3569151ef9d5cf31364e99b9b0808f5f62cb45ffacb48cb68ccc85bd5774443231056a648fe9db45ac9c9f6d83e47a0c2d7
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/credential-provider-ini@npm:3.460.0":
   version: 3.460.0
   resolution: "@aws-sdk/credential-provider-ini@npm:3.460.0"
@@ -639,6 +808,24 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/credential-provider-ini@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/credential-provider-ini@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/credential-provider-env": "npm:3.485.0"
+    "@aws-sdk/credential-provider-process": "npm:3.485.0"
+    "@aws-sdk/credential-provider-sso": "npm:3.485.0"
+    "@aws-sdk/credential-provider-web-identity": "npm:3.485.0"
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/credential-provider-imds": "npm:^2.0.0"
+    "@smithy/property-provider": "npm:^2.0.0"
+    "@smithy/shared-ini-file-loader": "npm:^2.0.6"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: 3176b03ee14ae1b84340a235161e1812f16da95115330917c22b0bbcc7fa86a051e53d493d589a537466cb5bbd4866897a8088d43d7d337c36aa9bb5b12b8525
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/credential-provider-node@npm:3.460.0":
   version: 3.460.0
   resolution: "@aws-sdk/credential-provider-node@npm:3.460.0"
@@ -677,6 +864,25 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/credential-provider-node@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/credential-provider-node@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/credential-provider-env": "npm:3.485.0"
+    "@aws-sdk/credential-provider-ini": "npm:3.485.0"
+    "@aws-sdk/credential-provider-process": "npm:3.485.0"
+    "@aws-sdk/credential-provider-sso": "npm:3.485.0"
+    "@aws-sdk/credential-provider-web-identity": "npm:3.485.0"
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/credential-provider-imds": "npm:^2.0.0"
+    "@smithy/property-provider": "npm:^2.0.0"
+    "@smithy/shared-ini-file-loader": "npm:^2.0.6"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: d31e5a95ea06436dc10978c39ce33263339dbd295934c37c2e762000039054456f623cf3549a07e1a2250c9f2b0552236e38dbe4cd97df809b6f7e946a65d98e
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/credential-provider-process@npm:3.460.0":
   version: 3.460.0
   resolution: "@aws-sdk/credential-provider-process@npm:3.460.0"
@@ -703,6 +909,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/credential-provider-process@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/credential-provider-process@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/property-provider": "npm:^2.0.0"
+    "@smithy/shared-ini-file-loader": "npm:^2.0.6"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: e740fb949ec17afd4e3a7515aa6f279eb587539f9114455e0869e762a1fb90b8349c3b7e2ceed1de307e3bef2ef9916a00e89ee5a3c1b1b6caf02a686a2d4be0
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/credential-provider-sso@npm:3.460.0":
   version: 3.460.0
   resolution: "@aws-sdk/credential-provider-sso@npm:3.460.0"
@@ -733,6 +952,21 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/credential-provider-sso@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/credential-provider-sso@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/client-sso": "npm:3.485.0"
+    "@aws-sdk/token-providers": "npm:3.485.0"
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/property-provider": "npm:^2.0.0"
+    "@smithy/shared-ini-file-loader": "npm:^2.0.6"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: 7269315797a1a6a9e3d1dbfd2a65ad7c092fcc4631be880f748b3561cebab30631d10d7c281880d46113306c7794b10f2b7b3f934e8047c8559e0ec0da15d0fb
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/credential-provider-web-identity@npm:3.460.0":
   version: 3.460.0
   resolution: "@aws-sdk/credential-provider-web-identity@npm:3.460.0"
@@ -757,6 +991,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/credential-provider-web-identity@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/credential-provider-web-identity@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/property-provider": "npm:^2.0.0"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: 33125ce0b7c3bf36ff3f52bda6657f667698e25bd09cb0c302e1e85b8d8b20b322e225981c887363bbe8ba47e22721184ad83ff30271b2887d4ea25de270b760
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/middleware-bucket-endpoint@npm:3.484.0":
   version: 3.484.0
   resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.484.0"
@@ -824,6 +1070,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/middleware-host-header@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/middleware-host-header@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/protocol-http": "npm:^3.0.12"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: 9ca3da2a264885448a84efe37d3262909118c5ca7d74d0459f74dd9291e036660835d648f8962144a27ef25f9b8662610e104f2534e40cb2765c2747cb7a22b9
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/middleware-location-constraint@npm:3.468.0":
   version: 3.468.0
   resolution: "@aws-sdk/middleware-location-constraint@npm:3.468.0"
@@ -857,6 +1115,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/middleware-logger@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/middleware-logger@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: 2fcb731794ecc665517b2aaba20ae24f42046cf1c7e663288afc7e97263116b6d9fa9dd36c1ba47c6ea01954a15519d6da57653b50097dab0853c6a344a69492
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/middleware-recursion-detection@npm:3.460.0":
   version: 3.460.0
   resolution: "@aws-sdk/middleware-recursion-detection@npm:3.460.0"
@@ -881,6 +1150,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/middleware-recursion-detection@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/middleware-recursion-detection@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/protocol-http": "npm:^3.0.12"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: afdea18930299e12a961f68a9a898f34f737c2ce9709c70e0043d6b7ec2ea2c6ad8df05877ab013f8d7d3f6322253bbf1ff6100ab6be445ef91a95a865f74ffa
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/middleware-sdk-s3@npm:3.484.0":
   version: 3.484.0
   resolution: "@aws-sdk/middleware-sdk-s3@npm:3.484.0"
@@ -966,6 +1247,21 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/middleware-signing@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/middleware-signing@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/property-provider": "npm:^2.0.0"
+    "@smithy/protocol-http": "npm:^3.0.12"
+    "@smithy/signature-v4": "npm:^2.0.0"
+    "@smithy/types": "npm:^2.8.0"
+    "@smithy/util-middleware": "npm:^2.0.9"
+    tslib: "npm:^2.5.0"
+  checksum: f9dbb39d8dda2841350c015eacbaa9f2444fa9b48080a5da2a41465252241f45da65007c21ce56a1961c75bf7d5395fadf39ce11ec78c96a7923a8b98a6684a2
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/middleware-ssec@npm:3.468.0":
   version: 3.468.0
   resolution: "@aws-sdk/middleware-ssec@npm:3.468.0"
@@ -1003,6 +1299,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/middleware-user-agent@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/middleware-user-agent@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/types": "npm:3.485.0"
+    "@aws-sdk/util-endpoints": "npm:3.485.0"
+    "@smithy/protocol-http": "npm:^3.0.12"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: a8fc812aff61a1a98dbd1cb6555786b7156a1024d3aba6554d56f5302e90b1b660e141f274c0a00e107276b0f4129ce4978faeb5847a54c54cc9289dd2834035
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/region-config-resolver@npm:3.451.0":
   version: 3.451.0
   resolution: "@aws-sdk/region-config-resolver@npm:3.451.0"
@@ -1029,6 +1338,19 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/region-config-resolver@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/region-config-resolver@npm:3.485.0"
+  dependencies:
+    "@smithy/node-config-provider": "npm:^2.1.9"
+    "@smithy/types": "npm:^2.8.0"
+    "@smithy/util-config-provider": "npm:^2.1.0"
+    "@smithy/util-middleware": "npm:^2.0.9"
+    tslib: "npm:^2.5.0"
+  checksum: 55bc5128b8dd325f7826afb741544689ff89da8ed5cfe4a704c90eff1c477ecf71b023cb6f52d0a2d5045eecfb4b61d82e3f98f10feb913d3720f14cce4df632
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/signature-v4-multi-region@npm:3.484.0":
   version: 3.484.0
   resolution: "@aws-sdk/signature-v4-multi-region@npm:3.484.0"
@@ -1133,6 +1455,51 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/token-providers@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/token-providers@npm:3.485.0"
+  dependencies:
+    "@aws-crypto/sha256-browser": "npm:3.0.0"
+    "@aws-crypto/sha256-js": "npm:3.0.0"
+    "@aws-sdk/middleware-host-header": "npm:3.485.0"
+    "@aws-sdk/middleware-logger": "npm:3.485.0"
+    "@aws-sdk/middleware-recursion-detection": "npm:3.485.0"
+    "@aws-sdk/middleware-user-agent": "npm:3.485.0"
+    "@aws-sdk/region-config-resolver": "npm:3.485.0"
+    "@aws-sdk/types": "npm:3.485.0"
+    "@aws-sdk/util-endpoints": "npm:3.485.0"
+    "@aws-sdk/util-user-agent-browser": "npm:3.485.0"
+    "@aws-sdk/util-user-agent-node": "npm:3.485.0"
+    "@smithy/config-resolver": "npm:^2.0.23"
+    "@smithy/fetch-http-handler": "npm:^2.3.2"
+    "@smithy/hash-node": "npm:^2.0.18"
+    "@smithy/invalid-dependency": "npm:^2.0.16"
+    "@smithy/middleware-content-length": "npm:^2.0.18"
+    "@smithy/middleware-endpoint": "npm:^2.3.0"
+    "@smithy/middleware-retry": "npm:^2.0.26"
+    "@smithy/middleware-serde": "npm:^2.0.16"
+    "@smithy/middleware-stack": "npm:^2.0.10"
+    "@smithy/node-config-provider": "npm:^2.1.9"
+    "@smithy/node-http-handler": "npm:^2.2.2"
+    "@smithy/property-provider": "npm:^2.0.0"
+    "@smithy/protocol-http": "npm:^3.0.12"
+    "@smithy/shared-ini-file-loader": "npm:^2.0.6"
+    "@smithy/smithy-client": "npm:^2.2.1"
+    "@smithy/types": "npm:^2.8.0"
+    "@smithy/url-parser": "npm:^2.0.16"
+    "@smithy/util-base64": "npm:^2.0.1"
+    "@smithy/util-body-length-browser": "npm:^2.0.1"
+    "@smithy/util-body-length-node": "npm:^2.1.0"
+    "@smithy/util-defaults-mode-browser": "npm:^2.0.24"
+    "@smithy/util-defaults-mode-node": "npm:^2.0.32"
+    "@smithy/util-endpoints": "npm:^1.0.8"
+    "@smithy/util-retry": "npm:^2.0.9"
+    "@smithy/util-utf8": "npm:^2.0.2"
+    tslib: "npm:^2.5.0"
+  checksum: aed270b62546e34a476e034f90a1d10fbb05a8a14be6c93bbcd5dd8ab77cfa3b2915bae0d6a0c21044bdd039851fcbbac01f4c931b35d4f7cd5317be5b3eab24
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/types@npm:3.460.0":
   version: 3.460.0
   resolution: "@aws-sdk/types@npm:3.460.0"
@@ -1153,6 +1520,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/types@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/types@npm:3.485.0"
+  dependencies:
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  checksum: 588aae4b494d7914475280bcecb579690170ca2a1c8b6d9c64ed05819dfd79de225eaa64a455f3d168f57b365eca743a363c27be464aa3ac59a2f2199d125ae5
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/types@npm:^3.222.0":
   version: 3.342.0
   resolution: "@aws-sdk/types@npm:3.342.0"
@@ -1193,6 +1570,17 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/util-endpoints@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/util-endpoints@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/util-endpoints": "npm:^1.0.8"
+    tslib: "npm:^2.5.0"
+  checksum: c1844fed8bb8fd01490fde9ed6cf3dad5647a51b72a964f7fd525f82a812bb79a128ff24fe0c3a3afb459290da21435548ef32e249d0018fe53bb3b4d4635bcb
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/util-locate-window@npm:^3.0.0":
   version: 3.310.0
   resolution: "@aws-sdk/util-locate-window@npm:3.310.0"
@@ -1226,6 +1614,18 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/util-user-agent-browser@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/util-user-agent-browser@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/types": "npm:^2.8.0"
+    bowser: "npm:^2.11.0"
+    tslib: "npm:^2.5.0"
+  checksum: d1e4d4c63508e91a38f0a1a35330cc7233d235efe315b56925f0d4cd4ea88baef3a6ca0f995571e3b81084e1b2ef229a5c0c9e72e2787321bb7da889231a8f77
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/util-user-agent-node@npm:3.460.0":
   version: 3.460.0
   resolution: "@aws-sdk/util-user-agent-node@npm:3.460.0"
@@ -1260,6 +1660,23 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@aws-sdk/util-user-agent-node@npm:3.485.0":
+  version: 3.485.0
+  resolution: "@aws-sdk/util-user-agent-node@npm:3.485.0"
+  dependencies:
+    "@aws-sdk/types": "npm:3.485.0"
+    "@smithy/node-config-provider": "npm:^2.1.9"
+    "@smithy/types": "npm:^2.8.0"
+    tslib: "npm:^2.5.0"
+  peerDependencies:
+    aws-crt: ">=1.0.0"
+  peerDependenciesMeta:
+    aws-crt:
+      optional: true
+  checksum: e2805ef37b90677673b73acc9dc009cdd70f6167d1f0f83fdfe65e867057f6091b924642f8ff5338aff711b8c623fe9e765f2438cdeaa232953359f756947dd5
+  languageName: node
+  linkType: hard
+
 "@aws-sdk/util-utf8-browser@npm:^3.0.0":
   version: 3.259.0
   resolution: "@aws-sdk/util-utf8-browser@npm:3.259.0"
@@ -3894,7 +4311,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@smithy/core@npm:^1.2.1":
+"@smithy/core@npm:^1.2.1, @smithy/core@npm:^1.2.2":
   version: 1.2.2
   resolution: "@smithy/core@npm:1.2.2"
   dependencies:
@@ -4066,7 +4483,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@smithy/hash-node@npm:^2.0.17":
+"@smithy/hash-node@npm:^2.0.17, @smithy/hash-node@npm:^2.0.18":
   version: 2.0.18
   resolution: "@smithy/hash-node@npm:2.0.18"
   dependencies:
@@ -4099,7 +4516,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@smithy/invalid-dependency@npm:^2.0.15":
+"@smithy/invalid-dependency@npm:^2.0.15, @smithy/invalid-dependency@npm:^2.0.16":
   version: 2.0.16
   resolution: "@smithy/invalid-dependency@npm:2.0.16"
   dependencies:
@@ -4151,7 +4568,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@smithy/middleware-content-length@npm:^2.0.17":
+"@smithy/middleware-content-length@npm:^2.0.17, @smithy/middleware-content-length@npm:^2.0.18":
   version: 2.0.18
   resolution: "@smithy/middleware-content-length@npm:2.0.18"
   dependencies:
@@ -4667,7 +5084,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@smithy/util-defaults-mode-browser@npm:^2.0.23":
+"@smithy/util-defaults-mode-browser@npm:^2.0.23, @smithy/util-defaults-mode-browser@npm:^2.0.24":
   version: 2.0.24
   resolution: "@smithy/util-defaults-mode-browser@npm:2.0.24"
   dependencies:
@@ -4695,7 +5112,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@smithy/util-defaults-mode-node@npm:^2.0.31":
+"@smithy/util-defaults-mode-node@npm:^2.0.31, @smithy/util-defaults-mode-node@npm:^2.0.32":
   version: 2.0.32
   resolution: "@smithy/util-defaults-mode-node@npm:2.0.32"
   dependencies:
@@ -4721,7 +5138,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@smithy/util-endpoints@npm:^1.0.7":
+"@smithy/util-endpoints@npm:^1.0.7, @smithy/util-endpoints@npm:^1.0.8":
   version: 1.0.8
   resolution: "@smithy/util-endpoints@npm:1.0.8"
   dependencies:
@@ -4853,7 +5270,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@smithy/util-waiter@npm:^2.0.15":
+"@smithy/util-waiter@npm:^2.0.15, @smithy/util-waiter@npm:^2.0.16":
   version: 2.0.16
   resolution: "@smithy/util-waiter@npm:2.0.16"
   dependencies:
@@ -5442,6 +5859,7 @@ __metadata:
   version: 0.0.0-use.local
   resolution: "@standardnotes/syncing-server@workspace:packages/syncing-server"
   dependencies:
+    "@aws-sdk/client-cloudwatch": "npm:^3.485.0"
     "@aws-sdk/client-s3": "npm:^3.484.0"
     "@aws-sdk/client-sns": "npm:^3.484.0"
     "@aws-sdk/client-sqs": "npm:^3.484.0"