chore: migrate three packages to MSW v2

Migrates test files in config-loader, events-node and kubernetes-react
packages from MSW 1.x to MSW 2.x API to fix compatibility issues with
Jest 30 and JSDOM v26.

Changes:
- Updated msw dependency from ^1.0.0 to ^2.0.0
- Changed imports from `rest` to `http` and `HttpResponse`
- Converted handler syntax from `res(ctx.*)` to `HttpResponse.*`
- Changed `toStrictEqual` to `toEqual` for response assertions

Affected packages:
- @backstage/config-loader
- @backstage/plugin-events-node
- @backstage/plugin-kubernetes-react

Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
Johan Persson
2025-12-02 15:52:05 +01:00
parent cd0b8a11a3
commit e70ff47db3
8 changed files with 182 additions and 214 deletions
+1 -1
View File
@@ -57,7 +57,7 @@
"@backstage/cli": "workspace:^",
"@types/json-schema-merge-allof": "^0.6.0",
"@types/minimist": "^1.2.5",
"msw": "^1.0.0",
"msw": "^2.0.0",
"zen-observable": "^0.10.0"
}
}
+9 -13
View File
@@ -17,7 +17,7 @@
import { AppConfig } from '@backstage/config';
import { loadConfig } from './loader';
import fs from 'fs-extra';
import { rest } from 'msw';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { createMockDirectory } from '@backstage/backend-test-utils';
import { createDeferred } from '@backstage/types';
@@ -74,32 +74,28 @@ describe('loadConfig', () => {
});
const server = setupServer();
const initialLoaderHandler = rest.get(
const initialLoaderHandler = http.get(
`https://some.domain.io/app-config.yaml`,
(_req, res, ctx) => {
return res(
ctx.body(
`app:
() => {
return new HttpResponse(
`app:
title: Remote Example App
sessionKey: 'abc123'
escaped: \$\${Escaped}
`,
),
);
},
);
const reloadHandler = rest.get(
const reloadHandler = http.get(
`https://some.domain.io/app-config.yaml`,
(_req, res, ctx) => {
return res(
ctx.body(
`app:
() => {
return new HttpResponse(
`app:
title: NEW ReMOTe ExaMPLe App
sessionKey: 'abc123'
escaped: \$\${Escaped}
`,
),
);
},
);
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { rest } from 'msw';
import { http, HttpResponse } from 'msw';
import { registerMswTestHooks } from '@backstage/backend-test-utils';
import { setupServer } from 'msw/node';
import { RemoteConfigSource } from './RemoteConfigSource';
@@ -26,15 +26,15 @@ describe('RemoteConfigSource', () => {
it('should load config from a remote URL', async () => {
worker.use(
rest.get('http://localhost/config.yaml', (_req, res, ctx) =>
res(
ctx.body(`
http.get(
'http://localhost/config.yaml',
() =>
new HttpResponse(`
app:
title: Example App
substituted: \${VALUE}
escaped: \$\${VALUE}
`),
),
),
);
@@ -61,9 +61,10 @@ app:
it('should load and parse config from a remote URL', async () => {
worker.use(
rest.get('http://localhost/config.json', (_req, res, ctx) =>
res(
ctx.body(
http.get(
'http://localhost/config.json',
() =>
new HttpResponse(
JSON.stringify({
app: {
title: 'Example App',
@@ -72,7 +73,6 @@ app:
},
}),
),
),
),
);
@@ -102,12 +102,12 @@ app:
let fetched = false;
worker.use(
rest.get('http://localhost/config.yaml', (_req, res, ctx) => {
http.get('http://localhost/config.yaml', () => {
if (!fetched) {
fetched = true;
return res(ctx.body('x: 1'));
return new HttpResponse('x: 1');
}
return res(ctx.body('x: 2'));
return new HttpResponse('x: 2');
}),
);
+1 -1
View File
@@ -65,7 +65,7 @@
"devDependencies": {
"@backstage/backend-test-utils": "workspace:^",
"@backstage/cli": "workspace:^",
"msw": "^1.0.0"
"msw": "^2.0.0"
},
"configSchema": "config.d.ts"
}
@@ -17,7 +17,7 @@
import { DefaultEventsService } from './DefaultEventsService';
import { EventParams } from './EventParams';
import { EVENTS_NOTIFY_TIMEOUT_HEADER } from './EventsService';
import { rest } from 'msw';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import {
mockServices,
@@ -129,18 +129,18 @@ describe('DefaultEventsService', () => {
});
mswServer.use(
rest.put(
http.put(
'http://localhost:0/api/events/bus/v1/subscriptions/a.tester',
(_req, res, ctx) => res(ctx.status(200)),
() => new HttpResponse(null, { status: 200 }),
),
rest.get(
http.get(
'http://localhost:0/api/events/bus/v1/subscriptions/a.tester/events',
(_req, res, ctx) =>
res(
ctx.status(200),
ctx.json({
() =>
HttpResponse.json(
{
events: [{ topic: 'test', payload: { foo: 'bar' } }],
}),
},
{ status: 200 },
),
),
);
@@ -180,38 +180,36 @@ describe('DefaultEventsService', () => {
});
mswServer.use(
rest.put(
http.put(
'http://localhost:0/api/events/bus/v1/subscriptions/a.tester',
(_req, res, ctx) => res(ctx.status(200)),
() => new HttpResponse(null, { status: 200 }),
),
// The first and third calls result in a blocking 202 that is resolved after 100ms
// The second and fourth calls result in a 200 with an event
// The fifth call blocks until the end of the test
// No more than 5 calls should be made
rest.get(
http.get(
'http://localhost:0/api/events/bus/v1/subscriptions/a.tester/events',
(_req, res, ctx) => {
() => {
callCount += 1;
if (callCount === 1 || callCount === 3) {
return res(
ctx.status(202),
ctx.body(
new ReadableStream({
start(controller) {
setTimeout(() => controller.close(), 100);
},
}),
),
return new HttpResponse(
new ReadableStream({
start(controller) {
setTimeout(() => controller.close(), 100);
},
}),
{ status: 202 },
);
} else if (callCount === 2 || callCount === 4) {
return res(
ctx.status(200),
ctx.json({
return HttpResponse.json(
{
events: [{ topic: 'test', payload: { callCount } }],
}),
},
{ status: 200 },
);
} else if (callCount === 5) {
return res(ctx.status(202), ctx.body(blockingStream));
return new HttpResponse(blockingStream, { status: 202 });
}
throw new Error(`events endpoint called too many times`);
},
@@ -268,45 +266,42 @@ describe('DefaultEventsService', () => {
});
mswServer.use(
rest.put(
http.put(
'http://localhost:0/api/events/bus/v1/subscriptions/a.tester',
(_req, res, ctx) => res(ctx.status(200)),
() => new HttpResponse(null, { status: 200 }),
),
// The first and third calls result in a blocking 202 that is resolved after 100ms
// The second and fourth calls result in a 200 with an event
// The fifth call blocks until the end of the test
// No more than 5 calls should be made
rest.get(
http.get(
'http://localhost:0/api/events/bus/v1/subscriptions/a.tester/events',
(_req, res, ctx) => {
() => {
callCount += 1;
if (callCount === 1 || callCount === 3) {
return res(
ctx.status(202),
ctx.body(
new ReadableStream({
start(controller) {
setTimeout(() => controller.close(), 100);
},
}),
),
return new HttpResponse(
new ReadableStream({
start(controller) {
setTimeout(() => controller.close(), 100);
},
}),
{ status: 202 },
);
} else if (callCount === 2 || callCount === 4) {
return res(
ctx.status(200),
ctx.json({
return HttpResponse.json(
{
events: [{ topic: 'test', payload: { callCount } }],
}),
},
{ status: 200 },
);
} else if (callCount === 5) {
// 5th call has a timeout header so polling should proceed to the next call
return res(
ctx.set(EVENTS_NOTIFY_TIMEOUT_HEADER, '100'),
ctx.status(202),
ctx.body(blockingStream),
);
return new HttpResponse(blockingStream, {
status: 202,
headers: { [EVENTS_NOTIFY_TIMEOUT_HEADER]: '100' },
});
} else if (callCount === 6) {
return res(ctx.status(202), ctx.body(blockingStream));
return new HttpResponse(blockingStream, { status: 202 });
}
throw new Error(`events endpoint called too many times`);
},
@@ -358,11 +353,11 @@ describe('DefaultEventsService', () => {
let calledApi = false;
mswServer.use(
rest.put(
http.put(
'http://localhost:0/api/events/bus/v1/subscriptions/a.tester',
(_req, res, ctx) => {
() => {
calledApi = true;
res(ctx.status(200));
return new HttpResponse(null, { status: 200 });
},
),
);
@@ -393,9 +388,9 @@ describe('DefaultEventsService', () => {
});
mswServer.use(
rest.put(
http.put(
'http://localhost:0/api/events/bus/v1/subscriptions/a.tester',
(_req, res, ctx) => res(ctx.status(404)),
() => new HttpResponse(null, { status: 404 }),
),
);
@@ -432,9 +427,9 @@ describe('DefaultEventsService', () => {
});
mswServer.use(
rest.put(
http.put(
'http://localhost:0/api/events/bus/v1/subscriptions/a.tester',
(_req, res, ctx) => res(ctx.status(404)),
() => new HttpResponse(null, { status: 404 }),
),
);
+1 -1
View File
@@ -84,7 +84,7 @@
"@testing-library/react": "^16.0.0",
"@types/react": "^18.0.0",
"jest-websocket-mock": "^2.5.0",
"msw": "^1.3.1",
"msw": "^2.0.0",
"react": "^18.0.2",
"react-dom": "^18.0.2",
"react-router-dom": "^6.3.0"
@@ -16,7 +16,7 @@
import { KubernetesAuthProvidersApi } from '../kubernetes-auth-provider';
import { KubernetesBackendClient } from './KubernetesBackendClient';
import { rest } from 'msw';
import { http, HttpResponse } from 'msw';
import { UrlPatternDiscovery } from '@backstage/core-app-api';
import { setupServer } from 'msw/node';
import { MockFetchApi, registerMswTestHooks } from '@backstage/test-utils';
@@ -89,23 +89,24 @@ describe('KubernetesBackendClient', () => {
it('hits the /clusters API', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.get('http://localhost:1234/api/kubernetes/clusters', (_, res, ctx) =>
res(ctx.json({ items: [{ name: 'cluster-a', authProvider: 'aws' }] })),
http.get('http://localhost:1234/api/kubernetes/clusters', () =>
HttpResponse.json({
items: [{ name: 'cluster-a', authProvider: 'aws' }],
}),
),
);
const clusters = await backendClient.getClusters();
expect(clusters).toStrictEqual([
{ name: 'cluster-a', authProvider: 'aws' },
]);
expect(clusters).toEqual([{ name: 'cluster-a', authProvider: 'aws' }]);
});
it('/clusters API throws a 404 Error', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.get('http://localhost:1234/api/kubernetes/clusters', (_, res, ctx) =>
res(ctx.status(404)),
http.get(
'http://localhost:1234/api/kubernetes/clusters',
() => new HttpResponse(null, { status: 404 }),
),
);
@@ -117,8 +118,9 @@ describe('KubernetesBackendClient', () => {
it('/clusters API throws a 500 Error', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.get('http://localhost:1234/api/kubernetes/clusters', (_, res, ctx) =>
res(ctx.status(500)),
http.get(
'http://localhost:1234/api/kubernetes/clusters',
() => new HttpResponse(null, { status: 500 }),
),
);
@@ -130,9 +132,9 @@ describe('KubernetesBackendClient', () => {
it('hits the /resources/custom/query API', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.post(
http.post(
'http://localhost:1234/api/kubernetes/resources/custom/query',
(_, res, ctx) => res(ctx.json(mockResponse)),
() => HttpResponse.json(mockResponse),
),
);
@@ -157,15 +159,15 @@ describe('KubernetesBackendClient', () => {
const customObject: ObjectsByEntityResponse =
await backendClient.getCustomObjectsByEntity(request);
expect(customObject).toStrictEqual(mockResponse);
expect(customObject).toEqual(mockResponse);
});
it('/resources/custom/query API throws a 404 error', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.post(
http.post(
'http://localhost:1234/api/kubernetes/resources/custom/query',
(_, res, ctx) => res(ctx.status(404)),
() => new HttpResponse(null, { status: 404 }),
),
);
@@ -197,9 +199,9 @@ describe('KubernetesBackendClient', () => {
it('/resources/custom/query API throws a 500 error', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.post(
http.post(
'http://localhost:1234/api/kubernetes/resources/custom/query',
(_, res, ctx) => res(ctx.status(500)),
() => new HttpResponse(null, { status: 500 }),
),
);
@@ -231,9 +233,8 @@ describe('KubernetesBackendClient', () => {
it('hits the /services/{entityName} API', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.post(
'http://localhost:1234/api/kubernetes/services/test-name',
(_, res, ctx) => res(ctx.json(mockResponse)),
http.post('http://localhost:1234/api/kubernetes/services/test-name', () =>
HttpResponse.json(mockResponse),
),
);
@@ -250,15 +251,15 @@ describe('KubernetesBackendClient', () => {
const entityObject: ObjectsByEntityResponse =
await backendClient.getObjectsByEntity(request);
expect(entityObject).toStrictEqual(mockResponse);
expect(entityObject).toEqual(mockResponse);
});
it('services/{entityName} API throws a 404 error', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.post(
http.post(
'http://localhost:1234/api/kubernetes/services/test-name',
(_, res, ctx) => res(ctx.status(404)),
() => new HttpResponse(null, { status: 404 }),
),
);
@@ -282,9 +283,9 @@ describe('KubernetesBackendClient', () => {
it('services/{entityName} API throws a 500 error', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.post(
http.post(
'http://localhost:1234/api/kubernetes/services/test-name',
(_, res, ctx) => res(ctx.status(500)),
() => new HttpResponse(null, { status: 500 }),
),
);
@@ -308,9 +309,9 @@ describe('KubernetesBackendClient', () => {
it('hits the /resources/workloads/query API', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.post(
http.post(
'http://localhost:1234/api/kubernetes/resources/workloads/query',
(_, res, ctx) => res(ctx.json(mockResponse)),
() => HttpResponse.json(mockResponse),
),
);
@@ -328,15 +329,15 @@ describe('KubernetesBackendClient', () => {
const response: ObjectsByEntityResponse =
await backendClient.getWorkloadsByEntity(request);
expect(response).toStrictEqual(mockResponse);
expect(response).toEqual(mockResponse);
});
it('/resources/workloads/query API throws a 404 error', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.post(
http.post(
'http://localhost:1234/api/kubernetes/resources/workloads/query',
(_, res, ctx) => res(ctx.status(404)),
() => new HttpResponse(null, { status: 404 }),
),
);
@@ -361,9 +362,9 @@ describe('KubernetesBackendClient', () => {
it('/resources/workloads/query API throws a 500 error', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.post(
http.post(
'http://localhost:1234/api/kubernetes/resources/workloads/query',
(_, res, ctx) => res(ctx.status(500)),
() => new HttpResponse(null, { status: 500 }),
),
);
@@ -388,12 +389,10 @@ describe('KubernetesBackendClient', () => {
describe('proxy', () => {
beforeEach(() => {
worker.use(
rest.get(
'http://localhost:1234/api/kubernetes/clusters',
(_, res, ctx) =>
res(
ctx.json({ items: [{ name: 'cluster-a', authProvider: 'aws' }] }),
),
http.get('http://localhost:1234/api/kubernetes/clusters', () =>
HttpResponse.json({
items: [{ name: 'cluster-a', authProvider: 'aws' }],
}),
),
);
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
@@ -401,20 +400,16 @@ describe('KubernetesBackendClient', () => {
it('hits the /proxy API with oidc as protocol and okta as auth provider', async () => {
worker.use(
rest.get(
'http://localhost:1234/api/kubernetes/clusters',
(_, res, ctx) =>
res(
ctx.json({
items: [
{
name: 'cluster-a',
authProvider: 'oidc',
oidcTokenProvider: 'okta',
},
],
}),
),
http.get('http://localhost:1234/api/kubernetes/clusters', () =>
HttpResponse.json({
items: [
{
name: 'cluster-a',
authProvider: 'oidc',
oidcTokenProvider: 'okta',
},
],
}),
),
);
kubernetesAuthProvidersApi.getCredentials.mockResolvedValue({
@@ -428,16 +423,14 @@ describe('KubernetesBackendClient', () => {
},
};
worker.use(
rest.get(
http.get(
'http://localhost:1234/api/kubernetes/proxy/api/v1/namespaces',
(req, res, ctx) =>
res(
req.headers.get(
'Backstage-Kubernetes-Authorization-oidc-okta',
) === 'k8-token3'
? ctx.json(nsResponse)
: ctx.status(403),
),
({ request }) =>
request.headers.get(
'Backstage-Kubernetes-Authorization-oidc-okta',
) === 'k8-token3'
? HttpResponse.json(nsResponse)
: new HttpResponse(null, { status: 403 }),
),
);
@@ -448,7 +441,7 @@ describe('KubernetesBackendClient', () => {
const response = await backendClient.proxy(request);
await expect(response.json()).resolves.toStrictEqual(nsResponse);
await expect(response.json()).resolves.toEqual(nsResponse);
expect(kubernetesAuthProvidersApi.getCredentials).toHaveBeenCalledWith(
'oidc.okta',
);
@@ -457,19 +450,15 @@ describe('KubernetesBackendClient', () => {
it('hits the /proxy API with serviceAccount as auth provider', async () => {
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
worker.use(
rest.get(
'http://localhost:1234/api/kubernetes/clusters',
(_, res, ctx) =>
res(
ctx.json({
items: [
{
name: 'cluster-a',
authProvider: 'serviceAccount',
},
],
}),
),
http.get('http://localhost:1234/api/kubernetes/clusters', () =>
HttpResponse.json({
items: [
{
name: 'cluster-a',
authProvider: 'serviceAccount',
},
],
}),
),
);
@@ -481,14 +470,12 @@ describe('KubernetesBackendClient', () => {
},
};
worker.use(
rest.get(
http.get(
'http://localhost:1234/api/kubernetes/proxy/api/v1/namespaces',
(req, res, ctx) =>
res(
req.headers.get('Authorization') === 'Bearer idToken'
? ctx.json(nsResponse)
: ctx.status(403),
),
({ request }) =>
request.headers.get('Authorization') === 'Bearer idToken'
? HttpResponse.json(nsResponse)
: new HttpResponse(null, { status: 403 }),
),
);
@@ -499,7 +486,7 @@ describe('KubernetesBackendClient', () => {
const response = await backendClient.proxy(request);
await expect(response.json()).resolves.toStrictEqual(nsResponse);
await expect(response.json()).resolves.toEqual(nsResponse);
expect(kubernetesAuthProvidersApi.getCredentials).toHaveBeenCalledWith(
'serviceAccount',
);
@@ -507,24 +494,20 @@ describe('KubernetesBackendClient', () => {
it('ignores oidcTokenProvider for non-oidc auth provider', async () => {
worker.use(
rest.get(
'http://localhost:1234/api/kubernetes/clusters',
(_, res, ctx) =>
res(
ctx.json({
items: [
{
name: 'cluster-a',
authProvider: 'not oidc',
oidcTokenProvider: 'should be ignored',
},
],
}),
),
http.get('http://localhost:1234/api/kubernetes/clusters', () =>
HttpResponse.json({
items: [
{
name: 'cluster-a',
authProvider: 'not oidc',
oidcTokenProvider: 'should be ignored',
},
],
}),
),
rest.get(
http.get(
'http://localhost:1234/api/kubernetes/proxy/api/v1/namespaces',
(_, res, ctx) => res(ctx.json([])),
() => HttpResponse.json([]),
),
);
@@ -552,15 +535,13 @@ describe('KubernetesBackendClient', () => {
},
};
worker.use(
rest.get(
http.get(
'http://localhost:1234/api/kubernetes/proxy/api/v1/namespaces',
(req, res, ctx) =>
res(
req.headers.get('Backstage-Kubernetes-Authorization-aws') ===
'k8-token'
? ctx.json(nsResponse)
: ctx.status(403),
),
({ request }) =>
request.headers.get('Backstage-Kubernetes-Authorization-aws') ===
'k8-token'
? HttpResponse.json(nsResponse)
: new HttpResponse(null, { status: 403 }),
),
);
@@ -570,7 +551,7 @@ describe('KubernetesBackendClient', () => {
};
const response = await backendClient.proxy(request);
await expect(response.json()).resolves.toStrictEqual(nsResponse);
await expect(response.json()).resolves.toEqual(nsResponse);
});
it('/proxy API throws a 404 error', async () => {
@@ -578,9 +559,9 @@ describe('KubernetesBackendClient', () => {
token: 'k8-token',
});
worker.use(
rest.get(
http.get(
'http://localhost:1234/api/kubernetes/proxy/api/v1/namespaces',
(_, res, ctx) => res(ctx.status(404)),
() => new HttpResponse(null, { status: 404 }),
),
);
@@ -616,15 +597,13 @@ describe('KubernetesBackendClient', () => {
},
};
worker.use(
rest.get(
http.get(
'http://localhost:1234/api/kubernetes/proxy/api/v1/namespaces',
(req, res, ctx) =>
res(
req.headers.get('Backstage-Kubernetes-Authorization') ===
'Bearer k8-token'
? ctx.json(nsResponse)
: ctx.status(403),
),
({ request }) =>
request.headers.get('Backstage-Kubernetes-Authorization') ===
'Bearer k8-token'
? HttpResponse.json(nsResponse)
: new HttpResponse(null, { status: 403 }),
),
);
@@ -646,14 +625,12 @@ describe('KubernetesBackendClient', () => {
},
};
worker.use(
rest.get(
http.get(
'http://localhost:1234/api/kubernetes/proxy/api/v1/namespaces/new-ns',
(req, res, ctx) =>
res(
req.headers.get('Backstage-Kubernetes-Authorization')
? ctx.status(403)
: ctx.json(nsResponse),
),
({ request }) =>
request.headers.get('Backstage-Kubernetes-Authorization')
? new HttpResponse(null, { status: 403 })
: HttpResponse.json(nsResponse),
),
);
kubernetesAuthProvidersApi.getCredentials.mockResolvedValue({});
@@ -663,7 +640,7 @@ describe('KubernetesBackendClient', () => {
path: '/api/v1/namespaces/new-ns',
});
await expect(response.json()).resolves.toStrictEqual(nsResponse);
await expect(response.json()).resolves.toEqual(nsResponse);
});
});
});
+3 -3
View File
@@ -3498,7 +3498,7 @@ __metadata:
json-schema-traverse: "npm:^1.0.0"
lodash: "npm:^4.17.21"
minimist: "npm:^1.2.5"
msw: "npm:^1.0.0"
msw: "npm:^2.0.0"
typescript-json-schema: "npm:^0.67.0"
yaml: "npm:^2.0.0"
zen-observable: "npm:^0.10.0"
@@ -5800,7 +5800,7 @@ __metadata:
content-type: "npm:^1.0.5"
cross-fetch: "npm:^4.0.0"
express: "npm:^4.22.0"
msw: "npm:^1.0.0"
msw: "npm:^2.0.0"
uri-template: "npm:^2.0.0"
languageName: unknown
linkType: soft
@@ -6048,7 +6048,7 @@ __metadata:
kubernetes-models: "npm:^4.3.1"
lodash: "npm:^4.17.21"
luxon: "npm:^3.0.0"
msw: "npm:^1.3.1"
msw: "npm:^2.0.0"
react: "npm:^18.0.2"
react-dom: "npm:^18.0.2"
react-router-dom: "npm:^6.3.0"