From b791d832afddb29f65ee07ebb6b54b16d835810b Mon Sep 17 00:00:00 2001 From: gonzalojaubert <111118818+gonzalojaubert@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:27:39 +0100 Subject: [PATCH] Remove events (#1416) * Remove events * fix doc * rush change * linter fix * fix rebase * fix rebase * Post-merge fixes * pnpm-lock.yaml update * Refactor configuration method in Projects decorator * Revert "Refactor configuration method in Projects decorator" This reverts commit 7b4b1e420ef147ef471750d5e1b1af4379987a98. * Refactor configuration method in Projects decorator * Refactor getProjections method in read-model-store.ts --------- Co-authored-by: Castro, Mario --- .../remove_events_2023-06-14-10-02.json | 10 + common/config/rush/pnpm-lock.yaml | 767 +++++++++--------- .../src/booster-delete-event-dispatcher.ts | 21 + .../src/booster-event-processor.ts | 2 + packages/framework-core/src/booster.ts | 6 + .../framework-core/src/decorators/projects.ts | 65 +- .../graphql-query-events-generator.ts | 1 + .../src/services/raw-events-parser.ts | 9 +- .../src/services/read-model-store.ts | 86 +- .../graphql/graphql-query-generator.test.ts | 2 +- .../test/services/read-model-store.test.ts | 4 +- .../end-to-end/delete.events.integration.ts | 485 +++++++++++ .../src/commands/hard-delete.ts | 18 + .../src/read-models/product-read-model.ts | 26 +- .../read-models/product-updates-read-model.ts | 16 +- .../src/read-models/products-by-sku.ts | 19 +- .../read-models/special-reports-read-model.ts | 17 +- .../src/library/events-adapter.ts | 6 +- .../src/library/events-searcher-adapter.ts | 1 + packages/framework-provider-aws/src/setup.ts | 4 + .../library/events-search-adapter.test.ts | 11 +- .../src/helpers/query-helper.ts | 21 +- .../framework-provider-azure/src/index.ts | 5 + .../src/library/event-delete-adapter.ts | 135 +++ .../src/library/events-adapter.ts | 6 +- .../src/library/events-searcher-adapter.ts | 6 +- .../src/library/events-searcher-builder.ts | 1 + .../src/library/read-model-adapter.ts | 18 +- .../test/library/events-adapter.test.ts | 2 +- .../library/events-searcher-adapter.test.ts | 4 +- .../framework-provider-local/src/index.ts | 5 + .../src/library/event-delete-adapter.ts | 133 +++ .../src/library/events-adapter.ts | 1 + .../src/library/events-search-adapter.ts | 6 +- .../src/library/events-searcher-builder.ts | 1 + .../src/library/read-model-adapter.ts | 8 +- .../src/library/searcher-adapter.ts | 4 +- .../src/services/event-registry.ts | 19 + .../test/library/events-adapter.test.ts | 6 + .../library/events-search-adapter.test.ts | 22 +- packages/framework-types/src/booster-app.ts | 2 + .../src/concepts/read-model.ts | 9 + packages/framework-types/src/config.ts | 1 + packages/framework-types/src/envelope.ts | 15 + packages/framework-types/src/provider.ts | 42 + .../docs/10_going-deeper/remove-events.mdx | 91 +++ website/sidebars.js | 1 + 47 files changed, 1677 insertions(+), 463 deletions(-) create mode 100644 common/changes/@boostercloud/framework-core/remove_events_2023-06-14-10-02.json create mode 100644 packages/framework-core/src/booster-delete-event-dispatcher.ts create mode 100644 packages/framework-integration-tests/integration/provider-unaware/end-to-end/delete.events.integration.ts create mode 100644 packages/framework-integration-tests/src/commands/hard-delete.ts create mode 100644 packages/framework-provider-azure/src/library/event-delete-adapter.ts create mode 100644 packages/framework-provider-local/src/library/event-delete-adapter.ts create mode 100644 website/docs/10_going-deeper/remove-events.mdx diff --git a/common/changes/@boostercloud/framework-core/remove_events_2023-06-14-10-02.json b/common/changes/@boostercloud/framework-core/remove_events_2023-06-14-10-02.json new file mode 100644 index 000000000..987865687 --- /dev/null +++ b/common/changes/@boostercloud/framework-core/remove_events_2023-06-14-10-02.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@boostercloud/framework-core", + "comment": "Add remove events", + "type": "minor" + } + ], + "packageName": "@boostercloud/framework-core" +} \ No newline at end of file diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index a867549d2..79c6adadc 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -8,8 +8,8 @@ importers: ../../packages/application-tester: specifiers: '@apollo/client': 3.7.13 - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/framework-types': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/framework-types': workspace:^2.17.0 '@effect-ts/core': ^0.60.4 '@types/jsonwebtoken': 9.0.1 '@types/node': ^18.18.2 @@ -46,12 +46,12 @@ importers: jsonwebtoken: 9.0.1 sinon: 9.2.3 subscriptions-transport-ws: 0.11.0_graphql@16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 ws: 8.17.1 devDependencies: '@boostercloud/eslint-config': link:../../tools/eslint-config '@types/jsonwebtoken': 9.0.1 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/ws': 8.5.4 '@typescript-eslint/eslint-plugin': 5.62.0_isoa4rovreaplj6y6c4wy6pss4 '@typescript-eslint/parser': 5.62.0_trrslaohprr5r73nykufww5lry @@ -62,7 +62,7 @@ importers: eslint-plugin-import: 2.29.1_eslint@8.57.0 eslint-plugin-prettier: 3.4.0_ddm2pio5nc2sobczsauzpxvcae eslint-plugin-unicorn: 44.0.2_eslint@8.57.0 - fast-check: 3.20.0 + fast-check: 3.22.0 prettier: 2.3.0 rimraf: 5.0.10 sinon-chai: 3.5.0_chai@4.2.0+sinon@9.2.3 @@ -70,10 +70,10 @@ importers: ../../packages/cli: specifiers: - '@boostercloud/application-tester': workspace:^2.16.0 - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/framework-core': workspace:^2.16.0 - '@boostercloud/framework-types': workspace:^2.16.0 + '@boostercloud/application-tester': workspace:^2.17.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/framework-core': workspace:^2.17.0 + '@boostercloud/framework-types': workspace:^2.17.0 '@effect-ts/core': ^0.60.4 '@oclif/core': 3.15.0 '@oclif/plugin-help': ^5 @@ -129,7 +129,7 @@ importers: '@boostercloud/framework-types': link:../framework-types '@effect-ts/core': 0.60.5 '@oclif/core': 3.15.0_typescript@5.1.6 - '@oclif/plugin-help': 5.2.20_prqahbuxbjzfz737e746ioxvhu + '@oclif/plugin-help': 5.2.20_lkkcy4kiv3gczc6zag7kwihdkm chalk: 2.4.2 child-process-promise: 2.2.1 execa: 2.1.0 @@ -140,7 +140,7 @@ importers: mustache: 4.1.0 ora: 3.4.0 ts-morph: 19.0.0 - tslib: 2.6.3 + tslib: 2.7.0 devDependencies: '@boostercloud/application-tester': link:../application-tester '@boostercloud/eslint-config': link:../../tools/eslint-config @@ -154,7 +154,7 @@ importers: '@types/inquirer': 6.5.0 '@types/mocha': 10.0.1 '@types/mustache': 4.1.0 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/rewire': 2.5.30 '@types/sinon': 10.0.0 '@types/sinon-chai': 3.2.5 @@ -177,14 +177,14 @@ importers: rimraf: 5.0.10 sinon: 9.2.3 sinon-chai: 3.5.0_chai@4.2.0+sinon@9.2.3 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm ts-patch: 3.1.2 typescript: 5.1.6 ../../packages/framework-common-helpers: specifiers: - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/framework-types': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/framework-types': workspace:^2.17.0 '@effect-ts/core': ^0.60.4 '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 @@ -223,7 +223,7 @@ importers: '@effect-ts/core': 0.60.5 child-process-promise: 2.2.1 class-transformer: 0.5.1 - tslib: 2.6.3 + tslib: 2.7.0 devDependencies: '@boostercloud/eslint-config': link:../../tools/eslint-config '@types/chai': 4.2.18 @@ -231,7 +231,7 @@ importers: '@types/child-process-promise': 2.2.6 '@types/faker': 5.1.5 '@types/mocha': 10.0.1 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/rewire': 2.5.30 '@types/sinon': 10.0.0 '@types/sinon-chai': 3.2.5 @@ -253,15 +253,15 @@ importers: rimraf: 5.0.10 sinon: 9.2.3 sinon-chai: 3.5.0_chai@4.2.0+sinon@9.2.3 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm typescript: 5.1.6 ../../packages/framework-core: specifiers: - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/framework-common-helpers': workspace:^2.16.0 - '@boostercloud/framework-types': workspace:^2.16.0 - '@boostercloud/metadata-booster': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/framework-common-helpers': workspace:^2.17.0 + '@boostercloud/framework-types': workspace:^2.17.0 + '@boostercloud/metadata-booster': workspace:^2.17.0 '@effect/cli': 0.35.26 '@effect/platform': 0.48.24 '@effect/platform-node': 0.45.26 @@ -323,10 +323,10 @@ importers: '@effect/platform-node': 0.45.26_cahjalgelcnk6vcj6x2oc46m3a '@effect/printer': 0.32.2_67ibgamlfqfgywvgecp7hwrxja '@effect/printer-ansi': 0.32.26_67ibgamlfqfgywvgecp7hwrxja - '@effect/schema': 0.64.18_snixcscoukgak74x66m47k3t4i + '@effect/schema': 0.64.18_lr76zgfnixwkqlplfmwtcihp7a '@effect/typeclass': 0.23.17_effect@2.4.17 effect: 2.4.17 - fast-check: 3.20.0 + fast-check: 3.22.0 fp-ts: 2.16.9 graphql-scalars: 1.23.0_graphql@16.9.0 graphql-subscriptions: 2.0.0_graphql@16.9.0 @@ -335,7 +335,7 @@ importers: jsonwebtoken: 9.0.1 jwks-rsa: 3.0.1 reflect-metadata: 0.1.13 - tslib: 2.6.3 + tslib: 2.7.0 validator: 13.7.0 ws: 8.17.1 devDependencies: @@ -347,7 +347,7 @@ importers: '@types/inflected': 1.1.29 '@types/jsonwebtoken': 9.0.1 '@types/mocha': 10.0.1 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/sinon': 10.0.0 '@types/sinon-chai': 3.2.5 '@types/validator': 13.1.3 @@ -371,26 +371,26 @@ importers: rimraf: 5.0.10 sinon: 9.2.3 sinon-chai: 3.5.0_chai@4.2.0+sinon@9.2.3 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm ts-patch: 3.1.2 typescript: 5.1.6 ../../packages/framework-integration-tests: specifiers: '@apollo/client': 3.7.13 - '@boostercloud/application-tester': workspace:^2.16.0 - '@boostercloud/cli': workspace:^2.16.0 - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/framework-common-helpers': workspace:^2.16.0 - '@boostercloud/framework-core': workspace:^2.16.0 - '@boostercloud/framework-provider-aws': workspace:^2.16.0 - '@boostercloud/framework-provider-aws-infrastructure': workspace:^2.16.0 - '@boostercloud/framework-provider-azure': workspace:^2.16.0 - '@boostercloud/framework-provider-azure-infrastructure': workspace:^2.16.0 - '@boostercloud/framework-provider-local': workspace:^2.16.0 - '@boostercloud/framework-provider-local-infrastructure': workspace:^2.16.0 - '@boostercloud/framework-types': workspace:^2.16.0 - '@boostercloud/metadata-booster': workspace:^2.16.0 + '@boostercloud/application-tester': workspace:^2.17.0 + '@boostercloud/cli': workspace:^2.17.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/framework-common-helpers': workspace:^2.17.0 + '@boostercloud/framework-core': workspace:^2.17.0 + '@boostercloud/framework-provider-aws': workspace:^2.17.0 + '@boostercloud/framework-provider-aws-infrastructure': workspace:^2.17.0 + '@boostercloud/framework-provider-azure': workspace:^2.17.0 + '@boostercloud/framework-provider-azure-infrastructure': workspace:^2.17.0 + '@boostercloud/framework-provider-local': workspace:^2.17.0 + '@boostercloud/framework-provider-local-infrastructure': workspace:^2.17.0 + '@boostercloud/framework-types': workspace:^2.17.0 + '@boostercloud/metadata-booster': workspace:^2.17.0 '@effect-ts/core': ^0.60.4 '@effect/cli': 0.35.26 '@effect/platform': 0.48.24 @@ -467,15 +467,15 @@ importers: '@effect/platform-node': 0.45.26_cahjalgelcnk6vcj6x2oc46m3a '@effect/printer': 0.32.2_67ibgamlfqfgywvgecp7hwrxja '@effect/printer-ansi': 0.32.26_67ibgamlfqfgywvgecp7hwrxja - '@effect/schema': 0.64.18_snixcscoukgak74x66m47k3t4i + '@effect/schema': 0.64.18_lr76zgfnixwkqlplfmwtcihp7a '@effect/typeclass': 0.23.17_effect@2.4.17 aws-sdk: 2.853.0 effect: 2.4.17 express: 4.19.2 express-unless: 2.1.3 - fast-check: 3.20.0 + fast-check: 3.22.0 graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 ws: 8.17.1 devDependencies: '@apollo/client': 3.7.13_jxorufveymi4xbrutgakjkp5wa @@ -496,7 +496,7 @@ importers: '@types/jsonwebtoken': 9.0.1 '@types/mocha': 10.0.1 '@types/nedb': 1.8.16 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/sinon': 10.0.0 '@types/sinon-chai': 3.2.5 '@typescript-eslint/eslint-plugin': 5.62.0_isoa4rovreaplj6y6c4wy6pss4 @@ -530,15 +530,15 @@ importers: serverless-artillery: 0.5.2 sinon: 9.2.3 subscriptions-transport-ws: 0.11.0_graphql@16.9.0 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm ts-patch: 3.1.2 typescript: 5.1.6 ../../packages/framework-provider-aws: specifiers: - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/framework-common-helpers': workspace:^2.16.0 - '@boostercloud/framework-types': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/framework-common-helpers': workspace:^2.17.0 + '@boostercloud/framework-types': workspace:^2.17.0 '@effect-ts/core': ^0.60.4 '@types/aws-lambda': 8.10.48 '@types/chai': 4.2.18 @@ -577,7 +577,7 @@ importers: '@boostercloud/framework-common-helpers': link:../framework-common-helpers '@boostercloud/framework-types': link:../framework-types '@effect-ts/core': 0.60.5 - tslib: 2.6.3 + tslib: 2.7.0 devDependencies: '@boostercloud/eslint-config': link:../../tools/eslint-config '@types/aws-lambda': 8.10.48 @@ -586,7 +586,7 @@ importers: '@types/chai-as-promised': 7.1.4 '@types/faker': 5.1.5 '@types/mocha': 10.0.1 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/rewire': 2.5.30 '@types/sinon': 10.0.0 '@types/sinon-chai': 3.2.5 @@ -609,7 +609,7 @@ importers: rimraf: 5.0.10 sinon: 9.2.3 sinon-chai: 3.5.0_chai@4.2.0+sinon@9.2.3 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm typescript: 5.1.6 velocityjs: 2.0.6 @@ -632,10 +632,10 @@ importers: '@aws-cdk/core': ^1.170.0 '@aws-cdk/custom-resources': ^1.170.0 '@aws-cdk/cx-api': ^1.170.0 - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/framework-common-helpers': workspace:^2.16.0 - '@boostercloud/framework-provider-aws': workspace:^2.16.0 - '@boostercloud/framework-types': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/framework-common-helpers': workspace:^2.17.0 + '@boostercloud/framework-provider-aws': workspace:^2.17.0 + '@boostercloud/framework-types': workspace:^2.17.0 '@effect-ts/core': ^0.60.4 '@types/archiver': 5.1.0 '@types/aws-lambda': 8.10.48 @@ -706,7 +706,7 @@ importers: colors: 1.4.0 constructs: 3.4.344 promptly: 3.2.0 - tslib: 2.6.3 + tslib: 2.7.0 yaml: 1.10.2 devDependencies: '@boostercloud/eslint-config': link:../../tools/eslint-config @@ -717,7 +717,7 @@ importers: '@types/chai-as-promised': 7.1.4 '@types/faker': 5.1.5 '@types/mocha': 10.0.1 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/rewire': 2.5.30 '@types/sinon': 10.0.0 '@types/sinon-chai': 3.2.5 @@ -738,7 +738,7 @@ importers: rimraf: 5.0.10 sinon: 9.2.3 sinon-chai: 3.5.0_chai@4.2.0+sinon@9.2.3 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm typescript: 5.1.6 velocityjs: 2.0.6 @@ -749,9 +749,9 @@ importers: '@azure/functions': ^1.2.2 '@azure/identity': ~2.1.0 '@azure/web-pubsub': ~1.1.0 - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/framework-common-helpers': workspace:^2.16.0 - '@boostercloud/framework-types': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/framework-common-helpers': workspace:^2.17.0 + '@boostercloud/framework-types': workspace:^2.17.0 '@effect-ts/core': ^0.60.4 '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 @@ -780,22 +780,22 @@ importers: tslib: ^2.4.0 typescript: 5.1.6 dependencies: - '@azure/cosmos': 4.0.0 + '@azure/cosmos': 4.1.0 '@azure/event-hubs': 5.11.1 '@azure/functions': 1.2.3 '@azure/identity': 2.1.0 - '@azure/web-pubsub': 1.1.1 + '@azure/web-pubsub': 1.1.3 '@boostercloud/framework-common-helpers': link:../framework-common-helpers '@boostercloud/framework-types': link:../framework-types '@effect-ts/core': 0.60.5 - tslib: 2.6.3 + tslib: 2.7.0 devDependencies: '@boostercloud/eslint-config': link:../../tools/eslint-config '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 '@types/faker': 5.1.5 '@types/mocha': 10.0.1 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/sinon': 10.0.0 '@types/sinon-chai': 3.2.5 '@typescript-eslint/eslint-plugin': 5.62.0_isoa4rovreaplj6y6c4wy6pss4 @@ -814,7 +814,7 @@ importers: rimraf: 5.0.10 sinon: 9.2.3 sinon-chai: 3.5.0_chai@4.2.0+sinon@9.2.3 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm typescript: 5.1.6 ../../packages/framework-provider-azure-infrastructure: @@ -823,11 +823,11 @@ importers: '@azure/arm-resources': ^5.0.1 '@azure/cosmos': ^4.0.0 '@azure/identity': ~2.1.0 - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/framework-common-helpers': workspace:^2.16.0 - '@boostercloud/framework-core': workspace:^2.16.0 - '@boostercloud/framework-provider-azure': workspace:^2.16.0 - '@boostercloud/framework-types': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/framework-common-helpers': workspace:^2.17.0 + '@boostercloud/framework-core': workspace:^2.17.0 + '@boostercloud/framework-provider-azure': workspace:^2.17.0 + '@boostercloud/framework-types': workspace:^2.17.0 '@cdktf/provider-azurerm': 11.2.0 '@cdktf/provider-time': 9.0.2 '@effect-ts/core': ^0.60.4 @@ -877,7 +877,7 @@ importers: dependencies: '@azure/arm-appservice': 13.0.3 '@azure/arm-resources': 5.2.0 - '@azure/cosmos': 4.0.0 + '@azure/cosmos': 4.1.0 '@azure/identity': 2.1.0 '@boostercloud/framework-common-helpers': link:../framework-common-helpers '@boostercloud/framework-core': link:../framework-core @@ -902,7 +902,7 @@ importers: ora: 3.4.0 react: 17.0.2 sinon-chai: 3.5.0_chai@4.2.0+sinon@9.2.3 - tslib: 2.6.3 + tslib: 2.7.0 uuid: 8.3.2 devDependencies: '@boostercloud/eslint-config': link:../../tools/eslint-config @@ -912,7 +912,7 @@ importers: '@types/fs-extra': 9.0.13 '@types/mocha': 10.0.1 '@types/mustache': 4.1.0 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/sinon': 10.0.0 '@types/sinon-chai': 3.2.5 '@types/uuid': 8.3.0 @@ -929,14 +929,14 @@ importers: prettier: 2.3.0 rimraf: 5.0.10 sinon: 9.2.3 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm typescript: 5.1.6 ../../packages/framework-provider-local: specifiers: - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/framework-common-helpers': workspace:^2.16.0 - '@boostercloud/framework-types': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/framework-common-helpers': workspace:^2.17.0 + '@boostercloud/framework-types': workspace:^2.17.0 '@effect-ts/core': ^0.60.4 '@seald-io/nedb': 4.0.2 '@types/chai': 4.2.18 @@ -976,7 +976,7 @@ importers: '@boostercloud/framework-types': link:../framework-types '@effect-ts/core': 0.60.5 '@seald-io/nedb': 4.0.2 - tslib: 2.6.3 + tslib: 2.7.0 ws: 8.17.1 devDependencies: '@boostercloud/eslint-config': link:../../tools/eslint-config @@ -985,7 +985,7 @@ importers: '@types/express': 4.17.21 '@types/faker': 5.1.5 '@types/mocha': 10.0.1 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/sinon': 10.0.0 '@types/sinon-chai': 3.2.5 '@types/sinon-express-mock': 1.3.12 @@ -1008,15 +1008,15 @@ importers: sinon: 9.2.3 sinon-chai: 3.5.0_chai@4.2.0+sinon@9.2.3 sinon-express-mock: 2.2.1_sinon@9.2.3 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm typescript: 5.1.6 ../../packages/framework-provider-local-infrastructure: specifiers: - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/framework-common-helpers': workspace:^2.16.0 - '@boostercloud/framework-provider-local': workspace:^2.16.0 - '@boostercloud/framework-types': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/framework-common-helpers': workspace:^2.17.0 + '@boostercloud/framework-provider-local': workspace:^2.17.0 + '@boostercloud/framework-types': workspace:^2.17.0 '@effect-ts/core': ^0.60.4 '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 @@ -1060,7 +1060,7 @@ importers: cors: 2.8.5 express: 4.19.2 node-schedule: 2.1.1 - tslib: 2.6.3 + tslib: 2.7.0 devDependencies: '@boostercloud/eslint-config': link:../../tools/eslint-config '@types/chai': 4.2.18 @@ -1069,7 +1069,7 @@ importers: '@types/express': 4.17.21 '@types/faker': 5.1.5 '@types/mocha': 10.0.1 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/node-schedule': 1.3.2 '@types/sinon': 10.0.0 '@types/sinon-chai': 3.2.5 @@ -1091,13 +1091,13 @@ importers: sinon: 9.2.3 sinon-chai: 3.5.0_chai@4.2.0+sinon@9.2.3 sinon-express-mock: 2.2.1_sinon@9.2.3 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm typescript: 5.1.6 ../../packages/framework-types: specifiers: - '@boostercloud/eslint-config': workspace:^2.16.0 - '@boostercloud/metadata-booster': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.17.0 + '@boostercloud/metadata-booster': workspace:^2.17.0 '@effect-ts/core': ^0.60.4 '@effect-ts/node': ~0.39.0 '@effect/cli': 0.35.26 @@ -1143,10 +1143,10 @@ importers: '@effect/platform': 0.48.24_aixcdyfeuz2zct7jthaihye4ri '@effect/printer': 0.32.2_67ibgamlfqfgywvgecp7hwrxja '@effect/printer-ansi': 0.32.26_67ibgamlfqfgywvgecp7hwrxja - '@effect/schema': 0.64.18_snixcscoukgak74x66m47k3t4i + '@effect/schema': 0.64.18_lr76zgfnixwkqlplfmwtcihp7a '@effect/typeclass': 0.23.17_effect@2.4.17 effect: 2.4.17 - tslib: 2.6.3 + tslib: 2.7.0 uuid: 8.3.2 web-streams-polyfill: 3.3.3 ws: 8.17.1 @@ -1156,7 +1156,7 @@ importers: '@types/chai': 4.2.18 '@types/chai-as-promised': 7.1.4 '@types/mocha': 10.0.1 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/sinon': 10.0.0 '@types/sinon-chai': 3.2.5 '@types/uuid': 8.3.0 @@ -1169,7 +1169,7 @@ importers: eslint-plugin-import: 2.29.1_eslint@8.57.0 eslint-plugin-prettier: 3.4.0_ddm2pio5nc2sobczsauzpxvcae eslint-plugin-unicorn: 44.0.2_eslint@8.57.0 - fast-check: 3.20.0 + fast-check: 3.22.0 graphql: 16.9.0 mocha: 10.2.0 nyc: 15.1.0 @@ -1181,7 +1181,7 @@ importers: ../../packages/metadata-booster: specifiers: - '@boostercloud/eslint-config': workspace:^2.16.0 + '@boostercloud/eslint-config': workspace:^2.17.0 '@effect-ts/core': ^0.60.4 '@types/node': ^18.18.2 '@typescript-eslint/eslint-plugin': ^5.0.0 @@ -1204,10 +1204,10 @@ importers: '@effect-ts/core': 0.60.5 reflect-metadata: 0.1.13 ts-morph: 19.0.0 - tslib: 2.6.3 + tslib: 2.7.0 devDependencies: '@boostercloud/eslint-config': link:../../tools/eslint-config - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@typescript-eslint/eslint-plugin': 5.62.0_isoa4rovreaplj6y6c4wy6pss4 '@typescript-eslint/parser': 5.62.0_trrslaohprr5r73nykufww5lry eslint: 8.57.0 @@ -1218,7 +1218,7 @@ importers: prettier: 2.3.0 rimraf: 5.0.10 sinon: 9.2.3 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm ts-patch: 3.1.2 typescript: 5.1.6 @@ -1294,7 +1294,7 @@ packages: subscriptions-transport-ws: 0.11.0_graphql@16.9.0 symbol-observable: 4.0.0 ts-invariant: 0.10.3 - tslib: 2.6.3 + tslib: 2.7.0 zen-observable-ts: 1.2.5 dev: true @@ -1329,7 +1329,7 @@ packages: subscriptions-transport-ws: 0.11.0_graphql@16.9.0 symbol-observable: 4.0.0 ts-invariant: 0.10.3 - tslib: 2.6.3 + tslib: 2.7.0 zen-observable-ts: 1.2.5 dev: false @@ -1660,7 +1660,7 @@ packages: '@aws-cdk/aws-codecommit': 1.204.0_scjupxxta56mdpzkdveav52ufq '@aws-cdk/aws-codestarnotifications': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu '@aws-cdk/aws-ec2': 1.204.0_r4d2a6r7lnkv26zjzkdsvuam2a - '@aws-cdk/aws-ecr': 1.204.0_4bnk2gpayjo75fecjckge2dkni + '@aws-cdk/aws-ecr': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-ecr-assets': 1.204.0_scjupxxta56mdpzkdveav52ufq '@aws-cdk/aws-events': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm '@aws-cdk/aws-iam': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu @@ -1668,11 +1668,10 @@ packages: '@aws-cdk/aws-logs': 1.204.0_l4ztnfmrjykhsbk6ow7yhidayu '@aws-cdk/aws-s3': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-s3-assets': 1.204.0_l4ztnfmrjykhsbk6ow7yhidayu - '@aws-cdk/aws-secretsmanager': 1.204.0_336juigttbrwz7tyvm6a6wfpy4 + '@aws-cdk/aws-secretsmanager': 1.204.0_2o53qceqenzlpxe4mjswmsqfiq '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam '@aws-cdk/region-info': 1.204.0 constructs: 3.4.344 - yaml: 1.10.2 transitivePeerDependencies: - '@aws-cdk/aws-lambda' - '@aws-cdk/cx-api' @@ -1788,7 +1787,6 @@ packages: '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam '@aws-cdk/custom-resources': 1.204.0_c23kgzmvfhgnr6qpzzlbsfzuc4 constructs: 3.4.344 - punycode: 2.3.1 transitivePeerDependencies: - '@aws-cdk/aws-ec2' - '@aws-cdk/aws-logs' @@ -1875,7 +1873,7 @@ packages: constructs: ^3.3.69 dependencies: '@aws-cdk/assets': 1.204.0_uszt2j4mor3yrbm3tre3az4zvy - '@aws-cdk/aws-ecr': 1.204.0_4bnk2gpayjo75fecjckge2dkni + '@aws-cdk/aws-ecr': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-iam': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu '@aws-cdk/aws-s3': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam @@ -1885,7 +1883,7 @@ packages: - '@aws-cdk/aws-events' dev: false - /@aws-cdk/aws-ecr/1.204.0_4bnk2gpayjo75fecjckge2dkni: + /@aws-cdk/aws-ecr/1.204.0_bi2u42js5xhxqcsg5gqefde4xi: resolution: {integrity: sha512-oCts9e+ackWoFHeyn/3oKm3X1lSizleWNNXHp5WGM38lpNVrtCLMKSShu5iXJBhqRH2Mz1AcA4fDMWhe8DvJFA==} engines: {node: '>= 14.15.0'} deprecated: |- @@ -1901,8 +1899,11 @@ packages: dependencies: '@aws-cdk/aws-events': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm '@aws-cdk/aws-iam': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu + '@aws-cdk/aws-kms': 1.204.0_cttdkzy7hngahjug7jmkfylr2y '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam constructs: 3.4.344 + transitivePeerDependencies: + - '@aws-cdk/cx-api' dev: false /@aws-cdk/aws-ecs/1.204.0_iu2vquo67t63xu6vdymsg3ufny: @@ -1928,7 +1929,7 @@ packages: '@aws-cdk/aws-certificatemanager': 1.204.0_xtqk4litqxecxsqs3sd6ajo2ja '@aws-cdk/aws-cloudwatch': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm '@aws-cdk/aws-ec2': 1.204.0_r4d2a6r7lnkv26zjzkdsvuam2a - '@aws-cdk/aws-ecr': 1.204.0_4bnk2gpayjo75fecjckge2dkni + '@aws-cdk/aws-ecr': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-ecr-assets': 1.204.0_scjupxxta56mdpzkdveav52ufq '@aws-cdk/aws-elasticloadbalancing': 1.204.0_s2iwowsvskkmujjbrmx4g5hlsi '@aws-cdk/aws-elasticloadbalancingv2': 1.204.0_xbmlyikxd4zabyotfrt4oo4gli @@ -1940,7 +1941,7 @@ packages: '@aws-cdk/aws-route53-targets': 1.204.0_2eviprr3zwoouaslbumtdekrhi '@aws-cdk/aws-s3': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-s3-assets': 1.204.0_l4ztnfmrjykhsbk6ow7yhidayu - '@aws-cdk/aws-secretsmanager': 1.204.0_336juigttbrwz7tyvm6a6wfpy4 + '@aws-cdk/aws-secretsmanager': 1.204.0_2o53qceqenzlpxe4mjswmsqfiq '@aws-cdk/aws-servicediscovery': 1.204.0_nu23nesxfni464wb5cy4ehgagi '@aws-cdk/aws-sns': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-sqs': 1.204.0_cttdkzy7hngahjug7jmkfylr2y @@ -2241,7 +2242,7 @@ packages: '@aws-cdk/aws-lambda': 1.204.0_afnjft5qr3fswieaeg3dwwhnvm '@aws-cdk/aws-s3': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-s3-notifications': 1.204.0_xguspq3b5n56mo6dsez57f32qa - '@aws-cdk/aws-secretsmanager': 1.204.0_336juigttbrwz7tyvm6a6wfpy4 + '@aws-cdk/aws-secretsmanager': 1.204.0_2o53qceqenzlpxe4mjswmsqfiq '@aws-cdk/aws-sns': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-sns-subscriptions': 1.204.0_bpkznh2gsccwq6qpaogbkb4psu '@aws-cdk/aws-sqs': 1.204.0_cttdkzy7hngahjug7jmkfylr2y @@ -2274,7 +2275,7 @@ packages: '@aws-cdk/aws-cloudwatch': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm '@aws-cdk/aws-codeguruprofiler': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm '@aws-cdk/aws-ec2': 1.204.0_r4d2a6r7lnkv26zjzkdsvuam2a - '@aws-cdk/aws-ecr': 1.204.0_4bnk2gpayjo75fecjckge2dkni + '@aws-cdk/aws-ecr': 1.204.0_bi2u42js5xhxqcsg5gqefde4xi '@aws-cdk/aws-ecr-assets': 1.204.0_scjupxxta56mdpzkdveav52ufq '@aws-cdk/aws-efs': 1.204.0_r4d2a6r7lnkv26zjzkdsvuam2a '@aws-cdk/aws-events': 1.204.0_w2xl3dexbzdynnzeafah4cuzfm @@ -2435,7 +2436,6 @@ packages: '@aws-cdk/aws-s3-assets': 1.204.0_l4ztnfmrjykhsbk6ow7yhidayu '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam '@aws-cdk/lambda-layer-awscli': 1.204.0_e7ybiu4yrrtvf3zlvzrvcjkvyy - case: 1.6.3 constructs: 3.4.344 transitivePeerDependencies: - '@aws-cdk/assets' @@ -2511,7 +2511,7 @@ packages: constructs: 3.4.344 dev: false - /@aws-cdk/aws-secretsmanager/1.204.0_336juigttbrwz7tyvm6a6wfpy4: + /@aws-cdk/aws-secretsmanager/1.204.0_2o53qceqenzlpxe4mjswmsqfiq: resolution: {integrity: sha512-ykpjYmP6qVOFbHtkaQBu3Xk7xp2UTR0ouzk7pb+zrEHKGmRvzGq+8J0IU+qXBJgQIVwFAPf2IgOSTzj6FJPdyA==} engines: {node: '>= 14.15.0'} deprecated: |- @@ -2526,12 +2526,18 @@ packages: '@aws-cdk/cx-api': 1.204.0 constructs: ^3.3.69 dependencies: + '@aws-cdk/aws-ec2': 1.204.0_r4d2a6r7lnkv26zjzkdsvuam2a '@aws-cdk/aws-iam': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu + '@aws-cdk/aws-kms': 1.204.0_cttdkzy7hngahjug7jmkfylr2y '@aws-cdk/aws-lambda': 1.204.0_afnjft5qr3fswieaeg3dwwhnvm '@aws-cdk/aws-sam': 1.204.0_add7c2jq5lcc6idtuigbkwnzeu '@aws-cdk/core': 1.204.0_hol6usdabdbzhugfw355k4ebam '@aws-cdk/cx-api': 1.203.0 constructs: 3.4.344 + transitivePeerDependencies: + - '@aws-cdk/assets' + - '@aws-cdk/aws-logs' + - '@aws-cdk/aws-s3' dev: false /@aws-cdk/aws-servicediscovery/1.204.0_nu23nesxfni464wb5cy4ehgagi: @@ -2709,9 +2715,6 @@ packages: /@aws-cdk/cloud-assembly-schema/1.203.0: resolution: {integrity: sha512-r252InZ8Oh7q7ztriaA3n6F48QOFVfNcT/KO4XOlYyt1xDWRMENDYf+D+DVr6O5klcaa3ivvvDT7DRuW3xdVOQ==} engines: {node: '>= 14.15.0'} - dependencies: - jsonschema: 1.4.1 - semver: 7.6.3 dev: false bundledDependencies: - jsonschema @@ -2725,9 +2728,6 @@ packages: This package is no longer being updated, and users should migrate to AWS CDK v2. For more information on how to migrate, see https://docs.aws.amazon.com/cdk/v2/guide/migrating-v2.html - dependencies: - jsonschema: 1.4.1 - semver: 7.6.3 dev: false bundledDependencies: - jsonschema @@ -2736,9 +2736,6 @@ packages: /@aws-cdk/cloud-assembly-schema/2.39.1: resolution: {integrity: sha512-lSVaaedXWeK08uoq0IXDCspz9U/H4qIERemdsMQrMUDTiUe/JBby7vtmyMvOdEscE8GMAmiOzoPmAE0Uf+yw5A==} engines: {node: '>= 14.15.0'} - dependencies: - jsonschema: 1.4.1 - semver: 7.6.3 dev: false bundledDependencies: - jsonschema @@ -2772,11 +2769,7 @@ packages: '@aws-cdk/cloud-assembly-schema': 1.204.0 '@aws-cdk/cx-api': 1.203.0 '@aws-cdk/region-info': 1.204.0 - '@balena/dockerignore': 1.0.2 constructs: 3.4.344 - fs-extra: 9.1.0 - ignore: 5.3.1 - minimatch: 3.1.2 dev: false bundledDependencies: - fs-extra @@ -2819,7 +2812,6 @@ packages: engines: {node: '>= 14.15.0'} dependencies: '@aws-cdk/cloud-assembly-schema': 1.203.0 - semver: 7.6.3 dev: false bundledDependencies: - semver @@ -2834,7 +2826,6 @@ packages: For more information on how to migrate, see https://docs.aws.amazon.com/cdk/v2/guide/migrating-v2.html dependencies: '@aws-cdk/cloud-assembly-schema': 1.204.0 - semver: 7.6.3 dev: false bundledDependencies: - semver @@ -2844,7 +2835,6 @@ packages: engines: {node: '>= 14.15.0'} dependencies: '@aws-cdk/cloud-assembly-schema': 2.39.1 - semver: 7.6.3 dev: false bundledDependencies: - semver @@ -2881,14 +2871,14 @@ packages: resolution: {integrity: sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==} engines: {node: '>=12.0.0'} dependencies: - tslib: 2.6.3 + tslib: 2.7.0 dev: false /@azure/abort-controller/2.1.2: resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} engines: {node: '>=18.0.0'} dependencies: - tslib: 2.6.3 + tslib: 2.7.0 dev: false /@azure/arm-appservice/13.0.3: @@ -2901,7 +2891,7 @@ packages: '@azure/core-lro': 2.7.2 '@azure/core-paging': 1.6.2 '@azure/core-rest-pipeline': 1.16.3 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - supports-color dev: false @@ -2916,7 +2906,7 @@ packages: '@azure/core-lro': 2.7.2 '@azure/core-paging': 1.6.2 '@azure/core-rest-pipeline': 1.16.3 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - supports-color dev: false @@ -2935,7 +2925,7 @@ packages: process: 0.11.10 rhea: 3.0.2 rhea-promise: 3.0.3 - tslib: 2.6.3 + tslib: 2.7.0 util: 0.12.5 transitivePeerDependencies: - supports-color @@ -2947,7 +2937,7 @@ packages: dependencies: '@azure/abort-controller': 2.1.2 '@azure/core-util': 1.9.2 - tslib: 2.6.3 + tslib: 2.7.0 dev: false /@azure/core-client/1.9.2: @@ -2960,7 +2950,7 @@ packages: '@azure/core-tracing': 1.1.2 '@azure/core-util': 1.9.2 '@azure/logger': 1.1.4 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - supports-color dev: false @@ -2972,14 +2962,14 @@ packages: '@azure/abort-controller': 2.1.2 '@azure/core-util': 1.9.2 '@azure/logger': 1.1.4 - tslib: 2.6.3 + tslib: 2.7.0 dev: false /@azure/core-paging/1.6.2: resolution: {integrity: sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==} engines: {node: '>=18.0.0'} dependencies: - tslib: 2.6.3 + tslib: 2.7.0 dev: false /@azure/core-rest-pipeline/1.16.3: @@ -2993,7 +2983,7 @@ packages: '@azure/logger': 1.1.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.5 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - supports-color dev: false @@ -3002,7 +2992,7 @@ packages: resolution: {integrity: sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA==} engines: {node: '>=18.0.0'} dependencies: - tslib: 2.6.3 + tslib: 2.7.0 dev: false /@azure/core-util/1.9.2: @@ -3010,26 +3000,23 @@ packages: engines: {node: '>=18.0.0'} dependencies: '@azure/abort-controller': 2.1.2 - tslib: 2.6.3 + tslib: 2.7.0 dev: false - /@azure/cosmos/4.0.0: - resolution: {integrity: sha512-/Z27p1+FTkmjmm8jk90zi/HrczPHw2t8WecFnsnTe4xGocWl0Z4clP0YlLUTJPhRLWYa5upwD9rMvKJkS1f1kg==} - engines: {node: '>=14.0.0'} + /@azure/cosmos/4.1.0: + resolution: {integrity: sha512-+m085WKIGkf6wyw4vT85FFXl9j3U35u+LFFVwmLqfPbolnQAtoX24cowXz+vseW4BWKyx6Lamb+Zz+jl69zn6g==} + engines: {node: '>=18.0.0'} dependencies: - '@azure/abort-controller': 1.1.0 + '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.7.2 '@azure/core-rest-pipeline': 1.16.3 '@azure/core-tracing': 1.1.2 - debug: 4.3.6 + '@azure/core-util': 1.9.2 fast-json-stable-stringify: 2.1.0 - jsbi: 3.2.5 - node-abort-controller: 3.1.1 - priorityqueuejs: 1.0.0 + jsbi: 4.3.0 + priorityqueuejs: 2.0.0 semaphore: 1.1.0 - tslib: 2.6.3 - universal-user-agent: 6.0.1 - uuid: 8.3.2 + tslib: 2.7.0 transitivePeerDependencies: - supports-color dev: false @@ -3049,7 +3036,7 @@ packages: jssha: 3.3.1 process: 0.11.10 rhea-promise: 3.0.3 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - supports-color dev: false @@ -3076,7 +3063,7 @@ packages: jws: 4.0.0 open: 8.4.2 stoppable: 1.1.0 - tslib: 2.6.3 + tslib: 2.7.0 uuid: 8.3.2 transitivePeerDependencies: - supports-color @@ -3086,7 +3073,7 @@ packages: resolution: {integrity: sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==} engines: {node: '>=18.0.0'} dependencies: - tslib: 2.6.3 + tslib: 2.7.0 dev: false /@azure/msal-browser/2.39.0: @@ -3121,9 +3108,9 @@ packages: uuid: 8.3.2 dev: false - /@azure/web-pubsub/1.1.1: - resolution: {integrity: sha512-YqPVPv3KX36LIsboNOlF2qjTj1HMWExcFOOH1QIFpbv9ptXI2McIrr5goScgLTtDWO0JIFJjz0sKSWucWPgb1w==} - engines: {node: '>=14.0.0'} + /@azure/web-pubsub/1.1.3: + resolution: {integrity: sha512-l1HHPcFCRhZ8P6oQ8C/8A2eElHWqmG7CxVUlDcXi3Fs9SUr8GYKv5euYJWS5uboaokudjAiqJ6u/oM6h+pQ4+w==} + engines: {node: '>=18.0.0'} dependencies: '@azure/core-auth': 1.7.2 '@azure/core-client': 1.9.2 @@ -3131,7 +3118,7 @@ packages: '@azure/core-tracing': 1.1.2 '@azure/logger': 1.1.4 jsonwebtoken: 9.0.1 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - supports-color dev: false @@ -3143,8 +3130,8 @@ packages: '@babel/highlight': 7.24.7 picocolors: 1.0.1 - /@babel/compat-data/7.25.2: - resolution: {integrity: sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==} + /@babel/compat-data/7.25.4: + resolution: {integrity: sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==} engines: {node: '>=6.9.0'} dev: true @@ -3154,14 +3141,14 @@ packages: dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.24.7 - '@babel/generator': 7.25.0 + '@babel/generator': 7.25.5 '@babel/helper-compilation-targets': 7.25.2 '@babel/helper-module-transforms': 7.25.2_@babel+core@7.25.2 '@babel/helpers': 7.25.0 - '@babel/parser': 7.25.3 + '@babel/parser': 7.25.4 '@babel/template': 7.25.0 - '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 + '@babel/traverse': 7.25.4 + '@babel/types': 7.25.4 convert-source-map: 2.0.0 debug: 4.3.6 gensync: 1.0.0-beta.2 @@ -3171,11 +3158,11 @@ packages: - supports-color dev: true - /@babel/generator/7.25.0: - resolution: {integrity: sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==} + /@babel/generator/7.25.5: + resolution: {integrity: sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 @@ -3184,7 +3171,7 @@ packages: resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/compat-data': 7.25.2 + '@babel/compat-data': 7.25.4 '@babel/helper-validator-option': 7.24.8 browserslist: 4.23.3 lru-cache: 5.1.1 @@ -3195,8 +3182,8 @@ packages: resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 + '@babel/traverse': 7.25.4 + '@babel/types': 7.25.4 transitivePeerDependencies: - supports-color dev: true @@ -3211,7 +3198,7 @@ packages: '@babel/helper-module-imports': 7.24.7 '@babel/helper-simple-access': 7.24.7 '@babel/helper-validator-identifier': 7.24.7 - '@babel/traverse': 7.25.3 + '@babel/traverse': 7.25.4 transitivePeerDependencies: - supports-color dev: true @@ -3220,8 +3207,8 @@ packages: resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/traverse': 7.25.3 - '@babel/types': 7.25.2 + '@babel/traverse': 7.25.4 + '@babel/types': 7.25.4 transitivePeerDependencies: - supports-color dev: true @@ -3244,7 +3231,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.25.0 - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 dev: true /@babel/highlight/7.24.7: @@ -3256,15 +3243,15 @@ packages: js-tokens: 4.0.0 picocolors: 1.0.1 - /@babel/parser/7.25.3: - resolution: {integrity: sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==} + /@babel/parser/7.25.4: + resolution: {integrity: sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 - /@babel/runtime/7.25.0: - resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==} + /@babel/runtime/7.25.4: + resolution: {integrity: sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==} engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 @@ -3275,36 +3262,32 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.24.7 - '@babel/parser': 7.25.3 - '@babel/types': 7.25.2 + '@babel/parser': 7.25.4 + '@babel/types': 7.25.4 - /@babel/traverse/7.25.3: - resolution: {integrity: sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==} + /@babel/traverse/7.25.4: + resolution: {integrity: sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.24.7 - '@babel/generator': 7.25.0 - '@babel/parser': 7.25.3 + '@babel/generator': 7.25.5 + '@babel/parser': 7.25.4 '@babel/template': 7.25.0 - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 debug: 4.3.6 globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/types/7.25.2: - resolution: {integrity: sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==} + /@babel/types/7.25.4: + resolution: {integrity: sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.24.8 '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 - /@balena/dockerignore/1.0.2: - resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} - dev: false - /@cdktf/cli-core/0.19.2_react@17.0.2: resolution: {integrity: sha512-kjgEUhrHx3kUPfL7KsTo6GrurVUPT77FmOUf7wWXt7ajNE5zCPvx/HKGmQruzt0n6eLZp1aKT+r/D6YRfXcIGA==} dependencies: @@ -3313,7 +3296,7 @@ packages: '@cdktf/hcl2json': 0.19.2 '@cdktf/node-pty-prebuilt-multiarch': 0.10.1-pre.11 '@cdktf/provider-schema': 0.19.2 - '@sentry/node': 7.118.0 + '@sentry/node': 7.119.0 archiver: 5.3.2 cdktf: 0.19.2_constructs@10.3.0 chalk: 4.1.2 @@ -3335,9 +3318,9 @@ packages: ink-spinner: 4.0.3_ink@3.2.0+react@17.0.2 ink-testing-library: 2.1.0 ink-use-stdout-dimensions: 1.0.5_ink@3.2.0+react@17.0.2 - jsii: 5.4.31 + jsii: 5.5.1 jsii-pacmak: 1.102.0 - jsii-srcmak: 0.1.1204 + jsii-srcmak: 0.1.1226 lodash.isequal: 4.5.0 log4js: 6.9.1 minimatch: 5.1.6 @@ -3369,7 +3352,7 @@ packages: /@cdktf/commons/0.19.2: resolution: {integrity: sha512-5rOeb0cSREHQa5XVsGFEV6Ce8Zwo2WxE8GIhmGd/JzeSAByhK8scHFlD3+eENl83W/8lwIkm/nSl9oDHEkENIg==} dependencies: - '@sentry/node': 7.118.0 + '@sentry/node': 7.119.0 cdktf: 0.19.2_constructs@10.3.0 ci-info: 3.9.0 codemaker: 1.102.0 @@ -3387,9 +3370,9 @@ packages: /@cdktf/hcl2cdk/0.19.2: resolution: {integrity: sha512-v0UNRvvzuCi3SnmSAgBFAnWavT0ybR1AzkK8ndgfbB5JLDoNm0iJV0MOTURZF+I0O3V9u4RZsw4DVNPdil2EEA==} dependencies: - '@babel/generator': 7.25.0 + '@babel/generator': 7.25.5 '@babel/template': 7.25.0 - '@babel/types': 7.25.2 + '@babel/types': 7.25.4 '@cdktf/commons': 0.19.2 '@cdktf/hcl2json': 0.19.2 '@cdktf/provider-generator': 0.19.2 @@ -3399,7 +3382,7 @@ packages: glob: 10.4.5 graphology: 0.25.4_graphology-types@0.24.7 graphology-types: 0.24.7 - jsii-rosetta: 5.4.29 + jsii-rosetta: 5.5.1 prettier: 2.8.8 reserved-words: 0.1.2 zod: 3.23.8 @@ -3440,7 +3423,7 @@ packages: codemaker: 1.102.0 deepmerge: 4.3.1 fs-extra: 8.1.0 - jsii-srcmak: 0.1.1204 + jsii-srcmak: 0.1.1226 transitivePeerDependencies: - debug - supports-color @@ -3501,7 +3484,7 @@ packages: '@effect/platform': 0.48.24_aixcdyfeuz2zct7jthaihye4ri '@effect/printer': 0.32.2_67ibgamlfqfgywvgecp7hwrxja '@effect/printer-ansi': 0.32.26_67ibgamlfqfgywvgecp7hwrxja - '@effect/schema': 0.64.18_snixcscoukgak74x66m47k3t4i + '@effect/schema': 0.64.18_lr76zgfnixwkqlplfmwtcihp7a effect: 2.4.17 ini: 4.1.3 toml: 3.0.0 @@ -3517,7 +3500,7 @@ packages: '@effect/platform': 0.48.24_aixcdyfeuz2zct7jthaihye4ri '@parcel/watcher': 2.4.1 effect: 2.4.17 - multipasta: 0.2.4 + multipasta: 0.2.5 dev: false /@effect/platform-node/0.45.26_cahjalgelcnk6vcj6x2oc46m3a: @@ -3530,7 +3513,7 @@ packages: '@effect/platform-node-shared': 0.3.29_cahjalgelcnk6vcj6x2oc46m3a effect: 2.4.17 mime: 3.0.0 - undici: 6.19.5 + undici: 6.19.8 ws: 8.17.1 transitivePeerDependencies: - bufferutil @@ -3543,11 +3526,11 @@ packages: '@effect/schema': ^0.64.18 effect: ^2.4.17 dependencies: - '@effect/schema': 0.64.18_snixcscoukgak74x66m47k3t4i + '@effect/schema': 0.64.18_lr76zgfnixwkqlplfmwtcihp7a effect: 2.4.17 find-my-way-ts: 0.1.5 isomorphic-ws: 5.0.0_ws@8.17.1 - multipasta: 0.2.4 + multipasta: 0.2.5 path-browserify: 1.0.1 transitivePeerDependencies: - ws @@ -3574,14 +3557,14 @@ packages: effect: 2.4.17 dev: false - /@effect/schema/0.64.18_snixcscoukgak74x66m47k3t4i: + /@effect/schema/0.64.18_lr76zgfnixwkqlplfmwtcihp7a: resolution: {integrity: sha512-utMVAjcKqmNlkJ8hzdXf3FWBOsekKbe3xhYzWLLLFCpdbTMkCFeN52l1QRXTk7rla87sNTYdMA+xcES9maOEog==} peerDependencies: effect: ^2.4.17 fast-check: ^3.13.2 dependencies: effect: 2.4.17 - fast-check: 3.20.0 + fast-check: 3.22.0 dev: false /@effect/typeclass/0.23.17_effect@2.4.17: @@ -3613,7 +3596,7 @@ packages: debug: 4.3.6 espree: 9.6.1 globals: 13.24.0 - ignore: 5.3.1 + ignore: 5.3.2 import-fresh: 3.3.0 js-yaml: 4.1.0 minimatch: 3.1.2 @@ -3675,7 +3658,7 @@ packages: dependencies: '@inquirer/type': 1.5.2 '@types/mute-stream': 0.0.1 - '@types/node': 20.14.14 + '@types/node': 20.16.1 '@types/wrap-ansi': 3.0.0 ansi-escapes: 4.3.2 chalk: 4.1.2 @@ -3694,7 +3677,7 @@ packages: dependencies: '@inquirer/type': 1.5.2 '@types/mute-stream': 0.0.4 - '@types/node': 20.14.14 + '@types/node': 20.16.1 '@types/wrap-ansi': 3.0.0 ansi-escapes: 4.3.2 chalk: 4.1.2 @@ -3881,7 +3864,7 @@ packages: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - /@oclif/core/2.16.0_prqahbuxbjzfz737e746ioxvhu: + /@oclif/core/2.16.0_lkkcy4kiv3gczc6zag7kwihdkm: resolution: {integrity: sha512-dL6atBH0zCZl1A1IXCKJgLPrM/wR7K+Wi401E/IvqsK8m2iCHW+0TEOGrans/cuN3oTW+uxIyJFHJ8Im0k4qBw==} engines: {node: '>=14.0.0'} dependencies: @@ -3908,8 +3891,8 @@ packages: strip-ansi: 6.0.1 supports-color: 8.1.1 supports-hyperlinks: 2.3.0 - ts-node: 10.9.2_prqahbuxbjzfz737e746ioxvhu - tslib: 2.6.3 + ts-node: 10.9.2_lkkcy4kiv3gczc6zag7kwihdkm + tslib: 2.7.0 widest-line: 3.1.0 wordwrap: 1.0.0 wrap-ansi: 7.0.0 @@ -3989,11 +3972,11 @@ packages: wrap-ansi: 7.0.0 dev: true - /@oclif/plugin-help/5.2.20_prqahbuxbjzfz737e746ioxvhu: + /@oclif/plugin-help/5.2.20_lkkcy4kiv3gczc6zag7kwihdkm: resolution: {integrity: sha512-u+GXX/KAGL9S10LxAwNUaWdzbEBARJ92ogmM7g3gDVud2HioCmvWQCDohNRVZ9GYV9oKwZ/M8xwd6a1d95rEKQ==} engines: {node: '>=12.0.0'} dependencies: - '@oclif/core': 2.16.0_prqahbuxbjzfz737e746ioxvhu + '@oclif/core': 2.16.0_lkkcy4kiv3gczc6zag7kwihdkm transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -4126,7 +4109,7 @@ packages: dependencies: detect-libc: 1.0.3 is-glob: 4.0.3 - micromatch: 4.0.7 + micromatch: 4.0.8 node-addon-api: 7.1.1 optionalDependencies: '@parcel/watcher-android-arm64': 2.4.1 @@ -4159,49 +4142,49 @@ packages: localforage: 1.10.0 util: 0.12.5 - /@sentry-internal/tracing/7.118.0: - resolution: {integrity: sha512-dERAshKlQLrBscHSarhHyUeGsu652bDTUN1FK0m4e3X48M3I5/s+0N880Qjpe5MprNLcINlaIgdQ9jkisvxjfw==} + /@sentry-internal/tracing/7.119.0: + resolution: {integrity: sha512-oKdFJnn+56f0DHUADlL8o9l8jTib3VDLbWQBVkjD9EprxfaCwt2m8L5ACRBdQ8hmpxCEo4I8/6traZ7qAdBUqA==} engines: {node: '>=8'} dependencies: - '@sentry/core': 7.118.0 - '@sentry/types': 7.118.0 - '@sentry/utils': 7.118.0 + '@sentry/core': 7.119.0 + '@sentry/types': 7.119.0 + '@sentry/utils': 7.119.0 - /@sentry/core/7.118.0: - resolution: {integrity: sha512-ol0xBdp3/K11IMAYSQE0FMxBOOH9hMsb/rjxXWe0hfM5c72CqYWL3ol7voPci0GELJ5CZG+9ImEU1V9r6gK64g==} + /@sentry/core/7.119.0: + resolution: {integrity: sha512-CS2kUv9rAJJEjiRat6wle3JATHypB0SyD7pt4cpX5y0dN5dZ1JrF57oLHRMnga9fxRivydHz7tMTuBhSSwhzjw==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.118.0 - '@sentry/utils': 7.118.0 + '@sentry/types': 7.119.0 + '@sentry/utils': 7.119.0 - /@sentry/integrations/7.118.0: - resolution: {integrity: sha512-C2rR4NvIMjokF8jP5qzSf1o2zxDx7IeYnr8u15Kb2+HdZtX559owALR0hfgwnfeElqMhGlJBaKUWZ48lXJMzCQ==} + /@sentry/integrations/7.119.0: + resolution: {integrity: sha512-OHShvtsRW0A+ZL/ZbMnMqDEtJddPasndjq+1aQXw40mN+zeP7At/V1yPZyFaURy86iX7Ucxw5BtmzuNy7hLyTA==} engines: {node: '>=8'} dependencies: - '@sentry/core': 7.118.0 - '@sentry/types': 7.118.0 - '@sentry/utils': 7.118.0 + '@sentry/core': 7.119.0 + '@sentry/types': 7.119.0 + '@sentry/utils': 7.119.0 localforage: 1.10.0 - /@sentry/node/7.118.0: - resolution: {integrity: sha512-79N63DvYKkNPqzmc0cjO+vMZ/nU7+CbE3K3COQNiV7gk58+666G9mRZQJuZVOVebatq5wM5UR0G4LPkwD+J84g==} + /@sentry/node/7.119.0: + resolution: {integrity: sha512-9PFzN8xS6U0oZCflpVxS2SSIsHkCaj7qYBlsvHj4CTGWfao9ImwrU6+smy4qoG6oxwPfoVb5pOOMb4WpWOvXcQ==} engines: {node: '>=8'} dependencies: - '@sentry-internal/tracing': 7.118.0 - '@sentry/core': 7.118.0 - '@sentry/integrations': 7.118.0 - '@sentry/types': 7.118.0 - '@sentry/utils': 7.118.0 + '@sentry-internal/tracing': 7.119.0 + '@sentry/core': 7.119.0 + '@sentry/integrations': 7.119.0 + '@sentry/types': 7.119.0 + '@sentry/utils': 7.119.0 - /@sentry/types/7.118.0: - resolution: {integrity: sha512-2drqrD2+6kgeg+W/ycmiti3G4lJrV3hGjY9PpJ3bJeXrh6T2+LxKPzlgSEnKFaeQWkXdZ4eaUbtTXVebMjb5JA==} + /@sentry/types/7.119.0: + resolution: {integrity: sha512-27qQbutDBPKGbuJHROxhIWc1i0HJaGLA90tjMu11wt0E4UNxXRX+UQl4Twu68v4EV3CPvQcEpQfgsViYcXmq+w==} engines: {node: '>=8'} - /@sentry/utils/7.118.0: - resolution: {integrity: sha512-43qItc/ydxZV1Zb3Kn2M54RwL9XXFa3IAYBO8S82Qvq5YUYmU2AmJ1jgg7DabXlVSWgMA1HntwqnOV3JLaEnTQ==} + /@sentry/utils/7.119.0: + resolution: {integrity: sha512-ZwyXexWn2ZIe2bBoYnXJVPc2esCSbKpdc6+0WJa8eutXfHq3FRKg4ohkfCBpfxljQGEfP1+kfin945lA21Ka+A==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.118.0 + '@sentry/types': 7.119.0 /@serverless/dashboard-plugin/6.4.0_supports-color@8.1.1: resolution: {integrity: sha512-2yJQym94sXZhEFbcOVRMJgJ4a2H9Qly94UeUesPwf8bfWCxtiB4l5rxLnCB2aLTuUf/djcuD5/VrNPY1pRU7DA==} @@ -4246,12 +4229,12 @@ packages: resolution: {integrity: sha512-XltmO/029X76zi0LUFmhsnanhE2wnqH1xf+WBt5K8gumQA9LnrfwLgPxj+VA+mm6wQhy+PCp7H5SS0ZPu7F2Cw==} engines: {node: '>=10.0'} dependencies: - adm-zip: 0.5.14 + adm-zip: 0.5.15 archiver: 5.3.0 - axios: 1.7.3 + axios: 1.7.5 fast-glob: 3.3.2 https-proxy-agent: 5.0.1_supports-color@8.1.1 - ignore: 5.3.1 + ignore: 5.3.2 isomorphic-ws: 4.0.1_ws@7.5.10 js-yaml: 3.14.1 jwt-decode: 2.2.0 @@ -4337,8 +4320,8 @@ packages: '@sinonjs/commons': 3.0.1 dev: true - /@sinonjs/fake-timers/11.2.2: - resolution: {integrity: sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==} + /@sinonjs/fake-timers/11.3.1: + resolution: {integrity: sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==} dependencies: '@sinonjs/commons': 3.0.1 dev: true @@ -4368,8 +4351,8 @@ packages: type-detect: 4.1.0 dev: true - /@sinonjs/text-encoding/0.7.2: - resolution: {integrity: sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==} + /@sinonjs/text-encoding/0.7.3: + resolution: {integrity: sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==} /@szmarczak/http-timer/4.0.6: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} @@ -4423,14 +4406,14 @@ packages: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: '@types/connect': 3.4.38 - '@types/node': 18.19.43 + '@types/node': 18.19.46 /@types/cacheable-request/6.0.3: resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/responselike': 1.0.3 dev: true @@ -4453,29 +4436,29 @@ packages: /@types/child-process-promise/2.2.6: resolution: {integrity: sha512-g0pOHijr6Trug43D2bV0PLSIsSHa/xHEES2HeX5BAlduq1vW0nZcq27Zeud5lgmNB+kPYYVqiMap32EHGTco/w==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 dev: true /@types/cli-progress/3.11.6: resolution: {integrity: sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 /@types/connect/3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 /@types/cors/2.8.17: resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 dev: true /@types/express-serve-static-core/4.19.5: resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -4495,14 +4478,14 @@ packages: /@types/fs-extra/9.0.13: resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 dev: true /@types/glob/8.1.0: resolution: {integrity: sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 18.19.43 + '@types/node': 18.19.46 /@types/http-cache-semantics/4.0.4: resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} @@ -4531,12 +4514,12 @@ packages: /@types/jsonwebtoken/9.0.1: resolution: {integrity: sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 /@types/keyv/3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 dev: true /@types/lodash/4.17.7: @@ -4560,29 +4543,29 @@ packages: /@types/mute-stream/0.0.1: resolution: {integrity: sha512-0yQLzYhCqGz7CQPE3iDmYjhb7KMBFOP+tBkyw+/Y2YyDI5wpS7itXXxneN1zSsUwWx3Ji6YiVYrhAnpQGS/vkw==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 /@types/mute-stream/0.0.4: resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 /@types/nedb/1.8.16: resolution: {integrity: sha512-ND+uzwAZk7ZI9byOvHGOcZe2R9XUcLF698yDJKn00trFvh+GaemkX3gQKCSKtObjDpv8Uuou+k8v4x4scPr4TA==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 dev: true /@types/needle/2.5.3: resolution: {integrity: sha512-RwgTwMRaedfyCBe5SSWMpm1Yqzc5UPZEMw0eAd09OSyV93nLRj9/evMGZmgFeHKzUOd4xxtHvgtc+rjcBjI1Qg==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 dev: false /@types/node-schedule/1.3.2: resolution: {integrity: sha512-Y0CqdAr+lCpArT8CJJjJq4U2v8Bb5e7ru2nV/NhDdaptCMCRdOL3Y7tAhen39HluQMaIKWvPbDuiFBUQpg7Srw==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 dev: true /@types/node/10.17.60: @@ -4594,15 +4577,15 @@ packages: dependencies: undici-types: 5.26.5 - /@types/node/18.19.43: - resolution: {integrity: sha512-Mw/YlgXnyJdEwLoFv2dpuJaDFriX+Pc+0qOBJ57jC1H6cDxIj2xc5yUrdtArDVG0m+KV6622a4p2tenEqB3C/g==} + /@types/node/18.19.46: + resolution: {integrity: sha512-vnRgMS7W6cKa1/0G3/DTtQYpVrZ8c0Xm6UkLaVFrb9jtcVC3okokW09Ki1Qdrj9ISokszD69nY4WDLRlvHlhAA==} dependencies: undici-types: 5.26.5 - /@types/node/20.14.14: - resolution: {integrity: sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==} + /@types/node/20.16.1: + resolution: {integrity: sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==} dependencies: - undici-types: 5.26.5 + undici-types: 6.19.8 /@types/normalize-package-data/2.4.4: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -4617,7 +4600,7 @@ packages: /@types/responselike/1.0.3: resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 dev: true /@types/rewire/2.5.30: @@ -4631,13 +4614,13 @@ packages: resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} dependencies: '@types/mime': 1.3.5 - '@types/node': 18.19.43 + '@types/node': 18.19.46 /@types/serve-static/1.15.7: resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} dependencies: '@types/http-errors': 2.0.4 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/send': 0.17.4 /@types/sinon-chai/3.2.5: @@ -4662,7 +4645,7 @@ packages: /@types/through/0.0.33: resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 dev: true /@types/uuid/8.3.0: @@ -4679,14 +4662,14 @@ packages: /@types/ws/8.5.4: resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 dev: true /@types/yauzl/2.10.3: resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} requiresBuild: true dependencies: - '@types/node': 18.19.43 + '@types/node': 18.19.46 optional: true /@types/yoga-layout/1.9.2: @@ -4711,7 +4694,7 @@ packages: debug: 4.3.6 eslint: 8.57.0 graphemer: 1.4.0 - ignore: 5.3.1 + ignore: 5.3.2 natural-compare-lite: 1.4.0 semver: 7.6.3 tsutils: 3.21.0_typescript@5.1.6 @@ -4821,19 +4804,19 @@ packages: resolution: {integrity: sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==} engines: {node: '>=8'} dependencies: - tslib: 2.6.3 + tslib: 2.7.0 /@wry/equality/0.5.7: resolution: {integrity: sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==} engines: {node: '>=8'} dependencies: - tslib: 2.6.3 + tslib: 2.7.0 /@wry/trie/0.3.2: resolution: {integrity: sha512-yRTyhWSls2OY/pYLfwff867r8ekooZ4UI+/gxot5Wj8EFwSf2rG+n+Mo/6LoLQm1TKA4GRj2+LCpbfS937dClQ==} engines: {node: '>=8'} dependencies: - tslib: 2.6.3 + tslib: 2.7.0 /@xmldom/xmldom/0.8.10: resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} @@ -4882,8 +4865,8 @@ packages: resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} engines: {node: '>= 10.0.0'} - /adm-zip/0.5.14: - resolution: {integrity: sha512-DnyqqifT4Jrcvb8USYjp6FHtBpEIz1mnXu6pTRHZ0RL69LbQYiO+0lDFg5+OKA7U29oWSs3a/i8fhn8ZcceIWg==} + /adm-zip/0.5.15: + resolution: {integrity: sha512-jYPWSeOA8EFoZnucrKCNihqBjoEGQSU4HKgHYQgKNEQ0pQF9a/DYuo/+fAxY76k4qe75LUlLWpAM1QWcBMTOKw==} engines: {node: '>=12.0'} dev: true @@ -5044,7 +5027,7 @@ packages: engines: {node: '>= 10'} dependencies: archiver-utils: 2.1.0 - async: 3.2.5 + async: 3.2.6 buffer-crc32: 0.2.13 readable-stream: 3.6.2 readdir-glob: 1.1.3 @@ -5056,7 +5039,7 @@ packages: engines: {node: '>= 10'} dependencies: archiver-utils: 2.1.0 - async: 3.2.5 + async: 3.2.6 buffer-crc32: 0.2.13 readable-stream: 3.6.2 readdir-glob: 1.1.3 @@ -5169,8 +5152,8 @@ packages: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} - /async/3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + /async/3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} /asynckit/0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -5198,8 +5181,8 @@ packages: fsevents: 2.3.2 dev: false - /aws-sdk/2.1667.0: - resolution: {integrity: sha512-hE4FmdZRMc3bYeC5LUAAU/ryYpjhEm1xdi4aVtUiZ14rrfMd0li6XQIM00a9ctZwDJpwJppcSXfDj6bVBCzvXQ==} + /aws-sdk/2.1683.0: + resolution: {integrity: sha512-uy53mN2oHU0Jx5tkH7qG6h/42DeMAQD5jMi/6294hVgGCa29MK/ZiFIOdkEJNS9tbB03RTabg+5i4c6DxBpG8g==} engines: {node: '>= 10.0.0'} requiresBuild: true dependencies: @@ -5228,8 +5211,8 @@ packages: uuid: 3.3.2 xml2js: 0.4.19 - /axios/1.7.3: - resolution: {integrity: sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==} + /axios/1.7.5: + resolution: {integrity: sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==} dependencies: follow-redirects: 1.15.6 form-data: 4.0.0 @@ -5317,8 +5300,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001649 - electron-to-chromium: 1.5.4 + caniuse-lite: 1.0.30001653 + electron-to-chromium: 1.5.13 node-releases: 2.0.18 update-browserslist-db: 1.1.0_browserslist@4.23.3 dev: true @@ -5433,8 +5416,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - /caniuse-lite/1.0.30001649: - resolution: {integrity: sha512-fJegqZZ0ZX8HOWr6rcafGr72+xcgJKI9oWfDW5DrD7ExUtgZC7a7R7ZYmZqplh7XDocFdGeIFn7roAxhOeYrPQ==} + /caniuse-lite/1.0.30001653: + resolution: {integrity: sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==} dev: true /cardinal/2.1.1: @@ -5456,7 +5439,7 @@ packages: '@aws-cdk/cloud-assembly-schema': 2.39.1 '@aws-cdk/cx-api': 2.39.1 archiver: 5.3.2 - aws-sdk: 2.1667.0 + aws-sdk: 2.1683.0 glob: 7.2.3 mime: 2.6.0 yargs: 16.2.0 @@ -5471,7 +5454,7 @@ packages: '@cdktf/hcl2cdk': 0.19.2 '@cdktf/hcl2json': 0.19.2 '@inquirer/prompts': 2.3.1 - '@sentry/node': 7.118.0 + '@sentry/node': 7.119.0 cdktf: 0.19.2_constructs@10.3.0 ci-info: 3.9.0 codemaker: 1.102.0 @@ -5480,7 +5463,7 @@ packages: https-proxy-agent: 5.0.1 ink-select-input: 4.2.2_ink@3.2.0+react@17.0.2 ink-table: 3.1.0_ink@3.2.0+react@17.0.2 - jsii: 5.4.31 + jsii: 5.5.1 jsii-pacmak: 1.102.0 minimatch: 5.1.6 node-fetch: 2.7.0 @@ -5507,10 +5490,7 @@ packages: peerDependencies: constructs: ^10.0.25 dependencies: - archiver: 5.3.2 constructs: 10.3.0 - json-stable-stringify: 1.1.1 - semver: 7.6.3 bundledDependencies: - archiver - json-stable-stringify @@ -6079,15 +6059,15 @@ packages: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} dependencies: - '@babel/runtime': 7.25.0 + '@babel/runtime': 7.25.4 dev: true /date-format/4.0.14: resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} engines: {node: '>=4.0'} - /dayjs/1.11.12: - resolution: {integrity: sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==} + /dayjs/1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} dev: true /debug/2.6.9: @@ -6402,7 +6382,7 @@ packages: dependencies: semver: 7.6.3 shelljs: 0.8.5 - typescript: 5.6.0-dev.20240819 + typescript: 5.7.0-dev.20240827 /duration/0.2.2: resolution: {integrity: sha512-06kgtea+bGreF5eKYgI/36A6pLXggY7oR4p1pq4SmdFBn1ReOL5D8RhG64VrqfTTKNucqqtBAwEj8aB88mcqrg==} @@ -6433,8 +6413,8 @@ packages: dependencies: jake: 10.9.2 - /electron-to-chromium/1.5.4: - resolution: {integrity: sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==} + /electron-to-chromium/1.5.13: + resolution: {integrity: sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==} dev: true /emoji-regex/7.0.3: @@ -6650,11 +6630,11 @@ packages: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: debug: 3.2.7 - is-core-module: 2.15.0 + is-core-module: 2.15.1 resolve: 1.22.8 - /eslint-module-utils/2.8.1_eslint@8.57.0: - resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + /eslint-module-utils/2.8.2_eslint@8.57.0: + resolution: {integrity: sha512-3XnC5fDyc8M4J2E8pt8pmSVRX2M+5yWMCfI/kDZwauQeFgzQOuhcRBFKjTeJagqgk4sFKxe1mvNVnaWwImx/Tg==} engines: {node: '>=4'} peerDependencies: eslint: '*' @@ -6679,9 +6659,9 @@ packages: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1_eslint@8.57.0 + eslint-module-utils: 2.8.2_eslint@8.57.0 hasown: 2.0.2 - is-core-module: 2.15.0 + is-core-module: 2.15.1 is-glob: 4.0.3 minimatch: 3.1.2 object.fromentries: 2.0.8 @@ -6850,7 +6830,7 @@ packages: glob-parent: 6.0.2 globals: 13.24.0 graphemer: 1.4.0 - ignore: 5.3.1 + ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 @@ -7084,19 +7064,19 @@ packages: dependencies: '@types/chai': 4.2.18 '@types/lodash': 4.17.7 - '@types/node': 18.19.43 + '@types/node': 18.19.46 '@types/sinon': 10.0.0 lodash: 4.17.21 mock-stdin: 1.0.0 - nock: 13.5.4 + nock: 13.5.5 sinon: 16.1.3 stdout-stderr: 0.1.13 transitivePeerDependencies: - supports-color dev: true - /fast-check/3.20.0: - resolution: {integrity: sha512-pZIjqLpOZgdSLecec4GKC3Zq5702MZ34upMKxojnNVSWA0K64V3pXOBT1Wdsrc3AphLtzRBbsi8bRWF4TUGmUg==} + /fast-check/3.22.0: + resolution: {integrity: sha512-8HKz3qXqnHYp/VCNn2qfjHdAdcI8zcSqOyX64GOMukp7SL2bfzfeDKjSd+UyECtejccaZv3LcvZTm9YDD22iCQ==} engines: {node: '>=8.0.0'} dependencies: pure-rand: 6.1.0 @@ -7115,7 +7095,7 @@ packages: '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.7 + micromatch: 4.0.8 /fast-json-stable-stringify/2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -7328,8 +7308,8 @@ packages: signal-exit: 3.0.7 dev: true - /foreground-child/3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + /foreground-child/3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} dependencies: cross-spawn: 7.0.3 @@ -7423,7 +7403,7 @@ packages: deferred: 0.7.11 es5-ext: 0.10.64 event-emitter: 0.3.5 - ignore: 5.3.1 + ignore: 5.3.2 memoizee: 0.4.17 type: 2.7.3 dev: true @@ -7543,7 +7523,7 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true dependencies: - foreground-child: 3.2.1 + foreground-child: 3.3.0 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 @@ -7614,7 +7594,7 @@ packages: array-union: 2.1.0 dir-glob: 3.0.1 fast-glob: 3.3.2 - ignore: 5.3.1 + ignore: 5.3.2 merge2: 1.4.1 slash: 3.0.0 @@ -7671,7 +7651,7 @@ packages: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 dev: false /graphql-subscriptions/2.0.0_graphql@16.9.0: @@ -7690,7 +7670,7 @@ packages: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 16.9.0 - tslib: 2.6.3 + tslib: 2.7.0 /graphql/16.9.0: resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} @@ -7849,8 +7829,8 @@ packages: engines: {node: '>= 4'} dev: true - /ignore/5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + /ignore/5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} /immediate/3.0.6: @@ -8103,8 +8083,8 @@ packages: dependencies: ci-info: 2.0.0 - /is-core-module/2.15.0: - resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==} + /is-core-module/2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} engines: {node: '>= 0.4'} dependencies: hasown: 2.0.2 @@ -8388,7 +8368,7 @@ packages: engines: {node: '>=10'} hasBin: true dependencies: - async: 3.2.5 + async: 3.2.6 chalk: 4.1.2 filelist: 1.0.4 minimatch: 3.1.2 @@ -8420,8 +8400,8 @@ packages: dependencies: argparse: 2.0.1 - /jsbi/3.2.5: - resolution: {integrity: sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ==} + /jsbi/4.3.0: + resolution: {integrity: sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==} dev: false /jsesc/2.5.2: @@ -8449,7 +8429,7 @@ packages: xmlbuilder: 15.1.1 yargs: 16.2.0 - /jsii-pacmak/1.102.0_jsii-rosetta@5.4.29: + /jsii-pacmak/1.102.0_jsii-rosetta@5.5.1: resolution: {integrity: sha512-3/nqBYNH8n/5IWI0sBFBYl1yATokEDUDQtYFLjzk7oXNWpUJ23/encI78Cs55ZS6UXcfWN3xczGLqCWnsgEpnw==} engines: {node: '>= 14.17.0'} hasBin: true @@ -8464,7 +8444,7 @@ packages: escape-string-regexp: 4.0.0 fs-extra: 10.1.0 jsii-reflect: 1.102.0 - jsii-rosetta: 5.4.29 + jsii-rosetta: 5.5.1 semver: 7.6.3 spdx-license-list: 6.9.0 xmlbuilder: 15.1.1 @@ -8482,8 +8462,8 @@ packages: oo-ascii-tree: 1.102.0 yargs: 16.2.0 - /jsii-rosetta/5.4.29: - resolution: {integrity: sha512-6/eQFAS+O1szcdczogAn+P6yqjJu88kqZbpGPL0Ks9LxA94EbwNxiAPwsh1g3GvY+ETDVKfND7iEE0tiVpvTsA==} + /jsii-rosetta/5.5.1: + resolution: {integrity: sha512-7rLkwjtEKZ/lgxgu53XIx42KzadKosx+tajMPqNKvBGT21XhV9+pr60uBJ/aX+wJ85S49dlF3D0P3ujKK3jgwg==} engines: {node: '>= 18.12.0'} hasBin: true dependencies: @@ -8493,31 +8473,31 @@ packages: chalk: 4.1.2 commonmark: 0.31.1 fast-glob: 3.3.2 - jsii: 5.4.31 + jsii: 5.5.1 semver: 7.6.3 semver-intersect: 1.5.0 stream-json: 1.8.0 - typescript: 5.4.5 + typescript: 5.5.4 workerpool: 6.5.1 yargs: 17.7.2 transitivePeerDependencies: - supports-color - /jsii-srcmak/0.1.1204: - resolution: {integrity: sha512-mPVD+bEnikHP6ssZ5cRqx+DirjV9/LvnPiunMko5MQDrLeyVdGYLi6kQ4QDF4uMw08oPaXXkhL8EhlZwp4tTDg==} + /jsii-srcmak/0.1.1226: + resolution: {integrity: sha512-8cn7ws1rgc3B1ONT0Fsf7geowpdABVB1sNBa19x9NG8MiEGo/t1S4wG2EY6PGwYIEr5zwBnQyIY6uVyt26vbSg==} hasBin: true dependencies: fs-extra: 9.1.0 - jsii: 5.4.31 - jsii-pacmak: 1.102.0_jsii-rosetta@5.4.29 - jsii-rosetta: 5.4.29 + jsii: 5.5.1 + jsii-pacmak: 1.102.0_jsii-rosetta@5.5.1 + jsii-rosetta: 5.5.1 ncp: 2.0.0 yargs: 17.7.2 transitivePeerDependencies: - supports-color - /jsii/5.4.31: - resolution: {integrity: sha512-qxiV/NMucgvHHupZJ36QACejcgZ3qY1FzjVHMOBmDHm+dISZ39p7dH7Hiq2ErMEwCDzdvQgR1OwCsUnrBH6oVQ==} + /jsii/5.5.1: + resolution: {integrity: sha512-YEkXUyCLU97geGu0pv5l2gP1P1S0ZMvanYI4FKvmsSHCtNXV//jIBI0Q80yjdUi7k5XQL1ECs+m4+IZHAEb92w==} engines: {node: '>= 18.12.0'} hasBin: true dependencies: @@ -8532,7 +8512,7 @@ packages: semver-intersect: 1.5.0 sort-json: 2.0.1 spdx-license-list: 6.9.0 - typescript: 5.4.5 + typescript: 5.5.4 yargs: 17.7.2 transitivePeerDependencies: - supports-color @@ -8575,15 +8555,6 @@ packages: /json-stable-stringify-without-jsonify/1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - /json-stable-stringify/1.1.1: - resolution: {integrity: sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - isarray: 2.0.5 - jsonify: 0.0.1 - object-keys: 1.1.1 - /json-stringify-safe/5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} dev: true @@ -8612,13 +8583,6 @@ packages: optionalDependencies: graceful-fs: 4.2.11 - /jsonify/0.0.1: - resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==} - - /jsonschema/1.4.1: - resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==} - dev: false - /jsonwebtoken/8.5.1: resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} engines: {node: '>=4', npm: '>=1.4.28'} @@ -9031,8 +8995,8 @@ packages: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - /micromatch/4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + /micromatch/4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} dependencies: braces: 3.0.3 @@ -9228,8 +9192,8 @@ packages: /ms/2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - /multipasta/0.2.4: - resolution: {integrity: sha512-uS6VKZou4iDU4evtrNKGvRqgqkFtPdjG/ql9XDICfnzKEedgf0OZ+oEhM0ps0FL5IVFhkIzOBgLV/KpEEJMthw==} + /multipasta/0.2.5: + resolution: {integrity: sha512-c8eMDb1WwZcE02WVjHoOmUVk7fnKU/RmUcosHACglrWAuPQsEJv+E8430sXj6jNc1jHw0zrS16aCjQh4BcEb4A==} dev: false /mustache/4.1.0: @@ -9317,7 +9281,7 @@ packages: dependencies: '@sinonjs/commons': 1.8.6 '@sinonjs/fake-timers': 6.0.1 - '@sinonjs/text-encoding': 0.7.2 + '@sinonjs/text-encoding': 0.7.3 just-extend: 4.2.1 path-to-regexp: 1.8.0 @@ -9325,8 +9289,8 @@ packages: resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==} dependencies: '@sinonjs/commons': 3.0.1 - '@sinonjs/fake-timers': 11.2.2 - '@sinonjs/text-encoding': 0.7.2 + '@sinonjs/fake-timers': 11.3.1 + '@sinonjs/text-encoding': 0.7.3 just-extend: 6.2.0 path-to-regexp: 6.2.2 dev: true @@ -9344,8 +9308,8 @@ packages: - supports-color dev: true - /nock/13.5.4: - resolution: {integrity: sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==} + /nock/13.5.5: + resolution: {integrity: sha512-XKYnqUrCwXC8DGG1xX4YH5yNIrlh9c065uaMZZHUoeUUINTOyt+x/G+ezYk0Ft6ExSREVIs+qBJDK503viTfFA==} engines: {node: '>= 10.13'} dependencies: debug: 4.3.6 @@ -9355,16 +9319,12 @@ packages: - supports-color dev: true - /node-abi/3.65.0: - resolution: {integrity: sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==} + /node-abi/3.67.0: + resolution: {integrity: sha512-bLn/fU/ALVBE9wj+p4Y21ZJWYFjUXLXPi/IewyLZkx3ApxKDNBWCKdReeKOtD8dWpOdDCeMyLh6ZewzcLsG2Nw==} engines: {node: '>=10'} dependencies: semver: 7.6.3 - /node-abort-controller/3.1.1: - resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} - dev: false - /node-addon-api/7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} dev: false @@ -9958,7 +9918,7 @@ packages: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 1.0.2 - node-abi: 3.65.0 + node-abi: 3.67.0 pump: 3.0.0 rc: 1.2.8 simple-get: 4.0.1 @@ -9990,8 +9950,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - /priorityqueuejs/1.0.0: - resolution: {integrity: sha512-lg++21mreCEOuGWTbO5DnJKAdxfjrdN0S9ysoW9SzdSJvbkWpkaDdpG/cdsPCsEnoLUwmd9m3WcZhngW7yKA2g==} + /priorityqueuejs/2.0.0: + resolution: {integrity: sha512-19BMarhgpq3x4ccvVi8k2QpJZcymo/iFUcrhPd4V96kYGovOdTsWwy7fxChYi4QY+m2EnGBWSX9Buakz+tWNQQ==} dev: false /process-nextick-args/2.0.1: @@ -10317,7 +10277,7 @@ packages: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true dependencies: - is-core-module: 2.15.0 + is-core-module: 2.15.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -10366,7 +10326,7 @@ packages: dependencies: debug: 4.3.6 rhea: 3.0.2 - tslib: 2.6.3 + tslib: 2.7.0 transitivePeerDependencies: - supports-color dev: false @@ -10437,7 +10397,7 @@ packages: /rxjs/7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - tslib: 2.6.3 + tslib: 2.7.0 dev: true /safe-array-concat/1.1.2: @@ -10579,7 +10539,7 @@ packages: ajv: 8.17.1 ajv-formats: 2.1.1 archiver: 5.3.0 - aws-sdk: 2.1667.0 + aws-sdk: 2.1683.0 bluebird: 3.7.2 cachedir: 2.4.0 chalk: 4.1.2 @@ -10587,7 +10547,7 @@ packages: ci-info: 3.9.0 cli-progress-footer: 2.3.3 d: 1.0.2 - dayjs: 1.11.12 + dayjs: 1.11.13 decompress: 4.2.1 dotenv: 10.0.0 dotenv-expand: 5.1.0 @@ -10607,7 +10567,7 @@ packages: json-refs: 3.0.15_supports-color@8.1.1 lodash: 4.17.21 memoizee: 0.4.17 - micromatch: 4.0.7 + micromatch: 4.0.8 node-fetch: 2.7.0 npm-registry-utilities: 1.0.0 object-hash: 2.2.0 @@ -10865,7 +10825,7 @@ packages: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.18 + spdx-license-ids: 3.0.20 dev: true /spdx-exceptions/2.5.0: @@ -10876,11 +10836,11 @@ packages: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.18 + spdx-license-ids: 3.0.20 dev: true - /spdx-license-ids/3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} + /spdx-license-ids/3.0.20: + resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} dev: true /spdx-license-list/6.9.0: @@ -11338,7 +11298,7 @@ packages: resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} engines: {node: '>=8'} dependencies: - tslib: 2.6.3 + tslib: 2.7.0 /ts-morph/19.0.0: resolution: {integrity: sha512-D6qcpiJdn46tUqV45vr5UGM2dnIEuTGNxVhg0sk5NX11orcouwj6i1bMqZIz2mZTZB1Hcgy7C3oEVhAT+f6mbQ==} @@ -11347,7 +11307,7 @@ packages: code-block-writer: 12.0.0 dev: false - /ts-node/10.9.2_prqahbuxbjzfz737e746ioxvhu: + /ts-node/10.9.2_lkkcy4kiv3gczc6zag7kwihdkm: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: @@ -11366,7 +11326,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 18.19.43 + '@types/node': 18.19.46 acorn: 8.12.1 acorn-walk: 8.3.3 arg: 4.1.3 @@ -11413,8 +11373,8 @@ packages: /tslib/1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - /tslib/2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + /tslib/2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} /tsutils/3.21.0_typescript@5.1.6: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} @@ -11547,13 +11507,13 @@ packages: engines: {node: '>=14.17'} hasBin: true - /typescript/5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + /typescript/5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} engines: {node: '>=14.17'} hasBin: true - /typescript/5.6.0-dev.20240819: - resolution: {integrity: sha512-uNOMaNm8jBELjhuXZG5tSS6Pa6O/Wf89hawKkWaZcZvbkgkY2ykvNTNrkCP7QCQTSS3jwEVvd+pRhxJPxUeG4g==} + /typescript/5.7.0-dev.20240827: + resolution: {integrity: sha512-qNwNQBg18O4Z5RRGb07O562OpDlAVlytNcKfqcx8JQRJcs3p/KLHXjr0FbUbJ3SKoxA2vaQ3Zt89YLWHuCXzUw==} engines: {node: '>=14.17'} hasBin: true @@ -11575,8 +11535,11 @@ packages: /undici-types/5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - /undici/6.19.5: - resolution: {integrity: sha512-LryC15SWzqQsREHIOUybavaIHF5IoL0dJ9aWWxL/PgT1KfqAW5225FZpDUFlt9xiDMS2/S7DOKhFWA7RLksWdg==} + /undici-types/6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + /undici/6.19.8: + resolution: {integrity: sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==} engines: {node: '>=18.17'} dev: false @@ -11586,10 +11549,6 @@ packages: type: 2.7.3 dev: true - /universal-user-agent/6.0.1: - resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} - dev: false - /universalify/0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} diff --git a/packages/framework-core/src/booster-delete-event-dispatcher.ts b/packages/framework-core/src/booster-delete-event-dispatcher.ts new file mode 100644 index 000000000..2f92c7e54 --- /dev/null +++ b/packages/framework-core/src/booster-delete-event-dispatcher.ts @@ -0,0 +1,21 @@ +import { BoosterConfig, EventDeleteParameters } from '@boostercloud/framework-types' +import { ReadModelStore } from './services/read-model-store' + +export class BoosterDeleteEventDispatcher { + public static async deleteEvent(config: BoosterConfig, parameters: EventDeleteParameters): Promise { + const readModelStore = new ReadModelStore(config) + const events = await config.provider.events.findDeletableEvent(config, parameters) + if (!events || events.length === 0) { + return false + } + for (const event of events) { + const snapshots = await config.provider.events.findDeletableSnapshot(config, event) + for (const snapshot of snapshots) { + await readModelStore.project(snapshot, true) + } + await config.provider.events.deleteSnapshot(config, snapshots) + } + await config.provider.events.deleteEvent(config, events) + return true + } +} diff --git a/packages/framework-core/src/booster-event-processor.ts b/packages/framework-core/src/booster-event-processor.ts index 79dbb2a36..9abb8ec2d 100644 --- a/packages/framework-core/src/booster-event-processor.ts +++ b/packages/framework-core/src/booster-event-processor.ts @@ -127,6 +127,8 @@ export class BoosterEventProcessor { ): Promise { const register = new Register(eventEnvelope.requestID, {}, RegisterHandler.flush, eventEnvelope.currentUser) try { + const logger = getLogger(config, 'BoosterEventProcessor#handleEvent') + logger.debug('Calling "handle" method on event handler: ', eventHandler) await eventHandler.handle(eventInstance, register) } catch (e) { const globalErrorDispatcher = new BoosterGlobalErrorDispatcher(config) diff --git a/packages/framework-core/src/booster.ts b/packages/framework-core/src/booster.ts index a21fa5665..eec3dc0b4 100644 --- a/packages/framework-core/src/booster.ts +++ b/packages/framework-core/src/booster.ts @@ -4,6 +4,7 @@ import { BoosterConfigTag, Class, EntityInterface, + EventDeleteParameters, EventSearchParameters, EventSearchResponse, PaginatedEntitiesIdsResult, @@ -21,6 +22,7 @@ import { JwksUriTokenVerifier, JWT_ENV_VARS } from './services/token-verifiers' import { BoosterAuthorizer } from './booster-authorizer' import { BoosterEntityTouched } from './core-concepts/touch-entity/events/booster-entity-touched' import { readModelSearcher } from './services/read-model-searcher' +import { BoosterDeleteEventDispatcher } from './booster-delete-event-dispatcher' import { eventSearch } from './booster-event-search' import { Effect, pipe } from 'effect' import { Command } from '@effect/cli' @@ -117,6 +119,10 @@ export class Booster { return await this.config.provider.events.searchEntitiesIDs(this.config, limit, afterCursor, entityTypeName) } + public static async deleteEvent(parameters: EventDeleteParameters): Promise { + return await BoosterDeleteEventDispatcher.deleteEvent(this.config, parameters) + } + /** * Fetches the last known version of an entity * @param entityClass Name of the entity class diff --git a/packages/framework-core/src/decorators/projects.ts b/packages/framework-core/src/decorators/projects.ts index 11e5db474..55203f8ed 100644 --- a/packages/framework-core/src/decorators/projects.ts +++ b/packages/framework-core/src/decorators/projects.ts @@ -2,6 +2,7 @@ import { Booster } from '../booster' import { Class, EntityInterface, + ProjectionInfo, ProjectionMetadata, ProjectionResult, ReadModelInterface, @@ -20,10 +21,16 @@ type JoinKeyType( +export function Projects< + TEntity extends EntityInterface, + TJoinKey extends keyof TEntity, + TReadModel extends ReadModelInterface +>( originEntity: Class, - joinKey: JoinKeyType + joinKey: JoinKeyType, + unProject?: UnprojectionMethod> ): ( readModelClass: Class, methodName: string, @@ -36,6 +43,14 @@ export function Projects registerProjection(originEntity.name, projectionMetadata) + if (unProject) { + const unProjectionMetadata = { + joinKey, + class: readModelClass, + methodName: unProject.name, + } as ProjectionMetadata + registerUnProjection(originEntity.name, unProjectionMetadata) + } } } @@ -44,15 +59,45 @@ function registerProjection( projectionMetadata: ProjectionMetadata ): void { Booster.configureCurrentEnv((config): void => { - const entityProjections = config.projections[originName] || [] - if (entityProjections.indexOf(projectionMetadata) < 0) { - // Skip duplicate registrations - entityProjections.push(projectionMetadata) - config.projections[originName] = entityProjections - } + configure(originName, projectionMetadata, config.projections) }) } +function registerUnProjection( + originName: string, + projectionMetadata: ProjectionMetadata +): void { + Booster.configureCurrentEnv((config): void => { + configure(originName, projectionMetadata, config.unProjections) + }) +} + +function configure( + originName: string, + projectionMetadata: ProjectionMetadata, + configuration: Record>> +): void { + const entityProjections = configuration[originName] || [] + if (entityProjections.indexOf(projectionMetadata) < 0) { + // Skip duplicate registrations + entityProjections.push(projectionMetadata) + configuration[originName] = entityProjections + } +} + +type ProjectionMethodDefinitionForArray = ( + _: TEntity, + readModelID: UUID, + readModel?: TReadModel, + projectionInfo?: ProjectionInfo +) => ProjectionResult + +type ProjectionMethodDefinition = ( + _: TEntity, + readModel?: TReadModel, + projectionInfo?: ProjectionInfo +) => ProjectionResult + type ProjectionMethod< TEntity extends EntityInterface, TReadModel extends ReadModelInterface, @@ -79,3 +124,7 @@ type ProjectionMethodWithEntityReadModelIdAndReadModel< TEntity extends EntityInterface, TReadModel extends ReadModelInterface > = TypedPropertyDescriptor<(_: TEntity, readModelID: UUID, readModel?: TReadModel) => ProjectionResult> + +type UnprojectionMethod = TPropType extends Array + ? ProjectionMethodDefinitionForArray + : ProjectionMethodDefinition diff --git a/packages/framework-core/src/services/graphql/query-generators/graphql-query-events-generator.ts b/packages/framework-core/src/services/graphql/query-generators/graphql-query-events-generator.ts index 7c2013c3a..1c7e2ed37 100644 --- a/packages/framework-core/src/services/graphql/query-generators/graphql-query-events-generator.ts +++ b/packages/framework-core/src/services/graphql/query-generators/graphql-query-events-generator.ts @@ -72,6 +72,7 @@ export class GraphqlQueryEventsGenerator { }, createdAt: { type: new GraphQLNonNull(GraphQLString) }, value: { type: new GraphQLNonNull(GraphQLJSON) }, + deletedAt: { type: GraphQLString }, }, }) ) diff --git a/packages/framework-core/src/services/raw-events-parser.ts b/packages/framework-core/src/services/raw-events-parser.ts index 1a470351e..0715b2349 100644 --- a/packages/framework-core/src/services/raw-events-parser.ts +++ b/packages/framework-core/src/services/raw-events-parser.ts @@ -16,7 +16,10 @@ export class RawEventsParser { callbackFn: EventsStreamingCallback ): Promise { const logger = getLogger(config, 'RawEventsParser#streamPerEntityEvents') - const eventEnvelopesPerEntity = eventEnvelopes.filter(isEventKind).reduce(groupByEntity, {}) + const eventEnvelopesPerEntity = eventEnvelopes + .filter(isEventKind) + .filter(isNotDeleted) + .reduce(groupByEntity, {}) const processes = Object.values(eventEnvelopesPerEntity).map(async (entityEnvelopes) => { // All envelopes are for the same entity type/ID, so we get the first one to get those values @@ -43,6 +46,10 @@ function isEventKind(envelope: EventEnvelope): boolean { return envelope.kind == 'event' } +function isNotDeleted(envelope: EventEnvelope): boolean { + return !envelope?.deletedAt +} + function groupByEntity(envelopesPerEntity: EnvelopesPerEntity, envelope: EventEnvelope): EnvelopesPerEntity { const entityKey = `${envelope.entityTypeName}-${envelope.entityID}` if (!envelopesPerEntity[entityKey]) { diff --git a/packages/framework-core/src/services/read-model-store.ts b/packages/framework-core/src/services/read-model-store.ts index d83e5b323..2fbbf7f05 100644 --- a/packages/framework-core/src/services/read-model-store.ts +++ b/packages/framework-core/src/services/read-model-store.ts @@ -8,6 +8,8 @@ import { FilterFor, OptimisticConcurrencyUnexpectedVersionError, ProjectionGlobalError, + ProjectionInfo, + ProjectionInfoReason, ProjectionMetadata, ProjectionResult, ReadModelAction, @@ -17,16 +19,18 @@ import { UUID, } from '@boostercloud/framework-types' import { createInstance, getLogger, Promises, retryIfError } from '@boostercloud/framework-common-helpers' +import { BoosterGlobalErrorDispatcher } from '../booster-global-error-dispatcher' import { readModelSearcher } from './read-model-searcher' import { ReadModelSchemaMigrator } from '../read-model-schema-migrator' -import { BoosterGlobalErrorDispatcher } from '../booster-global-error-dispatcher' export class ReadModelStore { public constructor(readonly config: BoosterConfig) {} - public async project(entitySnapshotEnvelope: EntitySnapshotEnvelope): Promise { + public async project(entitySnapshotEnvelope: EntitySnapshotEnvelope, deleteEvent = false): Promise { const logger = getLogger(this.config, 'ReadModelStore#project') - const projections = this.config.projections[entitySnapshotEnvelope.entityTypeName] + const projections = deleteEvent + ? this.getUnProjections(entitySnapshotEnvelope) + : this.entityProjections(entitySnapshotEnvelope) if (!projections) { logger.debug(`No projections found for entity ${entitySnapshotEnvelope.entityTypeName}. Skipping...`) return @@ -51,6 +55,7 @@ export class ReadModelStore { entityMetadata, entitySnapshotEnvelope, readModelName, + deleteEvent, sequenceKey ) } @@ -132,6 +137,57 @@ export class ReadModelStore { } } + private getUnProjections( + entitySnapshotEnvelope: EntitySnapshotEnvelope + ): Array> { + const unProjections: Array> = + this.entityUnProjections(entitySnapshotEnvelope) + const projections: Array> = + this.entityProjections(entitySnapshotEnvelope) + if (projections?.length > 0) { + if (!unProjections) { + throw new Error(`Missing UnProjections for entity ${entitySnapshotEnvelope.entityTypeName}`) + } + const missingProjection = this.findFirstMissingProjection(projections, unProjections) + if (missingProjection) { + throw new Error( + `Missing UnProjection for ReadModel ${missingProjection.class.name} with joinKey ${missingProjection.joinKey} for entity ${entitySnapshotEnvelope.entityTypeName}` + ) + } + } + return unProjections + } + + private entityProjections( + entitySnapshotEnvelope: EntitySnapshotEnvelope + ): Array> { + return this.config.projections[entitySnapshotEnvelope.entityTypeName] + } + + private entityUnProjections( + entitySnapshotEnvelope: EntitySnapshotEnvelope + ): Array> { + return this.config.unProjections[entitySnapshotEnvelope.entityTypeName] + } + + private findFirstMissingProjection( + sources: Array>, + to: Array> + ): ProjectionMetadata | undefined { + return sources.find( + (source: ProjectionMetadata) => !this.someProjection(to, source) + ) + } + + private someProjection( + sources: Array>, + to: ProjectionMetadata + ): boolean { + const contains = (source: ProjectionMetadata) => + source.class.name === to.class.name && source.joinKey.toString() === to.joinKey.toString() + return sources.some(contains) + } + private sequenceKeyForProjection( entity: EntityInterface, projectionMetadata: ProjectionMetadata @@ -150,6 +206,7 @@ export class ReadModelStore { entityMetadata: EntityMetadata, entitySnapshotEnvelope: EntitySnapshotEnvelope, readModelName: string, + deleteEvent: boolean, sequenceKey?: SequenceKey ): Promise> | undefined> { const currentReadModels: Array = await this.getReadModels( @@ -167,6 +224,7 @@ export class ReadModelStore { projectionMetadata, entityInstance, entityMetadata, + deleteEvent, currentReadModel ) existingReadModelsProjections.push(...newProjections) @@ -179,7 +237,8 @@ export class ReadModelStore { sequenceKey, projectionMetadata, entityInstance, - entityMetadata + entityMetadata, + deleteEvent ) return Promises.allSettledAndFulfilled(newProjections) } @@ -191,6 +250,7 @@ export class ReadModelStore { projectionMetadata: ProjectionMetadata, entityInstance: EntityInterface, entityMetadata: EntityMetadata, + deleteEvent: boolean, currentReadModel?: ReadModelInterface ): Promise>> { const projections: Array> = [] @@ -214,6 +274,7 @@ export class ReadModelStore { sequenceKey, entityInstance, projectionMetadata, + deleteEvent, readModelId, readModel ) @@ -228,6 +289,7 @@ export class ReadModelStore { sequenceKey, entityInstance, projectionMetadata, + deleteEvent, currentReadModel?.id, currentReadModel ) @@ -242,6 +304,7 @@ export class ReadModelStore { sequenceKey: SequenceKey | undefined, entityInstance: EntityInterface, projectionMetadata: ProjectionMetadata, + deleteEvent: boolean, readModelId?: UUID, currentReadModel?: ReadModelInterface ): Promise { @@ -259,6 +322,7 @@ export class ReadModelStore { entitySnapshotEnvelope, entityInstance, projectionMetadata, + deleteEvent, currentReadModel, entitySnapshotEnvelope, readModelId, @@ -285,6 +349,7 @@ export class ReadModelStore { entitySnapshotEnvelope: EntitySnapshotEnvelope, entity: EntityInterface, projectionMetadata: ProjectionMetadata, + deleteEvent: boolean, currentReadModel?: ReadModelInterface, lastProjectedEntity?: EntitySnapshotEnvelope, currentReadModelID?: UUID, @@ -319,13 +384,17 @@ export class ReadModelStore { const currentDatabaseVersion: number = migratedReadModel?.boosterMetadata?.version ?? 0 let newReadModel: any + const projectionInfo: ProjectionInfo = { + reason: deleteEvent ? ProjectionInfoReason.ENTITY_DELETED : ProjectionInfoReason.ENTITY_PROJECTED, + } try { newReadModel = await this.callProjectionFunction( entitySnapshotEnvelope, projectionMetadata, entity, migratedReadModel, - readModelID + readModelID, + projectionInfo ) } catch (e) { const globalErrorDispatcher = new BoosterGlobalErrorDispatcher(this.config) @@ -399,15 +468,16 @@ export class ReadModelStore { projectionMetadata: ProjectionMetadata, entity: EntityInterface, migratedReadModel: ReadModelInterface | undefined, - readModelID: UUID | undefined + readModelID: UUID | undefined, + projectionInfo: ProjectionInfo ): Promise | undefined> { try { const projectionMetadataJoinKey = projectionMetadata.joinKey const projectionFunction = this.getProjectionFunction(projectionMetadata) if (this.isJoinKeyByEntity(projectionMetadataJoinKey)) { return Array.isArray(entity[projectionMetadataJoinKey]) - ? projectionFunction(entity, readModelID, migratedReadModel || null) - : projectionFunction(entity, migratedReadModel || null) + ? projectionFunction(entity, readModelID, migratedReadModel || null, projectionInfo) + : projectionFunction(entity, migratedReadModel || null, projectionInfo) } return projectionFunction(entity, readModelID, migratedReadModel || null) } catch (e) { diff --git a/packages/framework-core/test/services/graphql/graphql-query-generator.test.ts b/packages/framework-core/test/services/graphql/graphql-query-generator.test.ts index b7189363c..eb9071369 100644 --- a/packages/framework-core/test/services/graphql/graphql-query-generator.test.ts +++ b/packages/framework-core/test/services/graphql/graphql-query-generator.test.ts @@ -527,7 +527,7 @@ describe('GraphQLQueryGenerator', () => { const returnElementType = (queryReturnType as GraphQLList).ofType expect(returnElementType.name).to.be.equal('EventQueryResponse') expect(new Set(Object.keys(returnElementType.getFields()))).to.be.deep.equal( - new Set(['type', 'entity', 'requestID', 'entityID', 'user', 'createdAt', 'value']) + new Set(['type', 'entity', 'requestID', 'entityID', 'user', 'createdAt', 'value', 'deletedAt']) ) const userType = returnElementType.getFields()['user'].type as GraphQLObjectType expect(new Set(Object.keys(userType.getFields()))).to.be.deep.equal(new Set(['id', 'username', 'roles'])) diff --git a/packages/framework-core/test/services/read-model-store.test.ts b/packages/framework-core/test/services/read-model-store.test.ts index 0987edb01..cb7969206 100644 --- a/packages/framework-core/test/services/read-model-store.test.ts +++ b/packages/framework-core/test/services/read-model-store.test.ts @@ -843,10 +843,12 @@ describe('ReadModelStore', () => { anEntitySnapshot, anEntityInstance, projectionMetadata, + false, undefined, anEntitySnapshot, 'joinColumnID', - readModelClassName === 'AnotherReadModel' ? { name: 'count', value: 123 } : undefined + readModelClassName === 'AnotherReadModel' ? { name: 'count', value: 123 } : undefined, + 1 ) } }) diff --git a/packages/framework-integration-tests/integration/provider-unaware/end-to-end/delete.events.integration.ts b/packages/framework-integration-tests/integration/provider-unaware/end-to-end/delete.events.integration.ts new file mode 100644 index 000000000..62b8e3e2c --- /dev/null +++ b/packages/framework-integration-tests/integration/provider-unaware/end-to-end/delete.events.integration.ts @@ -0,0 +1,485 @@ +import { ApolloClient, ApolloQueryResult, gql, NormalizedCacheObject } from '@apollo/client' +import { internet, random } from 'faker' +import { expect } from '../../helper/expect' +import { waitForIt } from '../../helper/sleep' +import { applicationUnderTest } from './setup' +import { + EventSearchResponse, + EventTimeParameterFilter, + PaginatedEntitiesIdsResult, +} from '@boostercloud/framework-types' +import 'mocha' + +describe('Remove Events end-to-end tests', async () => { + if (process.env.TESTED_PROVIDER === 'AWS') { + console.log('****************** Warning **********************') + console.log('AWS provider does not support remove events so these tests are skipped for AWS') + console.log('*************************************************') + return + } + let anonymousClient: ApolloClient + let loggedClient: ApolloClient + let adminTokenClient: ApolloClient + let specialTokenClient: ApolloClient + const firstCartId = random.uuid() + const firstProductId = random.uuid() + const firstProductSku = random.uuid() + const secondProductId = random.uuid() + const secondProductSku = random.uuid() + + before(async () => { + anonymousClient = applicationUnderTest.graphql.client() + const userEmail = internet.email() + const userToken = applicationUnderTest.token.forUser(userEmail, 'UserWithEmail') + loggedClient = applicationUnderTest.graphql.client(userToken) + const adminToken = applicationUnderTest.token.forUser('admin@example.com', 'Admin') + adminTokenClient = applicationUnderTest.graphql.client(adminToken) + const tokenWithSpecialAccess = applicationUnderTest.token.forUser(userEmail, undefined, { + customClaims: { + specialReportAccess: 'true', + }, + }) + specialTokenClient = applicationUnderTest.graphql.client(tokenWithSpecialAccess) + }) + + it('When there are not events to delete', async () => { + const deleted = await deleteEvent(anonymousClient, '1', 'Product', '') + expect(deleted).to.be.false + }) + + describe('When there are events to delete', async () => { + let firstCartReadModel + let firstProduct + let firstProductBySkuReadModel + let firstProductUpdatesReadModel + let firstSpecialReportsReadModel + let secondProduct + let secondProductBySkuReadModel + let secondProductUpdatesReadModel + let secondSpecialReportsReadModel + let events: Array + let productEvent + let firstProductEvent: any + let secondProductEvent: any + let firstCartEvent: any + + it('Create a cart', async () => { + await createCart(anonymousClient, firstCartId, firstProductId) + firstCartReadModel = await getCart(anonymousClient, firstCartId) + expect(firstCartReadModel.id).to.be.eq(firstCartId) + }) + + it('and create a first product', async () => { + await createProduct(loggedClient, firstProductId, firstProductSku) + }) + + it('then the related ReadModels are created', async () => { + firstProduct = await getProduct(loggedClient, firstProductId) + expect(firstProduct.id).to.be.eq(firstProductId) + const firstProductBySkuReadModel = await getProductBySku(loggedClient, firstProductSku) + expect(firstProductBySkuReadModel.id).to.be.eq(firstProductSku) + const firstProductUpdatesReadModel = await getProductUpdates(adminTokenClient, firstProductId) + expect(firstProductUpdatesReadModel.id).to.be.eq(firstProductId) + const firstSpecialReportsReadModel = await getSpecialReportsReadModel(specialTokenClient, firstProductId) + expect(firstSpecialReportsReadModel.id).to.be.eq(firstProductId) + }) + + it('when a second product is created', async () => { + await createProduct(loggedClient, secondProductId, secondProductSku) + }) + + it('then the related ReadModels for the second product are created', async () => { + secondProduct = await getProduct(loggedClient, secondProductId) + expect(secondProduct.id).to.be.eq(secondProductId) + const secondProductBySkuReadModel = await getProductBySku(loggedClient, secondProductSku) + expect(secondProductBySkuReadModel.id).to.be.eq(secondProductSku) + const secondProductUpdatesReadModel = await getProductUpdates(adminTokenClient, secondProductId) + expect(secondProductUpdatesReadModel.id).to.be.eq(secondProductId) + const secondSpecialReportsReadModel = await getSpecialReportsReadModel(specialTokenClient, secondProductId) + expect(secondSpecialReportsReadModel.id).to.be.eq(secondProductId) + }) + + it('when find the event for the first product', async () => { + events = await queryProductByEntity(loggedClient, firstProductId) + productEvent = events.filter((event) => event.entityID === firstProductId) + firstProductEvent = productEvent[0] + }) + + it('and delete it', async () => { + const deleted = await deleteEvent( + anonymousClient, + firstProductEvent.entityID.toString(), + firstProductEvent.entity, + firstProductEvent.createdAt + ) + expect(deleted).to.be.true + }) + + it('when find the event for the first product after delete', async () => { + events = await queryProductByEntity(loggedClient, firstProductId) + productEvent = events.filter((event) => event.entityID === firstProductId) + firstProductEvent = productEvent[0] + }) + + it('then the first event is set as deleted', async () => { + expect(firstProductEvent.deletedAt).not.to.be.undefined + expect(firstProductEvent.value.productId).to.be.undefined + }) + + it('and there are not ReadModels for first product id', async () => { + firstProduct = await getProductUndefined(loggedClient, firstProductId) + expect(firstProduct).to.be.null + firstProductBySkuReadModel = await getProductBySkuUndefined(loggedClient, firstProductSku) + expect(firstProductBySkuReadModel).to.be.null + firstProductUpdatesReadModel = await getProductUpdatesUndefined(adminTokenClient, firstProductId) + expect(firstProductUpdatesReadModel).to.be.null + firstSpecialReportsReadModel = await getSpecialReportsReadModelUndefined(specialTokenClient, firstProductId) + expect(firstSpecialReportsReadModel).to.be.null + }) + + it('when find the event for the second product', async () => { + events = await queryProductByEntity(loggedClient, secondProductId) + productEvent = events.filter((event) => event.entityID === secondProductId) + secondProductEvent = productEvent[0] + }) + + it('then the second event is not changed', async () => { + expect(secondProductEvent.deletedAt).to.be.null + expect(secondProductEvent.value.productId).to.be.eq(secondProductId) + }) + + it('and ReadModels for second product id are not deleted', async () => { + secondProduct = await getProduct(loggedClient, secondProductId) + expect(secondProduct.id).to.be.eq(secondProductId) + secondProductBySkuReadModel = await getProductBySku(loggedClient, secondProductSku) + expect(secondProductBySkuReadModel.id).to.be.eq(secondProductSku) + secondProductUpdatesReadModel = await getProductUpdates(adminTokenClient, secondProductId) + expect(secondProductUpdatesReadModel.id).to.be.eq(secondProductId) + secondSpecialReportsReadModel = await getSpecialReportsReadModel(specialTokenClient, secondProductId) + expect(secondSpecialReportsReadModel.id).to.be.eq(secondProductId) + }) + + it('and there are not entities for first product but for product 2', async () => { + const productsEntities = await findEntities(loggedClient, 'Product') + expect(productsEntities.items.some((product) => product.entityID === firstProductId)).to.be.false + expect(productsEntities.items.some((product) => product.entityID === secondProductId)).to.be.true + }) + + it('when find the event for the cart', async () => { + events = await queryCartByEntity(loggedClient, firstCartId) + const cartEvent = events.filter((event) => event.entityID === firstCartId) + firstCartEvent = cartEvent[0] + }) + + it('then the cart event is not changed', async () => { + expect(firstCartEvent.deletedAt).to.be.null + expect(firstCartEvent.value.cartId).to.be.eq(firstCartId) + }) + + it('and ReadModels for the cart id are not deleted', async () => { + firstCartReadModel = await getCart(loggedClient, firstCartId) + expect(firstCartReadModel.id).to.be.eq(firstCartId) + }) + }) +}) + +async function createCart(client: ApolloClient, cartId: string, productId: string): Promise { + await client.mutate({ + variables: { + cartId: cartId, + productId: productId, + }, + mutation: gql` + mutation ChangeCartItem($cartId: ID!, $productId: ID!) { + ChangeCartItem(input: { cartId: $cartId, productId: $productId, quantity: 1 }) + } + `, + }) +} + +async function createProduct(client: ApolloClient, productId: string, sku: string): Promise { + await client.mutate({ + variables: { + productId: productId, + sku: sku, + }, + mutation: gql` + mutation CreateProduct($productId: ID!, $sku: String!) { + CreateProduct( + input: { + productID: $productId + sku: $sku + displayName: "any" + description: "any" + currency: "any" + priceInCents: 1 + } + ) + } + `, + }) +} + +async function getCart(client: ApolloClient, id: string): Promise { + const result = await waitForIt( + () => { + return client.query({ + variables: { + id: id, + }, + query: gql` + query CartReadModel($id: ID!) { + CartReadModel(id: $id) { + id + } + } + `, + }) + }, + (result) => { + const resultId = result?.data?.CartReadModel?.id + return resultId === id + } + ) + return result.data.CartReadModel +} + +async function waitForProduct( + client: ApolloClient, + id: string, + checkFunction: (result: any) => boolean | string +): Promise { + return await waitForIt(() => { + return client.query({ + variables: { + id: id, + }, + query: gql` + query ProductReadModel($id: ID!) { + ProductReadModel(id: $id) { + id + } + } + `, + }) + }, checkFunction) +} + +async function getProduct(client: ApolloClient, id: string): Promise { + const product = await waitForProduct(client, id, (result) => { + const resultId = result?.data?.ProductReadModel?.id + return resultId === id + }) + return product.data.ProductReadModel +} + +async function getProductUndefined(client: ApolloClient, id: string): Promise { + const product = await waitForProduct(client, id, (result) => { + const resultId = result?.data?.ProductReadModel + return resultId === null + }) + return product.data.ProductReadModel +} + +async function awaitForProductUpdates( + client: ApolloClient, + id: string, + checkResult: (result: any) => boolean | string +) { + return await waitForIt(() => { + return client.query({ + variables: { + id: id, + }, + query: gql` + query ProductUpdatesReadModel($id: ID!) { + ProductUpdatesReadModel(id: $id) { + id + } + } + `, + }) + }, checkResult) +} + +async function getProductUpdates(client: ApolloClient, id: string): Promise { + const result = await awaitForProductUpdates(client, id, (result) => { + const resultId = result?.data?.ProductUpdatesReadModel?.id + return resultId === id + }) + return result.data.ProductUpdatesReadModel +} + +async function getProductUpdatesUndefined(client: ApolloClient, id: string): Promise { + const result = await awaitForProductUpdates(client, id, (result) => { + const resultId = result?.data?.ProductUpdatesReadModel + return resultId === null + }) + return result.data.ProductUpdatesReadModel +} + +async function waitForProductBySku( + client: ApolloClient, + id: string, + checkResult: (result: any) => boolean | string +) { + return await waitForIt(() => { + return client.query({ + variables: { + id: id, + }, + query: gql` + query ProductsBySKU($id: ID!) { + ProductsBySKU(id: $id) { + id + } + } + `, + }) + }, checkResult) +} + +async function getProductBySku(client: ApolloClient, id: string): Promise { + const result = await waitForProductBySku(client, id, (result) => { + const resultId = result?.data?.ProductsBySKU?.id + return resultId === id + }) + return result.data.ProductsBySKU +} + +async function getProductBySkuUndefined(client: ApolloClient, id: string): Promise { + const result = await waitForProductBySku(client, id, (result) => { + const resultId = result?.data?.ProductsBySKU + return resultId === null + }) + return result.data.ProductsBySKU +} + +async function waitForSpecialReports( + client: ApolloClient, + id: string, + checkResult: (result: any) => boolean | string +) { + return await waitForIt(() => { + return client.query({ + variables: { + id: id, + }, + query: gql` + query SpecialReportsReadModel($id: ID!) { + SpecialReportsReadModel(id: $id) { + id + } + } + `, + }) + }, checkResult) +} + +async function getSpecialReportsReadModel(client: ApolloClient, id: string): Promise { + const result = await waitForSpecialReports(client, id, (result) => { + const resultId = result?.data?.SpecialReportsReadModel?.id + return resultId === id + }) + return result.data.SpecialReportsReadModel +} + +async function getSpecialReportsReadModelUndefined(client: ApolloClient, id: string): Promise { + const result = await waitForSpecialReports(client, id, (result) => { + const resultId = result?.data?.SpecialReportsReadModel + return resultId === null + }) + return result.data.SpecialReportsReadModel +} + +async function deleteEvent( + client: ApolloClient, + entityId: string, + entityTypeName: string, + createdAt: string +): Promise { + const result = await client.mutate({ + variables: { + entityId: entityId, + entityTypeName: entityTypeName, + createdAt: createdAt, + }, + mutation: gql` + mutation HardDelete($entityId: String!, $entityTypeName: String!, $createdAt: String!) { + HardDelete(input: { entityId: $entityId, entityTypeName: $entityTypeName, createdAt: $createdAt }) + } + `, + }) + return result?.data.HardDelete +} + +function queryByEntity( + client: ApolloClient, + entity: string, + timeFilters?: EventTimeParameterFilter, + entityID?: string, + limit?: number +): Promise> { + const queryTimeFilters = timeFilters ? `, from:"${timeFilters.from}" to:"${timeFilters.to}"` : '' + const queryEntityID = entityID ? `, entityID:"${entityID}"` : '' + const queryLimit = limit ? `, limit:${limit}` : '' + return client.query({ + query: gql` + query { + eventsByEntity(entity: ${entity}${queryEntityID}${queryTimeFilters}${queryLimit}) { + createdAt + entity + entityID + requestID + type + user { + id + roles + username + } + value + deletedAt + } + } + `, + }) +} + +async function queryProductByEntity(client: ApolloClient, entityID: string): Promise { + const result = await waitForIt( + () => queryByEntity(client, 'Product', undefined, entityID), + (result) => { + const events: Array = result.data['eventsByEntity'] + const found = events?.find((event) => event.entityID === entityID) + return found !== undefined + } + ) + return result.data['eventsByEntity'] +} + +async function queryCartByEntity(client: ApolloClient, entityID: string): Promise { + const result = await waitForIt( + () => queryByEntity(client, 'Cart', undefined, entityID), + (result) => { + const events: Array = result.data['eventsByEntity'] + const found = events?.find((event) => event.entityID === entityID) + return found !== undefined + } + ) + return result.data['eventsByEntity'] +} + +async function findEntities(client: ApolloClient, entityName: string): Promise { + const result = await client.mutate({ + variables: { + entityName: entityName, + limit: 99999, + }, + mutation: gql` + mutation EntitiesIdsFinder($entityName: String!, $limit: Float!) { + EntitiesIdsFinder(input: { entityName: $entityName, limit: $limit }) + } + `, + }) + return result?.data?.EntitiesIdsFinder +} diff --git a/packages/framework-integration-tests/src/commands/hard-delete.ts b/packages/framework-integration-tests/src/commands/hard-delete.ts new file mode 100644 index 000000000..405edbef3 --- /dev/null +++ b/packages/framework-integration-tests/src/commands/hard-delete.ts @@ -0,0 +1,18 @@ +import { Booster, Command } from '@boostercloud/framework-core' +import { EventDeleteParameters } from '@boostercloud/framework-types' + +@Command({ + authorize: 'all', +}) +export class HardDelete { + public constructor(readonly entityId: string, readonly entityTypeName: string, readonly createdAt: string) {} + + public static async handle(command: HardDelete): Promise { + const parameters: EventDeleteParameters = { + entityID: command.entityId, + entityTypeName: command.entityTypeName, + createdAt: command.createdAt, + } + return await Booster.deleteEvent(parameters) + } +} diff --git a/packages/framework-integration-tests/src/read-models/product-read-model.ts b/packages/framework-integration-tests/src/read-models/product-read-model.ts index 759e71db9..634dc6591 100644 --- a/packages/framework-integration-tests/src/read-models/product-read-model.ts +++ b/packages/framework-integration-tests/src/read-models/product-read-model.ts @@ -1,6 +1,12 @@ import { Projects, ReadModel } from '@boostercloud/framework-core' import { UserWithEmail } from '../roles' -import { ProjectionResult, ReadModelAction, UUID } from '@boostercloud/framework-types' +import { + ProjectionInfo, + ProjectionInfoReason, + ProjectionResult, + ReadModelAction, + UUID, +} from '@boostercloud/framework-types' import { Product, ProductType } from '../entities/product' import { Money } from '../common/money' import { Pack } from '../entities/pack' @@ -23,7 +29,7 @@ export class ProductReadModel { readonly packs?: Array ) {} - @Projects(Product, 'id') + @Projects(Product, 'id', ProductReadModel.unProjectWithProduct) public static updateWithProduct(product: Product): ProjectionResult { if (product.deleted) { return ReadModelAction.Delete @@ -43,12 +49,24 @@ export class ProductReadModel { } } - @Projects(Pack, 'products') + public static unProjectWithProduct( + _product: Product, + _currentProductReadModel?: ProductReadModel, + _projectionInfo?: ProjectionInfo + ): ProjectionResult { + return ReadModelAction.Delete + } + + @Projects(Pack, 'products', ProductReadModel.updateWithPack) public static updateWithPack( pack: Pack, readModelID: UUID, - currentProductReadModel?: ProductReadModel + currentProductReadModel?: ProductReadModel, + projectionInfo?: ProjectionInfo ): ProjectionResult { + if (projectionInfo?.reason === ProjectionInfoReason.ENTITY_DELETED) { + return ReadModelAction.Delete + } if (!currentProductReadModel) { return ReadModelAction.Nothing } else { diff --git a/packages/framework-integration-tests/src/read-models/product-updates-read-model.ts b/packages/framework-integration-tests/src/read-models/product-updates-read-model.ts index 90229454a..085777012 100644 --- a/packages/framework-integration-tests/src/read-models/product-updates-read-model.ts +++ b/packages/framework-integration-tests/src/read-models/product-updates-read-model.ts @@ -1,6 +1,12 @@ import { ReadModel, Projects } from '@boostercloud/framework-core' import { Admin } from '../roles' -import { ProjectionResult, UUID } from '@boostercloud/framework-types' +import { + ProjectionInfo, + ProjectionInfoReason, + ProjectionResult, + ReadModelAction, + UUID, +} from '@boostercloud/framework-types' import { Product } from '../entities/product' // This is an example read model for a possible admin-exclusive report to show last and previous updates to products @@ -15,11 +21,15 @@ export class ProductUpdatesReadModel { readonly previousUpdate?: Date ) {} - @Projects(Product, 'id') + @Projects(Product, 'id', ProductUpdatesReadModel.updateWithProduct) public static updateWithProduct( product: Product, - previous?: ProductUpdatesReadModel + previous?: ProductUpdatesReadModel, + projectionInfo?: ProjectionInfo ): ProjectionResult { + if (projectionInfo?.reason === ProjectionInfoReason.ENTITY_DELETED) { + return ReadModelAction.Delete + } return new ProductUpdatesReadModel(product.id, product.availability, new Date(), previous?.lastUpdate) } } diff --git a/packages/framework-integration-tests/src/read-models/products-by-sku.ts b/packages/framework-integration-tests/src/read-models/products-by-sku.ts index b19f610c0..a59ae8486 100644 --- a/packages/framework-integration-tests/src/read-models/products-by-sku.ts +++ b/packages/framework-integration-tests/src/read-models/products-by-sku.ts @@ -1,5 +1,11 @@ import { ReadModel, Projects } from '@boostercloud/framework-core' -import { UUID, ProjectionResult } from '@boostercloud/framework-types' +import { + UUID, + ProjectionResult, + ProjectionInfoReason, + ReadModelAction, + ProjectionInfo, +} from '@boostercloud/framework-types' import { Product } from '../entities/product' interface ProductIDWithPrice { @@ -18,8 +24,15 @@ export class ProductsBySKU { readonly record?: Record ) {} - @Projects(Product, 'sku') - public static projectProduct(entity: Product, currentProductsBySKU?: ProductsBySKU): ProjectionResult { + @Projects(Product, 'sku', ProductsBySKU.projectProduct) + public static projectProduct( + entity: Product, + currentProductsBySKU?: ProductsBySKU, + projectionInfo?: ProjectionInfo + ): ProjectionResult { + if (projectionInfo?.reason === ProjectionInfoReason.ENTITY_DELETED) { + return ReadModelAction.Delete + } // The purpose of this projection is to test the Optimistic concurrency. We have a read model that accumulates entities // with different IDs. One instance of this read model (with id == product sku) will be updated when multiple instances // of the product entity (with the same SKU but different id) are changed. diff --git a/packages/framework-integration-tests/src/read-models/special-reports-read-model.ts b/packages/framework-integration-tests/src/read-models/special-reports-read-model.ts index 78e9b9e79..8499d4f87 100644 --- a/packages/framework-integration-tests/src/read-models/special-reports-read-model.ts +++ b/packages/framework-integration-tests/src/read-models/special-reports-read-model.ts @@ -1,5 +1,12 @@ import { Projects, ReadModel } from '@boostercloud/framework-core' -import { ProjectionResult, UserEnvelope, UUID } from '@boostercloud/framework-types' +import { + ProjectionInfo, + ProjectionInfoReason, + ProjectionResult, + ReadModelAction, + UserEnvelope, + UUID, +} from '@boostercloud/framework-types' import { Product } from '../entities/product' @ReadModel({ @@ -13,12 +20,16 @@ import { Product } from '../entities/product' export class SpecialReportsReadModel { public constructor(readonly id: UUID, readonly luck: number) {} - @Projects(Product, 'id') + @Projects(Product, 'id', SpecialReportsReadModel.updateCounter) public static updateCounter( product: Product, // eslint-disable-next-line @typescript-eslint/no-unused-vars - _oldCounterReadModel?: SpecialReportsReadModel + _oldCounterReadModel?: SpecialReportsReadModel, + projectionInfo?: ProjectionInfo ): ProjectionResult { + if (projectionInfo?.reason === ProjectionInfoReason.ENTITY_DELETED) { + return ReadModelAction.Delete + } const luck = product.description.length + product.displayName.length + Math.random() return new SpecialReportsReadModel(product.id, luck) } diff --git a/packages/framework-provider-aws/src/library/events-adapter.ts b/packages/framework-provider-aws/src/library/events-adapter.ts index 0ef0781e7..b9c8be791 100644 --- a/packages/framework-provider-aws/src/library/events-adapter.ts +++ b/packages/framework-provider-aws/src/library/events-adapter.ts @@ -48,11 +48,13 @@ export async function readEntityEventsSince( ScanIndexForward: true, // Ascending order (older timestamps first) }) .promise() + const resultItems: Array = result.Items as Array + const validEvents = resultItems.filter((item) => !item.deletedAt) logger.debug( `[EventsAdapter#readEntityEventsSince] Loaded events for entity ${entityTypeName} with ID ${entityID} with result:`, - result.Items + validEvents ) - return result.Items as Array + return validEvents } export async function readEntityLatestSnapshot( diff --git a/packages/framework-provider-aws/src/library/events-searcher-adapter.ts b/packages/framework-provider-aws/src/library/events-searcher-adapter.ts index 23a5419a1..5008ce751 100644 --- a/packages/framework-provider-aws/src/library/events-searcher-adapter.ts +++ b/packages/framework-provider-aws/src/library/events-searcher-adapter.ts @@ -240,6 +240,7 @@ function convertToSearchResult(eventEnvelopes: Array): Array { diff --git a/packages/framework-provider-aws/src/setup.ts b/packages/framework-provider-aws/src/setup.ts index 2b30e45e7..afaf3b809 100644 --- a/packages/framework-provider-aws/src/setup.ts +++ b/packages/framework-provider-aws/src/setup.ts @@ -77,6 +77,10 @@ export const Provider = (rockets?: RocketDescriptor[]): ProviderLibrary => { store: storeEvents.bind(null, dynamoDB), storeSnapshot: storeSnapshot.bind(null, dynamoDB), storeDispatched: storeDispatchedEvent, + findDeletableEvent: notImplementedResult as any, + findDeletableSnapshot: notImplementedResult as any, + deleteEvent: notImplementedResult as any, + deleteSnapshot: notImplementedResult as any, }, // ProviderReadModelsLibrary readModels: { diff --git a/packages/framework-provider-aws/test/library/events-search-adapter.test.ts b/packages/framework-provider-aws/test/library/events-search-adapter.test.ts index 0883b9497..5ae7c5a07 100644 --- a/packages/framework-provider-aws/test/library/events-search-adapter.test.ts +++ b/packages/framework-provider-aws/test/library/events-search-adapter.test.ts @@ -291,7 +291,16 @@ describe('Events searcher adapter', () => { expect(res.map((item) => item.entityID)).to.be.deep.equal([occurredThirdID, occurredSecondID, occurredFirstID]) // Check they have the right structure for (const item of res) { - expect(item).to.have.keys(['type', 'entity', 'entityID', 'requestID', 'user', 'createdAt', 'value']) + expect(item).to.have.keys([ + 'type', + 'entity', + 'entityID', + 'requestID', + 'user', + 'createdAt', + 'value', + 'deletedAt', + ]) } }) }) diff --git a/packages/framework-provider-azure/src/helpers/query-helper.ts b/packages/framework-provider-azure/src/helpers/query-helper.ts index 868d3c144..98eb30a07 100644 --- a/packages/framework-provider-azure/src/helpers/query-helper.ts +++ b/packages/framework-provider-azure/src/helpers/query-helper.ts @@ -1,4 +1,4 @@ -import { CosmosClient, SqlParameter, SqlQuerySpec } from '@azure/cosmos' +import { CosmosClient, ItemDefinition, SqlParameter, SqlQuerySpec } from '@azure/cosmos' import { BoosterConfig, FilterFor, @@ -10,6 +10,25 @@ import { } from '@boostercloud/framework-types' import { getLogger } from '@boostercloud/framework-common-helpers' +export async function replaceOrDeleteItem( + cosmosDb: CosmosClient, + container: string, + config: BoosterConfig, + id: string, + partitionKey: string, + newValue?: ItemDefinition +): Promise { + if (newValue) { + await cosmosDb + .database(config.resourceNames.applicationStack) + .container(container) + .item(id, partitionKey) + .replace(newValue) + } else { + await cosmosDb.database(config.resourceNames.applicationStack).container(container).item(id, partitionKey).delete() + } +} + export async function search( cosmosDb: CosmosClient, config: BoosterConfig, diff --git a/packages/framework-provider-azure/src/index.ts b/packages/framework-provider-azure/src/index.ts index 0e402c6d6..cb7e653ad 100644 --- a/packages/framework-provider-azure/src/index.ts +++ b/packages/framework-provider-azure/src/index.ts @@ -47,6 +47,7 @@ import { isGraphQLFunctionUp, rawRequestToSensorHealth, } from './library/health-adapter' +import { deleteEvent, deleteSnapshot, findDeletableEvent, findDeletableSnapshot } from './library/event-delete-adapter' import { storeEvents } from './library/events-store-adapter' let cosmosClient: CosmosClient @@ -107,6 +108,10 @@ export const Provider = (rockets?: RocketDescriptor[]): ProviderLibrary => ({ search: searchEvents.bind(null, cosmosClient), searchEntitiesIDs: searchEntitiesIds.bind(null, cosmosClient), storeDispatched: storeDispatchedEvent.bind(null, cosmosClient), + findDeletableEvent: findDeletableEvent.bind(null, cosmosClient), + findDeletableSnapshot: findDeletableSnapshot.bind(null, cosmosClient), + deleteEvent: deleteEvent.bind(null, cosmosClient), + deleteSnapshot: deleteSnapshot.bind(null, cosmosClient), }, // ProviderReadModelsLibrary readModels: { diff --git a/packages/framework-provider-azure/src/library/event-delete-adapter.ts b/packages/framework-provider-azure/src/library/event-delete-adapter.ts new file mode 100644 index 000000000..31058c5f4 --- /dev/null +++ b/packages/framework-provider-azure/src/library/event-delete-adapter.ts @@ -0,0 +1,135 @@ +import { + BoosterConfig, + EventEnvelope, + EventDeleteParameters, + FilterFor, + SnapshotDeleteParameters, + UUID, + EventEnvelopeFromDatabase, + EntitySnapshotEnvelopeFromDatabase, +} from '@boostercloud/framework-types' +import { getLogger } from '@boostercloud/framework-common-helpers' +import { CosmosClient } from '@azure/cosmos' +import { replaceOrDeleteItem, search } from '../helpers/query-helper' + +export async function findDeletableEvent( + cosmosDb: CosmosClient, + config: BoosterConfig, + parameters: EventDeleteParameters +): Promise> { + const logger = getLogger(config, 'events-delete-adapter#findDeletableEvent') + const stringifyParameters = JSON.stringify(parameters) + logger.debug(`Initiating a deletable event search for ${stringifyParameters}`) + + const eventStore = config.resourceNames.eventsStore + const filter = buildDeleteEventFilter(parameters.entityTypeName, parameters.entityID, parameters.createdAt) + const result = (await search(cosmosDb, config, eventStore, filter)) as Array + logger.debug(`Finished deletable event search for ${stringifyParameters}`) + return result +} + +export async function findDeletableSnapshot( + cosmosDb: CosmosClient, + config: BoosterConfig, + parameters: SnapshotDeleteParameters +): Promise> { + const logger = getLogger(config, 'events-delete-adapter#findDeletableSnapshot') + const stringifyParameters = JSON.stringify(parameters) + logger.debug(`Initiating a deletable snapshot search for ${stringifyParameters}`) + + const eventStore = config.resourceNames.eventsStore + const filter = buildDeleteEntityFilter(parameters.entityTypeName, parameters.entityID, parameters.createdAt) + const result = (await search(cosmosDb, config, eventStore, filter)) as Array + logger.debug(`Finished deletable snapshot search for ${stringifyParameters}`) + return result +} + +export async function deleteEvent( + cosmosDb: CosmosClient, + config: BoosterConfig, + events: Array +): Promise { + const logger = getLogger(config, 'events-delete-adapter#deleteEvent') + const stringifyParameters = JSON.stringify(events) + logger.debug(`Initiating an event delete for ${stringifyParameters}`) + + const eventStore = config.resourceNames.eventsStore + if (!events || events.length === 0) { + logger.warn('Could not find events to delete') + return + } + for (const event of events) { + const newEvent = buildNewEvent(event) + const partitionKey = partitionKeyBuilder(event.entityTypeName, event.entityID, 'event') + await replaceOrDeleteItem(cosmosDb, eventStore, config, event.id, partitionKey, newEvent) + } + logger.debug(`Finished event delete for ${stringifyParameters}`) +} + +export async function deleteSnapshot( + cosmosDb: CosmosClient, + config: BoosterConfig, + snapshots: Array +): Promise { + const logger = getLogger(config, 'events-delete-adapter#deleteSnapshot') + const stringifyParameters = JSON.stringify(snapshots) + logger.debug(`Initiating a snapshot delete for ${stringifyParameters}`) + + const eventStore = config.resourceNames.eventsStore + if (!snapshots || snapshots.length === 0) { + logger.warn('Could not find snapshot to delete') + return + } + for (const snapshot of snapshots) { + const partitionKey = partitionKeyBuilder(snapshot.entityTypeName, snapshot.entityID, 'snapshot') + await replaceOrDeleteItem(cosmosDb, eventStore, config, snapshot.id, partitionKey) + } + logger.debug(`Finished snapshot delete for ${stringifyParameters}`) +} + +interface DeleteQueryFields { + createdAt: string + entityTypeName_entityID_kind: string + kind: string + deletedAt: string +} + +function buildNewEvent(existingEvent: EventEnvelopeFromDatabase): EventEnvelope { + return { + ...existingEvent, + deletedAt: new Date().toISOString(), + value: {}, + } +} + +function buildDeleteEventFilter( + entityTypeName: string, + entityId: UUID, + createdAt: string +): FilterFor { + const value = `${entityTypeName}-${entityId}-event` + return { + entityTypeName_entityID_kind: { eq: value }, + createdAt: { eq: createdAt }, + kind: { eq: 'event' }, + deletedAt: { isDefined: false }, + } +} + +function buildDeleteEntityFilter( + entityTypeName: string, + entityId: UUID, + createdAt: string +): FilterFor { + const value = `${entityTypeName}-${entityId}-snapshot` + return { + entityTypeName_entityID_kind: { eq: value }, + kind: { eq: 'snapshot' }, + createdAt: { eq: createdAt }, + deletedAt: { isDefined: false }, + } +} + +function partitionKeyBuilder(entityTypeName: string, entityID: UUID, kind: 'event' | 'snapshot'): string { + return `${entityTypeName}-${entityID}-${kind}` +} diff --git a/packages/framework-provider-azure/src/library/events-adapter.ts b/packages/framework-provider-azure/src/library/events-adapter.ts index 9ddf11886..3ab740f56 100644 --- a/packages/framework-provider-azure/src/library/events-adapter.ts +++ b/packages/framework-provider-azure/src/library/events-adapter.ts @@ -24,11 +24,14 @@ export async function readEntityEventsSince( entityID: UUID, since?: string ): Promise> { + const logger = getLogger(config, 'events-adapter#readEntityEventsSince') const fromTime = since ? since : originOfTime const querySpec: SqlQuerySpec = { query: `SELECT * FROM c WHERE c["${eventsStoreAttributes.partitionKey}"] = @partitionKey ` + - `AND c["${eventsStoreAttributes.sortKey}"] > @fromTime ORDER BY c["${eventsStoreAttributes.sortKey}"] ASC`, + `AND c["${eventsStoreAttributes.sortKey}"] > @fromTime ` + + 'AND NOT IS_DEFINED(c["deletedAt"]) ' + + `ORDER BY c["${eventsStoreAttributes.sortKey}"] ASC`, parameters: [ { name: '@partitionKey', @@ -45,6 +48,7 @@ export async function readEntityEventsSince( .container(config.resourceNames.eventsStore) .items.query(querySpec) .fetchAll() + logger.debug(`Loaded events for entity ${entityTypeName} with ID ${entityID} with result:`, resources) return resources as Array } diff --git a/packages/framework-provider-azure/src/library/events-searcher-adapter.ts b/packages/framework-provider-azure/src/library/events-searcher-adapter.ts index 00f585ce3..1d3872dab 100644 --- a/packages/framework-provider-azure/src/library/events-searcher-adapter.ts +++ b/packages/framework-provider-azure/src/library/events-searcher-adapter.ts @@ -42,7 +42,11 @@ export async function searchEntitiesIds( afterCursor )}, entityTypeName: ${entityTypeName}` ) - const filterQuery = { kind: { eq: 'event' }, entityTypeName: { eq: entityTypeName } } + const filterQuery = { + kind: { eq: 'event' }, + entityTypeName: { eq: entityTypeName }, + deletedAt: { isDefined: false }, + } const eventStore = config.resourceNames.eventsStore const result = (await search( diff --git a/packages/framework-provider-azure/src/library/events-searcher-builder.ts b/packages/framework-provider-azure/src/library/events-searcher-builder.ts index 052e57773..3b38d0fcb 100644 --- a/packages/framework-provider-azure/src/library/events-searcher-builder.ts +++ b/packages/framework-provider-azure/src/library/events-searcher-builder.ts @@ -57,6 +57,7 @@ export function resultToEventSearchResponse(result: any[]): Array { const logger = getLogger(config, 'read-model-adapter#deleteReadModel') - logger.debug(`[ReadModelAdapter#deleteReadModel] Entering to Read model deleted. ID = ${readModel.id}`) - await db - .database(config.resourceNames.applicationStack) - .container(config.resourceNames.forReadModel(readModelName)) - .item(readModel.id as string, readModel.id as string) - .delete() - logger.debug(`[ReadModelAdapter#deleteReadModel] Read model deleted. ID = ${readModel.id}`) + logger.debug(`Entering to Read model deleted. ${readModelName} ID = ${readModel.id}`) + try { + await db + .database(config.resourceNames.applicationStack) + .container(config.resourceNames.forReadModel(readModelName)) + .item(readModel.id as string, readModel.id as string) + .delete() + logger.debug(`Read model deleted. ${readModelName} ID = ${readModel.id}`) + } catch (e) { + logger.warn(`Read model to delete ${readModelName} ID = ${readModel.id} not found`) + } } export async function rawReadModelEventsToEnvelopes( diff --git a/packages/framework-provider-azure/test/library/events-adapter.test.ts b/packages/framework-provider-azure/test/library/events-adapter.test.ts index e58b285c2..47019c1ad 100644 --- a/packages/framework-provider-azure/test/library/events-adapter.test.ts +++ b/packages/framework-provider-azure/test/library/events-adapter.test.ts @@ -73,7 +73,7 @@ describe('Events adapter', () => { match({ query: `SELECT * FROM c WHERE c["${eventsStoreAttributes.partitionKey}"] = @partitionKey ` + - `AND c["${eventsStoreAttributes.sortKey}"] > @fromTime ORDER BY c["${eventsStoreAttributes.sortKey}"] ASC`, + `AND c["${eventsStoreAttributes.sortKey}"] > @fromTime AND NOT IS_DEFINED(c["deletedAt"]) ORDER BY c["${eventsStoreAttributes.sortKey}"] ASC`, parameters: [ { name: '@partitionKey', diff --git a/packages/framework-provider-azure/test/library/events-searcher-adapter.test.ts b/packages/framework-provider-azure/test/library/events-searcher-adapter.test.ts index 208028a1c..933a2fe97 100644 --- a/packages/framework-provider-azure/test/library/events-searcher-adapter.test.ts +++ b/packages/framework-provider-azure/test/library/events-searcher-adapter.test.ts @@ -205,7 +205,7 @@ describe('Events Searcher adapter', () => { mockCosmosDbClient, mockConfig, eventStoreName, - { kind: { eq: 'event' }, entityTypeName: { eq: 'entity' } }, + { kind: { eq: 'event' }, entityTypeName: { eq: 'entity' }, deletedAt: { isDefined: false } }, 1, { id: '1' }, true, @@ -225,7 +225,7 @@ describe('Events Searcher adapter', () => { mockCosmosDbClient, mockConfig, eventStoreName, - { kind: { eq: 'event' }, entityTypeName: { eq: 'entity' } }, + { kind: { eq: 'event' }, entityTypeName: { eq: 'entity' }, deletedAt: { isDefined: false } }, 1, undefined, true, diff --git a/packages/framework-provider-local/src/index.ts b/packages/framework-provider-local/src/index.ts index 3676d89d3..e7933d6f7 100644 --- a/packages/framework-provider-local/src/index.ts +++ b/packages/framework-provider-local/src/index.ts @@ -48,6 +48,7 @@ import { isGraphQLFunctionUp, rawRequestToSensorHealth, } from './library/health-adapter' +import { deleteEvent, deleteSnapshot, findDeletableEvent, findDeletableSnapshot } from './library/event-delete-adapter' import * as process from 'process' export * from './paths' @@ -82,6 +83,10 @@ export const Provider = (rocketDescriptors?: RocketDescriptor[]): ProviderLibrar search: searchEvents.bind(null, eventRegistry), searchEntitiesIDs: searchEntitiesIds.bind(null, eventRegistry), storeDispatched: storeDispatchedEvent, + findDeletableEvent: findDeletableEvent.bind(null, eventRegistry), + findDeletableSnapshot: findDeletableSnapshot.bind(null, eventRegistry), + deleteEvent: deleteEvent.bind(null, eventRegistry), + deleteSnapshot: deleteSnapshot.bind(null, eventRegistry), }, // ProviderReadModelsLibrary readModels: { diff --git a/packages/framework-provider-local/src/library/event-delete-adapter.ts b/packages/framework-provider-local/src/library/event-delete-adapter.ts new file mode 100644 index 000000000..b6258304b --- /dev/null +++ b/packages/framework-provider-local/src/library/event-delete-adapter.ts @@ -0,0 +1,133 @@ +import { + BoosterConfig, + EventEnvelope, + EventDeleteParameters, + SnapshotDeleteParameters, + UUID, + EventEnvelopeFromDatabase, + EntitySnapshotEnvelopeFromDatabase, + EntitySnapshotEnvelope, +} from '@boostercloud/framework-types' +import { getLogger } from '@boostercloud/framework-common-helpers' +import { EventRegistry } from '../services' +import { QueryOperation, QueryValue } from './searcher-adapter' + +type DatabaseEventEnvelopeWithId = EventEnvelope & { _id: string } +type DatabaseEntitySnapshotEnvelopeWithId = EntitySnapshotEnvelope & { _id: string } + +export async function findDeletableEvent( + eventRegistry: EventRegistry, + config: BoosterConfig, + parameters: EventDeleteParameters +): Promise> { + const logger = getLogger(config, 'events-delete-adapter#findDeletableEvent') + const stringifyParameters = JSON.stringify(parameters) + logger.debug(`Initiating a deletable event search for ${stringifyParameters}`) + + const filter = buildDeleteEventFilter(parameters.entityTypeName, parameters.entityID, parameters.createdAt) + const events = (await eventRegistry.query(filter)) as Array + const result = events.map((event) => { + return { + ...event, + id: event._id, + } + }) as Array + logger.debug(`Finished deletable event search for ${stringifyParameters}`) + return result +} + +export async function findDeletableSnapshot( + eventRegistry: EventRegistry, + config: BoosterConfig, + parameters: SnapshotDeleteParameters +): Promise> { + const logger = getLogger(config, 'events-delete-adapter#findDeletableSnapshot') + const stringifyParameters = JSON.stringify(parameters) + logger.debug(`Initiating a deletable snapshot search for ${stringifyParameters}`) + + const filter = buildDeleteEntityFilter(parameters.entityTypeName, parameters.entityID, parameters.createdAt) + const snapshots = (await eventRegistry.query(filter)) as Array + const result: Array = snapshots.map((snapshot) => { + return { + ...snapshot, + id: snapshot._id, + } + }) + logger.debug(`Finished deletable snapshot search for ${stringifyParameters}`) + return result +} + +export async function deleteEvent( + eventRegistry: EventRegistry, + config: BoosterConfig, + events: Array +): Promise { + const logger = getLogger(config, 'events-delete-adapter#deleteEvent') + const stringifyParameters = JSON.stringify(events) + logger.debug(`Initiating an event delete for ${stringifyParameters}`) + + if (!events || events.length === 0) { + logger.warn('Could not find events to delete') + return + } + for (const event of events) { + const newEvent = buildNewEvent(event) + await eventRegistry.replaceOrDeleteItem(event.id, newEvent) + } + logger.debug(`Finished event delete for ${stringifyParameters}`) +} + +export async function deleteSnapshot( + eventRegistry: EventRegistry, + config: BoosterConfig, + snapshots: Array +): Promise { + const logger = getLogger(config, 'events-delete-adapter#deleteSnapshot') + const stringifyParameters = JSON.stringify(snapshots) + logger.debug(`Initiating a snapshot delete for ${stringifyParameters}`) + + if (!snapshots || snapshots.length === 0) { + logger.warn('Could not find snapshot to delete') + return + } + for (const snapshot of snapshots) { + await eventRegistry.replaceOrDeleteItem(snapshot.id) + } + logger.debug(`Finished snapshot delete for ${stringifyParameters}`) +} + +function buildNewEvent(existingEvent: EventEnvelopeFromDatabase): EventEnvelope { + return { + ...existingEvent, + deletedAt: new Date().toISOString(), + value: {}, + } +} + +function buildDeleteEventFilter( + entityTypeName: string, + entityId: UUID, + createdAt: string +): Record> { + return { + entityID: entityId.toString(), + entityTypeName: entityTypeName, + createdAt: createdAt, + kind: 'event', + deletedAt: { $exists: false }, + } +} + +function buildDeleteEntityFilter( + entityTypeName: string, + entityId: UUID, + createdAt: string +): Record> { + return { + entityTypeName: entityTypeName, + entityID: entityId.toString(), + kind: 'snapshot', + createdAt: createdAt, + deletedAt: { $exists: false }, + } +} diff --git a/packages/framework-provider-local/src/library/events-adapter.ts b/packages/framework-provider-local/src/library/events-adapter.ts index f722ab6db..87fa1e0ec 100644 --- a/packages/framework-provider-local/src/library/events-adapter.ts +++ b/packages/framework-provider-local/src/library/events-adapter.ts @@ -35,6 +35,7 @@ export async function readEntityEventsSince( createdAt: { $gt: fromTime, }, + deletedAt: { $exists: false }, } const result = await eventRegistry.query(query) diff --git a/packages/framework-provider-local/src/library/events-search-adapter.ts b/packages/framework-provider-local/src/library/events-search-adapter.ts index b704fab99..d77ac8e65 100644 --- a/packages/framework-provider-local/src/library/events-search-adapter.ts +++ b/packages/framework-provider-local/src/library/events-search-adapter.ts @@ -46,7 +46,11 @@ export async function searchEntitiesIds( afterCursor )}, entityTypeName: ${entityTypeName}` ) - const filterQuery = { ...DEFAULT_KIND_FILTER, entityTypeName: entityTypeName } + const filterQuery = { + ...DEFAULT_KIND_FILTER, + entityTypeName: entityTypeName, + deletedAt: { $exists: false }, + } const result = (await eventRegistry.query(filterQuery, DEFAULT_CREATED_AT_SORT_ORDER, undefined, { entityID: 1, diff --git a/packages/framework-provider-local/src/library/events-searcher-builder.ts b/packages/framework-provider-local/src/library/events-searcher-builder.ts index 206cfcf6f..61f7a7038 100644 --- a/packages/framework-provider-local/src/library/events-searcher-builder.ts +++ b/packages/framework-provider-local/src/library/events-searcher-builder.ts @@ -56,6 +56,7 @@ export function resultToEventSearchResponse(result: Array | null) user: item.currentUser, createdAt: item.createdAt, value: item.value, + deletedAt: item.deletedAt, } as EventSearchResponse }) return eventSearchResult ?? [] diff --git a/packages/framework-provider-local/src/library/read-model-adapter.ts b/packages/framework-provider-local/src/library/read-model-adapter.ts index 819a23d0f..7dd32b848 100644 --- a/packages/framework-provider-local/src/library/read-model-adapter.ts +++ b/packages/framework-provider-local/src/library/read-model-adapter.ts @@ -114,6 +114,10 @@ export async function deleteReadModel( ): Promise { const logger = getLogger(config, 'read-model-adapter#deleteReadModel') logger.debug(`Entering to Read model deleted. ID=${readModel.id}.Name=${readModelName}`) - await db.deleteById(readModel.id, readModelName) - logger.debug(`Read model deleted. ID=${readModel.id}. Name=${readModelName}`) + try { + await db.deleteById(readModel.id, readModelName) + logger.debug(`Read model deleted. ${readModelName} ID = ${readModel.id}`) + } catch (e) { + logger.warn(`Read model to delete ${readModelName} ID = ${readModel.id} not found`) + } } diff --git a/packages/framework-provider-local/src/library/searcher-adapter.ts b/packages/framework-provider-local/src/library/searcher-adapter.ts index 90828a02b..05ac6e93d 100644 --- a/packages/framework-provider-local/src/library/searcher-adapter.ts +++ b/packages/framework-provider-local/src/library/searcher-adapter.ts @@ -49,8 +49,8 @@ function filterToQuery(filter: FilterFor): QueryOperation { return query } -type QueryValue = number | string | boolean -type QueryOperation = +export type QueryValue = number | string | boolean +export type QueryOperation = // In the case that the operation is `eq`, NeDB matches directly | TValue // For these, the value must be single as a result diff --git a/packages/framework-provider-local/src/services/event-registry.ts b/packages/framework-provider-local/src/services/event-registry.ts index 0a3f9f3e9..fd0284a00 100644 --- a/packages/framework-provider-local/src/services/event-registry.ts +++ b/packages/framework-provider-local/src/services/event-registry.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/ban-types */ import { EntitySnapshotEnvelope, EventEnvelope, EventStoreEntryEnvelope } from '@boostercloud/framework-types' import { eventsDatabase } from '../paths' + const DataStore = require('@seald-io/nedb') export class EventRegistry { @@ -37,6 +38,24 @@ export class EventRegistry { return await cursor.execAsync() } + public async replaceOrDeleteItem(id: string, newValue?: EventEnvelope | EntitySnapshotEnvelope): Promise { + if (newValue) { + await new Promise((resolve, reject) => + this.events.update({ _id: id }, newValue, { multi: true }, (err: any, numRemoved: number) => { + if (err) reject(err) + else resolve(numRemoved) + }) + ) + } else { + await new Promise((resolve, reject) => + this.events.remove({ _id: id }, { multi: true }, (err: any, numRemoved: number) => { + if (err) reject(err) + else resolve(numRemoved) + }) + ) + } + } + public async queryLatestSnapshot(query: object): Promise { await this.loadDatabaseIfNeeded() const cursor = this.events.findAsync({ ...query, kind: 'snapshot' }).sort({ snapshottedEventCreatedAt: -1 }) // Sort in descending order (newer timestamps first) diff --git a/packages/framework-provider-local/test/library/events-adapter.test.ts b/packages/framework-provider-local/test/library/events-adapter.test.ts index 029499e01..e03dd0029 100644 --- a/packages/framework-provider-local/test/library/events-adapter.test.ts +++ b/packages/framework-provider-local/test/library/events-adapter.test.ts @@ -123,6 +123,9 @@ describe('events-adapter', () => { kind: 'event', entityID: mockEntityID, entityTypeName: mockEntityTypeName, + deletedAt: { + $exists: false, + }, }) }) @@ -149,6 +152,9 @@ describe('events-adapter', () => { kind: 'event', entityID: mockEntityID, entityTypeName: mockEntityTypeName, + deletedAt: { + $exists: false, + }, }) }) diff --git a/packages/framework-provider-local/test/library/events-search-adapter.test.ts b/packages/framework-provider-local/test/library/events-search-adapter.test.ts index a34b8fb0f..6f21b8bef 100644 --- a/packages/framework-provider-local/test/library/events-search-adapter.test.ts +++ b/packages/framework-provider-local/test/library/events-search-adapter.test.ts @@ -29,9 +29,14 @@ describe('The "searchEntitiesIDs" method', () => { const entityTypeName = 'entity' await searchEntitiesIds(mockEventRegistry as any, mockConfig, limit, afterCursor, entityTypeName) - expect(queryStub).to.have.been.calledWithExactly({ kind: 'event', entityTypeName: 'entity' }, -1, undefined, { - entityID: 1, - }) + expect(queryStub).to.have.been.calledWithExactly( + { kind: 'event', entityTypeName: 'entity', deletedAt: { $exists: false } }, + -1, + undefined, + { + entityID: 1, + } + ) }) it('Generate query for entityTypeName, limit has all fields', async () => { @@ -39,8 +44,13 @@ describe('The "searchEntitiesIDs" method', () => { const entityTypeName = 'entity' await searchEntitiesIds(mockEventRegistry as any, mockConfig, limit, undefined, entityTypeName) - expect(queryStub).to.have.been.calledWithExactly({ kind: 'event', entityTypeName: 'entity' }, -1, undefined, { - entityID: 1, - }) + expect(queryStub).to.have.been.calledWithExactly( + { kind: 'event', entityTypeName: 'entity', deletedAt: { $exists: false } }, + -1, + undefined, + { + entityID: 1, + } + ) }) }) diff --git a/packages/framework-types/src/booster-app.ts b/packages/framework-types/src/booster-app.ts index 4cc9218ce..b17fcaca4 100644 --- a/packages/framework-types/src/booster-app.ts +++ b/packages/framework-types/src/booster-app.ts @@ -8,6 +8,7 @@ import { EventSearchParameters, EventSearchResponse, PaginatedEntitiesIdsResult, + EventDeleteParameters, } from '.' /** @@ -27,5 +28,6 @@ export interface BoosterApp { limit: number, afterCursor: Record | undefined ): Promise + deleteEvent(parameters: EventDeleteParameters): Promise configuredEnvironments: Set } diff --git a/packages/framework-types/src/concepts/read-model.ts b/packages/framework-types/src/concepts/read-model.ts index e4b96f08e..3ee2e0977 100644 --- a/packages/framework-types/src/concepts/read-model.ts +++ b/packages/framework-types/src/concepts/read-model.ts @@ -28,3 +28,12 @@ export interface ReadModelMetadata['before']> } + +export enum ProjectionInfoReason { + ENTITY_PROJECTED, + ENTITY_DELETED, +} + +export interface ProjectionInfo { + reason: ProjectionInfoReason +} diff --git a/packages/framework-types/src/config.ts b/packages/framework-types/src/config.ts index 224f939a2..bc4bd8c72 100644 --- a/packages/framework-types/src/config.ts +++ b/packages/framework-types/src/config.ts @@ -85,6 +85,7 @@ export class BoosterConfig { public readonly eventHandlers: Record> = {} public readonly readModels: Record = {} public readonly projections: Record>> = {} + public readonly unProjections: Record>> = {} public readonly readModelSequenceKeys: Record = {} public readonly roles: Record = {} public readonly schemaMigrations: Record> = {} diff --git a/packages/framework-types/src/envelope.ts b/packages/framework-types/src/envelope.ts index 348fdf772..113088650 100644 --- a/packages/framework-types/src/envelope.ts +++ b/packages/framework-types/src/envelope.ts @@ -58,6 +58,7 @@ export interface NonPersistedEventEnvelope extends EventStoreEntryEnvelope { export interface EventEnvelope extends NonPersistedEventEnvelope { id?: string createdAt: string + deletedAt?: string } export interface NonPersistedEntitySnapshotEnvelope extends EventStoreEntryEnvelope { @@ -104,6 +105,7 @@ export interface EventSearchResponse { user?: UserEnvelope createdAt: string value: EventInterface | NotificationInterface + deletedAt?: string } export interface ReadModelEnvelope { @@ -214,3 +216,16 @@ export interface ContextEnvelope { /** Provider-dependent raw request context object */ rawContext: unknown } + +export type EventEnvelopeFromDatabase = EventEnvelope & { id: string } +export type EntitySnapshotEnvelopeFromDatabase = EntitySnapshotEnvelope & { id: string } +export interface EventDeleteParameters { + entityTypeName: string + entityID: string + createdAt: string +} +export interface SnapshotDeleteParameters { + entityID: UUID + entityTypeName: string + createdAt: string +} diff --git a/packages/framework-types/src/provider.ts b/packages/framework-types/src/provider.ts index efb2efae0..c5a96525b 100644 --- a/packages/framework-types/src/provider.ts +++ b/packages/framework-types/src/provider.ts @@ -3,7 +3,10 @@ import { BoosterConfig } from './config' import { ConnectionDataEnvelope, EntitySnapshotEnvelope, + EntitySnapshotEnvelopeFromDatabase, + EventDeleteParameters, EventEnvelope, + EventEnvelopeFromDatabase, EventSearchParameters, EventSearchResponse, GraphQLRequestEnvelope, @@ -15,6 +18,7 @@ import { ReadModelEnvelope, ReadModelListResult, ScheduledCommandEnvelope, + SnapshotDeleteParameters, SubscriptionEnvelope, } from './envelope' import { FilterFor, ProjectionFor, SortFor } from './searcher' @@ -155,6 +159,44 @@ export interface ProviderEventsLibrary { * table, throws an error on any other type of error. */ storeDispatched(eventEnvelope: EventEnvelope, config: BoosterConfig): Promise + + /** + * Find all events to be removed based on the parameters + * + * @param config + * @param parameters + */ + findDeletableEvent( + config: BoosterConfig, + parameters: EventDeleteParameters + ): Promise> + + /** + * Find all snapshots to be removed based on the parameters + * + * @param config + * @param parameters + */ + findDeletableSnapshot( + config: BoosterConfig, + parameters: SnapshotDeleteParameters + ): Promise> + + /** + * Delete events + * + * @param config + * @param events + */ + deleteEvent(config: BoosterConfig, events: Array): Promise + + /** + * Delete snapshots + * + * @param config + * @param snapshots + */ + deleteSnapshot(config: BoosterConfig, snapshots: Array): Promise } export interface ProviderReadModelsLibrary { diff --git a/website/docs/10_going-deeper/remove-events.mdx b/website/docs/10_going-deeper/remove-events.mdx new file mode 100644 index 000000000..e5e8ada01 --- /dev/null +++ b/website/docs/10_going-deeper/remove-events.mdx @@ -0,0 +1,91 @@ +# Remove events + +> **Warning**: This is an experimental functionality. Please note that this functionality is only supported by Azure and Local providers. + +Booster allows to delete past events and their related entities as to update the affected ReadModels. + +By using the `Booster.deleteEvent` command it is possible to indicate the event to be deleted. To do so, you must indicate: + +* entityID: The `id` of the entity of the event to be deleted +* entityTypeName: The entity type name of the event entity to be deleted +* createdAt: The date of creation of the event. + +Example: +```typescript +import { Booster, Command } from '@boostercloud/framework-core' +import { EventDeleteParameters } from '@boostercloud/framework-types' + +@Command({ + authorize: 'all', +}) +export class HardDelete { + public constructor(readonly entityId: string, readonly entityTypeName: string, readonly createdAt: string) {} + + public static async handle(command: HardDelete): Promise { + const parameters: EventDeleteParameters = { + entityID: command.entityId, + entityTypeName: command.entityTypeName, + createdAt: command.createdAt, + } + return await Booster.deleteEvent(parameters) + } +} +``` + +When executing this command, Booster will update the selected event in the corresponding database with an empty value and a deletion date. +This way, it will be reflected in the system that there was an event that was subsequently deleted. + +Deleted events are ignored by Booster, but they can be accessed using the corresponding methods (`eventsByEntity` and `eventsByType`). + +The entities associated with a deleted event will be permanently removed from the database. + +ReadModels are not automatically modified or deleted and it is up to the user to act accordingly. +To do so, the methods annotated with `@Project` of the ReadModels have a third parameter `unProject` which allows to define a +function that will be executed when the entity defined in the projection and with the `joinKey` defined in the projection is deleted. + +This third parameter will be a static function with the same parameters as the method we are projecting. + +It is possible to use the same method that is used for projecting to resolve the deletions by simply specifying this +method as `unProject`. + +Example: +```typescript + @Projects(Pack, 'products', ProductReadModel.updateWithPack) + public static updateWithPack( + pack: Pack, + readModelID: UUID, + currentProductReadModel?: ProductReadModel, + projectionInfo?: ProjectionInfo + ): ProjectionResult { + if (projectionInfo?.reason === ProjectionInfoReason.ENTITY_DELETED) { + return ReadModelAction.Delete + } + // ... other code + } +``` + +In this case, if the `Pack` entity with the joinKey `products` is deleted, the `updateWithPack` method will be executed and will include a last parameter called `projectionInfo`. +This parameter contains the `reason` field, which in this case will be set to `ENTITY_DELETED` to indicate that the entity is being deleted. + +Another option is to define your own deletion method independent of the projection method. In case of deletion the method +called will be the newly defined method. + +Example: +```typescript + @Projects(Product, 'id', ProductReadModel.unProjectWithProduct) + public static updateWithProduct(product: Product): ProjectionResult { + // ... other code + } + + public static unProjectWithProduct( + _product: Product, + _currentProductReadModel?: ProductReadModel, + _projectionInfo?: ProjectionInfo + ): ProjectionResult { + return ReadModelAction.Delete + } +``` + +([See more details about how to delete a ReadModel in the docs](https://docs.boosterframework.com/architecture/read-model/#deleting-read-models)) + +> **Warning**: Please note that these changes are final and it is not possible to retrieve the information once they have been made. diff --git a/website/sidebars.js b/website/sidebars.js index 335f534cf..377a95ca4 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -113,6 +113,7 @@ const sidebars = { 'going-deeper/custom-templates', 'going-deeper/framework-packages', 'going-deeper/instrumentation', + 'going-deeper/remove-events', 'going-deeper/azure-scale' ], },