From 0bad9101fc7c78b0081f7513399b875d35350bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Fournier?= Date: Tue, 17 Jun 2025 15:37:57 +0200 Subject: [PATCH 01/41] chore: use logger instead of console.error (#10150) --- .../client-metrics/metrics-service-v2.ts | 26 ++++++++++++++----- .../features/scheduler/schedule-services.ts | 4 +-- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/lib/features/metrics/client-metrics/metrics-service-v2.ts b/src/lib/features/metrics/client-metrics/metrics-service-v2.ts index 00271320a9..be8ede5511 100644 --- a/src/lib/features/metrics/client-metrics/metrics-service-v2.ts +++ b/src/lib/features/metrics/client-metrics/metrics-service-v2.ts @@ -72,7 +72,13 @@ export default class ClientMetricsServiceV2 { } async clearMetrics(hoursAgo: number) { - return this.clientMetricsStoreV2.clearMetrics(hoursAgo); + try { + await this.clientMetricsStoreV2.clearMetrics(hoursAgo); + } catch (e) { + this.logger.warn( + `Failed to clear client metrics older than ${hoursAgo} hours: ${e.message}`, + ); + } } async clearDailyMetrics(daysAgo: number) { @@ -260,12 +266,18 @@ export default class ClientMetricsServiceV2 { async bulkAdd(): Promise { if (this.unsavedMetrics.length > 0) { - // Make a copy of `unsavedMetrics` in case new metrics - // arrive while awaiting `batchInsertMetrics`. - const copy = [...this.unsavedMetrics]; - this.unsavedMetrics = []; - await this.clientMetricsStoreV2.batchInsertMetrics(copy); - this.config.eventBus.emit(CLIENT_METRICS_ADDED, copy); + try { + // Make a copy of `unsavedMetrics` in case new metrics + // arrive while awaiting `batchInsertMetrics`. + const copy = [...this.unsavedMetrics]; + this.unsavedMetrics = []; + await this.clientMetricsStoreV2.batchInsertMetrics(copy); + this.config.eventBus.emit(CLIENT_METRICS_ADDED, copy); + } catch (error) { + this.logger.warn( + `Failed to bulk add client metrics: ${error.message}`, + ); + } } } diff --git a/src/lib/features/scheduler/schedule-services.ts b/src/lib/features/scheduler/schedule-services.ts index de64a296e7..f3f8b23257 100644 --- a/src/lib/features/scheduler/schedule-services.ts +++ b/src/lib/features/scheduler/schedule-services.ts @@ -152,13 +152,13 @@ export const scheduleServices = ( ); schedulerService.schedule( - () => clientMetricsServiceV2.bulkAdd().catch(console.error), + () => clientMetricsServiceV2.bulkAdd(), secondsToMilliseconds(5), 'bulkAddMetrics', ); schedulerService.schedule( - () => clientMetricsServiceV2.clearMetrics(48).catch(console.error), + () => clientMetricsServiceV2.clearMetrics(48), hoursToMilliseconds(12), 'clearMetrics', ); From 95049b8f9f7d5d7ba14d9508d1561e6f092bcf60 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Tue, 17 Jun 2025 15:38:42 +0200 Subject: [PATCH 02/41] Fix/constraint editing is broken in segment form (#10152) This adds constraint ids to segment constraints used in editing segments. Without them, there was a bug where when you went to edit the segment, all constraints would be invisibly set to the same constraint. --- .../EditableConstraintsList.tsx | 2 +- .../segments/EditSegment/EditSegment.tsx | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx index d4a5589c1b..29d43f183e 100644 --- a/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx +++ b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx @@ -70,7 +70,7 @@ export const EditableConstraintsList = forwardRef< {constraints.map((constraint, index) => ( onDelete(index)} onUpdate={onAutoSave(constraint[constraintId])} diff --git a/frontend/src/component/segments/EditSegment/EditSegment.tsx b/frontend/src/component/segments/EditSegment/EditSegment.tsx index b385177d2e..c0df6ef2ee 100644 --- a/frontend/src/component/segments/EditSegment/EditSegment.tsx +++ b/frontend/src/component/segments/EditSegment/EditSegment.tsx @@ -24,15 +24,31 @@ import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentL import { useOptionalPathParam } from 'hooks/useOptionalPathParam'; import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi'; import { useHighestPermissionChangeRequestEnvironment } from 'hooks/useHighestPermissionChangeRequestEnvironment'; +import type { ISegment } from 'interfaces/segment.ts'; +import { constraintId } from 'constants/constraintId.ts'; +import { v4 as uuidv4 } from 'uuid'; interface IEditSegmentProps { modal?: boolean; } +const addIdSymbolToConstraints = (segment?: ISegment): ISegment | undefined => { + if (!segment) return; + + const constraints = segment.constraints.map((constraint) => { + return { ...constraint, [constraintId]: uuidv4() }; + }); + + return { ...segment, constraints }; +}; + export const EditSegment = ({ modal }: IEditSegmentProps) => { const projectId = useOptionalPathParam('projectId'); const segmentId = useRequiredPathParam('segmentId'); - const { segment } = useSegment(Number(segmentId)); + const { segment: segmentWithoutConstraintIds } = useSegment( + Number(segmentId), + ); + const segment = addIdSymbolToConstraints(segmentWithoutConstraintIds); const { uiConfig } = useUiConfig(); const { setToastData, setToastApiError } = useToast(); const navigate = useNavigate(); From 80e1fc3119e7fb82933914e42530fed1b1779fcb Mon Sep 17 00:00:00 2001 From: Github Actions Bot <> Date: Tue, 17 Jun 2025 13:48:58 +0000 Subject: [PATCH 03/41] docs: Update CHANGELOG.md --- CHANGELOG.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c0c44ec60..f429866bd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,44 @@ All notable changes to this project will be documented in this file. +## [7.0.2] - 2025-06-17 + +### Bug Fixes + +- Correct upgrade link ([#10138](https://github.com/Unleash/unleash/issues/10138)) + + +### Features + +- Report hostedBy and licenseType ([#10141](https://github.com/Unleash/unleash/issues/10141)) + +- Translate impact metrics to prom format ([#10147](https://github.com/Unleash/unleash/issues/10147)) + +- Expose impact metrics ([#10151](https://github.com/Unleash/unleash/issues/10151)) + + +### Miscellaneous Tasks + +- Clean up flag overview redesign ([#10140](https://github.com/Unleash/unleash/issues/10140)) + +- Remove flag enterprise-payg ([#10139](https://github.com/Unleash/unleash/issues/10139)) + +- Added flag for CR approver emails ([#10144](https://github.com/Unleash/unleash/issues/10144)) + +- Add PSF to approved licenses list ([#10148](https://github.com/Unleash/unleash/issues/10148)) + +- Now expose IFeatureUsageInfo to override telemetry checking ([#10149](https://github.com/Unleash/unleash/issues/10149)) + +- Improve json diff view ([#10146](https://github.com/Unleash/unleash/issues/10146)) + +- Use logger instead of console.error ([#10150](https://github.com/Unleash/unleash/issues/10150)) + + +### Refactor + +- Migrate from make-fetch-happen to ky and use ky natively ([#10134](https://github.com/Unleash/unleash/issues/10134)) + + ## [7.0.1] - 2025-06-13 ### Bug Fixes From 9bce7b967503602dc084e3ce33a1e1722e2611df Mon Sep 17 00:00:00 2001 From: Github Actions Bot <> Date: Tue, 17 Jun 2025 13:49:07 +0000 Subject: [PATCH 04/41] 7.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1a1534ee99..93377d8b33 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "unleash-server", "type": "module", "description": "Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.", - "version": "7.0.1", + "version": "7.0.2", "keywords": [ "unleash", "feature flag", From 6f7f48a361f44672120f1a0c3e8e87ff2a91ddf5 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Tue, 17 Jun 2025 17:20:39 +0200 Subject: [PATCH 05/41] Revert "chore: use logger instead of console.error" (#10153) Reverts Unleash/unleash#10150 --- .../client-metrics/metrics-service-v2.ts | 26 +++++-------------- .../features/scheduler/schedule-services.ts | 4 +-- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/lib/features/metrics/client-metrics/metrics-service-v2.ts b/src/lib/features/metrics/client-metrics/metrics-service-v2.ts index be8ede5511..00271320a9 100644 --- a/src/lib/features/metrics/client-metrics/metrics-service-v2.ts +++ b/src/lib/features/metrics/client-metrics/metrics-service-v2.ts @@ -72,13 +72,7 @@ export default class ClientMetricsServiceV2 { } async clearMetrics(hoursAgo: number) { - try { - await this.clientMetricsStoreV2.clearMetrics(hoursAgo); - } catch (e) { - this.logger.warn( - `Failed to clear client metrics older than ${hoursAgo} hours: ${e.message}`, - ); - } + return this.clientMetricsStoreV2.clearMetrics(hoursAgo); } async clearDailyMetrics(daysAgo: number) { @@ -266,18 +260,12 @@ export default class ClientMetricsServiceV2 { async bulkAdd(): Promise { if (this.unsavedMetrics.length > 0) { - try { - // Make a copy of `unsavedMetrics` in case new metrics - // arrive while awaiting `batchInsertMetrics`. - const copy = [...this.unsavedMetrics]; - this.unsavedMetrics = []; - await this.clientMetricsStoreV2.batchInsertMetrics(copy); - this.config.eventBus.emit(CLIENT_METRICS_ADDED, copy); - } catch (error) { - this.logger.warn( - `Failed to bulk add client metrics: ${error.message}`, - ); - } + // Make a copy of `unsavedMetrics` in case new metrics + // arrive while awaiting `batchInsertMetrics`. + const copy = [...this.unsavedMetrics]; + this.unsavedMetrics = []; + await this.clientMetricsStoreV2.batchInsertMetrics(copy); + this.config.eventBus.emit(CLIENT_METRICS_ADDED, copy); } } diff --git a/src/lib/features/scheduler/schedule-services.ts b/src/lib/features/scheduler/schedule-services.ts index f3f8b23257..de64a296e7 100644 --- a/src/lib/features/scheduler/schedule-services.ts +++ b/src/lib/features/scheduler/schedule-services.ts @@ -152,13 +152,13 @@ export const scheduleServices = ( ); schedulerService.schedule( - () => clientMetricsServiceV2.bulkAdd(), + () => clientMetricsServiceV2.bulkAdd().catch(console.error), secondsToMilliseconds(5), 'bulkAddMetrics', ); schedulerService.schedule( - () => clientMetricsServiceV2.clearMetrics(48), + () => clientMetricsServiceV2.clearMetrics(48).catch(console.error), hoursToMilliseconds(12), 'clearMetrics', ); From e4dad39582f1ec32b310ce300045c7e2c984fd21 Mon Sep 17 00:00:00 2001 From: Github Actions Bot <> Date: Tue, 17 Jun 2025 15:37:17 +0000 Subject: [PATCH 06/41] docs: Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f429866bd7..bc01f44185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. +## [7.0.3] - 2025-06-17 + ## [7.0.2] - 2025-06-17 ### Bug Fixes From c2e77094621723ae21fce6b2e9956461f750bb33 Mon Sep 17 00:00:00 2001 From: Github Actions Bot <> Date: Tue, 17 Jun 2025 15:37:27 +0000 Subject: [PATCH 07/41] 7.0.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 93377d8b33..bcfee164ee 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "unleash-server", "type": "module", "description": "Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.", - "version": "7.0.2", + "version": "7.0.3", "keywords": [ "unleash", "feature flag", From d3c55b4ddc9c3dc57aed7ef146e699aedd30b0cd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 18:00:08 +0200 Subject: [PATCH 08/41] chore(deps): update dependency del-cli to v6 (#9267) --- package.json | 2 +- yarn.lock | 626 ++++++--------------------------------------------- 2 files changed, 69 insertions(+), 559 deletions(-) diff --git a/package.json b/package.json index bcfee164ee..36c9769ccb 100644 --- a/package.json +++ b/package.json @@ -167,7 +167,7 @@ "concurrently": "^9.0.0", "copyfiles": "2.4.1", "coveralls": "^3.1.1", - "del-cli": "5.1.0", + "del-cli": "6.0.0", "faker": "5.5.3", "fast-check": "3.23.2", "fetch-mock": "^12.0.0", diff --git a/yarn.lock b/yarn.lock index 3370fae807..4f95e295df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -95,15 +95,6 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0": - version: 7.18.6 - resolution: "@babel/code-frame@npm:7.18.6" - dependencies: - "@babel/highlight": "npm:^7.18.6" - checksum: 10c0/e3966f2717b7ebd9610524730e10b75ee74154f62617e5e115c97dbbbabc5351845c9aa850788012cb4d9aee85c3dc59fe6bef36690f244e8dcfca34bd35e9c9 - languageName: node - linkType: hard - "@babel/code-frame@npm:^7.25.9": version: 7.26.0 resolution: "@babel/code-frame@npm:7.26.0" @@ -232,13 +223,6 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.18.6": - version: 7.19.1 - resolution: "@babel/helper-validator-identifier@npm:7.19.1" - checksum: 10c0/f978ecfea840f65b64ab9e17fac380625a45f4fe1361eeb29867fcfd1c9eaa72abd7023f2f40ac3168587d7e5153660d16cfccb352a557be2efd347a051b4b20 - languageName: node - linkType: hard - "@babel/helper-validator-identifier@npm:^7.25.9": version: 7.25.9 resolution: "@babel/helper-validator-identifier@npm:7.25.9" @@ -270,17 +254,6 @@ __metadata: languageName: node linkType: hard -"@babel/highlight@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/highlight@npm:7.18.6" - dependencies: - "@babel/helper-validator-identifier": "npm:^7.18.6" - chalk: "npm:^2.0.0" - js-tokens: "npm:^4.0.0" - checksum: 10c0/a6a6928d25099ef04c337fcbb829fab8059bb67d31ac37212efd611bdbe247d0e71a5096c4524272cb56399f40251fac57c025e42d3bc924db0183a6435a60ac - languageName: node - linkType: hard - "@babel/parser@npm:^7.25.4": version: 7.27.2 resolution: "@babel/parser@npm:7.27.2" @@ -1170,6 +1143,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/merge-streams@npm:^2.1.0": + version: 2.3.0 + resolution: "@sindresorhus/merge-streams@npm:2.3.0" + checksum: 10c0/69ee906f3125fb2c6bb6ec5cdd84e8827d93b49b3892bce8b62267116cc7e197b5cccf20c160a1d32c26014ecd14470a72a5e3ee37a58f1d6dadc0db1ccf3894 + languageName: node + linkType: hard + "@slack/logger@npm:^4.0.0": version: 4.0.0 resolution: "@slack/logger@npm:4.0.0" @@ -1567,13 +1547,6 @@ __metadata: languageName: node linkType: hard -"@types/minimist@npm:^1.2.2": - version: 1.2.2 - resolution: "@types/minimist@npm:1.2.2" - checksum: 10c0/f220f57f682bbc3793dab4518f8e2180faa79d8e2589c79614fd777d7182be203ba399020c3a056a115064f5d57a065004a32b522b2737246407621681b24137 - languageName: node - linkType: hard - "@types/murmurhash3js@npm:^3.0.7": version: 3.0.7 resolution: "@types/murmurhash3js@npm:3.0.7" @@ -1624,13 +1597,6 @@ __metadata: languageName: node linkType: hard -"@types/normalize-package-data@npm:^2.4.0": - version: 2.4.1 - resolution: "@types/normalize-package-data@npm:2.4.1" - checksum: 10c0/c90b163741f27a1a4c3b1869d7d5c272adbd355eb50d5f060f9ce122ce4342cf35f5b0005f55ef780596cacfeb69b7eee54cd3c2e02d37f75e664945b6e75fc6 - languageName: node - linkType: hard - "@types/owasp-password-strength-test@npm:1.3.2": version: 1.3.2 resolution: "@types/owasp-password-strength-test@npm:1.3.2" @@ -1935,16 +1901,6 @@ __metadata: languageName: node linkType: hard -"aggregate-error@npm:^4.0.0": - version: 4.0.1 - resolution: "aggregate-error@npm:4.0.1" - dependencies: - clean-stack: "npm:^4.0.0" - indent-string: "npm:^5.0.0" - checksum: 10c0/75fd739f5c4c60a667cce35ccaf0edf135e147ef0be9a029cab75de14ac9421779b15339d562e58d25b233ea0ef2bbd4c916f149fdbcb73c2b9a62209e611343 - languageName: node - linkType: hard - "ajv-draft-04@npm:^1.0.0": version: 1.0.0 resolution: "ajv-draft-04@npm:1.0.0" @@ -2036,15 +1992,6 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^3.2.1": - version: 3.2.1 - resolution: "ansi-styles@npm:3.2.1" - dependencies: - color-convert: "npm:^1.9.0" - checksum: 10c0/ece5a8ef069fcc5298f67e3f4771a663129abd174ea2dfa87923a2be2abf6cd367ef72ac87942da00ce85bd1d651d4cd8595aebdb1b385889b89b205860e977b - languageName: node - linkType: hard - "ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": version: 4.3.0 resolution: "ansi-styles@npm:4.3.0" @@ -2105,13 +2052,6 @@ __metadata: languageName: node linkType: hard -"arrify@npm:^1.0.1": - version: 1.0.1 - resolution: "arrify@npm:1.0.1" - checksum: 10c0/c35c8d1a81bcd5474c0c57fe3f4bad1a4d46a5fa353cedcff7a54da315df60db71829e69104b859dff96c5d68af46bd2be259fe5e50dc6aa9df3b36bea0383ab - languageName: node - linkType: hard - "asap@npm:^2.0.0": version: 2.0.6 resolution: "asap@npm:2.0.6" @@ -2349,18 +2289,6 @@ __metadata: languageName: node linkType: hard -"camelcase-keys@npm:^7.0.0": - version: 7.0.2 - resolution: "camelcase-keys@npm:7.0.2" - dependencies: - camelcase: "npm:^6.3.0" - map-obj: "npm:^4.1.0" - quick-lru: "npm:^5.1.1" - type-fest: "npm:^1.2.1" - checksum: 10c0/ae86a51168643e9e8a2f2c7bfa17850729979ec3dafc5253056a7d97931cbb0e3ef5b4185e59d54b7a56c54405dee2874b0c82033498d8626e512ff9034cb05c - languageName: node - linkType: hard - "camelcase@npm:^5.0.0": version: 5.3.1 resolution: "camelcase@npm:5.3.1" @@ -2368,13 +2296,6 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^6.3.0": - version: 6.3.0 - resolution: "camelcase@npm:6.3.0" - checksum: 10c0/0d701658219bd3116d12da3eab31acddb3f9440790c0792e0d398f0a520a6a4058018e546862b6fba89d7ae990efaeb97da71e1913e9ebf5a8b5621a3d55c710 - languageName: node - linkType: hard - "caniuse-lite@npm:^1.0.30001663": version: 1.0.30001669 resolution: "caniuse-lite@npm:1.0.30001669" @@ -2402,17 +2323,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^2.0.0": - version: 2.4.2 - resolution: "chalk@npm:2.4.2" - dependencies: - ansi-styles: "npm:^3.2.1" - escape-string-regexp: "npm:^1.0.5" - supports-color: "npm:^5.3.0" - checksum: 10c0/e6543f02ec877732e3a2d1c3c3323ddb4d39fbab687c23f526e25bd4c6a9bf3b83a696e8c769d078e04e5754921648f7821b2a2acfd16c550435fd630026e073 - languageName: node - linkType: hard - "chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -2451,15 +2361,6 @@ __metadata: languageName: node linkType: hard -"clean-stack@npm:^4.0.0": - version: 4.2.0 - resolution: "clean-stack@npm:4.2.0" - dependencies: - escape-string-regexp: "npm:5.0.0" - checksum: 10c0/2bdf981a0fef0a23c14255df693b30eb9ae27eedf212470d8c400a0c0b6fb82fbf1ff8c5216ccd5721e3670b700389c886b1dce5070776dc9fbcc040957758c0 - languageName: node - linkType: hard - "cli-cursor@npm:^5.0.0": version: 5.0.0 resolution: "cli-cursor@npm:5.0.0" @@ -2525,15 +2426,6 @@ __metadata: languageName: node linkType: hard -"color-convert@npm:^1.9.0": - version: 1.9.3 - resolution: "color-convert@npm:1.9.3" - dependencies: - color-name: "npm:1.1.3" - checksum: 10c0/5ad3c534949a8c68fca8fbc6f09068f435f0ad290ab8b2f76841b9e6af7e0bb57b98cb05b0e19fe33f5d91e5a8611ad457e5f69e0a484caad1f7487fd0e8253c - languageName: node - linkType: hard - "color-convert@npm:^2.0.1": version: 2.0.1 resolution: "color-convert@npm:2.0.1" @@ -2543,13 +2435,6 @@ __metadata: languageName: node linkType: hard -"color-name@npm:1.1.3": - version: 1.1.3 - resolution: "color-name@npm:1.1.3" - checksum: 10c0/566a3d42cca25b9b3cd5528cd7754b8e89c0eb646b7f214e8e2eaddb69994ac5f0557d9c175eb5d8f0ad73531140d9c47525085ee752a91a2ab15ab459caf6d6 - languageName: node - linkType: hard - "color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" @@ -2972,30 +2857,13 @@ __metadata: languageName: node linkType: hard -"decamelize-keys@npm:^1.1.0": - version: 1.1.1 - resolution: "decamelize-keys@npm:1.1.1" - dependencies: - decamelize: "npm:^1.1.0" - map-obj: "npm:^1.0.0" - checksum: 10c0/4ca385933127437658338c65fb9aead5f21b28d3dd3ccd7956eb29aab0953b5d3c047fbc207111672220c71ecf7a4d34f36c92851b7bbde6fca1a02c541bdd7d - languageName: node - linkType: hard - -"decamelize@npm:^1.1.0, decamelize@npm:^1.2.0": +"decamelize@npm:^1.2.0": version: 1.2.0 resolution: "decamelize@npm:1.2.0" checksum: 10c0/85c39fe8fbf0482d4a1e224ef0119db5c1897f8503bcef8b826adff7a1b11414972f6fef2d7dec2ee0b4be3863cf64ac1439137ae9e6af23a3d8dcbe26a5b4b2 languageName: node linkType: hard -"decamelize@npm:^5.0.0": - version: 5.0.1 - resolution: "decamelize@npm:5.0.1" - checksum: 10c0/3da71022bc1e85487810fa0833138effb599fa331ca21e179650e93a765d0c4dabeb1ecdd6ad1474fa0bacd2457953c63ea335afb6e53b35f2b4bf779514e2a3 - languageName: node - linkType: hard - "deep-eql@npm:^5.0.1": version: 5.0.2 resolution: "deep-eql@npm:5.0.2" @@ -3035,32 +2903,30 @@ __metadata: languageName: node linkType: hard -"del-cli@npm:5.1.0": - version: 5.1.0 - resolution: "del-cli@npm:5.1.0" +"del-cli@npm:6.0.0": + version: 6.0.0 + resolution: "del-cli@npm:6.0.0" dependencies: - del: "npm:^7.1.0" - meow: "npm:^10.1.3" + del: "npm:^8.0.0" + meow: "npm:^13.2.0" bin: del: cli.js del-cli: cli.js - checksum: 10c0/555dfbbdf254ec758f86eaa3960779ae378c11fb9f734ba9f09b1019237919a66ffdaa286dad4b8df3cb8f315157fd41b6ff63afb7fa46a6c61eed7bbac04488 + checksum: 10c0/920a57efd804afab7799b8304de97d3ebbaf98dc0a524a4938115a494d67bf116674e3b38375c9cd091cf7caa8b4c2a32cbda3a032f66e0554d30d03ed5eddbe languageName: node linkType: hard -"del@npm:^7.1.0": - version: 7.1.0 - resolution: "del@npm:7.1.0" +"del@npm:^8.0.0": + version: 8.0.0 + resolution: "del@npm:8.0.0" dependencies: - globby: "npm:^13.1.2" - graceful-fs: "npm:^4.2.10" + globby: "npm:^14.0.2" is-glob: "npm:^4.0.3" is-path-cwd: "npm:^3.0.0" is-path-inside: "npm:^4.0.0" - p-map: "npm:^5.5.0" - rimraf: "npm:^3.0.2" - slash: "npm:^4.0.0" - checksum: 10c0/5ad2777b69e386b414ba77f5eba23bb52422c096f4c084c0d1d829ee4776d1a025a6f69765906907c4137026e9bd071ee9d422fd531b1417ef546adc7eb6fada + p-map: "npm:^7.0.2" + slash: "npm:^5.1.0" + checksum: 10c0/dd9099dc245173caad16a6372c7c9eb316e19e75e7bebfdce86ee59572c2591be5e569e15e8768108bb451f5319c57407cfa7adf74424f150d4d29c7f6da5601 languageName: node linkType: hard @@ -3109,15 +2975,6 @@ __metadata: languageName: node linkType: hard -"dir-glob@npm:^3.0.1": - version: 3.0.1 - resolution: "dir-glob@npm:3.0.1" - dependencies: - path-type: "npm:^4.0.0" - checksum: 10c0/dcac00920a4d503e38bb64001acb19df4efc14536ada475725e12f52c16777afdee4db827f55f13a908ee7efc0cb282e2e3dbaeeb98c0993dd93d1802d3bf00c - languageName: node - linkType: hard - "dotenv@npm:^5.0.1": version: 5.0.1 resolution: "dotenv@npm:5.0.1" @@ -3244,15 +3101,6 @@ __metadata: languageName: node linkType: hard -"error-ex@npm:^1.3.1": - version: 1.3.2 - resolution: "error-ex@npm:1.3.2" - dependencies: - is-arrayish: "npm:^0.2.1" - checksum: 10c0/ba827f89369b4c93382cfca5a264d059dfefdaa56ecc5e338ffa58a6471f5ed93b71a20add1d52290a4873d92381174382658c885ac1a2305f7baca363ce9cce - languageName: node - linkType: hard - "errorhandler@npm:^1.5.1": version: 1.5.1 resolution: "errorhandler@npm:1.5.1" @@ -3438,20 +3286,6 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:5.0.0": - version: 5.0.0 - resolution: "escape-string-regexp@npm:5.0.0" - checksum: 10c0/6366f474c6f37a802800a435232395e04e9885919873e382b157ab7e8f0feb8fed71497f84a6f6a81a49aab41815522f5839112bd38026d203aea0c91622df95 - languageName: node - linkType: hard - -"escape-string-regexp@npm:^1.0.5": - version: 1.0.5 - resolution: "escape-string-regexp@npm:1.0.5" - checksum: 10c0/a968ad453dd0c2724e14a4f20e177aaf32bb384ab41b674a8454afe9a41c5e6fe8903323e0a1052f56289d04bd600f81278edf140b0fcc02f5cac98d0f5b5371 - languageName: node - linkType: hard - "esm@npm:^3.2.25": version: 3.2.25 resolution: "esm@npm:3.2.25" @@ -3693,16 +3527,16 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.11": - version: 3.2.12 - resolution: "fast-glob@npm:3.2.12" +"fast-glob@npm:^3.3.3": + version: 3.3.3 + resolution: "fast-glob@npm:3.3.3" dependencies: "@nodelib/fs.stat": "npm:^2.0.2" "@nodelib/fs.walk": "npm:^1.2.3" glob-parent: "npm:^5.1.2" merge2: "npm:^1.3.0" - micromatch: "npm:^4.0.4" - checksum: 10c0/08604fb8ef6442ce74068bef3c3104382bb1f5ab28cf75e4ee904662778b60ad620e1405e692b7edea598ef445f5d387827a965ba034e1892bf54b1dfde97f26 + micromatch: "npm:^4.0.8" + checksum: 10c0/f6aaa141d0d3384cf73cbcdfc52f475ed293f6d5b65bfc5def368b09163a9f7e5ec2b3014d80f733c405f58e470ee0cc451c2937685045cddcdeaa24199c43fe languageName: node linkType: hard @@ -3828,16 +3662,6 @@ __metadata: languageName: node linkType: hard -"find-up@npm:^5.0.0": - version: 5.0.0 - resolution: "find-up@npm:5.0.0" - dependencies: - locate-path: "npm:^6.0.0" - path-exists: "npm:^4.0.0" - checksum: 10c0/062c5a83a9c02f53cdd6d175a37ecf8f87ea5bbff1fdfb828f04bfa021441bc7583e8ebc0872a4c1baab96221fb8a8a275a19809fb93fbc40bd69ec35634069a - languageName: node - linkType: hard - "flatted@npm:^3.2.7": version: 3.2.7 resolution: "flatted@npm:3.2.7" @@ -4136,7 +3960,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.5, glob@npm:^7.1.3": +"glob@npm:^7.0.5": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -4157,16 +3981,17 @@ __metadata: languageName: node linkType: hard -"globby@npm:^13.1.2": - version: 13.1.3 - resolution: "globby@npm:13.1.3" +"globby@npm:^14.0.2": + version: 14.1.0 + resolution: "globby@npm:14.1.0" dependencies: - dir-glob: "npm:^3.0.1" - fast-glob: "npm:^3.2.11" - ignore: "npm:^5.2.0" - merge2: "npm:^1.4.1" - slash: "npm:^4.0.0" - checksum: 10c0/34199932fad67ae6a4cca764eaad8e7678efabd4321f553bfb8a52046e03f8e8e2f9c14216a6734b692b7c26c4da1b1cfe9ce23733d28d1777d73f4bf34b09c7 + "@sindresorhus/merge-streams": "npm:^2.1.0" + fast-glob: "npm:^3.3.3" + ignore: "npm:^7.0.3" + path-type: "npm:^6.0.0" + slash: "npm:^5.1.0" + unicorn-magic: "npm:^0.3.0" + checksum: 10c0/527a1063c5958255969620c6fa4444a2b2e9278caddd571d46dfbfa307cb15977afb746e84d682ba5b6c94fc081e8997f80ff05dd235441ba1cb16f86153e58e languageName: node linkType: hard @@ -4186,13 +4011,6 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.2.10": - version: 4.2.10 - resolution: "graceful-fs@npm:4.2.10" - checksum: 10c0/4223a833e38e1d0d2aea630c2433cfb94ddc07dfc11d511dbd6be1d16688c5be848acc31f9a5d0d0ddbfb56d2ee5a6ae0278aceeb0ca6a13f27e06b9956fb952 - languageName: node - linkType: hard - "har-schema@npm:^2.0.0": version: 2.0.0 resolution: "har-schema@npm:2.0.0" @@ -4210,20 +4028,6 @@ __metadata: languageName: node linkType: hard -"hard-rejection@npm:^2.1.0": - version: 2.1.0 - resolution: "hard-rejection@npm:2.1.0" - checksum: 10c0/febc3343a1ad575aedcc112580835b44a89a89e01f400b4eda6e8110869edfdab0b00cd1bd4c3bfec9475a57e79e0b355aecd5be46454b6a62b9a359af60e564 - languageName: node - linkType: hard - -"has-flag@npm:^3.0.0": - version: 3.0.0 - resolution: "has-flag@npm:3.0.0" - checksum: 10c0/1c6c83b14b8b1b3c25b0727b8ba3e3b647f99e9e6e13eb7322107261de07a4c1be56fc0d45678fc376e09772a3a1642ccdaf8fc69bdf123b6c086598397ce473 - languageName: node - linkType: hard - "has-flag@npm:^4.0.0": version: 4.0.0 resolution: "has-flag@npm:4.0.0" @@ -4291,15 +4095,6 @@ __metadata: languageName: node linkType: hard -"hosted-git-info@npm:^4.0.1": - version: 4.1.0 - resolution: "hosted-git-info@npm:4.1.0" - dependencies: - lru-cache: "npm:^6.0.0" - checksum: 10c0/150fbcb001600336d17fdbae803264abed013548eea7946c2264c49ebe2ebd8c4441ba71dd23dd8e18c65de79d637f98b22d4760ba5fb2e0b15d62543d0fff07 - languageName: node - linkType: hard - "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -4411,10 +4206,10 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.2.0": - version: 5.2.4 - resolution: "ignore@npm:5.2.4" - checksum: 10c0/7c7cd90edd9fea6e037f9b9da4b01bf0a86b198ce78345f9bbd983929d68ff14830be31111edc5d70c264921f4962404d75b7262b4d9cc3bc12381eccbd03096 +"ignore@npm:^7.0.3": + version: 7.0.5 + resolution: "ignore@npm:7.0.5" + checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d languageName: node linkType: hard @@ -4432,13 +4227,6 @@ __metadata: languageName: node linkType: hard -"indent-string@npm:^5.0.0": - version: 5.0.0 - resolution: "indent-string@npm:5.0.0" - checksum: 10c0/8ee77b57d92e71745e133f6f444d6fa3ed503ad0e1bcd7e80c8da08b42375c07117128d670589725ed07b1978065803fa86318c309ba45415b7fe13e7f170220 - languageName: node - linkType: hard - "inflection@npm:^1.10.0": version: 1.13.4 resolution: "inflection@npm:1.13.4" @@ -4508,13 +4296,6 @@ __metadata: languageName: node linkType: hard -"is-arrayish@npm:^0.2.1": - version: 0.2.1 - resolution: "is-arrayish@npm:0.2.1" - checksum: 10c0/e7fb686a739068bb70f860b39b67afc62acc62e36bb61c5f965768abce1873b379c563e61dd2adad96ebb7edf6651111b385e490cf508378959b0ed4cac4e729 - languageName: node - linkType: hard - "is-buffer@npm:^1.0.2, is-buffer@npm:^1.1.5": version: 1.1.6 resolution: "is-buffer@npm:1.1.6" @@ -4531,15 +4312,6 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.5.0": - version: 2.11.0 - resolution: "is-core-module@npm:2.11.0" - dependencies: - has: "npm:^1.0.3" - checksum: 10c0/fd8f78ef4e243c295deafa809f89381d89aff5aaf38bb63266b17ee6e34b6a051baa5bdc2365456863336d56af6a59a4c1df1256b4eff7d6b4afac618586b004 - languageName: node - linkType: hard - "is-core-module@npm:^2.9.0": version: 2.12.0 resolution: "is-core-module@npm:2.12.0" @@ -4644,13 +4416,6 @@ __metadata: languageName: node linkType: hard -"is-plain-obj@npm:^1.1.0": - version: 1.1.0 - resolution: "is-plain-obj@npm:1.1.0" - checksum: 10c0/daaee1805add26f781b413fdf192fc91d52409583be30ace35c82607d440da63cc4cac0ac55136716688d6c0a2c6ef3edb2254fecbd1fe06056d6bd15975ee8c - languageName: node - linkType: hard - "is-plain-object@npm:^2.0.1": version: 2.0.4 resolution: "is-plain-object@npm:2.0.4" @@ -4899,13 +4664,6 @@ __metadata: languageName: node linkType: hard -"json-parse-even-better-errors@npm:^2.3.0": - version: 2.3.1 - resolution: "json-parse-even-better-errors@npm:2.3.1" - checksum: 10c0/140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3 - languageName: node - linkType: hard - "json-schema-to-ts@npm:2.12.0": version: 2.12.0 resolution: "json-schema-to-ts@npm:2.12.0" @@ -5025,13 +4783,6 @@ __metadata: languageName: node linkType: hard -"kind-of@npm:^6.0.3": - version: 6.0.3 - resolution: "kind-of@npm:6.0.3" - checksum: 10c0/61cdff9623dabf3568b6445e93e31376bee1cdb93f8ba7033d86022c2a9b1791a1d9510e026e6465ebd701a6dd2f7b0808483ad8838341ac52f003f512e0b4c4 - languageName: node - linkType: hard - "knex@npm:3, knex@npm:^3.1.0": version: 3.1.0 resolution: "knex@npm:3.1.0" @@ -5122,13 +4873,6 @@ __metadata: languageName: node linkType: hard -"lines-and-columns@npm:^1.1.6": - version: 1.2.4 - resolution: "lines-and-columns@npm:1.2.4" - checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d - languageName: node - linkType: hard - "lint-staged@npm:15.4.3": version: 15.4.3 resolution: "lint-staged@npm:15.4.3" @@ -5172,15 +4916,6 @@ __metadata: languageName: node linkType: hard -"locate-path@npm:^6.0.0": - version: 6.0.0 - resolution: "locate-path@npm:6.0.0" - dependencies: - p-locate: "npm:^5.0.0" - checksum: 10c0/d3972ab70dfe58ce620e64265f90162d247e87159b6126b01314dd67be43d50e96a50b517bce2d9452a79409c7614054c277b5232377de50416564a77ac7aad3 - languageName: node - linkType: hard - "lodash.defaults@npm:^4.1.0": version: 4.2.0 resolution: "lodash.defaults@npm:4.2.0" @@ -5279,15 +5014,6 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^6.0.0": - version: 6.0.0 - resolution: "lru-cache@npm:6.0.0" - dependencies: - yallist: "npm:^4.0.0" - checksum: 10c0/cb53e582785c48187d7a188d3379c181b5ca2a9c78d2bce3e7dee36f32761d1c42983da3fe12b55cb74e1779fa94cdc2e5367c028a9b35317184ede0c07a30a9 - languageName: node - linkType: hard - "lru-cache@npm:^9.1.1": version: 9.1.1 resolution: "lru-cache@npm:9.1.1" @@ -5369,20 +5095,6 @@ __metadata: languageName: node linkType: hard -"map-obj@npm:^1.0.0": - version: 1.0.1 - resolution: "map-obj@npm:1.0.1" - checksum: 10c0/ccca88395e7d38671ed9f5652ecf471ecd546924be2fb900836b9da35e068a96687d96a5f93dcdfa94d9a27d649d2f10a84595590f89a347fb4dda47629dcc52 - languageName: node - linkType: hard - -"map-obj@npm:^4.1.0": - version: 4.3.0 - resolution: "map-obj@npm:4.3.0" - checksum: 10c0/1c19e1c88513c8abdab25c316367154c6a0a6a0f77e3e8c391bb7c0e093aefed293f539d026dc013d86219e5e4c25f23b0003ea588be2101ccd757bacc12d43b - languageName: node - linkType: hard - "map-stream@npm:~0.1.0": version: 0.1.0 resolution: "map-stream@npm:0.1.0" @@ -5413,23 +5125,10 @@ __metadata: languageName: node linkType: hard -"meow@npm:^10.1.3": - version: 10.1.5 - resolution: "meow@npm:10.1.5" - dependencies: - "@types/minimist": "npm:^1.2.2" - camelcase-keys: "npm:^7.0.0" - decamelize: "npm:^5.0.0" - decamelize-keys: "npm:^1.1.0" - hard-rejection: "npm:^2.1.0" - minimist-options: "npm:4.1.0" - normalize-package-data: "npm:^3.0.2" - read-pkg-up: "npm:^8.0.0" - redent: "npm:^4.0.0" - trim-newlines: "npm:^4.0.2" - type-fest: "npm:^1.2.2" - yargs-parser: "npm:^20.2.9" - checksum: 10c0/a513849022edd5ddcc41d28c679d31978abe414d9db5bc457e95e537a4327b2910fd2f699cdd883293f9a5da8951a50939bf60fbd62f7fe12b9ddf96a84b1b27 +"meow@npm:^13.2.0": + version: 13.2.0 + resolution: "meow@npm:13.2.0" + checksum: 10c0/d5b339ae314715bcd0b619dd2f8a266891928e21526b4800d49b4fba1cc3fff7e2c1ff5edd3344149fac841bc2306157f858e8c4d5eaee4d52ce52ad925664ce languageName: node linkType: hard @@ -5465,7 +5164,7 @@ __metadata: languageName: node linkType: hard -"merge2@npm:^1.3.0, merge2@npm:^1.4.1": +"merge2@npm:^1.3.0": version: 1.4.1 resolution: "merge2@npm:1.4.1" checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb @@ -5479,7 +5178,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": +"micromatch@npm:^4.0.8": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -5546,13 +5245,6 @@ __metadata: languageName: node linkType: hard -"min-indent@npm:^1.0.1": - version: 1.0.1 - resolution: "min-indent@npm:1.0.1" - checksum: 10c0/7e207bd5c20401b292de291f02913230cb1163abca162044f7db1d951fa245b174dc00869d40dd9a9f32a885ad6a5f3e767ee104cf278f399cb4e92d3f582d5c - languageName: node - linkType: hard - "minimatch@npm:^3.0.3, minimatch@npm:^3.1.1": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -5571,17 +5263,6 @@ __metadata: languageName: node linkType: hard -"minimist-options@npm:4.1.0": - version: 4.1.0 - resolution: "minimist-options@npm:4.1.0" - dependencies: - arrify: "npm:^1.0.1" - is-plain-obj: "npm:^1.1.0" - kind-of: "npm:^6.0.3" - checksum: 10c0/7871f9cdd15d1e7374e5b013e2ceda3d327a06a8c7b38ae16d9ef941e07d985e952c589e57213f7aa90a8744c60aed9524c0d85e501f5478382d9181f2763f54 - languageName: node - linkType: hard - "minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:^1.2.8": version: 1.2.8 resolution: "minimist@npm:1.2.8" @@ -5928,18 +5609,6 @@ __metadata: languageName: node linkType: hard -"normalize-package-data@npm:^3.0.2": - version: 3.0.3 - resolution: "normalize-package-data@npm:3.0.3" - dependencies: - hosted-git-info: "npm:^4.0.1" - is-core-module: "npm:^2.5.0" - semver: "npm:^7.3.4" - validate-npm-package-license: "npm:^3.0.1" - checksum: 10c0/e5d0f739ba2c465d41f77c9d950e291ea4af78f8816ddb91c5da62257c40b76d8c83278b0d08ffbcd0f187636ebddad20e181e924873916d03e6e5ea2ef026be - languageName: node - linkType: hard - "normalize-url@npm:^6.1.0": version: 6.1.0 resolution: "normalize-url@npm:6.1.0" @@ -6074,15 +5743,6 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^3.0.2": - version: 3.1.0 - resolution: "p-limit@npm:3.1.0" - dependencies: - yocto-queue: "npm:^0.1.0" - checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a - languageName: node - linkType: hard - "p-locate@npm:^4.1.0": version: 4.1.0 resolution: "p-locate@npm:4.1.0" @@ -6092,15 +5752,6 @@ __metadata: languageName: node linkType: hard -"p-locate@npm:^5.0.0": - version: 5.0.0 - resolution: "p-locate@npm:5.0.0" - dependencies: - p-limit: "npm:^3.0.2" - checksum: 10c0/2290d627ab7903b8b70d11d384fee714b797f6040d9278932754a6860845c4d3190603a0772a663c8cb5a7b21d1b16acb3a6487ebcafa9773094edc3dfe6009a - languageName: node - linkType: hard - "p-map@npm:^4.0.0": version: 4.0.0 resolution: "p-map@npm:4.0.0" @@ -6110,12 +5761,10 @@ __metadata: languageName: node linkType: hard -"p-map@npm:^5.5.0": - version: 5.5.0 - resolution: "p-map@npm:5.5.0" - dependencies: - aggregate-error: "npm:^4.0.0" - checksum: 10c0/410bce846b1e3db6bb2ccab6248372ecf4e635fc2b31331c8f56478e73fec9e146e8b4547585e635703160a3d252a6a65b8f855834aebc2c3408eb5789630cc4 +"p-map@npm:^7.0.2": + version: 7.0.3 + resolution: "p-map@npm:7.0.3" + checksum: 10c0/46091610da2b38ce47bcd1d8b4835a6fa4e832848a6682cf1652bc93915770f4617afc844c10a77d1b3e56d2472bb2d5622353fa3ead01a7f42b04fc8e744a5c languageName: node linkType: hard @@ -6171,18 +5820,6 @@ __metadata: languageName: node linkType: hard -"parse-json@npm:^5.2.0": - version: 5.2.0 - resolution: "parse-json@npm:5.2.0" - dependencies: - "@babel/code-frame": "npm:^7.0.0" - error-ex: "npm:^1.3.1" - json-parse-even-better-errors: "npm:^2.3.0" - lines-and-columns: "npm:^1.1.6" - checksum: 10c0/77947f2253005be7a12d858aedbafa09c9ae39eb4863adf330f7b416ca4f4a08132e453e08de2db46459256fb66afaac5ee758b44fe6541b7cdaf9d252e59585 - languageName: node - linkType: hard - "parseurl@npm:~1.3.2, parseurl@npm:~1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" @@ -6268,10 +5905,10 @@ __metadata: languageName: node linkType: hard -"path-type@npm:^4.0.0": - version: 4.0.0 - resolution: "path-type@npm:4.0.0" - checksum: 10c0/666f6973f332f27581371efaf303fd6c272cc43c2057b37aa99e3643158c7e4b2626549555d88626e99ea9e046f82f32e41bbde5f1508547e9a11b149b52387c +"path-type@npm:^6.0.0": + version: 6.0.0 + resolution: "path-type@npm:6.0.0" + checksum: 10c0/55baa8b1187d6dc683d5a9cfcc866168d6adff58e5db91126795376d818eee46391e00b2a4d53e44d844c7524a7d96aa68cc68f4f3e500d3d069a39e6535481c languageName: node linkType: hard @@ -6717,13 +6354,6 @@ __metadata: languageName: node linkType: hard -"quick-lru@npm:^5.1.1": - version: 5.1.1 - resolution: "quick-lru@npm:5.1.1" - checksum: 10c0/a24cba5da8cec30d70d2484be37622580f64765fb6390a928b17f60cd69e8dbd32a954b3ff9176fa1b86d86ff2ba05252fae55dc4d40d0291c60412b0ad096da - languageName: node - linkType: hard - "randexp@npm:^0.5.3": version: 0.5.3 resolution: "randexp@npm:0.5.3" @@ -6774,29 +6404,6 @@ __metadata: languageName: node linkType: hard -"read-pkg-up@npm:^8.0.0": - version: 8.0.0 - resolution: "read-pkg-up@npm:8.0.0" - dependencies: - find-up: "npm:^5.0.0" - read-pkg: "npm:^6.0.0" - type-fest: "npm:^1.0.1" - checksum: 10c0/cf3905ccbe5cd602f23192cc7ca65ed17561bab117eadb9aed817441d5bfc6b9a11215c2a3e9505f501d046818f3c4180dbea61fa83c42083e0b4e407d5cc745 - languageName: node - linkType: hard - -"read-pkg@npm:^6.0.0": - version: 6.0.0 - resolution: "read-pkg@npm:6.0.0" - dependencies: - "@types/normalize-package-data": "npm:^2.4.0" - normalize-package-data: "npm:^3.0.2" - parse-json: "npm:^5.2.0" - type-fest: "npm:^1.0.1" - checksum: 10c0/b51ee5eed75324f4fac34c9a40b5e4b403de4c532242be01959c9bbdb1ff9db1c6c2aefaba569622fec49d1ead866e97ba856ab145f6e11039b11f7bec1318ba - languageName: node - linkType: hard - "readable-stream@npm:~1.0.31": version: 1.0.34 resolution: "readable-stream@npm:1.0.34" @@ -6833,16 +6440,6 @@ __metadata: languageName: node linkType: hard -"redent@npm:^4.0.0": - version: 4.0.0 - resolution: "redent@npm:4.0.0" - dependencies: - indent-string: "npm:^5.0.0" - strip-indent: "npm:^4.0.0" - checksum: 10c0/a9b640c8f4b2b5b26a1a908706475ff404dd50a97d6f094bc3c59717be922622927cc7d601d4ae2857d897ad243fd979bd76d751a0481cee8be7024e5fb4c662 - languageName: node - linkType: hard - "regenerator-runtime@npm:^0.14.0": version: 0.14.0 resolution: "regenerator-runtime@npm:0.14.0" @@ -7024,17 +6621,6 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:^3.0.2": - version: 3.0.2 - resolution: "rimraf@npm:3.0.2" - dependencies: - glob: "npm:^7.1.3" - bin: - rimraf: bin.js - checksum: 10c0/9cb7757acb489bd83757ba1a274ab545eafd75598a9d817e0c3f8b164238dd90eba50d6b848bd4dcc5f3040912e882dc7ba71653e35af660d77b25c381d402e8 - languageName: node - linkType: hard - "rimraf@npm:^5.0.5": version: 5.0.10 resolution: "rimraf@npm:5.0.10" @@ -7362,10 +6948,10 @@ __metadata: languageName: node linkType: hard -"slash@npm:^4.0.0": - version: 4.0.0 - resolution: "slash@npm:4.0.0" - checksum: 10c0/b522ca75d80d107fd30d29df0549a7b2537c83c4c4ecd12cd7d4ea6c8aaca2ab17ada002e7a1d78a9d736a0261509f26ea5b489082ee443a3a810586ef8eff18 +"slash@npm:^5.1.0": + version: 5.1.0 + resolution: "slash@npm:5.1.0" + checksum: 10c0/eb48b815caf0bdc390d0519d41b9e0556a14380f6799c72ba35caf03544d501d18befdeeef074bc9c052acf69654bc9e0d79d7f1de0866284137a40805299eb3 languageName: node linkType: hard @@ -7450,40 +7036,6 @@ __metadata: languageName: node linkType: hard -"spdx-correct@npm:^3.0.0": - version: 3.1.1 - resolution: "spdx-correct@npm:3.1.1" - dependencies: - spdx-expression-parse: "npm:^3.0.0" - spdx-license-ids: "npm:^3.0.0" - checksum: 10c0/25909eecc4024963a8e398399dbdd59ddb925bd7dbecd9c9cf6df0d75c29b68cd30b82123564acc51810eb02cfc4b634a2e16e88aa982433306012e318849249 - languageName: node - linkType: hard - -"spdx-exceptions@npm:^2.1.0": - version: 2.3.0 - resolution: "spdx-exceptions@npm:2.3.0" - checksum: 10c0/83089e77d2a91cb6805a5c910a2bedb9e50799da091f532c2ba4150efdef6e53f121523d3e2dc2573a340dc0189e648b03157097f65465b3a0c06da1f18d7e8a - languageName: node - linkType: hard - -"spdx-expression-parse@npm:^3.0.0": - version: 3.0.1 - resolution: "spdx-expression-parse@npm:3.0.1" - dependencies: - spdx-exceptions: "npm:^2.1.0" - spdx-license-ids: "npm:^3.0.0" - checksum: 10c0/6f8a41c87759fa184a58713b86c6a8b028250f158159f1d03ed9d1b6ee4d9eefdc74181c8ddc581a341aa971c3e7b79e30b59c23b05d2436d5de1c30bdef7171 - languageName: node - linkType: hard - -"spdx-license-ids@npm:^3.0.0": - version: 3.0.12 - resolution: "spdx-license-ids@npm:3.0.12" - checksum: 10c0/b749db2fdecf4ac1893b8e4c435c3bfe5247af9cb412a3cd8375c8bc5a24ad7f3c4263dfe0fc04701f98613f189787700f1deac3e9272c96dfaffc01826c2d0f - languageName: node - linkType: hard - "split2@npm:^4.1.0": version: 4.2.0 resolution: "split2@npm:4.2.0" @@ -7713,15 +7265,6 @@ __metadata: languageName: node linkType: hard -"strip-indent@npm:^4.0.0": - version: 4.0.0 - resolution: "strip-indent@npm:4.0.0" - dependencies: - min-indent: "npm:^1.0.1" - checksum: 10c0/6b1fb4e22056867f5c9e7a6f3f45922d9a2436cac758607d58aeaac0d3b16ec40b1c43317de7900f1b8dd7a4107352fa47fb960f2c23566538c51e8585c8870e - languageName: node - linkType: hard - "strip-json-comments@npm:~2.0.1": version: 2.0.1 resolution: "strip-json-comments@npm:2.0.1" @@ -7773,15 +7316,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^5.3.0": - version: 5.5.0 - resolution: "supports-color@npm:5.5.0" - dependencies: - has-flag: "npm:^3.0.0" - checksum: 10c0/6ae5ff319bfbb021f8a86da8ea1f8db52fac8bd4d499492e30ec17095b58af11f0c55f8577390a749b1c4dde691b6a0315dab78f5f54c9b3d83f8fb5905c1c05 - languageName: node - linkType: hard - "supports-color@npm:^7.1.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" @@ -8005,13 +7539,6 @@ __metadata: languageName: node linkType: hard -"trim-newlines@npm:^4.0.2": - version: 4.0.2 - resolution: "trim-newlines@npm:4.0.2" - checksum: 10c0/48d022e9d14f27cf8b71983691af61cd8ce511d159ed0962452d2fa23f58298398d905e1ff982566f9034f93df3ef676868c1c14d13bcd849e7500dbfbd6101b - languageName: node - linkType: hard - "truncate-utf8-bytes@npm:^1.0.0": version: 1.0.2 resolution: "truncate-utf8-bytes@npm:1.0.2" @@ -8130,13 +7657,6 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^1.0.1, type-fest@npm:^1.2.1, type-fest@npm:^1.2.2": - version: 1.4.0 - resolution: "type-fest@npm:1.4.0" - checksum: 10c0/a3c0f4ee28ff6ddf800d769eafafcdeab32efa38763c1a1b8daeae681920f6e345d7920bf277245235561d8117dab765cb5f829c76b713b4c9de0998a5397141 - languageName: node - linkType: hard - "type-is@npm:^1.6.18, type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -8213,6 +7733,13 @@ __metadata: languageName: node linkType: hard +"unicorn-magic@npm:^0.3.0": + version: 0.3.0 + resolution: "unicorn-magic@npm:0.3.0" + checksum: 10c0/0a32a997d6c15f1c2a077a15b1c4ca6f268d574cf5b8975e778bb98e6f8db4ef4e86dfcae4e158cd4c7e38fb4dd383b93b13eefddc7f178dea13d3ac8a603271 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -8316,7 +7843,7 @@ __metadata: db-migrate-shared: "npm:1.2.0" deep-object-diff: "npm:^1.1.9" deepmerge: "npm:^4.3.1" - del-cli: "npm:5.1.0" + del-cli: "npm:6.0.0" errorhandler: "npm:^1.5.1" express: "npm:^4.21.2" express-rate-limit: "npm:^7.3.1" @@ -8476,16 +8003,6 @@ __metadata: languageName: node linkType: hard -"validate-npm-package-license@npm:^3.0.1": - version: 3.0.4 - resolution: "validate-npm-package-license@npm:3.0.4" - dependencies: - spdx-correct: "npm:^3.0.0" - spdx-expression-parse: "npm:^3.0.0" - checksum: 10c0/7b91e455a8de9a0beaa9fe961e536b677da7f48c9a493edf4d4d4a87fd80a7a10267d438723364e432c2fcd00b5650b5378275cded362383ef570276e6312f4f - languageName: node - linkType: hard - "validator@npm:^13.7.0": version: 13.11.0 resolution: "validator@npm:13.11.0" @@ -8826,7 +8343,7 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^20.2.2, yargs-parser@npm:^20.2.9": +"yargs-parser@npm:^20.2.2": version: 20.2.9 resolution: "yargs-parser@npm:20.2.9" checksum: 10c0/0685a8e58bbfb57fab6aefe03c6da904a59769bd803a722bb098bd5b0f29d274a1357762c7258fb487512811b8063fb5d2824a3415a0a4540598335b3b086c72 @@ -8896,13 +8413,6 @@ __metadata: languageName: node linkType: hard -"yocto-queue@npm:^0.1.0": - version: 0.1.0 - resolution: "yocto-queue@npm:0.1.0" - checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f - languageName: node - linkType: hard - "z-schema@npm:^5.0.1": version: 5.0.6 resolution: "z-schema@npm:5.0.6" From 25f1f3652120a5e30fde3db9145a3573951e1dbc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:04:35 +0000 Subject: [PATCH 09/41] chore(deps): update dependency @tanstack/react-virtual to v3.13.10 (#10154) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@tanstack/react-virtual](https://tanstack.com/virtual) ([source](https://redirect.github.com/TanStack/virtual/tree/HEAD/packages/react-virtual)) | [`3.13.9` -> `3.13.10`](https://renovatebot.com/diffs/npm/@tanstack%2freact-virtual/3.13.9/3.13.10) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@tanstack%2freact-virtual/3.13.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@tanstack%2freact-virtual/3.13.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@tanstack%2freact-virtual/3.13.9/3.13.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@tanstack%2freact-virtual/3.13.9/3.13.10?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
TanStack/virtual (@​tanstack/react-virtual) ### [`v3.13.10`](https://redirect.github.com/TanStack/virtual/blob/HEAD/packages/react-virtual/CHANGELOG.md#31310) [Compare Source](https://redirect.github.com/TanStack/virtual/compare/@tanstack/react-virtual@3.13.9...@tanstack/react-virtual@3.13.10) ##### Patch Changes - Updated dependencies \[[`b3b7e7d`](https://redirect.github.com/TanStack/virtual/commit/b3b7e7dc8b25daeebbd2da61b3b7ae3448babbdb)]: - [@​tanstack/virtual-core](https://redirect.github.com/tanstack/virtual-core)[@​3](https://redirect.github.com/3).13.10
--- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- frontend/yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 90cedd256e..d8c18fabe9 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2877,14 +2877,14 @@ __metadata: linkType: hard "@tanstack/react-virtual@npm:^3.11.3": - version: 3.13.9 - resolution: "@tanstack/react-virtual@npm:3.13.9" + version: 3.13.10 + resolution: "@tanstack/react-virtual@npm:3.13.10" dependencies: - "@tanstack/virtual-core": "npm:3.13.9" + "@tanstack/virtual-core": "npm:3.13.10" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 10c0/aa05fb24e30686516e74ccdec94a83d195615a4f29bc866a53ee6b0107e466c6d6e3e947b3fb0613b907b0f982d74b00367868cf6b1ac956562d0e7c24d6764b + checksum: 10c0/587ef4db703cc9d870ee68b3f1471118fe69920e8f59cf627a359d518331c6684069fb3e159cd35ac458ea60a829b66953b774314833e1cb10b365255791559e languageName: node linkType: hard @@ -2895,10 +2895,10 @@ __metadata: languageName: node linkType: hard -"@tanstack/virtual-core@npm:3.13.9": - version: 3.13.9 - resolution: "@tanstack/virtual-core@npm:3.13.9" - checksum: 10c0/6e9526a9d52f8ddc54af8a1dc7380814b10ab38d8a4265e362a5b69c3132097ace51496d4206fe8aa90e33129aaf1a177c93d7ed018b5564b78e057fc9cdb48d +"@tanstack/virtual-core@npm:3.13.10": + version: 3.13.10 + resolution: "@tanstack/virtual-core@npm:3.13.10" + checksum: 10c0/ecfe56cc37db088416abb1f1b9641cc7b05b387bb532e4fe42f30a5477e55fdbb724f54e8dc5c3d8b380a5e9f80cc11426519825f8ecbcb6ca717639ba702cc8 languageName: node linkType: hard From a1764ebc5e05bae07f012c928fc14da7f1420740 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:06:06 +0000 Subject: [PATCH 10/41] chore(deps): update dependency @types/express to v4.17.23 (#10155) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@types/express](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/express) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/express)) | [`4.17.21` -> `4.17.23`](https://renovatebot.com/diffs/npm/@types%2fexpress/4.17.21/4.17.23) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fexpress/4.17.23?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fexpress/4.17.23?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fexpress/4.17.21/4.17.23?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fexpress/4.17.21/4.17.23?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 36c9769ccb..010c0c3c1a 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,7 @@ "@swc/core": "1.11.31", "@types/bcryptjs": "2.4.6", "@types/cors": "2.8.19", - "@types/express": "4.17.21", + "@types/express": "4.17.23", "@types/express-session": "1.18.1", "@types/faker": "5.5.9", "@types/hash-sum": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 4f95e295df..5f579a26a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1438,15 +1438,15 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:4.17.21": - version: 4.17.21 - resolution: "@types/express@npm:4.17.21" +"@types/express@npm:4.17.23": + version: 4.17.23 + resolution: "@types/express@npm:4.17.23" dependencies: "@types/body-parser": "npm:*" "@types/express-serve-static-core": "npm:^4.17.33" "@types/qs": "npm:*" "@types/serve-static": "npm:*" - checksum: 10c0/12e562c4571da50c7d239e117e688dc434db1bac8be55613294762f84fd77fbd0658ccd553c7d3ab02408f385bc93980992369dd30e2ecd2c68c358e6af8fabf + checksum: 10c0/60490cd4f73085007247e7d4fafad0a7abdafa34fa3caba2757512564ca5e094ece7459f0f324030a63d513f967bb86579a8682af76ae2fd718e889b0a2a4fe8 languageName: node linkType: hard @@ -7801,7 +7801,7 @@ __metadata: "@swc/core": "npm:1.11.31" "@types/bcryptjs": "npm:2.4.6" "@types/cors": "npm:2.8.19" - "@types/express": "npm:4.17.21" + "@types/express": "npm:4.17.23" "@types/express-session": "npm:1.18.1" "@types/faker": "npm:5.5.9" "@types/hash-sum": "npm:^1.0.0" From 9b61977d3e6c867299c0744cce70cf969dd649eb Mon Sep 17 00:00:00 2001 From: Jaanus Sellin Date: Wed, 18 Jun 2025 11:55:26 +0300 Subject: [PATCH 11/41] fix: increase line height for project title (#10158) Removing overflow hidden did not fix it, also it breaks the overflow. Line height is by default 1.4, bumped it up to 1.5. This solved it. --- frontend/src/component/project/Project/Project.styles.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/component/project/Project/Project.styles.ts b/frontend/src/component/project/Project/Project.styles.ts index 95265cd284..fc5bc927fa 100644 --- a/frontend/src/component/project/Project/Project.styles.ts +++ b/frontend/src/component/project/Project/Project.styles.ts @@ -59,6 +59,7 @@ export const StyledProjectTitle = styled('h1')(({ theme }) => ({ alignItems: 'center', gap: theme.spacing(2), overflow: 'hidden', + lineHeight: 1.5, })); export const StyledSeparator = styled('div')(({ theme }) => ({ From 9c23ffbf01273cfd9522b6761a2cdf39f4d54160 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 09:03:12 +0000 Subject: [PATCH 12/41] chore(deps): update dependency @types/memoizee to v0.4.12 (#10157) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@types/memoizee](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/memoizee) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/memoizee)) | [`0.4.11` -> `0.4.12`](https://renovatebot.com/diffs/npm/@types%2fmemoizee/0.4.11/0.4.12) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fmemoizee/0.4.12?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fmemoizee/0.4.12?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fmemoizee/0.4.11/0.4.12?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fmemoizee/0.4.11/0.4.12?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 010c0c3c1a..e6f1614ebe 100644 --- a/package.json +++ b/package.json @@ -148,7 +148,7 @@ "@types/js-yaml": "4.0.9", "@types/lodash.groupby": "4.6.9", "@types/lodash.isequal": "^4.5.8", - "@types/memoizee": "0.4.11", + "@types/memoizee": "0.4.12", "@types/mime": "4.0.0", "@types/murmurhash3js": "^3.0.7", "@types/mustache": "^4.2.5", diff --git a/yarn.lock b/yarn.lock index 5f579a26a6..9268bba802 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1517,10 +1517,10 @@ __metadata: languageName: node linkType: hard -"@types/memoizee@npm:0.4.11": - version: 0.4.11 - resolution: "@types/memoizee@npm:0.4.11" - checksum: 10c0/4a8114e26971186b29b81add600e34ad45578f8847b5cf04209509f9f21c334171205405f19658f6176419fdad09cf18c084c21802257d71517f91761b97a10c +"@types/memoizee@npm:0.4.12": + version: 0.4.12 + resolution: "@types/memoizee@npm:0.4.12" + checksum: 10c0/573ca0c7e3db4306679bd0fbee92063e03406cd14d2bc4409d9a61ba731f1bb3925d3183a28ed6a8f480b3326ca03bdef6766c1649aa29592200f97fd0d38955 languageName: node linkType: hard @@ -7808,7 +7808,7 @@ __metadata: "@types/js-yaml": "npm:4.0.9" "@types/lodash.groupby": "npm:4.6.9" "@types/lodash.isequal": "npm:^4.5.8" - "@types/memoizee": "npm:0.4.11" + "@types/memoizee": "npm:0.4.12" "@types/mime": "npm:4.0.0" "@types/murmurhash3js": "npm:^3.0.7" "@types/mustache": "npm:^4.2.5" From 810890fe6705f2ee11dc728e298ef31a70f2132c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 09:03:26 +0000 Subject: [PATCH 13/41] chore(deps): update dependency @types/express-session to v1.18.2 (#10156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@types/express-session](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/express-session) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/express-session)) | [`1.18.1` -> `1.18.2`](https://renovatebot.com/diffs/npm/@types%2fexpress-session/1.18.1/1.18.2) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fexpress-session/1.18.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fexpress-session/1.18.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fexpress-session/1.18.1/1.18.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fexpress-session/1.18.1/1.18.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index e6f1614ebe..ba49839c92 100644 --- a/package.json +++ b/package.json @@ -142,7 +142,7 @@ "@types/bcryptjs": "2.4.6", "@types/cors": "2.8.19", "@types/express": "4.17.23", - "@types/express-session": "1.18.1", + "@types/express-session": "1.18.2", "@types/faker": "5.5.9", "@types/hash-sum": "^1.0.0", "@types/js-yaml": "4.0.9", diff --git a/yarn.lock b/yarn.lock index 9268bba802..377236f1d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1417,12 +1417,12 @@ __metadata: languageName: node linkType: hard -"@types/express-session@npm:1.18.1": - version: 1.18.1 - resolution: "@types/express-session@npm:1.18.1" +"@types/express-session@npm:1.18.2": + version: 1.18.2 + resolution: "@types/express-session@npm:1.18.2" dependencies: "@types/express": "npm:*" - checksum: 10c0/df2d439239f5cc9947772452d1d86d47a8d3e33f90776ebcf602f0fb7801bc85f6410d17557f40cdde881816bc5a2804a7373addfd3a5dbd8c54e7af8aa27000 + checksum: 10c0/5d5aa134ce8990920b35f2dd0aa55168af44faaf14789b6921d361ce016c43bdc66feba287753981a2fee33fd95b8a829c4418c3ca480b03961724b8bc13e453 languageName: node linkType: hard @@ -7802,7 +7802,7 @@ __metadata: "@types/bcryptjs": "npm:2.4.6" "@types/cors": "npm:2.8.19" "@types/express": "npm:4.17.23" - "@types/express-session": "npm:1.18.1" + "@types/express-session": "npm:1.18.2" "@types/faker": "npm:5.5.9" "@types/hash-sum": "npm:^1.0.0" "@types/js-yaml": "npm:4.0.9" From ce8d49be1059a257fba5c1d5dfd8762831cbcd72 Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Wed, 18 Jun 2025 11:52:29 +0200 Subject: [PATCH 14/41] task: added table for requested approvers for CRs (#10159) As part of the task to make it possible to send notifications to approvers for a CR, this PR adds a table that can link users to CRs they've been requested to make an approval for. --- ...618090103-create-cr-requested-approvals.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/migrations/20250618090103-create-cr-requested-approvals.js diff --git a/src/migrations/20250618090103-create-cr-requested-approvals.js b/src/migrations/20250618090103-create-cr-requested-approvals.js new file mode 100644 index 0000000000..409efac666 --- /dev/null +++ b/src/migrations/20250618090103-create-cr-requested-approvals.js @@ -0,0 +1,19 @@ +exports.up = function(db, cb) { + db.runSql(`CREATE TABLE change_request_requested_approvers( + change_request_id INTEGER REFERENCES change_requests(id) ON DELETE CASCADE, + user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, + requested_at TIMESTAMP WITH TIME ZONE DEFAULT (now() at time zone 'utc'), + PRIMARY KEY (change_request_id, user_id) + ); + CREATE INDEX IF NOT EXISTS change_request_requested_approvers_cr_id_idx ON change_request_requested_approvers(change_request_id); + CREATE INDEX IF NOT EXISTS change_request_requested_approvers_user_id_idx ON change_request_requested_approvers(user_id); + `, cb) +}; + +exports.down = function(db, cb) { + db.runSql(`DROP TABLE IF EXISTS change_request_requested_approvers`, cb); +}; + +exports._meta = { + "version": 1 +}; From 4c13bd63eea66cca9e812df4e67303a7fd78c069 Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Wed, 18 Jun 2025 13:26:48 +0200 Subject: [PATCH 15/41] task: added requested cr approval email template and emailService method (#10161) Initial template and emailService method in place here. --- src/lib/services/email-service.ts | 64 ++- .../requested-cr-approval.html.mustache | 379 ++++++++++++++++++ .../requested-cr-approval.plain.mustache | 3 + 3 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 src/mailtemplates/requested-cr-approval/requested-cr-approval.html.mustache create mode 100644 src/mailtemplates/requested-cr-approval/requested-cr-approval.plain.mustache diff --git a/src/lib/services/email-service.ts b/src/lib/services/email-service.ts index 64fe92ef85..9c2f2b0259 100644 --- a/src/lib/services/email-service.ts +++ b/src/lib/services/email-service.ts @@ -52,7 +52,8 @@ const SCHEDULED_CHANGE_CONFLICT_SUBJECT = 'Unleash - Scheduled changes can no longer be applied'; const SCHEDULED_EXECUTION_FAILED_SUBJECT = 'Unleash - Scheduled change request could not be applied'; - +const REQUESTED_CR_APPROVAL_SUBJECT = + 'Unleash - new change request waiting to be reviewed'; export const MAIL_ACCEPTED = '250 Accepted'; export type ChangeRequestScheduleConflictData = @@ -121,6 +122,67 @@ export class EmailService { } } + async sendRequestedCRApprovalEmail( + recipient: string, + changeRequestLink: string, + changeRequestTitle: string, + ): Promise { + if (this.configured()) { + const year = new Date().getFullYear(); + const bodyHtml = await this.compileTemplate( + 'requested-cr-approval', + TemplateFormat.HTML, + { + changeRequestLink, + changeRequestTitle, + year, + }, + ); + const bodyText = await this.compileTemplate( + 'requested-cr-approval', + TemplateFormat.PLAIN, + { + changeRequestLink, + changeRequestTitle, + year, + }, + ); + const email = { + from: this.sender, + to: recipient, + subject: REQUESTED_CR_APPROVAL_SUBJECT, + html: bodyHtml, + text: bodyText, + }; + process.nextTick(() => { + this.mailer!.sendMail(email).then( + () => + this.logger.info( + 'Successfully sent requested-cr-approval email', + ), + (e) => + this.logger.warn( + 'Failed to send requested-cr-approval email', + e, + ), + ); + }); + return Promise.resolve(email); + } + return new Promise((res) => { + this.logger.warn( + 'No mailer is configured. Please read the docs on how to configure an email service', + ); + this.logger.debug('Change request link: ', changeRequestLink); + res({ + from: this.sender, + to: recipient, + subject: REQUESTED_CR_APPROVAL_SUBJECT, + html: '', + text: '', + }); + }); + } async sendScheduledExecutionFailedEmail( recipient: string, changeRequestLink: string, diff --git a/src/mailtemplates/requested-cr-approval/requested-cr-approval.html.mustache b/src/mailtemplates/requested-cr-approval/requested-cr-approval.html.mustache new file mode 100644 index 0000000000..222f25a351 --- /dev/null +++ b/src/mailtemplates/requested-cr-approval/requested-cr-approval.html.mustache @@ -0,0 +1,379 @@ + + + + + *|MC:SUBJECT|* + + + +
+ + + + +
+ + + + + + + + + + + +
+ + + + + +
+ +
+ +
+ + + + + +
+

You have been added to review {{{ changeRequestTitle }}}

+

Click {{{changeRequestLink}}} to review it

+
+ +
+ + + + + + + + +
+ Unleash blog + Github + Slack community + Help center +
+ Copyright © {{ year }} | Bricks Software | All rights reserved. +
+ +
+ +
+
+ + diff --git a/src/mailtemplates/requested-cr-approval/requested-cr-approval.plain.mustache b/src/mailtemplates/requested-cr-approval/requested-cr-approval.plain.mustache new file mode 100644 index 0000000000..6e74deb6ad --- /dev/null +++ b/src/mailtemplates/requested-cr-approval/requested-cr-approval.plain.mustache @@ -0,0 +1,3 @@ +You have been added to review {{{ changeRequestTitle }}} + +Follow the link: {{{ changeRequestLink }}} to review it. From 2444bd7ccdbcdd4c33572721352f1f9d54d48346 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Wed, 18 Jun 2025 13:44:55 +0200 Subject: [PATCH 16/41] test: impact metrics collection e2e (#10162) --- .../metrics/impact/impact-metrics.e2e.test.ts | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/lib/features/metrics/impact/impact-metrics.e2e.test.ts diff --git a/src/lib/features/metrics/impact/impact-metrics.e2e.test.ts b/src/lib/features/metrics/impact/impact-metrics.e2e.test.ts new file mode 100644 index 0000000000..dcd2f3218c --- /dev/null +++ b/src/lib/features/metrics/impact/impact-metrics.e2e.test.ts @@ -0,0 +1,69 @@ +import { + type IUnleashTest, + setupAppWithCustomConfig, +} from '../../../../test/e2e/helpers/test-helper.js'; +import dbInit, { + type ITestDb, +} from '../../../../test/e2e/helpers/database-init.js'; +import getLogger from '../../../../test/fixtures/no-logger.js'; +import { MetricsTranslator } from './metrics-translator.js'; +import { impactRegister } from './impact-register.js'; + +let app: IUnleashTest; +let db: ITestDb; + +beforeAll(async () => { + db = await dbInit('impact_metrics', getLogger); + app = await setupAppWithCustomConfig(db.stores, { + experimental: { + flags: { + impactMetrics: true, + }, + }, + }); +}); + +afterAll(async () => { + await app.destroy(); + await db.destroy(); +}); + +test('should store impact metrics in memory and be able to retrieve them', async () => { + // TODO: replace with POST metrics when it's ready + const metricsTranslator = new MetricsTranslator(impactRegister); + + metricsTranslator.translateMetric({ + name: 'labeled_counter', + help: 'with labels', + type: 'counter' as const, + samples: [ + { + labels: { foo: 'bar' }, + value: 5, + }, + ], + }); + + metricsTranslator.translateMetric({ + name: 'labeled_counter', + help: 'with labels', + type: 'counter' as const, + samples: [ + { + labels: { foo: 'bar' }, + value: 10, + }, + ], + }); + + const response = await app.request + .get('/internal-backstage/impact/metrics') + .expect('Content-Type', /text/) + .expect(200); + + const metricsText = response.text; + + expect(metricsText).toContain('# HELP labeled_counter with labels'); + expect(metricsText).toContain('# TYPE labeled_counter counter'); + expect(metricsText).toMatch(/labeled_counter{foo="bar"} 15/); +}); From ef3ffc4d944a383bc296fa2bd2b5fdda289f8219 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Wed, 18 Jun 2025 13:47:37 +0200 Subject: [PATCH 17/41] fix: Order properties correctly when mapping from Editable Constraint to IConstraint (#10163) Prevents the property order from changing when constraints are set from the editable constraint component. When we render out the API command, we don't specify the order of properties in the objects, which means that it can change dramatically, which can be a little jarring. As it is right now, it first renders in one order when you open the strategy form: image And when you navigate to the targeting section of the strategy form, it changes to: image This also applies to constraints in segments. With this change, the order will remain the same before and after. Additionally, there's some extra tests around constraint ids being kept the same and being set if it doesn't exist. ## Further work This came about as part of the issue we had with constraint editing in segments being broken as of now. As part of that, I would like to make some further improvements (such as making the ID required when you use a list of editable constraints), but that will be in a follow-up. There are some complications that might not make it a viable option, sadly. We could also try to stabilize the property order in the API rendering methods (which I think might be a good idea), but because there's multiple different ones, this seems to be a faster solution. --- .../editable-constraint-type.test.ts | 44 +++++++++++++++++++ .../editable-constraint-type.ts | 30 ++++++++++--- .../useEditableConstraint.test.tsx | 12 ++--- 3 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.test.ts diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.test.ts b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.test.ts new file mode 100644 index 0000000000..234e6955ed --- /dev/null +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.test.ts @@ -0,0 +1,44 @@ +import { createEmptyConstraint } from 'utils/createEmptyConstraint'; +import { fromIConstraint, toIConstraint } from './editable-constraint-type.ts'; +import { constraintId } from 'constants/constraintId'; + +test('mapping to and from retains the constraint id', () => { + const constraint = createEmptyConstraint('context'); + + expect(toIConstraint(fromIConstraint(constraint))[constraintId]).toEqual( + constraint[constraintId], + ); +}); + +test('mapping to an editable constraint adds a constraint id if there is none', () => { + const constraint = createEmptyConstraint('context'); + delete constraint[constraintId]; + + const editableConstraint = fromIConstraint(constraint); + + expect(editableConstraint[constraintId]).toBeDefined(); + + const iConstraint = toIConstraint(editableConstraint); + expect(iConstraint[constraintId]).toEqual(editableConstraint[constraintId]); +}); + +test('mapping from an empty constraint removes redundant value / values', () => { + const constraint = createEmptyConstraint('context'); + expect(constraint).toHaveProperty('value'); + + const transformed = toIConstraint(fromIConstraint(constraint)); + expect(transformed).not.toHaveProperty('value'); +}); + +test('mapping to constraint returns properties in expected order', () => { + const constraint = createEmptyConstraint('context'); + const transformed = toIConstraint(fromIConstraint(constraint)); + + expect(Object.keys(transformed)).toEqual([ + 'values', + 'inverted', + 'operator', + 'contextName', + 'caseInsensitive', + ]); +}); diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.ts b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.ts index c55c546cfb..a0e2498346 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.ts +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.ts @@ -1,3 +1,4 @@ +import { constraintId } from 'constants/constraintId'; import { type DateOperator, isDateOperator, @@ -10,6 +11,7 @@ import { isSemVerOperator, } from 'constants/operators'; import type { IConstraint } from 'interfaces/strategy'; +import { v4 as uuidv4 } from 'uuid'; type EditableConstraintBase = Omit< IConstraint, @@ -72,12 +74,14 @@ export const fromIConstraint = ( const { value, values, operator, ...rest } = constraint; if (isSingleValueOperator(operator)) { return { + [constraintId]: uuidv4(), ...rest, operator, value: value ?? '', }; } else { return { + [constraintId]: uuidv4(), ...rest, operator, values: new Set(values), @@ -86,13 +90,29 @@ export const fromIConstraint = ( }; export const toIConstraint = (constraint: EditableConstraint): IConstraint => { + const { + inverted, + operator, + contextName, + caseInsensitive, + [constraintId]: id, + } = constraint; + const baseValues = { + inverted, + operator, + contextName, + caseInsensitive, + [constraintId]: id, + }; if ('value' in constraint) { - return constraint; - } else { - const { values, ...rest } = constraint; return { - ...rest, - values: Array.from(values), + value: constraint.value, + ...baseValues, + }; + } else { + return { + values: Array.from(constraint.values), + ...baseValues, }; } }; diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/useEditableConstraint.test.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/useEditableConstraint.test.tsx index fa2bab70f2..bdc671ea45 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/useEditableConstraint.test.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/useEditableConstraint.test.tsx @@ -40,11 +40,13 @@ test('calls onUpdate with new state', async () => { // gets called by useEffect, so we need to wait for the next render. await waitFor(() => { - expect(onUpdate).toHaveBeenCalledWith({ - contextName: 'context-field', - operator: IN, - values: [], - }); + expect(onUpdate).toHaveBeenCalledWith( + expect.objectContaining({ + contextName: 'context-field', + operator: IN, + values: [], + }), + ); }); }); From 967df825cb31a928c528521e8dc6dec4b80f52f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Fournier?= Date: Wed, 18 Jun 2025 17:40:21 +0200 Subject: [PATCH 18/41] feat: do not lock until migrations are needed (#10170) --- src/lib/server-impl.ts | 34 +++++++++++++++++++--------------- src/migrator.ts | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/lib/server-impl.ts b/src/lib/server-impl.ts index 54ac5d01b1..21e2cef94a 100644 --- a/src/lib/server-impl.ts +++ b/src/lib/server-impl.ts @@ -1,7 +1,7 @@ import stoppable, { type StoppableServer } from 'stoppable'; import { promisify } from 'util'; import version from './util/version.js'; -import { migrateDb, resetDb } from '../migrator.js'; +import { migrateDb, requiresMigration, resetDb } from '../migrator.js'; import getApp from './app.js'; import type MetricsMonitor from './metrics.js'; import { createMetricsMonitor } from './metrics.js'; @@ -336,21 +336,25 @@ async function start( if (config.db.disableMigration) { logger.info('DB migration: disabled'); } else { - logger.info('DB migration: start'); - if (config.flagResolver.isEnabled('migrationLock')) { - logger.info('Running migration with lock'); - const lock = withDbLock(config.db, { - lockKey: defaultLockKey, - timeout: defaultTimeout, - logger, - }); - await lock(migrateDb)(config); - } else { - logger.info('Running migration without lock'); - await migrateDb(config); - } + if (await requiresMigration(config)) { + logger.info('DB migration: start'); + if (config.flagResolver.isEnabled('migrationLock')) { + logger.info('Running migration with lock'); + const lock = withDbLock(config.db, { + lockKey: defaultLockKey, + timeout: defaultTimeout, + logger, + }); + await lock(migrateDb)(config); + } else { + logger.info('Running migration without lock'); + await migrateDb(config); + } - logger.info('DB migration: end'); + logger.info('DB migration: end'); + } else { + logger.info('DB migration: no migration needed'); + } } } catch (err) { logger.error('Failed to migrate db', err); diff --git a/src/migrator.ts b/src/migrator.ts index 82027b290b..8543cefb98 100644 --- a/src/migrator.ts +++ b/src/migrator.ts @@ -40,6 +40,28 @@ export async function migrateDb( }); } +export async function requiresMigration({ + db, +}: Pick): Promise { + return noDatabaseUrl(async () => { + const custom = { + ...db, + connectionTimeoutMillis: secondsToMilliseconds(10), + }; + + // disable Intellij/WebStorm from setting verbose CLI argument to db-migrator + process.argv = process.argv.filter((it) => !it.includes('--verbose')); + const dbm = getInstance(true, { + cwd: __dirname, + config: { custom }, + env: 'custom', + }); + + const pendingMigrations = await dbm.check(); + return pendingMigrations.length > 0; + }); +} + // This exists to ease testing export async function resetDb({ db }: IUnleashConfig): Promise { return noDatabaseUrl(async () => { From b356e23191ebb3f61e6dd7c108a8bfd9b69bfb56 Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Wed, 18 Jun 2025 17:53:08 +0200 Subject: [PATCH 19/41] feat: add prometheusImpactMetricsApi option to configuration (#10168) API url, similar to `config.prometheusApi`, for impact metrics --- src/lib/__snapshots__/create-config.test.ts.snap | 1 + src/lib/create-config.ts | 5 +++++ src/lib/types/option.ts | 2 ++ src/server-dev.ts | 1 + 4 files changed, 9 insertions(+) diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index 22116ade5f..455feba0d7 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -101,6 +101,7 @@ exports[`should create default config 1`] = ` "preHook": undefined, "preRouterHook": undefined, "prometheusApi": undefined, + "prometheusImpactMetricsApi": undefined, "publicFolder": undefined, "rateLimiting": { "callSignalEndpointMaxPerSecond": 1, diff --git a/src/lib/create-config.ts b/src/lib/create-config.ts index 45b9b588cd..b631cf7456 100644 --- a/src/lib/create-config.ts +++ b/src/lib/create-config.ts @@ -773,6 +773,10 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig { defaultDaysToBeConsideredInactive, ); + const prometheusImpactMetricsApi = + options.prometheusImpactMetricsApi || + process.env.PROMETHEUS_IMPACT_METRICS_API; + return { db, session, @@ -804,6 +808,7 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig { clientFeatureCaching, accessControlMaxAge, prometheusApi, + prometheusImpactMetricsApi, publicFolder: options.publicFolder, disableScheduler: options.disableScheduler, isEnterprise: isEnterprise, diff --git a/src/lib/types/option.ts b/src/lib/types/option.ts index fedb1eca03..6af0a001f9 100644 --- a/src/lib/types/option.ts +++ b/src/lib/types/option.ts @@ -164,6 +164,7 @@ export interface IUnleashOptions { clientFeatureCaching?: Partial; accessControlMaxAge?: number; prometheusApi?: string; + prometheusImpactMetricsApi?: string; publicFolder?: string; disableScheduler?: boolean; metricsRateLimiting?: Partial; @@ -288,6 +289,7 @@ export interface IUnleashConfig { clientFeatureCaching: IClientCachingOption; accessControlMaxAge: number; prometheusApi?: string; + prometheusImpactMetricsApi?: string; publicFolder?: string; disableScheduler?: boolean; isEnterprise: boolean; diff --git a/src/server-dev.ts b/src/server-dev.ts index 2d12dba8e5..bfc14360ca 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -69,6 +69,7 @@ process.nextTick(async () => { }, ], }, + prometheusImpactMetricsApi: 'http://localhost:9090', /* can be tweaked to control configuration caching for /api/client/features clientFeatureCaching: { enabled: true, From d49ca5081660fb06b17b57c763af3237c25943f9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 19:11:26 +0000 Subject: [PATCH 20/41] chore(deps): update dependency @types/node to v22.15.31 (#10172) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@types/node](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)) | [`22.15.18` -> `22.15.31`](https://renovatebot.com/diffs/npm/@types%2fnode/22.15.18/22.15.31) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fnode/22.15.31?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fnode/22.15.31?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fnode/22.15.18/22.15.31?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fnode/22.15.18/22.15.31?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- frontend/yarn.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index d8c18fabe9..399687220e 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -3199,20 +3199,20 @@ __metadata: linkType: hard "@types/node@npm:*": - version: 22.13.10 - resolution: "@types/node@npm:22.13.10" + version: 24.0.3 + resolution: "@types/node@npm:24.0.3" dependencies: - undici-types: "npm:~6.20.0" - checksum: 10c0/a3865f9503d6f718002374f7b87efaadfae62faa499c1a33b12c527cfb9fd86f733e1a1b026b80c5a0e4a965701174bc3305595a7d36078aa1abcf09daa5dee9 + undici-types: "npm:~7.8.0" + checksum: 10c0/9c3c4e87600d1cf11e291c2fd4bfd806a615455463c30a0ef6dc9c801b3423344d9b82b8084e3ccabce485a7421ebb61a66e9676181bd7d9aea4759998a120d5 languageName: node linkType: hard "@types/node@npm:^22.0.0": - version: 22.15.18 - resolution: "@types/node@npm:22.15.18" + version: 22.15.32 + resolution: "@types/node@npm:22.15.32" dependencies: undici-types: "npm:~6.21.0" - checksum: 10c0/e23178c568e2dc6b93b6aa3b8dfb45f9556e527918c947fe7406a4c92d2184c7396558912400c3b1b8d0fa952ec63819aca2b8e4d3545455fc6f1e9623e09ca6 + checksum: 10c0/63a2fa52adf1134d1b3bee8b1862d4b8e4550fffc190551068d3d41a41d9e5c0c8f1cb81faa18767b260637360f662115c26c5e4e7718868ead40c4a57cbc0e3 languageName: node linkType: hard @@ -10221,13 +10221,6 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~6.20.0": - version: 6.20.0 - resolution: "undici-types@npm:6.20.0" - checksum: 10c0/68e659a98898d6a836a9a59e6adf14a5d799707f5ea629433e025ac90d239f75e408e2e5ff086afc3cace26f8b26ee52155293564593fbb4a2f666af57fc59bf - languageName: node - linkType: hard - "undici-types@npm:~6.21.0": version: 6.21.0 resolution: "undici-types@npm:6.21.0" @@ -10235,6 +10228,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~7.8.0": + version: 7.8.0 + resolution: "undici-types@npm:7.8.0" + checksum: 10c0/9d9d246d1dc32f318d46116efe3cfca5a72d4f16828febc1918d94e58f6ffcf39c158aa28bf5b4fc52f410446bc7858f35151367bd7a49f21746cab6497b709b + languageName: node + linkType: hard + "unified@npm:^10.0.0": version: 10.1.2 resolution: "unified@npm:10.1.2" From 964544b6359ce3773d25d9ee2c461b23648cbf90 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 22:52:38 +0000 Subject: [PATCH 21/41] chore(deps): update dependency @types/pg to v8.15.4 (#10173) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@types/pg](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/pg) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/pg)) | [`8.15.2` -> `8.15.4`](https://renovatebot.com/diffs/npm/@types%2fpg/8.15.2/8.15.4) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fpg/8.15.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fpg/8.15.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fpg/8.15.2/8.15.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fpg/8.15.2/8.15.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 80 +++++----------------------------------------------- 2 files changed, 8 insertions(+), 74 deletions(-) diff --git a/package.json b/package.json index ba49839c92..6187596911 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "@types/node": "22.15.18", "@types/nodemailer": "6.4.17", "@types/owasp-password-strength-test": "1.3.2", - "@types/pg": "8.15.2", + "@types/pg": "8.15.4", "@types/semver": "7.5.8", "@types/slug": "^5.0.8", "@types/stoppable": "1.1.3", diff --git a/yarn.lock b/yarn.lock index 377236f1d1..ff5e9909d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1604,14 +1604,14 @@ __metadata: languageName: node linkType: hard -"@types/pg@npm:8.15.2": - version: 8.15.2 - resolution: "@types/pg@npm:8.15.2" +"@types/pg@npm:8.15.4": + version: 8.15.4 + resolution: "@types/pg@npm:8.15.4" dependencies: "@types/node": "npm:*" pg-protocol: "npm:*" - pg-types: "npm:^4.0.1" - checksum: 10c0/e3bc75f02af897ed960e83d1af9bd0cba1ff41cd0cbae0eaee323eae84f55e6d433f620aa1c72f7bd5107c80b018185c0e47de553cfc5439514c3da98768ef6c + pg-types: "npm:^2.2.0" + checksum: 10c0/7f9295cb2d934681bba84f7caad529c3b100d87e83ad0732c7fe496f4f79e42a795097321db54e010fcff22cb5e410cf683b4c9941907ee4564c822242816e91 languageName: node linkType: hard @@ -5646,13 +5646,6 @@ __metadata: languageName: node linkType: hard -"obuf@npm:~1.1.2": - version: 1.1.2 - resolution: "obuf@npm:1.1.2" - checksum: 10c0/520aaac7ea701618eacf000fc96ae458e20e13b0569845800fc582f81b386731ab22d55354b4915d58171db00e79cfcd09c1638c02f89577ef092b38c65b7d81 - languageName: node - linkType: hard - "on-finished@npm:2.4.1": version: 2.4.1 resolution: "on-finished@npm:2.4.1" @@ -5970,13 +5963,6 @@ __metadata: languageName: node linkType: hard -"pg-numeric@npm:1.0.2": - version: 1.0.2 - resolution: "pg-numeric@npm:1.0.2" - checksum: 10c0/43dd9884e7b52c79ddc28d2d282d7475fce8bba13452d33c04ceb2e0a65f561edf6699694e8e1c832ff9093770496363183c950dd29608e1bdd98f344b25bca9 - languageName: node - linkType: hard - "pg-pool@npm:^3.10.0": version: 3.10.0 resolution: "pg-pool@npm:3.10.0" @@ -6000,7 +5986,7 @@ __metadata: languageName: node linkType: hard -"pg-types@npm:2.2.0": +"pg-types@npm:2.2.0, pg-types@npm:^2.2.0": version: 2.2.0 resolution: "pg-types@npm:2.2.0" dependencies: @@ -6013,21 +5999,6 @@ __metadata: languageName: node linkType: hard -"pg-types@npm:^4.0.1": - version: 4.0.1 - resolution: "pg-types@npm:4.0.1" - dependencies: - pg-int8: "npm:1.0.1" - pg-numeric: "npm:1.0.2" - postgres-array: "npm:~3.0.1" - postgres-bytea: "npm:~3.0.0" - postgres-date: "npm:~2.0.1" - postgres-interval: "npm:^3.0.0" - postgres-range: "npm:^1.1.1" - checksum: 10c0/e2126b2775554ae8bacb3b104814487c2af2caff44cc52bee786b3887c65fe4c1fe031237e51e30ffed1cbb13b71776bd60cc1e65ac800c9946df4030849a074 - languageName: node - linkType: hard - "pg@npm:^8.11.2, pg@npm:^8.12.0": version: 8.16.0 resolution: "pg@npm:8.16.0" @@ -6128,13 +6099,6 @@ __metadata: languageName: node linkType: hard -"postgres-array@npm:~3.0.1": - version: 3.0.2 - resolution: "postgres-array@npm:3.0.2" - checksum: 10c0/644aa071f67a66a59f641f8e623887d2b915bc102a32643e2aa8b54c11acd343c5ad97831ea444dd37bd4b921ba35add4aa2cb0c6b76700a8252c2324aeba5b4 - languageName: node - linkType: hard - "postgres-bytea@npm:~1.0.0": version: 1.0.0 resolution: "postgres-bytea@npm:1.0.0" @@ -6142,15 +6106,6 @@ __metadata: languageName: node linkType: hard -"postgres-bytea@npm:~3.0.0": - version: 3.0.0 - resolution: "postgres-bytea@npm:3.0.0" - dependencies: - obuf: "npm:~1.1.2" - checksum: 10c0/41c79cc48aa730c5ba3eda6ab989a940034f07a1f57b8f2777dce56f1b8cca16c5870582932b5b10cc605048aef9b6157e06253c871b4717cafc6d00f55376aa - languageName: node - linkType: hard - "postgres-date@npm:~1.0.4": version: 1.0.7 resolution: "postgres-date@npm:1.0.7" @@ -6158,13 +6113,6 @@ __metadata: languageName: node linkType: hard -"postgres-date@npm:~2.0.1": - version: 2.0.1 - resolution: "postgres-date@npm:2.0.1" - checksum: 10c0/2d3698958f858b7d1df0a3929fb8750ccb43fa2c8ee9fec7a021e7926291f6c85ddd9d94d87cd6529d70bd2444f3e14fb5bb323af19ceaa733542cc05c5c653a - languageName: node - linkType: hard - "postgres-interval@npm:^1.1.0": version: 1.2.0 resolution: "postgres-interval@npm:1.2.0" @@ -6174,20 +6122,6 @@ __metadata: languageName: node linkType: hard -"postgres-interval@npm:^3.0.0": - version: 3.0.0 - resolution: "postgres-interval@npm:3.0.0" - checksum: 10c0/8b570b30ea37c685e26d136d34460f246f98935a1533defc4b53bb05ee23ae3dc7475b718ec7ea607a57894d8c6b4f1adf67ca9cc83a75bdacffd427d5c68de8 - languageName: node - linkType: hard - -"postgres-range@npm:^1.1.1": - version: 1.1.3 - resolution: "postgres-range@npm:1.1.3" - checksum: 10c0/f46bc379a198a9e3282a222c8e432d77494854bd4fa0706dff01641846db0bf4f09a9723e7fbb202da34ec3b2d88fc50e26e4bbeded7df19646e3acd6a7465ce - languageName: node - linkType: hard - "proc-log@npm:^3.0.0": version: 3.0.0 resolution: "proc-log@npm:3.0.0" @@ -7815,7 +7749,7 @@ __metadata: "@types/node": "npm:22.15.18" "@types/nodemailer": "npm:6.4.17" "@types/owasp-password-strength-test": "npm:1.3.2" - "@types/pg": "npm:8.15.2" + "@types/pg": "npm:8.15.4" "@types/semver": "npm:7.5.8" "@types/slug": "npm:^5.0.8" "@types/stoppable": "npm:1.1.3" From 43d61d02e1d7d444c11999a060574564dfc97385 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 02:46:46 +0000 Subject: [PATCH 22/41] chore(deps): update dependency @types/supertest to v6.0.3 (#10174) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@types/supertest](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/supertest) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/supertest)) | [`6.0.2` -> `6.0.3`](https://renovatebot.com/diffs/npm/@types%2fsupertest/6.0.2/6.0.3) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fsupertest/6.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fsupertest/6.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fsupertest/6.0.2/6.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fsupertest/6.0.2/6.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 6187596911..7192f866c7 100644 --- a/package.json +++ b/package.json @@ -159,7 +159,7 @@ "@types/semver": "7.5.8", "@types/slug": "^5.0.8", "@types/stoppable": "1.1.3", - "@types/supertest": "6.0.2", + "@types/supertest": "6.0.3", "@types/type-is": "1.6.7", "@types/uuid": "9.0.8", "@vitest/coverage-v8": "^3.1.3", diff --git a/yarn.lock b/yarn.lock index ff5e9909d0..d22625fd37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1680,13 +1680,13 @@ __metadata: languageName: node linkType: hard -"@types/supertest@npm:6.0.2": - version: 6.0.2 - resolution: "@types/supertest@npm:6.0.2" +"@types/supertest@npm:6.0.3": + version: 6.0.3 + resolution: "@types/supertest@npm:6.0.3" dependencies: "@types/methods": "npm:^1.1.4" "@types/superagent": "npm:^8.1.0" - checksum: 10c0/44a28f9b35b65800f4c7bcc23748e71c925098aa74ea504d14c98385c36d00de2a4f5aca15d7dc4514bc80533e0af21f985a4ab9f5f317c7266e9e75836aef39 + checksum: 10c0/a2080f870154b09db123864a484fb633bc9e2a0f7294a194388df4c7effe5af9de36d5a5ebf819f72b404fa47b5e813c47d5a3a51354251fd2fa8589bfb64f2c languageName: node linkType: hard @@ -7753,7 +7753,7 @@ __metadata: "@types/semver": "npm:7.5.8" "@types/slug": "npm:^5.0.8" "@types/stoppable": "npm:1.1.3" - "@types/supertest": "npm:6.0.2" + "@types/supertest": "npm:6.0.3" "@types/type-is": "npm:1.6.7" "@types/uuid": "npm:9.0.8" "@vitest/coverage-v8": "npm:^3.1.3" From 2e460b16fdba556bf3f20acf92ad9e405e41990e Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 19 Jun 2025 10:08:28 +0200 Subject: [PATCH 23/41] Chore(1 3836)/no changes in json diff (#10175) Contains several improvements for the new json diff render: 1. Removes extra left-inset of the +/- icons for changed lines. This means that the +/- icons will now show up in the change request popover, whereas they were hidden (pushed outside the frame) previously). 2. Change styling based on whether it's an edit or a full replacement. If it's an edit, keep the +/- icons per changed line. If it's a full replacement (e.g. the object has been created or deleted), don't show the +/- icons, and make the whole object the right color (specifically: this also colors the surrounding braces that weren't colored before). Not showing +/- for new/deleted objects is in line with how the previous system worked. 3. If there are no changes, show `(no changes)`, as we did before. Unfortunately, I don't think the component has any fallback for this case, so we can't render arbitrary code. However, when it does happen, the .diff container doesn't have any children, so we can render it with CSS. It's not ideal for accessibility reasons etc, but it's better than nothing, I think. 4. Adds a +/- to the button to open and close the full diff to indicate better what it does. Also, only shows the button if it's an edit. When it's a replacement, we show the full diff anyway, so the button has no effect. 5. Does not exclude any keys by default anymore, but makes it a parameter ## Left inset change + no excluded keys: Before: image After: image ## Edit vs full replacement Before: image (notice the surrounding braces (`{}`) aren't the right color) image After: image image ## No changes Before: image After: image (The show all props button will show the whole object regardless): image ## Button Before: image After: image --- .../component/events/EventDiff/EventDiff.tsx | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/frontend/src/component/events/EventDiff/EventDiff.tsx b/frontend/src/component/events/EventDiff/EventDiff.tsx index c0b3c8dfd9..f5bc7bf8fd 100644 --- a/frontend/src/component/events/EventDiff/EventDiff.tsx +++ b/frontend/src/component/events/EventDiff/EventDiff.tsx @@ -23,6 +23,7 @@ interface IEventDiffProps { * @deprecated remove with flag improvedJsonDiff */ sort?: (a: IEventDiffResult, b: IEventDiffResult) => number; + excludeKeys?: string[]; } const DiffStyles = styled('div')(({ theme }) => ({ @@ -37,7 +38,6 @@ const DiffStyles = styled('div')(({ theme }) => ({ position: 'absolute', left: 0, top: 0, - marginLeft: '-10px', }, }, @@ -47,35 +47,65 @@ const DiffStyles = styled('div')(({ theme }) => ({ content: '"+"', }, }, + '.deletion': { color: theme.palette.eventLog.diffSub, '::before': { content: '"-"', }, }, + + '&[data-change-type="replacement"]': { + ':has(.addition)': { + color: theme.palette.eventLog.diffAdd, + }, + ':has(.deletion)': { + color: theme.palette.eventLog.diffSub, + }, + '.addition::before, .deletion::before': { + content: 'none', + }, + }, + + '.diff:not(:has(*))': { + '::before': { + content: '"(no changes)"', + }, + }, })); -const NewEventDiff: FC = ({ entry }) => { +const ButtonIcon = styled('span')(({ theme }) => ({ + marginInlineEnd: theme.spacing(0.5), +})); + +const NewEventDiff: FC = ({ entry, excludeKeys }) => { + const changeType = entry.preData && entry.data ? 'edit' : 'replacement'; + const showExpandButton = changeType === 'edit'; const [full, setFull] = useState(false); const diffId = useId(); return ( <> - - + {showExpandButton ? ( + + ) : null} + From e466e72e0d708e41f04997d1449f02eabca00086 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 19 Jun 2025 10:09:38 +0200 Subject: [PATCH 24/41] chore(1-3842): don't reorder constraint properties / make id's non-optional (#10160) This PR takes two steps towards better constraint handling: ## New type: `IConstraintWithId` Introduces a new type, `IConstraintWithId`. This is the same as an `IConstraint`, except the constraint id property is required. The idea is that the list of editable constraints should move towards using this instead of just `IConstraint`. That should prevent us (on a type-level) from seeing more of the same kind of errors we saw with the segment constraints yesterday. I don't want to go ahead and update all the upstream uses of this to IConstraintWithId in this PR, so I'll look at that separately. ## API payload constraint replacer Introduces an api payload constraint "replacer", which we can use for [JSON.stringify's `replacer` parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter). The current implementation works both for strategies and for segments and has been added to edit + create forms for both of these resources. This has a couple benefits: 1. We can clearly state exactly how we want them to be rendered, including property order. I've decided to go with context -> operator -> value(s) as the main one (check the screenie), as I believe this is the most logical reading order. 2. We can exclude value/values (whichever one doesn't work with the operator) 3. It doesn't matter how we treat constraints internally, we can still present the payload how we want 4. Importantly: this only affects the stringification for the user-facing API payload, so it's very low risk. It does not affect anything that we actually send to the api. Here's what it can look like with ordered properties: image --- .../EditableConstraintsList.tsx | 16 +++++- .../editable-constraint-type.test.ts | 18 +----- .../editable-constraint-type.ts | 22 +------ .../FeatureStrategyCreate.tsx | 3 +- .../FeatureStrategyEdit.tsx | 7 ++- .../FeatureStrategyForm.tsx | 26 +-------- .../segments/CreateSegment/CreateSegment.tsx | 3 +- .../segments/EditSegment/EditSegment.tsx | 3 +- .../src/component/segments/SegmentForm.tsx | 4 +- .../segments/SegmentFormStepTwo.test.tsx | 4 +- .../component/segments/SegmentFormStepTwo.tsx | 4 +- .../segments/hooks/useSegmentForm.ts | 19 +++++-- frontend/src/interfaces/strategy.ts | 4 ++ .../api-payload-constraint-replacer.test.ts | 57 +++++++++++++++++++ .../utils/api-payload-constraint-replacer.ts | 39 +++++++++++++ frontend/src/utils/createEmptyConstraint.ts | 13 ++--- 16 files changed, 160 insertions(+), 82 deletions(-) create mode 100644 frontend/src/utils/api-payload-constraint-replacer.test.ts create mode 100644 frontend/src/utils/api-payload-constraint-replacer.ts diff --git a/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx index 29d43f183e..0c37645762 100644 --- a/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx +++ b/frontend/src/component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList.tsx @@ -1,5 +1,5 @@ import type React from 'react'; -import { useImperativeHandle } from 'react'; +import { useEffect, useImperativeHandle } from 'react'; import { forwardRef } from 'react'; import { styled } from '@mui/material'; import type { IConstraint } from 'interfaces/strategy'; @@ -9,6 +9,7 @@ import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsLis import { EditableConstraint } from 'component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/EditableConstraint'; import { createEmptyConstraint } from '../../../../utils/createEmptyConstraint.ts'; import { constraintId } from 'constants/constraintId.ts'; +import { v4 as uuidv4 } from 'uuid'; export interface IEditableConstraintsListRef { addConstraint?: (contextName: string) => void; } @@ -39,6 +40,17 @@ export const EditableConstraintsList = forwardRef< }, })); + useEffect(() => { + if (!constraints.every((constraint) => constraintId in constraint)) { + setConstraints( + constraints.map((constraint) => ({ + [constraintId]: uuidv4(), + ...constraint, + })), + ); + } + }, [constraints, setConstraints]); + const onDelete = (index: number) => { setConstraints( produce((draft) => { @@ -70,7 +82,7 @@ export const EditableConstraintsList = forwardRef< {constraints.map((constraint, index) => ( onDelete(index)} onUpdate={onAutoSave(constraint[constraintId])} diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.test.ts b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.test.ts index 234e6955ed..7eb54bdfaa 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.test.ts +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.test.ts @@ -1,6 +1,7 @@ import { createEmptyConstraint } from 'utils/createEmptyConstraint'; import { fromIConstraint, toIConstraint } from './editable-constraint-type.ts'; import { constraintId } from 'constants/constraintId'; +import type { IConstraint } from 'interfaces/strategy.ts'; test('mapping to and from retains the constraint id', () => { const constraint = createEmptyConstraint('context'); @@ -11,7 +12,7 @@ test('mapping to and from retains the constraint id', () => { }); test('mapping to an editable constraint adds a constraint id if there is none', () => { - const constraint = createEmptyConstraint('context'); + const constraint: IConstraint = createEmptyConstraint('context'); delete constraint[constraintId]; const editableConstraint = fromIConstraint(constraint); @@ -23,22 +24,9 @@ test('mapping to an editable constraint adds a constraint id if there is none', }); test('mapping from an empty constraint removes redundant value / values', () => { - const constraint = createEmptyConstraint('context'); + const constraint = { ...createEmptyConstraint('context'), value: '' }; expect(constraint).toHaveProperty('value'); const transformed = toIConstraint(fromIConstraint(constraint)); expect(transformed).not.toHaveProperty('value'); }); - -test('mapping to constraint returns properties in expected order', () => { - const constraint = createEmptyConstraint('context'); - const transformed = toIConstraint(fromIConstraint(constraint)); - - expect(Object.keys(transformed)).toEqual([ - 'values', - 'inverted', - 'operator', - 'contextName', - 'caseInsensitive', - ]); -}); diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.ts b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.ts index a0e2498346..80c798ba85 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.ts +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/editable-constraint-type.ts @@ -90,29 +90,13 @@ export const fromIConstraint = ( }; export const toIConstraint = (constraint: EditableConstraint): IConstraint => { - const { - inverted, - operator, - contextName, - caseInsensitive, - [constraintId]: id, - } = constraint; - const baseValues = { - inverted, - operator, - contextName, - caseInsensitive, - [constraintId]: id, - }; if ('value' in constraint) { - return { - value: constraint.value, - ...baseValues, - }; + return constraint; } else { + const { values, ...rest } = constraint; return { + ...rest, values: Array.from(constraint.values), - ...baseValues, }; } }; diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate.tsx index b37f23dbad..6e844ee14c 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyCreate/FeatureStrategyCreate.tsx @@ -37,6 +37,7 @@ import { useDefaultStrategy } from '../../../project/Project/ProjectSettings/Pro import { FeatureStrategyForm } from '../FeatureStrategyForm/FeatureStrategyForm.tsx'; import { NewStrategyVariants } from 'component/feature/StrategyTypes/NewStrategyVariants'; import { Limit } from 'component/common/Limit/Limit'; +import { apiPayloadConstraintReplacer } from 'utils/api-payload-constraint-replacer.ts'; const useStrategyLimit = (strategyCount: number) => { const { uiConfig } = useUiConfig(); @@ -280,7 +281,7 @@ export const formatAddStrategyApiCode = ( } const url = `${unleashUrl}/api/admin/projects/${projectId}/features/${featureId}/environments/${environmentId}/strategies`; - const payload = JSON.stringify(strategy, undefined, 2); + const payload = JSON.stringify(strategy, apiPayloadConstraintReplacer, 2); return `curl --location --request POST '${url}' \\ --header 'Authorization: INSERT_API_KEY' \\ diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx index 9ddeaf5847..25e1fc33ef 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit.tsx @@ -35,6 +35,7 @@ import { getChangeRequestConflictCreatedDataFromScheduleData, } from './change-request-conflict-data.ts'; import { constraintId } from 'constants/constraintId.ts'; +import { apiPayloadConstraintReplacer } from 'utils/api-payload-constraint-replacer.ts'; const useTitleTracking = () => { const [previousTitle, setPreviousTitle] = useState(''); @@ -352,7 +353,11 @@ export const formatUpdateStrategyApiCode = ( }; const url = `${unleashUrl}/api/admin/projects/${projectId}/features/${featureId}/environments/${environmentId}/strategies/${strategyId}`; - const payload = JSON.stringify(sortedStrategy, undefined, 2); + const payload = JSON.stringify( + sortedStrategy, + apiPayloadConstraintReplacer, + 2, + ); return `curl --location --request PUT '${url}' \\ --header 'Authorization: INSERT_API_KEY' \\ diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm.tsx index 494cb1ce23..40a1dcf1e9 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyForm/FeatureStrategyForm.tsx @@ -143,28 +143,6 @@ const StyledAlertBox = styled(Box)(({ theme }) => ({ }, })); -const StyledEnvironmentBox = styled(Box)(({ theme }) => ({ - display: 'flex', - alignItems: 'center', -})); - -const EnvironmentIconBox = styled(Box)(({ theme }) => ({ - transform: 'scale(0.9)', - display: 'flex', - alignItems: 'center', -})); - -const EnvironmentTypography = styled(Typography, { - shouldForwardProp: (prop) => prop !== 'enabled', -})<{ enabled: boolean }>(({ enabled }) => ({ - fontWeight: enabled ? 'bold' : 'normal', -})); - -const EnvironmentTypographyHeader = styled(Typography)(({ theme }) => ({ - marginRight: theme.spacing(0.5), - color: theme.palette.text.secondary, -})); - const StyledTab = styled(Tab)(({ theme }) => ({ width: '100px', })); @@ -173,11 +151,11 @@ const StyledBadge = styled(Badge)(({ theme }) => ({ marginLeft: theme.spacing(1), })); -const StyledConstraintSeparator = styled(ConstraintSeparator)(({ theme }) => ({ +const StyledConstraintSeparator = styled(ConstraintSeparator)({ top: '-10px', left: '0', transform: 'translateY(0)', -})); +}); export const FeatureStrategyForm = ({ projectId, diff --git a/frontend/src/component/segments/CreateSegment/CreateSegment.tsx b/frontend/src/component/segments/CreateSegment/CreateSegment.tsx index 92c3e0fea4..e470a99e18 100644 --- a/frontend/src/component/segments/CreateSegment/CreateSegment.tsx +++ b/frontend/src/component/segments/CreateSegment/CreateSegment.tsx @@ -21,6 +21,7 @@ import { useSegmentValuesCount } from 'component/segments/hooks/useSegmentValues import { SEGMENT_CREATE_BTN_ID } from 'utils/testIds'; import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits'; import { useOptionalPathParam } from 'hooks/useOptionalPathParam'; +import { apiPayloadConstraintReplacer } from 'utils/api-payload-constraint-replacer.ts'; interface ICreateSegmentProps { modal?: boolean; @@ -61,7 +62,7 @@ export const CreateSegment = ({ modal }: ICreateSegmentProps) => { return `curl --location --request POST '${uiConfig.unleashUrl}/api/admin/segments' \\ --header 'Authorization: INSERT_API_KEY' \\ --header 'Content-Type: application/json' \\ ---data-raw '${JSON.stringify(getSegmentPayload(), undefined, 2)}'`; +--data-raw '${JSON.stringify(getSegmentPayload(), apiPayloadConstraintReplacer, 2)}'`; }; const handleSubmit = async (e: React.FormEvent) => { diff --git a/frontend/src/component/segments/EditSegment/EditSegment.tsx b/frontend/src/component/segments/EditSegment/EditSegment.tsx index c0df6ef2ee..1a9d67cf0f 100644 --- a/frontend/src/component/segments/EditSegment/EditSegment.tsx +++ b/frontend/src/component/segments/EditSegment/EditSegment.tsx @@ -27,6 +27,7 @@ import { useHighestPermissionChangeRequestEnvironment } from 'hooks/useHighestPe import type { ISegment } from 'interfaces/segment.ts'; import { constraintId } from 'constants/constraintId.ts'; import { v4 as uuidv4 } from 'uuid'; +import { apiPayloadConstraintReplacer } from 'utils/api-payload-constraint-replacer.ts'; interface IEditSegmentProps { modal?: boolean; @@ -88,7 +89,7 @@ export const EditSegment = ({ modal }: IEditSegmentProps) => { }/api/admin/segments/${segmentId}' \\ --header 'Authorization: INSERT_API_KEY' \\ --header 'Content-Type: application/json' \\ ---data-raw '${JSON.stringify(getSegmentPayload(), undefined, 2)}'`; +--data-raw '${JSON.stringify(getSegmentPayload(), apiPayloadConstraintReplacer, 2)}'`; }; const highestPermissionChangeRequestEnv = diff --git a/frontend/src/component/segments/SegmentForm.tsx b/frontend/src/component/segments/SegmentForm.tsx index 8a1c38c7ad..0d3cfcee14 100644 --- a/frontend/src/component/segments/SegmentForm.tsx +++ b/frontend/src/component/segments/SegmentForm.tsx @@ -1,4 +1,4 @@ -import type { IConstraint } from 'interfaces/strategy'; +import type { IConstraint, IConstraintWithId } from 'interfaces/strategy'; import { SegmentFormStepOne } from './SegmentFormStepOne.tsx'; import { SegmentFormStepTwo } from './SegmentFormStepTwo.tsx'; import type React from 'react'; @@ -14,7 +14,7 @@ interface ISegmentProps { name: string; description: string; project?: string; - constraints: IConstraint[]; + constraints: IConstraintWithId[]; setName: React.Dispatch>; setDescription: React.Dispatch>; setProject: React.Dispatch>; diff --git a/frontend/src/component/segments/SegmentFormStepTwo.test.tsx b/frontend/src/component/segments/SegmentFormStepTwo.test.tsx index 84dcd21bf1..6a8efec383 100644 --- a/frontend/src/component/segments/SegmentFormStepTwo.test.tsx +++ b/frontend/src/component/segments/SegmentFormStepTwo.test.tsx @@ -3,12 +3,12 @@ import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { testServerRoute, testServerSetup } from 'utils/testServer'; import { SegmentFormStepTwo } from './SegmentFormStepTwo.tsx'; -import type { IConstraint } from 'interfaces/strategy'; import { vi } from 'vitest'; import { CREATE_SEGMENT, UPDATE_PROJECT_SEGMENT, } from 'component/providers/AccessProvider/permissions'; +import type { IConstraintWithId } from 'interfaces/strategy.ts'; const server = testServerSetup(); @@ -26,7 +26,7 @@ const setupRoutes = () => { const defaultProps = { project: undefined, - constraints: [] as IConstraint[], + constraints: [] as IConstraintWithId[], setConstraints: vi.fn(), setCurrentStep: vi.fn(), mode: 'create' as const, diff --git a/frontend/src/component/segments/SegmentFormStepTwo.tsx b/frontend/src/component/segments/SegmentFormStepTwo.tsx index ddb84cf021..abea3aeb9e 100644 --- a/frontend/src/component/segments/SegmentFormStepTwo.tsx +++ b/frontend/src/component/segments/SegmentFormStepTwo.tsx @@ -13,7 +13,7 @@ import { UPDATE_SEGMENT, } from 'component/providers/AccessProvider/permissions'; import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext'; -import type { IConstraint } from 'interfaces/strategy'; +import type { IConstraint, IConstraintWithId } from 'interfaces/strategy'; import { useNavigate } from 'react-router-dom'; import { EditableConstraintsList } from 'component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList'; import type { IEditableConstraintsListRef } from 'component/common/NewConstraintAccordion/ConstraintsList/EditableConstraintsList'; @@ -33,7 +33,7 @@ import { GO_BACK } from 'constants/navigate'; interface ISegmentFormPartTwoProps { project?: string; - constraints: IConstraint[]; + constraints: IConstraintWithId[]; setConstraints: React.Dispatch>; setCurrentStep: React.Dispatch>; mode: SegmentFormMode; diff --git a/frontend/src/component/segments/hooks/useSegmentForm.ts b/frontend/src/component/segments/hooks/useSegmentForm.ts index 09e106753e..951619715d 100644 --- a/frontend/src/component/segments/hooks/useSegmentForm.ts +++ b/frontend/src/component/segments/hooks/useSegmentForm.ts @@ -1,6 +1,8 @@ -import type { IConstraint } from 'interfaces/strategy'; +import type { IConstraint, IConstraintWithId } from 'interfaces/strategy'; import { useEffect, useState } from 'react'; import { useSegmentValidation } from 'hooks/api/getters/useSegmentValidation/useSegmentValidation'; +import { v4 as uuidv4 } from 'uuid'; +import { constraintId } from 'constants/constraintId'; export const useSegmentForm = ( initialName = '', @@ -11,8 +13,13 @@ export const useSegmentForm = ( const [name, setName] = useState(initialName); const [description, setDescription] = useState(initialDescription); const [project, setProject] = useState(initialProject); - const [constraints, setConstraints] = - useState(initialConstraints); + const initialConstraintsWithId = initialConstraints.map((constraint) => ({ + [constraintId]: uuidv4(), + ...constraint, + })); + const [constraints, setConstraints] = useState( + initialConstraintsWithId, + ); const [errors, setErrors] = useState({}); const nameError = useSegmentValidation(name, initialName); @@ -29,7 +36,7 @@ export const useSegmentForm = ( }, [initialProject]); useEffect(() => { - setConstraints(initialConstraints); + setConstraints(initialConstraintsWithId); // eslint-disable-next-line }, [JSON.stringify(initialConstraints)]); @@ -61,7 +68,9 @@ export const useSegmentForm = ( project, setProject, constraints, - setConstraints, + setConstraints: setConstraints as React.Dispatch< + React.SetStateAction + >, getSegmentPayload, clearErrors, errors, diff --git a/frontend/src/interfaces/strategy.ts b/frontend/src/interfaces/strategy.ts index b2aa226009..0f46621f79 100644 --- a/frontend/src/interfaces/strategy.ts +++ b/frontend/src/interfaces/strategy.ts @@ -68,6 +68,10 @@ export interface IConstraint { [constraintId]?: string; } +export interface IConstraintWithId extends IConstraint { + [constraintId]: string; +} + export interface IFeatureStrategySortOrder { id: string; sortOrder: number; diff --git a/frontend/src/utils/api-payload-constraint-replacer.test.ts b/frontend/src/utils/api-payload-constraint-replacer.test.ts new file mode 100644 index 0000000000..1790d2b6bd --- /dev/null +++ b/frontend/src/utils/api-payload-constraint-replacer.test.ts @@ -0,0 +1,57 @@ +import type { IConstraint } from 'interfaces/strategy'; +import { serializeConstraint } from './api-payload-constraint-replacer.ts'; +import { constraintId } from 'constants/constraintId'; + +test('keys are ordered in the expected order', () => { + const input: IConstraint = { + values: ['something'], + inverted: true, + operator: 'STR_CONTAINS', + contextName: 'context', + caseInsensitive: true, + }; + + const output = serializeConstraint(input); + + expect(Object.entries(output)).toStrictEqual([ + ['contextName', 'context'], + ['operator', 'STR_CONTAINS'], + ['values', ['something']], + ['caseInsensitive', true], + ['inverted', true], + ]); +}); + +test('only value OR values is present, not both', () => { + const input: IConstraint = { + value: 'something', + values: ['something else'], + inverted: true, + operator: 'IN', + contextName: 'context', + caseInsensitive: true, + }; + + const noValue = serializeConstraint(input); + expect(noValue.values).toStrictEqual(['something else']); + expect(noValue).not.toHaveProperty('value'); + + const noValues = serializeConstraint({ + ...input, + operator: 'SEMVER_EQ', + }); + expect(noValues.value).toStrictEqual('something'); + expect(noValues).not.toHaveProperty('values'); +}); + +test('constraint id is not included', () => { + const input: IConstraint = { + [constraintId]: 'constraint-id', + value: 'something', + operator: 'IN', + contextName: 'context', + }; + + const output = serializeConstraint(input); + expect(constraintId in output).toBeFalsy(); +}); diff --git a/frontend/src/utils/api-payload-constraint-replacer.ts b/frontend/src/utils/api-payload-constraint-replacer.ts new file mode 100644 index 0000000000..244b207d74 --- /dev/null +++ b/frontend/src/utils/api-payload-constraint-replacer.ts @@ -0,0 +1,39 @@ +import { isSingleValueOperator } from 'constants/operators'; +import type { IConstraint } from 'interfaces/strategy'; + +export const serializeConstraint = ({ + value, + values, + inverted, + operator, + contextName, + caseInsensitive, +}: IConstraint): IConstraint => { + const makeConstraint = ( + valueProp: { value: string } | { values: string[] }, + ): IConstraint => { + return { + contextName, + operator, + ...valueProp, + caseInsensitive, + inverted, + }; + }; + + if (isSingleValueOperator(operator)) { + return makeConstraint({ value: value || '' }); + } + + return makeConstraint({ values: values || [] }); +}; + +export const apiPayloadConstraintReplacer = (key: string, value: any) => { + if (key !== 'constraints' || !Array.isArray(value)) { + return value; + } + const orderedConstraints = (value as IConstraint[]).map( + serializeConstraint, + ); + return orderedConstraints; +}; diff --git a/frontend/src/utils/createEmptyConstraint.ts b/frontend/src/utils/createEmptyConstraint.ts index 8556812b46..49e2887cbf 100644 --- a/frontend/src/utils/createEmptyConstraint.ts +++ b/frontend/src/utils/createEmptyConstraint.ts @@ -1,16 +1,15 @@ import { constraintId } from 'constants/constraintId'; -import { dateOperators } from 'constants/operators'; -import type { IConstraint } from 'interfaces/strategy'; -import { oneOf } from 'utils/oneOf'; +import { isDateOperator } from 'constants/operators'; +import type { IConstraintWithId } from 'interfaces/strategy'; import { operatorsForContext } from 'utils/operatorsForContext'; import { v4 as uuidv4 } from 'uuid'; -export const createEmptyConstraint = (contextName: string): IConstraint => { +export const createEmptyConstraint = ( + contextName: string, +): IConstraintWithId => { const operator = operatorsForContext(contextName)[0]; - const value = oneOf(dateOperators, operator) - ? new Date().toISOString() - : ''; + const value = isDateOperator(operator) ? new Date().toISOString() : ''; return { contextName, From 40c7c25db96f2b949fcdad9efe7a804ad1e72069 Mon Sep 17 00:00:00 2001 From: Simon Hornby Date: Thu, 19 Jun 2025 11:31:51 +0200 Subject: [PATCH 25/41] feat: ingest new impact metrics (#10169) Accepts the new impact metrics into the singleton registry and then does nothing with them. If the relevant flag is off, the metrics are stripped from the existing metrics data format and dropped on the floor --- src/lib/features/metrics/instance/metrics.ts | 26 +++++++++++++++----- src/lib/routes/client-api/index.ts | 9 ++++++- src/lib/types/experimental.ts | 4 +++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/lib/features/metrics/instance/metrics.ts b/src/lib/features/metrics/instance/metrics.ts index 649bdd12e8..577eed59ac 100644 --- a/src/lib/features/metrics/instance/metrics.ts +++ b/src/lib/features/metrics/instance/metrics.ts @@ -27,6 +27,7 @@ import { CLIENT_METRICS } from '../../../events/index.js'; import type { CustomMetricsSchema } from '../../../openapi/spec/custom-metrics-schema.js'; import type { StoredCustomMetric } from '../custom/custom-metrics-store.js'; import type { CustomMetricsService } from '../custom/custom-metrics-service.js'; +import type { MetricsTranslator } from '../impact/metrics-translator.js'; export default class ClientMetricsController extends Controller { logger: Logger; @@ -39,6 +40,8 @@ export default class ClientMetricsController extends Controller { customMetricsService: CustomMetricsService; + metricsTranslator: MetricsTranslator; + flagResolver: IFlagResolver; constructor( @@ -55,6 +58,7 @@ export default class ClientMetricsController extends Controller { | 'customMetricsService' >, config: IUnleashConfig, + metricsTranslator: MetricsTranslator, ) { super(config); const { getLogger } = config; @@ -65,6 +69,7 @@ export default class ClientMetricsController extends Controller { this.metricsV2 = clientMetricsServiceV2; this.customMetricsService = customMetricsService; this.flagResolver = config.flagResolver; + this.metricsTranslator = metricsTranslator; this.route({ method: 'post', @@ -150,16 +155,25 @@ export default class ClientMetricsController extends Controller { } else { try { const { body: data, ip: clientIp, user } = req; - data.environment = this.metricsV2.resolveMetricsEnvironment( - user, - data, - ); + const { impactMetrics, ...metricsData } = data; + metricsData.environment = + this.metricsV2.resolveMetricsEnvironment(user, metricsData); await this.clientInstanceService.registerInstance( - data, + metricsData, clientIp, ); - await this.metricsV2.registerClientMetrics(data, clientIp); + await this.metricsV2.registerClientMetrics( + metricsData, + clientIp, + ); + if ( + this.flagResolver.isEnabled('impactMetrics') && + impactMetrics + ) { + this.metricsTranslator.translateMetrics(impactMetrics); + } + res.getHeaderNames().forEach((header) => res.removeHeader(header), ); diff --git a/src/lib/routes/client-api/index.ts b/src/lib/routes/client-api/index.ts index b58dc6824f..cb7cf3cd0c 100644 --- a/src/lib/routes/client-api/index.ts +++ b/src/lib/routes/client-api/index.ts @@ -4,13 +4,20 @@ import MetricsController from '../../features/metrics/instance/metrics.js'; import RegisterController from '../../features/metrics/instance/register.js'; import type { IUnleashConfig } from '../../types/index.js'; import type { IUnleashServices } from '../../services/index.js'; +import { impactRegister } from '../../features/metrics/impact/impact-register.js'; +import { MetricsTranslator } from '../../features/metrics/impact/metrics-translator.js'; export default class ClientApi extends Controller { constructor(config: IUnleashConfig, services: IUnleashServices) { super(config); + const metricsTranslator = new MetricsTranslator(impactRegister); + this.use('/features', new FeatureController(services, config).router); - this.use('/metrics', new MetricsController(services, config).router); + this.use( + '/metrics', + new MetricsController(services, config, metricsTranslator).router, + ); this.use('/register', new RegisterController(services, config).router); } } diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 41111f6c4a..cc3a22d432 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -292,6 +292,10 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_IMPROVED_JSON_DIFF, false, ), + impactMetrics: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_IMPACT_METRICS, + false, + ), }; export const defaultExperimentalOptions: IExperimentalOptions = { From 63a354ab6f8a5d8503856fc336152367682f1475 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Thu, 19 Jun 2025 12:34:22 +0200 Subject: [PATCH 26/41] test: impact metrics e2e (#10178) --- .../metrics/impact/impact-metrics.e2e.test.ts | 69 +++++++++++-------- .../metrics/impact/metrics-translator.ts | 4 +- src/server-dev.ts | 1 + 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/lib/features/metrics/impact/impact-metrics.e2e.test.ts b/src/lib/features/metrics/impact/impact-metrics.e2e.test.ts index dcd2f3218c..fcea2ff321 100644 --- a/src/lib/features/metrics/impact/impact-metrics.e2e.test.ts +++ b/src/lib/features/metrics/impact/impact-metrics.e2e.test.ts @@ -6,12 +6,26 @@ import dbInit, { type ITestDb, } from '../../../../test/e2e/helpers/database-init.js'; import getLogger from '../../../../test/fixtures/no-logger.js'; -import { MetricsTranslator } from './metrics-translator.js'; -import { impactRegister } from './impact-register.js'; +import type { Metric } from './metrics-translator.js'; let app: IUnleashTest; let db: ITestDb; +const sendImpactMetrics = async (impactMetrics: Metric[]) => + app.request + .post('/api/client/metrics') + .send({ + appName: 'impact-metrics-app', + instanceId: 'instance-id', + bucket: { + start: Date.now(), + stop: Date.now(), + toggles: {}, + }, + impactMetrics, + }) + .expect(202); + beforeAll(async () => { db = await dbInit('impact_metrics', getLogger); app = await setupAppWithCustomConfig(db.stores, { @@ -29,32 +43,33 @@ afterAll(async () => { }); test('should store impact metrics in memory and be able to retrieve them', async () => { - // TODO: replace with POST metrics when it's ready - const metricsTranslator = new MetricsTranslator(impactRegister); + await sendImpactMetrics([ + { + name: 'labeled_counter', + help: 'with labels', + type: 'counter', + samples: [ + { + labels: { foo: 'bar' }, + value: 5, + }, + ], + }, + ]); - metricsTranslator.translateMetric({ - name: 'labeled_counter', - help: 'with labels', - type: 'counter' as const, - samples: [ - { - labels: { foo: 'bar' }, - value: 5, - }, - ], - }); - - metricsTranslator.translateMetric({ - name: 'labeled_counter', - help: 'with labels', - type: 'counter' as const, - samples: [ - { - labels: { foo: 'bar' }, - value: 10, - }, - ], - }); + await sendImpactMetrics([ + { + name: 'labeled_counter', + help: 'with labels', + type: 'counter', + samples: [ + { + labels: { foo: 'bar' }, + value: 10, + }, + ], + }, + ]); const response = await app.request .get('/internal-backstage/impact/metrics') diff --git a/src/lib/features/metrics/impact/metrics-translator.ts b/src/lib/features/metrics/impact/metrics-translator.ts index 0f43128606..f27999d837 100644 --- a/src/lib/features/metrics/impact/metrics-translator.ts +++ b/src/lib/features/metrics/impact/metrics-translator.ts @@ -1,11 +1,11 @@ import { Counter, Gauge, type Registry } from 'prom-client'; -interface MetricSample { +export interface MetricSample { labels?: Record; value: number; } -interface Metric { +export interface Metric { name: string; help: string; type: 'counter' | 'gauge'; diff --git a/src/server-dev.ts b/src/server-dev.ts index bfc14360ca..b49244861d 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -56,6 +56,7 @@ process.nextTick(async () => { customMetrics: true, lifecycleMetrics: true, improvedJsonDiff: true, + impactMetrics: true, }, }, authentication: { From 40840c98cf48cbaaac7cb5e869a9e1959372fc65 Mon Sep 17 00:00:00 2001 From: Jaanus Sellin Date: Thu, 19 Jun 2025 14:46:14 +0300 Subject: [PATCH 27/41] feat: support event id in search (#10176) We added `id` as one of the query parameters for searching events. --- src/lib/features/events/event-service.ts | 2 +- .../spec/event-search-query-parameters.ts | 11 ++ src/lib/types/stores/event-store.ts | 1 + .../e2e/api/admin/event-search.e2e.test.ts | 100 +++++++++++++ src/test/e2e/stores/event-store.e2e.test.ts | 134 ++++++++++++++++++ 5 files changed, 247 insertions(+), 1 deletion(-) diff --git a/src/lib/features/events/event-service.ts b/src/lib/features/events/event-service.ts index 8ec37385e0..b55ddf0bd5 100644 --- a/src/lib/features/events/event-service.ts +++ b/src/lib/features/events/event-service.ts @@ -222,7 +222,7 @@ export default class EventService { if (parsed) queryParams.push(parsed); } - ['project', 'type', 'environment'].forEach((field) => { + ['project', 'type', 'environment', 'id'].forEach((field) => { if (params[field]) { const parsed = parseSearchOperatorValue(field, params[field]); if (parsed) queryParams.push(parsed); diff --git a/src/lib/openapi/spec/event-search-query-parameters.ts b/src/lib/openapi/spec/event-search-query-parameters.ts index d778aa738c..a36fc971c8 100644 --- a/src/lib/openapi/spec/event-search-query-parameters.ts +++ b/src/lib/openapi/spec/event-search-query-parameters.ts @@ -11,6 +11,17 @@ export const eventSearchQueryParameters = [ 'Find events by a free-text search query. The query will be matched against the event data payload (if any).', in: 'query', }, + { + name: 'id', + schema: { + type: 'string', + example: 'IS:123', + pattern: '^(IS|IS_ANY_OF):(.*?)(,([0-9]+))*$', + }, + description: + 'Filter by event ID using supported operators: IS, IS_ANY_OF.', + in: 'query', + }, { name: 'feature', schema: { diff --git a/src/lib/types/stores/event-store.ts b/src/lib/types/stores/event-store.ts index 0ee004b0e8..581cd03316 100644 --- a/src/lib/types/stores/event-store.ts +++ b/src/lib/types/stores/event-store.ts @@ -6,6 +6,7 @@ import type { IQueryOperations } from '../../features/events/event-store.js'; import type { IQueryParam } from '../../features/feature-toggle/types/feature-toggle-strategies-store-type.js'; export interface IEventSearchParams { + id?: string; project?: string; query?: string; feature?: string; diff --git a/src/test/e2e/api/admin/event-search.e2e.test.ts b/src/test/e2e/api/admin/event-search.e2e.test.ts index 8cfae7f7b8..778ab0c9c1 100644 --- a/src/test/e2e/api/admin/event-search.e2e.test.ts +++ b/src/test/e2e/api/admin/event-search.e2e.test.ts @@ -618,3 +618,103 @@ test('should filter events by environment using IS_ANY_OF', async () => { total: 2, }); }); + +test('should filter events by ID', async () => { + await eventService.storeEvent({ + type: FEATURE_CREATED, + project: 'default', + data: { name: 'feature1' }, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + }); + + await eventService.storeEvent({ + type: FEATURE_CREATED, + project: 'default', + data: { name: 'feature2' }, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + }); + + await eventService.storeEvent({ + type: FEATURE_CREATED, + project: 'default', + data: { name: 'feature3' }, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + }); + + const { body: allEventsResponse } = await searchEvents({}); + const targetEvent = allEventsResponse.events.find( + (e: any) => e.data.name === 'feature2', + ); + + const { body } = await searchEvents({ id: `IS:${targetEvent.id}` }); + + expect(body).toMatchObject({ + events: [ + { + id: targetEvent.id, + type: 'feature-created', + data: { name: 'feature2' }, + }, + ], + total: 1, + }); +}); + +test('should filter events by multiple IDs using IS_ANY_OF', async () => { + await eventService.storeEvent({ + type: FEATURE_CREATED, + project: 'default', + data: { name: 'feature1' }, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + }); + + await eventService.storeEvent({ + type: FEATURE_CREATED, + project: 'default', + data: { name: 'feature2' }, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + }); + + await eventService.storeEvent({ + type: FEATURE_CREATED, + project: 'default', + data: { name: 'feature3' }, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + }); + + const { body: allEventsResponse } = await searchEvents({}); + const targetEvent1 = allEventsResponse.events.find( + (e: any) => e.data.name === 'feature1', + ); + const targetEvent3 = allEventsResponse.events.find( + (e: any) => e.data.name === 'feature3', + ); + + const { body } = await searchEvents({ + id: `IS_ANY_OF:${targetEvent1.id},${targetEvent3.id}`, + }); + + expect(body.total).toBe(2); + expect(body.events).toHaveLength(2); + + const returnedIds = body.events.map((e: any) => e.id); + expect(returnedIds).toContain(targetEvent1.id); + expect(returnedIds).toContain(targetEvent3.id); + + const feature2Event = allEventsResponse.events.find( + (e: any) => e.data.name === 'feature2', + ); + expect(returnedIds).not.toContain(feature2Event.id); +}); diff --git a/src/test/e2e/stores/event-store.e2e.test.ts b/src/test/e2e/stores/event-store.e2e.test.ts index 1b1b507dbd..81a0def9a2 100644 --- a/src/test/e2e/stores/event-store.e2e.test.ts +++ b/src/test/e2e/stores/event-store.e2e.test.ts @@ -338,3 +338,137 @@ test('getMaxRevisionId should exclude FEATURE_CREATED and FEATURE_TAGGED events' expect(updatedEvent!.id).toBeGreaterThan(taggedEvent!.id); expect(segmentEvent!.id).toBeGreaterThan(updatedEvent!.id); }); + +test('Should filter events by ID using IS operator', async () => { + const event1 = { + type: FEATURE_CREATED, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + data: { name: 'feature1' }, + }; + const event2 = { + type: FEATURE_CREATED, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + data: { name: 'feature2' }, + }; + const event3 = { + type: FEATURE_CREATED, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + data: { name: 'feature3' }, + }; + + await eventStore.store(event1); + await eventStore.store(event2); + await eventStore.store(event3); + + const allEvents = await eventStore.getAll(); + const targetEvent = allEvents.find((e) => e.data.name === 'feature2'); + + const filteredEvents = await eventStore.searchEvents( + { + offset: 0, + limit: 10, + }, + [ + { + field: 'id', + operator: 'IS', + values: [targetEvent!.id.toString()], + }, + ], + ); + + expect(filteredEvents).toHaveLength(1); + expect(filteredEvents[0].id).toBe(targetEvent!.id); + expect(filteredEvents[0].data.name).toBe('feature2'); +}); + +test('Should filter events by ID using IS_ANY_OF operator', async () => { + const event1 = { + type: FEATURE_CREATED, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + data: { name: 'feature1' }, + }; + const event2 = { + type: FEATURE_CREATED, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + data: { name: 'feature2' }, + }; + const event3 = { + type: FEATURE_CREATED, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + data: { name: 'feature3' }, + }; + + await eventStore.store(event1); + await eventStore.store(event2); + await eventStore.store(event3); + + const allEvents = await eventStore.getAll(); + const targetEvent1 = allEvents.find((e) => e.data.name === 'feature1'); + const targetEvent3 = allEvents.find((e) => e.data.name === 'feature3'); + + const filteredEvents = await eventStore.searchEvents( + { + offset: 0, + limit: 10, + }, + [ + { + field: 'id', + operator: 'IS_ANY_OF', + values: [ + targetEvent1!.id.toString(), + targetEvent3!.id.toString(), + ], + }, + ], + ); + + expect(filteredEvents).toHaveLength(2); + const eventIds = filteredEvents.map((e) => e.id); + expect(eventIds).toContain(targetEvent1!.id); + expect(eventIds).toContain(targetEvent3!.id); + expect(eventIds).not.toContain( + allEvents.find((e) => e.data.name === 'feature2')!.id, + ); +}); + +test('Should return empty result when filtering by non-existent ID', async () => { + const event = { + type: FEATURE_CREATED, + createdBy: 'test-user', + createdByUserId: TEST_USER_ID, + ip: '127.0.0.1', + data: { name: 'feature1' }, + }; + + await eventStore.store(event); + + const filteredEvents = await eventStore.searchEvents( + { + offset: 0, + limit: 10, + }, + [ + { + field: 'id', + operator: 'IS', + values: ['999999'], + }, + ], + ); + + expect(filteredEvents).toHaveLength(0); +}); From 632f3a04cb762dac211004945a3629b3b97d5d72 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Thu, 19 Jun 2025 14:46:36 +0200 Subject: [PATCH 28/41] feat: validate impact metrics (#10181) --- .../client-metrics/metrics-service-v2.ts | 15 +++++++++- .../metrics/impact/impact-metrics.e2e.test.ts | 23 +++++++++++++-- src/lib/features/metrics/instance/metrics.ts | 4 +-- src/lib/features/metrics/shared/schema.ts | 29 +++++++++++++++++++ src/lib/routes/client-api/index.ts | 9 +----- 5 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/lib/features/metrics/client-metrics/metrics-service-v2.ts b/src/lib/features/metrics/client-metrics/metrics-service-v2.ts index 00271320a9..d2e2da83f2 100644 --- a/src/lib/features/metrics/client-metrics/metrics-service-v2.ts +++ b/src/lib/features/metrics/client-metrics/metrics-service-v2.ts @@ -6,7 +6,7 @@ import type { IClientMetricsEnv, IClientMetricsStoreV2, } from './client-metrics-store-v2-type.js'; -import { clientMetricsSchema } from '../shared/schema.js'; +import { clientMetricsSchema, impactMetricsSchema } from '../shared/schema.js'; import { compareAsc, secondsToMilliseconds } from 'date-fns'; import { CLIENT_METRICS, @@ -30,6 +30,11 @@ import { MAX_UNKNOWN_FLAGS, type UnknownFlagsService, } from '../unknown-flags/unknown-flags-service.js'; +import { + type Metric, + MetricsTranslator, +} from '../impact/metrics-translator.js'; +import { impactRegister } from '../impact/impact-register.js'; export default class ClientMetricsServiceV2 { private config: IUnleashConfig; @@ -46,6 +51,8 @@ export default class ClientMetricsServiceV2 { private logger: Logger; + private impactMetricsTranslator: MetricsTranslator; + private cachedFeatureNames: () => Promise; constructor( @@ -69,6 +76,7 @@ export default class ClientMetricsServiceV2 { maxAge: secondsToMilliseconds(10), }, ); + this.impactMetricsTranslator = new MetricsTranslator(impactRegister); } async clearMetrics(hoursAgo: number) { @@ -187,6 +195,11 @@ export default class ClientMetricsServiceV2 { this.lastSeenService.updateLastSeen(metrics); } + async registerImpactMetrics(impactMetrics: Metric[]) { + const value = await impactMetricsSchema.validateAsync(impactMetrics); + this.impactMetricsTranslator.translateMetrics(value); + } + async registerClientMetrics( data: ClientMetricsSchema, clientIp: string, diff --git a/src/lib/features/metrics/impact/impact-metrics.e2e.test.ts b/src/lib/features/metrics/impact/impact-metrics.e2e.test.ts index fcea2ff321..a1eb68ffb3 100644 --- a/src/lib/features/metrics/impact/impact-metrics.e2e.test.ts +++ b/src/lib/features/metrics/impact/impact-metrics.e2e.test.ts @@ -11,7 +11,7 @@ import type { Metric } from './metrics-translator.js'; let app: IUnleashTest; let db: ITestDb; -const sendImpactMetrics = async (impactMetrics: Metric[]) => +const sendImpactMetrics = async (impactMetrics: Metric[], status = 202) => app.request .post('/api/client/metrics') .send({ @@ -24,7 +24,7 @@ const sendImpactMetrics = async (impactMetrics: Metric[]) => }, impactMetrics, }) - .expect(202); + .expect(status); beforeAll(async () => { db = await dbInit('impact_metrics', getLogger); @@ -71,6 +71,25 @@ test('should store impact metrics in memory and be able to retrieve them', async }, ]); + await sendImpactMetrics([]); + // missing help + await sendImpactMetrics( + [ + // @ts-expect-error + { + name: 'labeled_counter', + type: 'counter', + samples: [ + { + labels: { foo: 'bar' }, + value: 10, + }, + ], + }, + ], + 400, + ); + const response = await app.request .get('/internal-backstage/impact/metrics') .expect('Content-Type', /text/) diff --git a/src/lib/features/metrics/instance/metrics.ts b/src/lib/features/metrics/instance/metrics.ts index 577eed59ac..8eabc96a82 100644 --- a/src/lib/features/metrics/instance/metrics.ts +++ b/src/lib/features/metrics/instance/metrics.ts @@ -58,7 +58,6 @@ export default class ClientMetricsController extends Controller { | 'customMetricsService' >, config: IUnleashConfig, - metricsTranslator: MetricsTranslator, ) { super(config); const { getLogger } = config; @@ -69,7 +68,6 @@ export default class ClientMetricsController extends Controller { this.metricsV2 = clientMetricsServiceV2; this.customMetricsService = customMetricsService; this.flagResolver = config.flagResolver; - this.metricsTranslator = metricsTranslator; this.route({ method: 'post', @@ -171,7 +169,7 @@ export default class ClientMetricsController extends Controller { this.flagResolver.isEnabled('impactMetrics') && impactMetrics ) { - this.metricsTranslator.translateMetrics(impactMetrics); + await this.metricsV2.registerImpactMetrics(impactMetrics); } res.getHeaderNames().forEach((header) => diff --git a/src/lib/features/metrics/shared/schema.ts b/src/lib/features/metrics/shared/schema.ts index 683e00a8b9..fa5d1ed586 100644 --- a/src/lib/features/metrics/shared/schema.ts +++ b/src/lib/features/metrics/shared/schema.ts @@ -85,6 +85,35 @@ export const customMetricsSchema = joi metrics: joi.array().items(customMetricSchema).required(), }); +export const metricSampleSchema = joi + .object() + .options({ stripUnknown: true }) + .keys({ + value: joi.number().required(), + labels: joi + .object() + .pattern( + joi.string(), + joi.alternatives().try(joi.string(), joi.number()), + ) + .optional(), + }); + +export const impactMetricSchema = joi + .object() + .options({ stripUnknown: true }) + .keys({ + name: joi.string().required(), + help: joi.string().required(), + type: joi.string().required(), + samples: joi.array().items(metricSampleSchema).required(), + }); + +export const impactMetricsSchema = joi + .array() + .items(impactMetricSchema) + .empty(); + export const batchMetricsSchema = joi .object() .options({ stripUnknown: true }) diff --git a/src/lib/routes/client-api/index.ts b/src/lib/routes/client-api/index.ts index cb7cf3cd0c..b58dc6824f 100644 --- a/src/lib/routes/client-api/index.ts +++ b/src/lib/routes/client-api/index.ts @@ -4,20 +4,13 @@ import MetricsController from '../../features/metrics/instance/metrics.js'; import RegisterController from '../../features/metrics/instance/register.js'; import type { IUnleashConfig } from '../../types/index.js'; import type { IUnleashServices } from '../../services/index.js'; -import { impactRegister } from '../../features/metrics/impact/impact-register.js'; -import { MetricsTranslator } from '../../features/metrics/impact/metrics-translator.js'; export default class ClientApi extends Controller { constructor(config: IUnleashConfig, services: IUnleashServices) { super(config); - const metricsTranslator = new MetricsTranslator(impactRegister); - this.use('/features', new FeatureController(services, config).router); - this.use( - '/metrics', - new MetricsController(services, config, metricsTranslator).router, - ); + this.use('/metrics', new MetricsController(services, config).router); this.use('/register', new RegisterController(services, config).router); } } From 406b985e5d8c2fc264b68c8d70a6d403817c321a Mon Sep 17 00:00:00 2001 From: Jaanus Sellin Date: Thu, 19 Jun 2025 19:09:49 +0300 Subject: [PATCH 29/41] feat: support id in search event (#10180) Now id will be parsed from query, it will be StringParam, as it is not coming from UI filters, its coming directly from query params. --- frontend/src/component/events/EventLog/useEventLogSearch.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/component/events/EventLog/useEventLogSearch.ts b/frontend/src/component/events/EventLog/useEventLogSearch.ts index b398c18335..86483c87b8 100644 --- a/frontend/src/component/events/EventLog/useEventLogSearch.ts +++ b/frontend/src/component/events/EventLog/useEventLogSearch.ts @@ -72,6 +72,7 @@ export const useEventLogSearch = ( createdBy: FilterItemParam, type: FilterItemParam, environment: FilterItemParam, + id: StringParam, ...extraParameters(logType), }; From a3d253ce526ff37aa863848762dcdcc9b3e0dd8e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 19:37:50 +0000 Subject: [PATCH 30/41] chore(deps): update dependency @uiw/codemirror-theme-duotone to v4.23.13 (#10184) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@uiw/codemirror-theme-duotone](https://uiwjs.github.io/react-codemirror/#/theme/data/duotone/light) ([source](https://redirect.github.com/uiwjs/react-codemirror)) | [`4.23.10` -> `4.23.13`](https://renovatebot.com/diffs/npm/@uiw%2fcodemirror-theme-duotone/4.23.10/4.23.13) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@uiw%2fcodemirror-theme-duotone/4.23.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@uiw%2fcodemirror-theme-duotone/4.23.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@uiw%2fcodemirror-theme-duotone/4.23.10/4.23.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@uiw%2fcodemirror-theme-duotone/4.23.10/4.23.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
uiwjs/react-codemirror (@​uiw/codemirror-theme-duotone) ### [`v4.23.13`](https://redirect.github.com/uiwjs/react-codemirror/releases/tag/v4.23.13) [Compare Source](https://redirect.github.com/uiwjs/react-codemirror/compare/v4.23.12...v4.23.13) [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@​uiw/react-codemirror@4.23.13/file/README.md) Documentation v4.23.13: https://raw.githack.com/uiwjs/react-codemirror/40401ed/index.html\ Comparing Changes: https://github.com/uiwjs/react-codemirror/compare/v4.23.12...v4.23.13 ```shell npm i @​uiw/react-codemirror@4.23.13 ``` - 🐞 fix(merge): Update react codemirror merge to work in shadow dom ([#​735](https://redirect.github.com/uiwjs/react-codemirror/issues/735)) [`154727c`](https://redirect.github.com/uiwjs/react-codemirror/commit/154727c) [@​boopeshmahendran227](https://redirect.github.com/boopeshmahendran227) - 🆎 type(merge): fix type error. [#​735](https://redirect.github.com/uiwjs/react-codemirror/issues/735) [`9e9776b`](https://redirect.github.com/uiwjs/react-codemirror/commit/9e9776b) [@​jaywcjlove](https://redirect.github.com/jaywcjlove) ### [`v4.23.12`](https://redirect.github.com/uiwjs/react-codemirror/releases/tag/v4.23.12) [Compare Source](https://redirect.github.com/uiwjs/react-codemirror/compare/v4.23.11...v4.23.12) [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@​uiw/react-codemirror@4.23.12/file/README.md) Documentation v4.23.12: https://raw.githack.com/uiwjs/react-codemirror/e780cd8/index.html\ Comparing Changes: https://github.com/uiwjs/react-codemirror/compare/v4.23.11...v4.23.12 ```shell npm i @​uiw/react-codemirror@4.23.12 ``` - 🐞 fix: Fix ESM import in core package by adding .babelrc and babel-plugin-add-import-extension ([#​732](https://redirect.github.com/uiwjs/react-codemirror/issues/732)) [`9a5ce28`](https://redirect.github.com/uiwjs/react-codemirror/commit/9a5ce28) [@​Shellishack](https://redirect.github.com/Shellishack) - 🐞 fix: resolve ESM import issue in the package. [#​732](https://redirect.github.com/uiwjs/react-codemirror/issues/732) [`8ce4ceb`](https://redirect.github.com/uiwjs/react-codemirror/commit/8ce4ceb) [@​jaywcjlove](https://redirect.github.com/jaywcjlove) ### [`v4.23.11`](https://redirect.github.com/uiwjs/react-codemirror/releases/tag/v4.23.11) [Compare Source](https://redirect.github.com/uiwjs/react-codemirror/compare/v4.23.10...v4.23.11) [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@​uiw/react-codemirror@4.23.11/file/README.md) Documentation v4.23.11: https://raw.githack.com/uiwjs/react-codemirror/4f9af7b/index.html\ Comparing Changes: https://github.com/uiwjs/react-codemirror/compare/v4.23.10...v4.23.11 ```shell npm i @​uiw/react-codemirror@4.23.11 ``` - 🌍 website: fix example issue. [#​717](https://redirect.github.com/uiwjs/react-codemirror/issues/717) [`d589c09`](https://redirect.github.com/uiwjs/react-codemirror/commit/d589c09) [@​jaywcjlove](https://redirect.github.com/jaywcjlove) - 📄 example: update nextjs example. [`3bf96a6`](https://redirect.github.com/uiwjs/react-codemirror/commit/3bf96a6) [@​jaywcjlove](https://redirect.github.com/jaywcjlove) - 🐞 fix: Set correct module type in basic package ([#​729](https://redirect.github.com/uiwjs/react-codemirror/issues/729)) [`d6ee7f8`](https://redirect.github.com/uiwjs/react-codemirror/commit/d6ee7f8) [@​danez](https://redirect.github.com/danez) - 🐞 fix(react-codemirror): Set correct module type in basic package ([#​729](https://redirect.github.com/uiwjs/react-codemirror/issues/729)) [`d1271a6`](https://redirect.github.com/uiwjs/react-codemirror/commit/d1271a6) [@​jaywcjlove](https://redirect.github.com/jaywcjlove)
--- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- frontend/package.json | 2 +- frontend/yarn.lock | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 612a71672a..27a9d9d368 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -66,7 +66,7 @@ "@types/react-test-renderer": "18.3.1", "@types/semver": "7.5.8", "@types/uuid": "^9.0.0", - "@uiw/codemirror-theme-duotone": "4.23.10", + "@uiw/codemirror-theme-duotone": "4.23.13", "@uiw/react-codemirror": "4.23.10", "@unleash/proxy-client-react": "^5.0.0", "@vitejs/plugin-react": "4.3.4", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 399687220e..9c13550e6c 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -3431,18 +3431,18 @@ __metadata: languageName: node linkType: hard -"@uiw/codemirror-theme-duotone@npm:4.23.10": - version: 4.23.10 - resolution: "@uiw/codemirror-theme-duotone@npm:4.23.10" +"@uiw/codemirror-theme-duotone@npm:4.23.13": + version: 4.23.13 + resolution: "@uiw/codemirror-theme-duotone@npm:4.23.13" dependencies: - "@uiw/codemirror-themes": "npm:4.23.10" - checksum: 10c0/058fe2ec927fb7d9fdd7a040c1fa52ed52b2b786e2917ae981a687b1aafeac26022900aac625c2018288c9bd7ab9e056854d6628eaaea46a4251b0022693df7c + "@uiw/codemirror-themes": "npm:4.23.13" + checksum: 10c0/3c0994d7731fdad1537d66f331b3bc1f4308e9388001b3a1d168c8e4b751f28661652533b0c830b3f30456132572686a298af0672f3f36872a08fdc1cf9b1e47 languageName: node linkType: hard -"@uiw/codemirror-themes@npm:4.23.10": - version: 4.23.10 - resolution: "@uiw/codemirror-themes@npm:4.23.10" +"@uiw/codemirror-themes@npm:4.23.13": + version: 4.23.13 + resolution: "@uiw/codemirror-themes@npm:4.23.13" dependencies: "@codemirror/language": "npm:^6.0.0" "@codemirror/state": "npm:^6.0.0" @@ -3451,7 +3451,7 @@ __metadata: "@codemirror/language": ">=6.0.0" "@codemirror/state": ">=6.0.0" "@codemirror/view": ">=6.0.0" - checksum: 10c0/29f980789a535ae021ca8d8bec3ecee4dda8cb11c5451729c04481310cc04cd7c00ddb3011137f5cc305d565bc0a3464ebd88fdde9359e296d8f0a6cf6477811 + checksum: 10c0/4e3bc3f12681727d41ae3c60c17fab5be267cb2675ace8ce6c56c66d6453715bf6d00c9b356d27df70f2104b0eaeda8c734faf2f05b38af05e7c5b2306f2a182 languageName: node linkType: hard @@ -10371,7 +10371,7 @@ __metadata: "@types/react-test-renderer": "npm:18.3.1" "@types/semver": "npm:7.5.8" "@types/uuid": "npm:^9.0.0" - "@uiw/codemirror-theme-duotone": "npm:4.23.10" + "@uiw/codemirror-theme-duotone": "npm:4.23.13" "@uiw/react-codemirror": "npm:4.23.10" "@unleash/proxy-client-react": "npm:^5.0.0" "@vitejs/plugin-react": "npm:4.3.4" From 3fe2fab47d364fdcadd75da321cdb27120e6c710 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:58:13 +0000 Subject: [PATCH 31/41] chore(deps): update dependency @uiw/react-codemirror to v4.23.13 (#10185) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@uiw/react-codemirror](https://uiwjs.github.io/react-codemirror) ([source](https://redirect.github.com/uiwjs/react-codemirror)) | [`4.23.10` -> `4.23.13`](https://renovatebot.com/diffs/npm/@uiw%2freact-codemirror/4.23.10/4.23.13) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@uiw%2freact-codemirror/4.23.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@uiw%2freact-codemirror/4.23.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@uiw%2freact-codemirror/4.23.10/4.23.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@uiw%2freact-codemirror/4.23.10/4.23.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
uiwjs/react-codemirror (@​uiw/react-codemirror) ### [`v4.23.13`](https://redirect.github.com/uiwjs/react-codemirror/releases/tag/v4.23.13) [Compare Source](https://redirect.github.com/uiwjs/react-codemirror/compare/v4.23.12...v4.23.13) [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@​uiw/react-codemirror@4.23.13/file/README.md) Documentation v4.23.13: https://raw.githack.com/uiwjs/react-codemirror/40401ed/index.html\ Comparing Changes: https://github.com/uiwjs/react-codemirror/compare/v4.23.12...v4.23.13 ```shell npm i @​uiw/react-codemirror@4.23.13 ``` - 🐞 fix(merge): Update react codemirror merge to work in shadow dom ([#​735](https://redirect.github.com/uiwjs/react-codemirror/issues/735)) [`154727c`](https://redirect.github.com/uiwjs/react-codemirror/commit/154727c) [@​boopeshmahendran227](https://redirect.github.com/boopeshmahendran227) - 🆎 type(merge): fix type error. [#​735](https://redirect.github.com/uiwjs/react-codemirror/issues/735) [`9e9776b`](https://redirect.github.com/uiwjs/react-codemirror/commit/9e9776b) [@​jaywcjlove](https://redirect.github.com/jaywcjlove) ### [`v4.23.12`](https://redirect.github.com/uiwjs/react-codemirror/releases/tag/v4.23.12) [Compare Source](https://redirect.github.com/uiwjs/react-codemirror/compare/v4.23.11...v4.23.12) [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@​uiw/react-codemirror@4.23.12/file/README.md) Documentation v4.23.12: https://raw.githack.com/uiwjs/react-codemirror/e780cd8/index.html\ Comparing Changes: https://github.com/uiwjs/react-codemirror/compare/v4.23.11...v4.23.12 ```shell npm i @​uiw/react-codemirror@4.23.12 ``` - 🐞 fix: Fix ESM import in core package by adding .babelrc and babel-plugin-add-import-extension ([#​732](https://redirect.github.com/uiwjs/react-codemirror/issues/732)) [`9a5ce28`](https://redirect.github.com/uiwjs/react-codemirror/commit/9a5ce28) [@​Shellishack](https://redirect.github.com/Shellishack) - 🐞 fix: resolve ESM import issue in the package. [#​732](https://redirect.github.com/uiwjs/react-codemirror/issues/732) [`8ce4ceb`](https://redirect.github.com/uiwjs/react-codemirror/commit/8ce4ceb) [@​jaywcjlove](https://redirect.github.com/jaywcjlove) ### [`v4.23.11`](https://redirect.github.com/uiwjs/react-codemirror/releases/tag/v4.23.11) [Compare Source](https://redirect.github.com/uiwjs/react-codemirror/compare/v4.23.10...v4.23.11) [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/@​uiw/react-codemirror@4.23.11/file/README.md) Documentation v4.23.11: https://raw.githack.com/uiwjs/react-codemirror/4f9af7b/index.html\ Comparing Changes: https://github.com/uiwjs/react-codemirror/compare/v4.23.10...v4.23.11 ```shell npm i @​uiw/react-codemirror@4.23.11 ``` - 🌍 website: fix example issue. [#​717](https://redirect.github.com/uiwjs/react-codemirror/issues/717) [`d589c09`](https://redirect.github.com/uiwjs/react-codemirror/commit/d589c09) [@​jaywcjlove](https://redirect.github.com/jaywcjlove) - 📄 example: update nextjs example. [`3bf96a6`](https://redirect.github.com/uiwjs/react-codemirror/commit/3bf96a6) [@​jaywcjlove](https://redirect.github.com/jaywcjlove) - 🐞 fix: Set correct module type in basic package ([#​729](https://redirect.github.com/uiwjs/react-codemirror/issues/729)) [`d6ee7f8`](https://redirect.github.com/uiwjs/react-codemirror/commit/d6ee7f8) [@​danez](https://redirect.github.com/danez) - 🐞 fix(react-codemirror): Set correct module type in basic package ([#​729](https://redirect.github.com/uiwjs/react-codemirror/issues/729)) [`d1271a6`](https://redirect.github.com/uiwjs/react-codemirror/commit/d1271a6) [@​jaywcjlove](https://redirect.github.com/jaywcjlove)
--- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- frontend/package.json | 2 +- frontend/yarn.lock | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 27a9d9d368..cd4eae0b45 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -67,7 +67,7 @@ "@types/semver": "7.5.8", "@types/uuid": "^9.0.0", "@uiw/codemirror-theme-duotone": "4.23.13", - "@uiw/react-codemirror": "4.23.10", + "@uiw/react-codemirror": "4.23.13", "@unleash/proxy-client-react": "^5.0.0", "@vitejs/plugin-react": "4.3.4", "cartesian": "^1.0.1", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 9c13550e6c..e5816f8325 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -3408,9 +3408,9 @@ __metadata: languageName: node linkType: hard -"@uiw/codemirror-extensions-basic-setup@npm:4.23.10": - version: 4.23.10 - resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.23.10" +"@uiw/codemirror-extensions-basic-setup@npm:4.23.13": + version: 4.23.13 + resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.23.13" dependencies: "@codemirror/autocomplete": "npm:^6.0.0" "@codemirror/commands": "npm:^6.0.0" @@ -3427,7 +3427,7 @@ __metadata: "@codemirror/search": ">=6.0.0" "@codemirror/state": ">=6.0.0" "@codemirror/view": ">=6.0.0" - checksum: 10c0/64c233857b1bf878bf630297b1d4b3df14e13761ff38ceccf7a1fa21d521be288311333b7cbff927889f9a899848f4fccefd975ea5fa8d626ea4aef088f13ee8 + checksum: 10c0/db0d1c60c8b13f69aa02b969618d3b9f5aafab23af3c8d9be9a88016aff94de1e45a1c850daf7740d265a3e5452a916112f73e5c22857346a7002c243c934159 languageName: node linkType: hard @@ -3455,15 +3455,15 @@ __metadata: languageName: node linkType: hard -"@uiw/react-codemirror@npm:4.23.10": - version: 4.23.10 - resolution: "@uiw/react-codemirror@npm:4.23.10" +"@uiw/react-codemirror@npm:4.23.13": + version: 4.23.13 + resolution: "@uiw/react-codemirror@npm:4.23.13" dependencies: "@babel/runtime": "npm:^7.18.6" "@codemirror/commands": "npm:^6.1.0" "@codemirror/state": "npm:^6.1.1" "@codemirror/theme-one-dark": "npm:^6.0.0" - "@uiw/codemirror-extensions-basic-setup": "npm:4.23.10" + "@uiw/codemirror-extensions-basic-setup": "npm:4.23.13" codemirror: "npm:^6.0.0" peerDependencies: "@babel/runtime": ">=7.11.0" @@ -3473,7 +3473,7 @@ __metadata: codemirror: ">=6.0.0" react: ">=16.8.0" react-dom: ">=16.8.0" - checksum: 10c0/cbcb928c84df2a6a03fd7789a3db317f6358961fd96197d2a3edb3ffbb00deff1cfa05fa1494c849393784bb89ae3f41425982f609f73bad0855a5e6522c328d + checksum: 10c0/95e79e50fa1a28cbfaf982def6ff82c5ecf18fe7eca077b4bc4cba1d43580541fcb1c38effbd299acff97f77372d8bc910642adc3d6e610c5125a9f0b85f379e languageName: node linkType: hard @@ -10372,7 +10372,7 @@ __metadata: "@types/semver": "npm:7.5.8" "@types/uuid": "npm:^9.0.0" "@uiw/codemirror-theme-duotone": "npm:4.23.13" - "@uiw/react-codemirror": "npm:4.23.10" + "@uiw/react-codemirror": "npm:4.23.13" "@unleash/proxy-client-react": "npm:^5.0.0" "@vitejs/plugin-react": "npm:4.3.4" cartesian: "npm:^1.0.1" From f9ca769179aa74ac88338fe936ceb609b0b4941a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 01:21:18 +0000 Subject: [PATCH 32/41] chore(deps): update dependency react-github-calendar to v4.5.7 (#10186) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [react-github-calendar](https://grubersjoe.github.io/react-github-calendar/) ([source](https://redirect.github.com/grubersjoe/react-github-calendar)) | [`4.5.6` -> `4.5.7`](https://renovatebot.com/diffs/npm/react-github-calendar/4.5.6/4.5.7) | [![age](https://developer.mend.io/api/mc/badges/age/npm/react-github-calendar/4.5.7?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/react-github-calendar/4.5.7?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/react-github-calendar/4.5.6/4.5.7?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/react-github-calendar/4.5.6/4.5.7?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
grubersjoe/react-github-calendar (react-github-calendar) ### [`v4.5.7`](https://redirect.github.com/grubersjoe/react-github-calendar/compare/5a4339780b936422deaa4ca37ebd78d377ab9854...cbc9989b034a92692390342e3807a9c46a6c2300) [Compare Source](https://redirect.github.com/grubersjoe/react-github-calendar/compare/5a4339780b936422deaa4ca37ebd78d377ab9854...cbc9989b034a92692390342e3807a9c46a6c2300)
--- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- frontend/yarn.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index e5816f8325..0a8ee6aac3 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -8548,14 +8548,14 @@ __metadata: languageName: node linkType: hard -"react-activity-calendar@npm:^2.7.8": - version: 2.7.8 - resolution: "react-activity-calendar@npm:2.7.8" +"react-activity-calendar@npm:^2.7.11": + version: 2.7.12 + resolution: "react-activity-calendar@npm:2.7.12" dependencies: date-fns: "npm:^4.1.0" peerDependencies: react: ^18.0.0 || ^19.0.0 - checksum: 10c0/e18d17cadc65480a8fa1df390598d7a4741a211b164091ca85fd2a21673fa667a5921f7b856e4ed92c2e8dedfb961426f163e517ea37336f2c3afb987740620d + checksum: 10c0/6e329c18d37e05c9f26efee3af229e9289744d9f6bac03a08b4911b62786a410d0f7cfe58ace1e52706da4adc250e82906a77ef6d2665e52d1e271edcdd8b60e languageName: node linkType: hard @@ -8630,14 +8630,14 @@ __metadata: languageName: node linkType: hard -"react-error-boundary@npm:^5.0.0": - version: 5.0.0 - resolution: "react-error-boundary@npm:5.0.0" +"react-error-boundary@npm:^6.0.0": + version: 6.0.0 + resolution: "react-error-boundary@npm:6.0.0" dependencies: "@babel/runtime": "npm:^7.12.5" peerDependencies: react: ">=16.13.1" - checksum: 10c0/38da5e7e81016a4750d3b090e3c740c2c1125c0bb9de14e1ab92ee3b5190d34517c199935302718a24aa35d3f89081412b3444edc23f63729bde2e862a2fbfec + checksum: 10c0/1914d600dee95a14f14af4afe9867b0d35c26c4f7826d23208800ba2a99728659029aad60a6ef95e13430b4d79c2c4c9b3585f50bf508450478760d2e4e732d8 languageName: node linkType: hard @@ -8665,14 +8665,14 @@ __metadata: linkType: hard "react-github-calendar@npm:^4.5.1": - version: 4.5.6 - resolution: "react-github-calendar@npm:4.5.6" + version: 4.5.7 + resolution: "react-github-calendar@npm:4.5.7" dependencies: - react-activity-calendar: "npm:^2.7.8" - react-error-boundary: "npm:^5.0.0" + react-activity-calendar: "npm:^2.7.11" + react-error-boundary: "npm:^6.0.0" peerDependencies: react: ^18.0.0 || ^19.0.0 - checksum: 10c0/74e995a528a3cf2a4ac4634415b614a0f6ad170f066d2d58142f15b815e8855de8b2ecf426ea054dd5d29143185dedc9a4ec7a8fa4dea4cbad6b7ef0f4411b5a + checksum: 10c0/a274776d8f6547d342573a42618196f436d4e555a5eb84b7b43fda0350a5174959ae74bc9d230c8cdca1b8982e4f586583200292126d4aa05cb1e3d350b579d4 languageName: node linkType: hard From 2ecb4b0a4ff8c45ae69fa3f2327ac15e435162ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivar=20Conradi=20=C3=98sthus?= Date: Fri, 20 Jun 2025 13:18:50 +0200 Subject: [PATCH 33/41] Chore/remove flag disable bulk toggle (#10183) Prompt used: ![image](https://github.com/user-attachments/assets/17d35ab4-cc2c-4f90-9bee-7fdc3550e1ec) LLM: Gemini 2.5 Pro (preview) @kwasniew do we still need this kill-switch? --- .../ProjectFeaturesBatchActions.tsx | 41 +++++++------------ frontend/src/interfaces/uiConfig.ts | 1 - .../feature-toggle-controller.ts | 10 ----- src/lib/types/experimental.ts | 5 --- 4 files changed, 14 insertions(+), 43 deletions(-) diff --git a/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ProjectFeaturesBatchActions.tsx b/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ProjectFeaturesBatchActions.tsx index 5f9bc5bbc8..75e58e01f8 100644 --- a/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ProjectFeaturesBatchActions.tsx +++ b/frontend/src/component/project/Project/ProjectFeatureToggles/ProjectFeaturesBatchActions/ProjectFeaturesBatchActions.tsx @@ -9,7 +9,6 @@ import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { BulkDisableDialog } from 'component/feature/FeatureToggleList/BulkDisableDialog'; import { BulkEnableDialog } from 'component/feature/FeatureToggleList/BulkEnableDialog'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; interface IProjectFeaturesBatchActionsProps { selectedIds: string[]; @@ -72,32 +71,20 @@ export const ProjectFeaturesBatchActions: FC< return ( <> - setShowBulkEnableDialog(true)} - > - Enable - - } - /> - setShowBulkDisableDialog(true)} - > - Disable - - } - /> + + service.bulkUpdateEnabled( projectId, @@ -950,11 +945,6 @@ export default class ProjectFeaturesController extends Controller { const { shouldActivateDisabledStrategies } = req.query; const { features } = req.body; - if (this.flagResolver.isEnabled('disableBulkToggle')) { - res.status(403).end(); - return; - } - await this.transactionalFeatureToggleService.transactional((service) => service.bulkUpdateEnabled( projectId, diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index cc3a22d432..5d2f714e73 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -17,7 +17,6 @@ export type IFlagKey = | 'migrationLock' | 'demo' | 'googleAuthEnabled' - | 'disableBulkToggle' | 'advancedPlayground' | 'filterInvalidClientMetrics' | 'disableMetrics' @@ -105,10 +104,6 @@ const flags: IFlags = { process.env.GOOGLE_AUTH_ENABLED, false, ), - disableBulkToggle: parseEnvVarBoolean( - process.env.DISABLE_BULK_TOGGLE, - false, - ), filterInvalidClientMetrics: parseEnvVarBoolean( process.env.FILTER_INVALID_CLIENT_METRICS, false, From b48be7b29c25a9d8cf8a2c41b95b081c24099c40 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:11:05 +0000 Subject: [PATCH 34/41] chore(deps): update dependency semver to v7.7.2 (#10188) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [semver](https://redirect.github.com/npm/node-semver) | [`7.7.1` -> `7.7.2`](https://renovatebot.com/diffs/npm/semver/7.7.1/7.7.2) | [![age](https://developer.mend.io/api/mc/badges/age/npm/semver/7.7.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/semver/7.7.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/semver/7.7.1/7.7.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/semver/7.7.1/7.7.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | | [@types/semver](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/semver) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/semver)) | [`7.5.8` -> `7.7.0`](https://renovatebot.com/diffs/npm/@types%2fsemver/7.5.8/7.7.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fsemver/7.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fsemver/7.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fsemver/7.5.8/7.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fsemver/7.5.8/7.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
npm/node-semver (semver) ### [`v7.7.2`](https://redirect.github.com/npm/node-semver/blob/HEAD/CHANGELOG.md#772-2025-05-12) [Compare Source](https://redirect.github.com/npm/node-semver/compare/v7.7.1...v7.7.2) ##### Bug Fixes - [`fcafb61`](https://redirect.github.com/npm/node-semver/commit/fcafb61ed566ff8ccf24818dd94b76738f037aa4) [#​780](https://redirect.github.com/npm/node-semver/pull/780) add missing `'use strict'` directives ([#​780](https://redirect.github.com/npm/node-semver/issues/780)) ([@​Fdawgs](https://redirect.github.com/Fdawgs)) - [`c99f336`](https://redirect.github.com/npm/node-semver/commit/c99f336fa3bdff465652f9041eab2127d2f52eb2) [#​781](https://redirect.github.com/npm/node-semver/pull/781) prerelease identifier starting with digits ([#​781](https://redirect.github.com/npm/node-semver/issues/781)) ([@​mbtools](https://redirect.github.com/mbtools)) ##### Chores - [`c760403`](https://redirect.github.com/npm/node-semver/commit/c760403b935d3ad35f83e9bbe5ebe1badef2fc71) [#​784](https://redirect.github.com/npm/node-semver/pull/784) template-oss-apply for workflow permissions ([#​784](https://redirect.github.com/npm/node-semver/issues/784)) ([@​wraithgar](https://redirect.github.com/wraithgar)) - [`2677f2a`](https://redirect.github.com/npm/node-semver/commit/2677f2a88334b0e728dbfe9ad9f5f57458437c87) [#​778](https://redirect.github.com/npm/node-semver/pull/778) bump [@​npmcli/template-oss](https://redirect.github.com/npmcli/template-oss) from 4.23.6 to 4.24.3 ([#​778](https://redirect.github.com/npm/node-semver/issues/778)) ([@​dependabot](https://redirect.github.com/dependabot)\[bot], [@​npm-cli-bot](https://redirect.github.com/npm-cli-bot))
--- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- frontend/package.json | 6 +++--- frontend/yarn.lock | 20 ++++++++++---------- package.json | 2 +- yarn.lock | 10 +++++----- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index cd4eae0b45..83d3c3165a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -64,7 +64,7 @@ "@types/react-router-dom": "5.3.3", "@types/react-table": "7.7.20", "@types/react-test-renderer": "18.3.1", - "@types/semver": "7.5.8", + "@types/semver": "7.7.0", "@types/uuid": "^9.0.0", "@uiw/codemirror-theme-duotone": "4.23.13", "@uiw/react-codemirror": "4.23.13", @@ -114,7 +114,7 @@ "react-table": "7.8.0", "react-test-renderer": "18.3.1", "sass": "1.85.1", - "semver": "7.7.1", + "semver": "7.7.2", "swr": "2.3.3", "tss-react": "4.9.15", "typescript": "5.8.3", @@ -134,7 +134,7 @@ "jsonpath-plus": "10.3.0", "json5": "^2.2.2", "vite": "5.4.19", - "semver": "7.7.1", + "semver": "7.7.2", "ws": "^8.18.0", "@types/react": "18.3.18" }, diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 0a8ee6aac3..a13931cd03 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -3313,10 +3313,10 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:7.5.8": - version: 7.5.8 - resolution: "@types/semver@npm:7.5.8" - checksum: 10c0/8663ff927234d1c5fcc04b33062cb2b9fcfbe0f5f351ed26c4d1e1581657deebd506b41ff7fdf89e787e3d33ce05854bc01686379b89e9c49b564c4cfa988efa +"@types/semver@npm:7.7.0": + version: 7.7.0 + resolution: "@types/semver@npm:7.7.0" + checksum: 10c0/6b5f65f647474338abbd6ee91a6bbab434662ddb8fe39464edcbcfc96484d388baad9eb506dff217b6fc1727a88894930eb1f308617161ac0f376fe06be4e1ee languageName: node linkType: hard @@ -9241,12 +9241,12 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.7.1": - version: 7.7.1 - resolution: "semver@npm:7.7.1" +"semver@npm:7.7.2": + version: 7.7.2 + resolution: "semver@npm:7.7.2" bin: semver: bin/semver.js - checksum: 10c0/fd603a6fb9c399c6054015433051bdbe7b99a940a8fb44b85c2b524c4004b023d7928d47cb22154f8d054ea7ee8597f586605e05b52047f048278e4ac56ae958 + checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea languageName: node linkType: hard @@ -10369,7 +10369,7 @@ __metadata: "@types/react-router-dom": "npm:5.3.3" "@types/react-table": "npm:7.7.20" "@types/react-test-renderer": "npm:18.3.1" - "@types/semver": "npm:7.5.8" + "@types/semver": "npm:7.7.0" "@types/uuid": "npm:^9.0.0" "@uiw/codemirror-theme-duotone": "npm:4.23.13" "@uiw/react-codemirror": "npm:4.23.13" @@ -10422,7 +10422,7 @@ __metadata: react-table: "npm:7.8.0" react-test-renderer: "npm:18.3.1" sass: "npm:1.85.1" - semver: "npm:7.7.1" + semver: "npm:7.7.2" swr: "npm:2.3.3" tss-react: "npm:4.9.15" typescript: "npm:5.8.3" diff --git a/package.json b/package.json index 7192f866c7..5c567e5bc9 100644 --- a/package.json +++ b/package.json @@ -156,7 +156,7 @@ "@types/nodemailer": "6.4.17", "@types/owasp-password-strength-test": "1.3.2", "@types/pg": "8.15.4", - "@types/semver": "7.5.8", + "@types/semver": "7.7.0", "@types/slug": "^5.0.8", "@types/stoppable": "1.1.3", "@types/supertest": "6.0.3", diff --git a/yarn.lock b/yarn.lock index d22625fd37..d2fbc1d86e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1636,10 +1636,10 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:7.5.8": - version: 7.5.8 - resolution: "@types/semver@npm:7.5.8" - checksum: 10c0/8663ff927234d1c5fcc04b33062cb2b9fcfbe0f5f351ed26c4d1e1581657deebd506b41ff7fdf89e787e3d33ce05854bc01686379b89e9c49b564c4cfa988efa +"@types/semver@npm:7.7.0": + version: 7.7.0 + resolution: "@types/semver@npm:7.7.0" + checksum: 10c0/6b5f65f647474338abbd6ee91a6bbab434662ddb8fe39464edcbcfc96484d388baad9eb506dff217b6fc1727a88894930eb1f308617161ac0f376fe06be4e1ee languageName: node linkType: hard @@ -7750,7 +7750,7 @@ __metadata: "@types/nodemailer": "npm:6.4.17" "@types/owasp-password-strength-test": "npm:1.3.2" "@types/pg": "npm:8.15.4" - "@types/semver": "npm:7.5.8" + "@types/semver": "npm:7.7.0" "@types/slug": "npm:^5.0.8" "@types/stoppable": "npm:1.1.3" "@types/supertest": "npm:6.0.3" From 818b9274bbceb0aa3c21efba7a91d96047d6409d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 00:38:35 +0000 Subject: [PATCH 35/41] chore(deps): update dependency semver to v7.7.2 (#10189) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [semver](https://redirect.github.com/npm/node-semver) | [`7.7.1` -> `7.7.2`](https://renovatebot.com/diffs/npm/semver/7.7.1/7.7.2) | [![age](https://developer.mend.io/api/mc/badges/age/npm/semver/7.7.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/semver/7.7.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/semver/7.7.1/7.7.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/semver/7.7.1/7.7.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes
npm/node-semver (semver) ### [`v7.7.2`](https://redirect.github.com/npm/node-semver/blob/HEAD/CHANGELOG.md#772-2025-05-12) [Compare Source](https://redirect.github.com/npm/node-semver/compare/v7.7.1...v7.7.2) ##### Bug Fixes - [`fcafb61`](https://redirect.github.com/npm/node-semver/commit/fcafb61ed566ff8ccf24818dd94b76738f037aa4) [#​780](https://redirect.github.com/npm/node-semver/pull/780) add missing `'use strict'` directives ([#​780](https://redirect.github.com/npm/node-semver/issues/780)) ([@​Fdawgs](https://redirect.github.com/Fdawgs)) - [`c99f336`](https://redirect.github.com/npm/node-semver/commit/c99f336fa3bdff465652f9041eab2127d2f52eb2) [#​781](https://redirect.github.com/npm/node-semver/pull/781) prerelease identifier starting with digits ([#​781](https://redirect.github.com/npm/node-semver/issues/781)) ([@​mbtools](https://redirect.github.com/mbtools)) ##### Chores - [`c760403`](https://redirect.github.com/npm/node-semver/commit/c760403b935d3ad35f83e9bbe5ebe1badef2fc71) [#​784](https://redirect.github.com/npm/node-semver/pull/784) template-oss-apply for workflow permissions ([#​784](https://redirect.github.com/npm/node-semver/issues/784)) ([@​wraithgar](https://redirect.github.com/wraithgar)) - [`2677f2a`](https://redirect.github.com/npm/node-semver/commit/2677f2a88334b0e728dbfe9ad9f5f57458437c87) [#​778](https://redirect.github.com/npm/node-semver/pull/778) bump [@​npmcli/template-oss](https://redirect.github.com/npmcli/template-oss) from 4.23.6 to 4.24.3 ([#​778](https://redirect.github.com/npm/node-semver/issues/778)) ([@​dependabot](https://redirect.github.com/dependabot)\[bot], [@​npm-cli-bot](https://redirect.github.com/npm-cli-bot))
--- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index d2fbc1d86e..60131018ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6721,11 +6721,11 @@ __metadata: linkType: hard "semver@npm:^7.6.2": - version: 7.7.1 - resolution: "semver@npm:7.7.1" + version: 7.7.2 + resolution: "semver@npm:7.7.2" bin: semver: bin/semver.js - checksum: 10c0/fd603a6fb9c399c6054015433051bdbe7b99a940a8fb44b85c2b524c4004b023d7928d47cb22154f8d054ea7ee8597f586605e05b52047f048278e4ac56ae958 + checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea languageName: node linkType: hard From 4f8124b653f06899bc60eefa204897979dfdfd8d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 11:47:59 +0200 Subject: [PATCH 36/41] chore(deps): update dependency @types/mustache to v4.2.6 (#10171) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@types/mustache](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/mustache) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/mustache)) | [`4.2.5` -> `4.2.6`](https://renovatebot.com/diffs/npm/@types%2fmustache/4.2.5/4.2.6) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fmustache/4.2.6?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@types%2fmustache/4.2.6?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@types%2fmustache/4.2.5/4.2.6?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fmustache/4.2.5/4.2.6?slim=true)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [x] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 60131018ba..d006c4decf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1555,9 +1555,9 @@ __metadata: linkType: hard "@types/mustache@npm:^4.2.5": - version: 4.2.5 - resolution: "@types/mustache@npm:4.2.5" - checksum: 10c0/624975c39068d47407eadb89628aaff5ef60f3b7a71eef92a254310896a4e90518a01dcf71d95779ab2c986034a6ca5403d22fea237c67ff87f2e2b3fb794ea6 + version: 4.2.6 + resolution: "@types/mustache@npm:4.2.6" + checksum: 10c0/f49a83b189e92c962e9b61094c80c979f115ea876bd746bdb9f725c38ab8981a6691e3d1d5cd008ade24b6e5040602970bc0bc60ade71772501d1df8adc7adad languageName: node linkType: hard From d58cb59e2b62e5e76ff0aeaa35bccd232147f40a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:10:39 +0000 Subject: [PATCH 37/41] chore(deps): update dpage/pgadmin4 docker tag to v9.4 (#10191) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Update | Change | |---|---|---| | dpage/pgadmin4 | minor | `9.3` -> `9.4` | --- ### Configuration 📅 **Schedule**: Branch creation - "after 7pm every weekday,before 5am every weekday" in timezone Europe/Madrid, Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Unleash/unleash). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- scripts/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml index 049f7d4cd1..54421c87a3 100644 --- a/scripts/docker-compose.yml +++ b/scripts/docker-compose.yml @@ -11,7 +11,7 @@ services: - 5432:5432 pgadmin: - image: dpage/pgadmin4:9.3 + image: dpage/pgadmin4:9.4 environment: PGADMIN_DEFAULT_EMAIL: 'admin@admin.com' PGADMIN_DEFAULT_PASSWORD: 'admin' From 6871f5210780e5493781c5d5c7a4ef55687a059c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 10:48:27 +0000 Subject: [PATCH 38/41] chore(deps): update actions/github-script action to v7 (#9040) --- .github/workflows/core-feature-alert.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/core-feature-alert.yml b/.github/workflows/core-feature-alert.yml index f7e9c2d62d..6de989ea33 100644 --- a/.github/workflows/core-feature-alert.yml +++ b/.github/workflows/core-feature-alert.yml @@ -19,7 +19,7 @@ jobs: run: echo "PR_CREATOR=${{ github.event.pull_request.user.login }}" >> $GITHUB_ENV - name: Post a notification about core feature changes if not already commented - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const prCreator = process.env.PR_CREATOR; @@ -45,7 +45,7 @@ jobs: console.log('Comment already exists, skipping.'); } - name: Add reviewers to the PR if they are not the creator - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const prCreator = process.env.PR_CREATOR; From 6aecc3f93e023d62b0d2e634e064ce710da8fe83 Mon Sep 17 00:00:00 2001 From: Christopher Kolstad Date: Mon, 23 Jun 2025 12:51:47 +0200 Subject: [PATCH 39/41] task: added notified at to change request requested approvals (#10196) Adding this should allow us to only notify users that haven't been notified before. Necessary because the change_added event does not include a preData that allowed us to diff requestedApprovers based on the event alone. Also added a index on this column, since we're going to be using it to filter. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ...20-cr-requested-approvals-add-notified-at-column.js | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/migrations/20250623100820-cr-requested-approvals-add-notified-at-column.js diff --git a/src/migrations/20250623100820-cr-requested-approvals-add-notified-at-column.js b/src/migrations/20250623100820-cr-requested-approvals-add-notified-at-column.js new file mode 100644 index 0000000000..46d44f701c --- /dev/null +++ b/src/migrations/20250623100820-cr-requested-approvals-add-notified-at-column.js @@ -0,0 +1,10 @@ +exports.up = function(db, cb) { + db.runSql(`ALTER TABLE change_request_requested_approvers ADD COLUMN notified_at TIMESTAMP WITH TIME ZONE; + CREATE INDEX IF NOT EXISTS cr_req_approvers_notified_at_idx ON change_request_requested_approvers(notified_at);`, cb); +}; + +exports.down = function(db, cb) { + db.runSql(` +DROP INDEX IF EXISTS cr_req_approvers_notified_at_idx; +ALTER TABLE change_request_requested_approvers DROP COLUMN notified_at;`, cb); +}; From daa6461d9b3943650fcda202ff2d2f20f21a20ee Mon Sep 17 00:00:00 2001 From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com> Date: Mon, 23 Jun 2025 13:25:25 +0200 Subject: [PATCH 40/41] chore: bulk metrics - remove a flag (#10190) --- src/lib/db/client-instance-store.ts | 29 ------------------- .../metrics/instance/instance-service.ts | 21 ++++---------- src/lib/types/experimental.ts | 5 ---- src/lib/types/stores/client-instance-store.ts | 5 ---- .../fixtures/fake-client-instance-store.ts | 4 --- 5 files changed, 6 insertions(+), 58 deletions(-) diff --git a/src/lib/db/client-instance-store.ts b/src/lib/db/client-instance-store.ts index bf61a076cb..516259153e 100644 --- a/src/lib/db/client-instance-store.ts +++ b/src/lib/db/client-instance-store.ts @@ -71,35 +71,6 @@ export default class ClientInstanceStore implements IClientInstanceStore { } } - /** - * @deprecated - * `bulkUpsert` is beeing used instead. remove with `lastSeenBulkQuery` flag - */ - async setLastSeen({ - appName, - instanceId, - environment, - clientIp, - }: INewClientInstance): Promise { - const stopTimer = this.metricTimer('setLastSeen'); - - await this.db(TABLE) - .insert({ - app_name: appName, - instance_id: instanceId, - environment, - last_seen: new Date(), - client_ip: clientIp, - }) - .onConflict(['app_name', 'instance_id', 'environment']) - .merge({ - last_seen: new Date(), - client_ip: clientIp, - }); - - stopTimer(); - } - async bulkUpsert(instances: INewClientInstance[]): Promise { const stopTimer = this.metricTimer('bulkUpsert'); diff --git a/src/lib/features/metrics/instance/instance-service.ts b/src/lib/features/metrics/instance/instance-service.ts index 82a8aea1a4..acc4ef4c53 100644 --- a/src/lib/features/metrics/instance/instance-service.ts +++ b/src/lib/features/metrics/instance/instance-service.ts @@ -101,21 +101,12 @@ export default class ClientInstanceService { ): Promise { const value = await clientMetricsSchema.validateAsync(data); - if (this.flagResolver.isEnabled('lastSeenBulkQuery')) { - this.seenClients[this.clientKey(value)] = { - appName: value.appName, - instanceId: value.instanceId, - environment: value.environment, - clientIp: clientIp, - }; - } else { - await this.clientInstanceStore.setLastSeen({ - appName: value.appName, - instanceId: value.instanceId, - environment: value.environment, - clientIp: clientIp, - }); - } + this.seenClients[this.clientKey(value)] = { + appName: value.appName, + instanceId: value.instanceId, + environment: value.environment, + clientIp: clientIp, + }; } public registerFrontendClient(data: IFrontendClientApp): void { diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 5d2f714e73..7bb2478619 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -56,7 +56,6 @@ export type IFlagKey = | 'edgeObservability' | 'registerFrontendClient' | 'reportUnknownFlags' - | 'lastSeenBulkQuery' | 'lifecycleMetrics' | 'customMetrics' | 'impactMetrics' @@ -267,10 +266,6 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_REPORT_UNKNOWN_FLAGS, false, ), - lastSeenBulkQuery: parseEnvVarBoolean( - process.env.UNLEASH_EXPERIMENTAL_LAST_SEEN_BULK_QUERY, - false, - ), lifecycleMetrics: parseEnvVarBoolean( process.env.UNLEASH_EXPERIMENTAL_LIFECYCLE_METRICS, false, diff --git a/src/lib/types/stores/client-instance-store.ts b/src/lib/types/stores/client-instance-store.ts index e835664cce..36695cd474 100644 --- a/src/lib/types/stores/client-instance-store.ts +++ b/src/lib/types/stores/client-instance-store.ts @@ -18,11 +18,6 @@ export interface IClientInstanceStore Pick > { bulkUpsert(instances: INewClientInstance[]): Promise; - /** - * @deprecated - * `bulkUpsert` is beeing used instead. remove with `lastSeenBulkQuery` flag - */ - setLastSeen(INewClientInstance): Promise; insert(details: INewClientInstance): Promise; getByAppName(appName: string): Promise; getRecentByAppNameAndEnvironment( diff --git a/src/test/fixtures/fake-client-instance-store.ts b/src/test/fixtures/fake-client-instance-store.ts index ffcce9dea4..4be697f1a2 100644 --- a/src/test/fixtures/fake-client-instance-store.ts +++ b/src/test/fixtures/fake-client-instance-store.ts @@ -28,10 +28,6 @@ export default class FakeClientInstanceStore implements IClientInstanceStore { ); } - setLastSeen(): Promise { - return Promise.resolve(); - } - async getBySdkName(sdkName: string): Promise { return this.instances.filter((instance) => instance.sdkVersion?.startsWith(sdkName), From b33765bc2b26bd756d3fcf31e485a9aebefc8d05 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Mon, 23 Jun 2025 13:37:33 +0200 Subject: [PATCH 41/41] feat: expand resolver interface with impact metrics (#10199) --- package.json | 2 +- src/lib/types/experimental.ts | 9 +++++++++ yarn.lock | 10 +++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 5c567e5bc9..c9798fc86a 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "ts-toolbelt": "^9.6.0", "type-is": "^1.6.18", "ulidx": "^2.4.1", - "unleash-client": "^6.6.0", + "unleash-client": "^6.7.0-beta.0", "uuid": "^9.0.0" }, "devDependencies": { diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 7bb2478619..aefdcb3d72 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -309,10 +309,19 @@ export interface IFlagResolver { isEnabled: (expName: IFlagKey, context?: IFlagContext) => boolean; getVariant: (expName: IFlagKey, context?: IFlagContext) => Variant; getStaticContext: () => IFlagContext; + impactMetrics?: IImpactMetricsResolver; } export interface IExternalFlagResolver { isEnabled: (flagName: IFlagKey, context?: IFlagContext) => boolean; getVariant: (flagName: IFlagKey, context?: IFlagContext) => Variant; getStaticContext: () => IFlagContext; + impactMetrics?: IImpactMetricsResolver; +} + +export interface IImpactMetricsResolver { + defineCounter(name: string, help: string); + defineGauge(name: string, help: string); + incrementCounter(name: string, value?: number, featureName?: string): void; + updateGauge(name: string, value: number, featureName?: string): void; } diff --git a/yarn.lock b/yarn.lock index d006c4decf..8e8fef902f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7706,9 +7706,9 @@ __metadata: languageName: node linkType: hard -"unleash-client@npm:^6.6.0": - version: 6.6.0 - resolution: "unleash-client@npm:6.6.0" +"unleash-client@npm:^6.7.0-beta.0": + version: 6.7.0-beta.0 + resolution: "unleash-client@npm:6.7.0-beta.0" dependencies: http-proxy-agent: "npm:^7.0.2" https-proxy-agent: "npm:^7.0.5" @@ -7718,7 +7718,7 @@ __metadata: murmurhash3js: "npm:^3.0.1" proxy-from-env: "npm:^1.1.0" semver: "npm:^7.6.2" - checksum: 10c0/d97805744874a0dd66af7304142c40e0c5eea1958b4a3ebbeb29fdbc0d4a1e9d8bde2f713ffa972d825cd80f11958f19d5bcf97503857a2692c542755ede01ff + checksum: 10c0/881db461777fe6dd6e5bca8f209a3a69312b0010317051cfc9d2f996db87072957f7acbe9d5ad6fecb17e79d68f3d1b1e89b5d45c2bbc1fd3e9183d96d5737a8 languageName: node linkType: hard @@ -7836,7 +7836,7 @@ __metadata: type-is: "npm:^1.6.18" typescript: "npm:5.8.3" ulidx: "npm:^2.4.1" - unleash-client: "npm:^6.6.0" + unleash-client: "npm:^6.7.0-beta.0" uuid: "npm:^9.0.0" vite-node: "npm:^3.1.3" vitest: "npm:^3.1.3"