mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-18 00:19:49 +01:00
Merge branch 'main' into melinda/upgrade-open-api-plugin
This commit is contained in:
commit
74b13374d7
@ -12,6 +12,7 @@ import { ADMIN } from '@server/types/permissions';
|
||||
import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature';
|
||||
import { useState } from 'react';
|
||||
import { TabPanel } from 'component/common/TabNav/TabPanel/TabPanel';
|
||||
import { usePageTitle } from 'hooks/usePageTitle';
|
||||
|
||||
export const AuthSettings = () => {
|
||||
const { authenticationType } = useUiConfig().uiConfig;
|
||||
@ -46,6 +47,7 @@ export const AuthSettings = () => {
|
||||
}
|
||||
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
usePageTitle(`Single sign-on: ${tabs[activeTab].label}`);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -14,14 +14,14 @@ describe('trim names and description', () => {
|
||||
expect(result.current.name).toBe('my role');
|
||||
});
|
||||
|
||||
test('description is trimmed before being set', () => {
|
||||
test('description is not trimmed before being set', () => {
|
||||
const { result } = renderHook(() => useRoleForm());
|
||||
|
||||
act(() => {
|
||||
result.current.setDescription(' my description ');
|
||||
});
|
||||
|
||||
expect(result.current.description).toBe('my description');
|
||||
expect(result.current.description).toBe(' my description ');
|
||||
});
|
||||
|
||||
test('name that is just whitespace triggers an error', () => {
|
||||
|
@ -29,8 +29,6 @@ export const useRoleForm = (
|
||||
const [name, setName] = useState(initialName);
|
||||
const setTrimmedName = (newName: string) => setName(newName.trim());
|
||||
const [description, setDescription] = useState(initialDescription);
|
||||
const setTrimmedDescription = (newDescription: string) =>
|
||||
setDescription(newDescription.trim());
|
||||
const [checkedPermissions, setCheckedPermissions] =
|
||||
useState<ICheckedPermissions>({});
|
||||
const [errors, setErrors] = useState<IRoleFormErrors>(DEFAULT_ERRORS);
|
||||
@ -147,7 +145,7 @@ export const useRoleForm = (
|
||||
setName: setTrimmedName,
|
||||
validateName,
|
||||
description,
|
||||
setDescription: setTrimmedDescription,
|
||||
setDescription,
|
||||
validateDescription,
|
||||
checkedPermissions,
|
||||
setCheckedPermissions,
|
||||
|
@ -14,6 +14,7 @@ import Add from '@mui/icons-material/Add';
|
||||
import ResponsiveButton from 'component/common/ResponsiveButton/ResponsiveButton';
|
||||
import type { IRole } from 'interfaces/role';
|
||||
import { TabLink } from 'component/common/TabNav/TabLink';
|
||||
import { usePageTitle } from 'hooks/usePageTitle';
|
||||
|
||||
const StyledHeader = styled('div')(() => ({
|
||||
display: 'flex',
|
||||
@ -31,6 +32,7 @@ const StyledActions = styled('div')({
|
||||
});
|
||||
|
||||
export const RolesPage = () => {
|
||||
usePageTitle('Roles');
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const { roles, projectRoles, loading } = useRoles();
|
||||
|
@ -5,7 +5,7 @@ import { sortTypes } from 'utils/sortTypes';
|
||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import useFeatureTypes from 'hooks/api/getters/useFeatureTypes/useFeatureTypes';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { Box } from '@mui/material';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@ -150,18 +150,7 @@ export const FeatureTypesList = () => {
|
||||
return (
|
||||
<PageContent
|
||||
isLoading={loading}
|
||||
header={
|
||||
<PageHeader>
|
||||
<Typography
|
||||
component='h2'
|
||||
sx={(theme) => ({
|
||||
fontSize: theme.fontSizes.mainHeader,
|
||||
})}
|
||||
>
|
||||
Feature flag types
|
||||
</Typography>
|
||||
</PageHeader>
|
||||
}
|
||||
header={<PageHeader title='Feature flag types' />}
|
||||
>
|
||||
<Table {...getTableProps()}>
|
||||
<SortableTableHeader headerGroups={headerGroups} />
|
||||
|
@ -69,6 +69,7 @@ export const InsightsHeader: VFC<DashboardHeaderProps> = ({ actions }) => {
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
title='Insights'
|
||||
titleElement={
|
||||
<Typography
|
||||
variant='h1'
|
||||
|
@ -27,6 +27,8 @@ import BillingIcon from '@mui/icons-material/CreditCardOutlined';
|
||||
import EventLogIcon from '@mui/icons-material/EventNoteOutlined';
|
||||
import GitHubIcon from '@mui/icons-material/GitHub';
|
||||
import LibraryBooksIcon from '@mui/icons-material/LibraryBooks';
|
||||
import LaunchIcon from '@mui/icons-material/Launch';
|
||||
import PersonalDashboardIcon from '@mui/icons-material/DashboardOutlined';
|
||||
import type { FC } from 'react';
|
||||
|
||||
// TODO: move to routes
|
||||
@ -56,6 +58,8 @@ const icons: Record<string, typeof SvgIcon> = {
|
||||
'/admin/cors': CorsIcon,
|
||||
'/admin/billing': BillingIcon,
|
||||
'/history': EventLogIcon,
|
||||
'/releases-management': LaunchIcon,
|
||||
'/personal': PersonalDashboardIcon,
|
||||
GitHub: GitHubIcon,
|
||||
Documentation: LibraryBooksIcon,
|
||||
};
|
||||
|
@ -230,6 +230,20 @@ exports[`returns all baseRoutes 1`] = `
|
||||
"title": "Strategy types",
|
||||
"type": "protected",
|
||||
},
|
||||
{
|
||||
"component": [Function],
|
||||
"enterprise": true,
|
||||
"flag": "releasePlans",
|
||||
"menu": {
|
||||
"advanced": true,
|
||||
"mode": [
|
||||
"enterprise",
|
||||
],
|
||||
},
|
||||
"path": "/releases-management",
|
||||
"title": "Release management",
|
||||
"type": "protected",
|
||||
},
|
||||
{
|
||||
"component": [Function],
|
||||
"menu": {},
|
||||
|
@ -48,6 +48,7 @@ import { Application } from 'component/application/Application';
|
||||
import { Signals } from 'component/signals/Signals';
|
||||
import { LazyCreateProject } from '../project/Project/CreateProject/LazyCreateProject';
|
||||
import { PersonalDashboard } from '../personalDashboard/PersonalDashboard';
|
||||
import { ReleaseManagement } from 'component/releases/ReleaseManagement';
|
||||
|
||||
export const routes: IRoute[] = [
|
||||
// Splash
|
||||
@ -246,6 +247,15 @@ export const routes: IRoute[] = [
|
||||
type: 'protected',
|
||||
menu: { mobile: true, advanced: true },
|
||||
},
|
||||
{
|
||||
path: '/releases-management',
|
||||
title: 'Release management',
|
||||
component: ReleaseManagement,
|
||||
type: 'protected',
|
||||
menu: { advanced: true, mode: ['enterprise'] },
|
||||
flag: 'releasePlans',
|
||||
enterprise: true,
|
||||
},
|
||||
{
|
||||
path: '/environments/create',
|
||||
title: 'Environments',
|
||||
|
@ -19,6 +19,7 @@ import useSplashApi from 'hooks/api/actions/useSplashApi/useSplashApi';
|
||||
import { useAuthSplash } from 'hooks/api/getters/useAuth/useAuthSplash';
|
||||
import { useDashboardState } from './useDashboardState';
|
||||
import { MyFlags } from './MyFlags';
|
||||
import { usePageTitle } from 'hooks/usePageTitle';
|
||||
|
||||
const WelcomeSection = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@ -103,9 +104,10 @@ export const PersonalDashboard = () => {
|
||||
const { trackEvent } = usePlausibleTracker();
|
||||
const { setSplashSeen } = useSplashApi();
|
||||
const { splash } = useAuthSplash();
|
||||
|
||||
const name = user?.name;
|
||||
|
||||
usePageTitle(`Dashboard: ${name}`);
|
||||
|
||||
const { personalDashboard, refetch: refetchDashboard } =
|
||||
usePersonalDashboard();
|
||||
|
||||
|
3
frontend/src/component/releases/ReleaseManagement.tsx
Normal file
3
frontend/src/component/releases/ReleaseManagement.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export const ReleaseManagement = () => {
|
||||
return null;
|
||||
};
|
@ -397,9 +397,10 @@ export const StrategiesList = () => {
|
||||
<PageContent
|
||||
isLoading={loading}
|
||||
header={
|
||||
<PageHeader>
|
||||
<PredefinedStrategyTitle />
|
||||
</PageHeader>
|
||||
<PageHeader
|
||||
titleElement={<PredefinedStrategyTitle />}
|
||||
title='Strategy types'
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box>
|
||||
|
@ -92,6 +92,7 @@ export type UiFlags = {
|
||||
personalDashboardUI?: boolean;
|
||||
purchaseAdditionalEnvironments?: boolean;
|
||||
unleashAI?: boolean;
|
||||
releasePlans?: boolean;
|
||||
};
|
||||
|
||||
export interface IVersionInfo {
|
||||
|
@ -2743,20 +2743,20 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:*":
|
||||
version: 22.5.5
|
||||
resolution: "@types/node@npm:22.5.5"
|
||||
version: 22.7.6
|
||||
resolution: "@types/node@npm:22.7.6"
|
||||
dependencies:
|
||||
undici-types: "npm:~6.19.2"
|
||||
checksum: 10c0/ead9495cfc6b1da5e7025856dcce2591e9bae635357410c0d2dd619fce797d2a1d402887580ca4b336cb78168b195224869967de370a23f61663cf1e4836121c
|
||||
checksum: 10c0/d4406a63afce981c363fb1d1954aaf1759ad2d487c0833ebf667565ea4e45ff217d6fab4b5343badbdeccdf9d2e4a0841d633e0c929ceabcb33c288663dd0c73
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:^20.12.12, @types/node@npm:^20.12.13":
|
||||
version: 20.16.5
|
||||
resolution: "@types/node@npm:20.16.5"
|
||||
version: 20.16.12
|
||||
resolution: "@types/node@npm:20.16.12"
|
||||
dependencies:
|
||||
undici-types: "npm:~6.19.2"
|
||||
checksum: 10c0/6af7994129815010bcbc4cf8221865559c8116ff43e74a6549525c2108267596fc2d18aff5d5ecfe089fb60a119f975631343e2c65c52bfa0955ed9dc56733d6
|
||||
checksum: 10c0/f6a3c90c6745881d47f8dae7eb39d0dd6df9a4060c8f1ab7004690f60b9b183d862c9c6b380b9e8d5a17dd670ac7b19d6318f24c170897c85a9729f9804f47cf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -173,10 +173,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@apidevtools/swagger-parser": "10.1.0",
|
||||
"@babel/core": "7.25.2",
|
||||
"@babel/core": "7.25.8",
|
||||
"@biomejs/biome": "^1.8.3",
|
||||
"@cyclonedx/yarn-plugin-cyclonedx": "^1.0.0-rc.7",
|
||||
"@swc/core": "1.7.26",
|
||||
"@swc/core": "1.7.35",
|
||||
"@swc/jest": "0.2.36",
|
||||
"@types/bcryptjs": "2.4.6",
|
||||
"@types/cors": "2.8.17",
|
||||
|
@ -4,11 +4,18 @@ import createStores from '../../../test/fixtures/store';
|
||||
import VersionService from '../../services/version-service';
|
||||
import { createFakeGetActiveUsers } from './getActiveUsers';
|
||||
import { createFakeGetProductionChanges } from './getProductionChanges';
|
||||
|
||||
import { registerPrometheusMetrics } from '../../metrics';
|
||||
import { register } from 'prom-client';
|
||||
import type { IClientInstanceStore } from '../../types';
|
||||
let instanceStatsService: InstanceStatsService;
|
||||
let versionService: VersionService;
|
||||
|
||||
let clientInstanceStore: IClientInstanceStore;
|
||||
let updateMetrics: () => Promise<void>;
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
register.clear();
|
||||
|
||||
const config = createTestConfig();
|
||||
const stores = createStores();
|
||||
versionService = new VersionService(
|
||||
@ -17,6 +24,7 @@ beforeEach(() => {
|
||||
createFakeGetActiveUsers(),
|
||||
createFakeGetProductionChanges(),
|
||||
);
|
||||
clientInstanceStore = stores.clientInstanceStore;
|
||||
instanceStatsService = new InstanceStatsService(
|
||||
stores,
|
||||
config,
|
||||
@ -25,23 +33,28 @@ beforeEach(() => {
|
||||
createFakeGetProductionChanges(),
|
||||
);
|
||||
|
||||
jest.spyOn(instanceStatsService, 'refreshAppCountSnapshot');
|
||||
jest.spyOn(instanceStatsService, 'getLabeledAppCounts');
|
||||
const { collectDbMetrics } = registerPrometheusMetrics(
|
||||
config,
|
||||
stores,
|
||||
undefined as unknown as string,
|
||||
config.eventBus,
|
||||
instanceStatsService,
|
||||
);
|
||||
updateMetrics = collectDbMetrics;
|
||||
|
||||
jest.spyOn(clientInstanceStore, 'getDistinctApplicationsCount');
|
||||
jest.spyOn(instanceStatsService, 'getStats');
|
||||
|
||||
// validate initial state without calls to these methods
|
||||
expect(instanceStatsService.refreshAppCountSnapshot).toHaveBeenCalledTimes(
|
||||
0,
|
||||
);
|
||||
expect(instanceStatsService.getStats).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
test('get snapshot should not call getStats', async () => {
|
||||
await instanceStatsService.refreshAppCountSnapshot();
|
||||
expect(instanceStatsService.getLabeledAppCounts).toHaveBeenCalledTimes(1);
|
||||
await updateMetrics();
|
||||
expect(
|
||||
clientInstanceStore.getDistinctApplicationsCount,
|
||||
).toHaveBeenCalledTimes(3);
|
||||
expect(instanceStatsService.getStats).toHaveBeenCalledTimes(0);
|
||||
|
||||
// subsequent calls to getStatsSnapshot don't call getStats
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const { clientApps } = await instanceStatsService.getStats();
|
||||
expect(clientApps).toStrictEqual([
|
||||
@ -51,12 +64,11 @@ test('get snapshot should not call getStats', async () => {
|
||||
]);
|
||||
}
|
||||
// after querying the stats snapshot no call to getStats should be issued
|
||||
expect(instanceStatsService.getLabeledAppCounts).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
clientInstanceStore.getDistinctApplicationsCount,
|
||||
).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
test('before the snapshot is refreshed we can still get the appCount', async () => {
|
||||
expect(instanceStatsService.refreshAppCountSnapshot).toHaveBeenCalledTimes(
|
||||
0,
|
||||
);
|
||||
expect(instanceStatsService.getAppCountSnapshot('7d')).toBeUndefined();
|
||||
});
|
||||
|
@ -109,9 +109,9 @@ export class InstanceStatsService {
|
||||
|
||||
private appCount?: Partial<{ [key in TimeRange]: number }>;
|
||||
|
||||
private getActiveUsers: GetActiveUsers;
|
||||
getActiveUsers: GetActiveUsers;
|
||||
|
||||
private getProductionChanges: GetProductionChanges;
|
||||
getProductionChanges: GetProductionChanges;
|
||||
|
||||
private featureStrategiesReadModel: IFeatureStrategiesReadModel;
|
||||
|
||||
@ -180,25 +180,6 @@ export class InstanceStatsService {
|
||||
this.featureStrategiesReadModel = featureStrategiesReadModel;
|
||||
}
|
||||
|
||||
async refreshAppCountSnapshot(): Promise<
|
||||
Partial<{ [key in TimeRange]: number }>
|
||||
> {
|
||||
try {
|
||||
this.appCount = await this.getLabeledAppCounts();
|
||||
return this.appCount;
|
||||
} catch (error) {
|
||||
this.logger.warn(
|
||||
'Unable to retrieve statistics. This will be retried',
|
||||
error,
|
||||
);
|
||||
return {
|
||||
'7d': 0,
|
||||
'30d': 0,
|
||||
allTime: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
getProjectModeCount(): Promise<ProjectModeCount[]> {
|
||||
return this.projectStore.getProjectModeCounts();
|
||||
}
|
||||
@ -231,9 +212,6 @@ export class InstanceStatsService {
|
||||
return settings?.enabled || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* use getStatsSnapshot for low latency, sacrificing data-freshness
|
||||
*/
|
||||
async getStats(): Promise<InstanceStats> {
|
||||
const versionInfo = await this.versionService.getVersionInfo();
|
||||
const [
|
||||
@ -265,22 +243,22 @@ export class InstanceStatsService {
|
||||
] = await Promise.all([
|
||||
this.getToggleCount(),
|
||||
this.getArchivedToggleCount(),
|
||||
this.userStore.count(),
|
||||
this.userStore.countServiceAccounts(),
|
||||
this.apiTokenStore.countByType(),
|
||||
this.getRegisteredUsers(),
|
||||
this.countServiceAccounts(),
|
||||
this.countApiTokensByType(),
|
||||
this.getActiveUsers(),
|
||||
this.getProjectModeCount(),
|
||||
this.contextFieldStore.count(),
|
||||
this.groupStore.count(),
|
||||
this.roleStore.count(),
|
||||
this.roleStore.filteredCount({ type: CUSTOM_ROOT_ROLE_TYPE }),
|
||||
this.roleStore.filteredCountInUse({ type: CUSTOM_ROOT_ROLE_TYPE }),
|
||||
this.environmentStore.count(),
|
||||
this.segmentStore.count(),
|
||||
this.strategyStore.count(),
|
||||
this.contextFieldCount(),
|
||||
this.groupCount(),
|
||||
this.roleCount(),
|
||||
this.customRolesCount(),
|
||||
this.customRolesCountInUse(),
|
||||
this.environmentCount(),
|
||||
this.segmentCount(),
|
||||
this.strategiesCount(),
|
||||
this.hasSAML(),
|
||||
this.hasOIDC(),
|
||||
this.appCount ? this.appCount : this.refreshAppCountSnapshot(),
|
||||
this.appCount ? this.appCount : this.getLabeledAppCounts(),
|
||||
this.eventStore.deprecatedFilteredCount({
|
||||
type: FEATURES_EXPORTED,
|
||||
}),
|
||||
@ -288,7 +266,7 @@ export class InstanceStatsService {
|
||||
type: FEATURES_IMPORTED,
|
||||
}),
|
||||
this.getProductionChanges(),
|
||||
this.clientMetricsStore.countPreviousDayHourlyMetricsBuckets(),
|
||||
this.countPreviousDayHourlyMetricsBuckets(),
|
||||
this.featureStrategiesReadModel.getMaxFeatureEnvironmentStrategies(),
|
||||
this.featureStrategiesReadModel.getMaxConstraintValues(),
|
||||
this.featureStrategiesReadModel.getMaxConstraintsPerStrategy(),
|
||||
@ -330,6 +308,59 @@ export class InstanceStatsService {
|
||||
};
|
||||
}
|
||||
|
||||
groupCount(): Promise<number> {
|
||||
return this.groupStore.count();
|
||||
}
|
||||
|
||||
roleCount(): Promise<number> {
|
||||
return this.roleStore.count();
|
||||
}
|
||||
|
||||
customRolesCount(): Promise<number> {
|
||||
return this.roleStore.filteredCount({ type: CUSTOM_ROOT_ROLE_TYPE });
|
||||
}
|
||||
|
||||
customRolesCountInUse(): Promise<number> {
|
||||
return this.roleStore.filteredCountInUse({
|
||||
type: CUSTOM_ROOT_ROLE_TYPE,
|
||||
});
|
||||
}
|
||||
|
||||
segmentCount(): Promise<number> {
|
||||
return this.segmentStore.count();
|
||||
}
|
||||
|
||||
contextFieldCount(): Promise<number> {
|
||||
return this.contextFieldStore.count();
|
||||
}
|
||||
|
||||
strategiesCount(): Promise<number> {
|
||||
return this.strategyStore.count();
|
||||
}
|
||||
|
||||
environmentCount(): Promise<number> {
|
||||
return this.environmentStore.count();
|
||||
}
|
||||
|
||||
countPreviousDayHourlyMetricsBuckets(): Promise<{
|
||||
enabledCount: number;
|
||||
variantCount: number;
|
||||
}> {
|
||||
return this.clientMetricsStore.countPreviousDayHourlyMetricsBuckets();
|
||||
}
|
||||
|
||||
countApiTokensByType(): Promise<Map<string, number>> {
|
||||
return this.apiTokenStore.countByType();
|
||||
}
|
||||
|
||||
getRegisteredUsers(): Promise<number> {
|
||||
return this.userStore.count();
|
||||
}
|
||||
|
||||
countServiceAccounts(): Promise<number> {
|
||||
return this.userStore.countServiceAccounts();
|
||||
}
|
||||
|
||||
async getLabeledAppCounts(): Promise<
|
||||
Partial<{ [key in TimeRange]: number }>
|
||||
> {
|
||||
@ -338,11 +369,12 @@ export class InstanceStatsService {
|
||||
this.clientInstanceStore.getDistinctApplicationsCount(30),
|
||||
this.clientInstanceStore.getDistinctApplicationsCount(),
|
||||
]);
|
||||
return {
|
||||
this.appCount = {
|
||||
'7d': t7d,
|
||||
'30d': t30d,
|
||||
allTime,
|
||||
};
|
||||
return this.appCount;
|
||||
}
|
||||
|
||||
getAppCountSnapshot(range: TimeRange): number | undefined {
|
||||
|
@ -59,8 +59,12 @@ export const scheduleServices = async (
|
||||
'updateLastSeen',
|
||||
);
|
||||
|
||||
// TODO this works fine for keeping labeledAppCounts up to date, but
|
||||
// it would be nice if we can keep client_apps_total prometheus metric
|
||||
// up to date. We'd need to have access to DbMetricsMonitor, which is
|
||||
// where the metric is registered and call an update only for that metric
|
||||
schedulerService.schedule(
|
||||
instanceStatsService.refreshAppCountSnapshot.bind(instanceStatsService),
|
||||
instanceStatsService.getLabeledAppCounts.bind(instanceStatsService),
|
||||
minutesToMilliseconds(5),
|
||||
'refreshAppCountSnapshot',
|
||||
);
|
||||
|
114
src/lib/metrics-gauge.test.ts
Normal file
114
src/lib/metrics-gauge.test.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { register } from 'prom-client';
|
||||
import { createTestConfig } from '../test/config/test-config';
|
||||
import type { IUnleashConfig } from './types';
|
||||
import { DbMetricsMonitor } from './metrics-gauge';
|
||||
|
||||
const prometheusRegister = register;
|
||||
let config: IUnleashConfig;
|
||||
let dbMetrics: DbMetricsMonitor;
|
||||
|
||||
beforeAll(async () => {
|
||||
config = createTestConfig({
|
||||
server: {
|
||||
serverMetrics: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
dbMetrics = new DbMetricsMonitor(config);
|
||||
});
|
||||
|
||||
test('should collect registered metrics', async () => {
|
||||
dbMetrics.registerGaugeDbMetric({
|
||||
name: 'my_metric',
|
||||
help: 'This is the answer to life, the univers, and everything',
|
||||
labelNames: [],
|
||||
query: () => Promise.resolve(42),
|
||||
map: (result) => ({ value: result }),
|
||||
});
|
||||
|
||||
await dbMetrics.refreshDbMetrics();
|
||||
|
||||
const metrics = await prometheusRegister.metrics();
|
||||
expect(metrics).toMatch(/my_metric 42/);
|
||||
});
|
||||
|
||||
test('should collect registered metrics with labels', async () => {
|
||||
dbMetrics.registerGaugeDbMetric({
|
||||
name: 'life_the_universe_and_everything',
|
||||
help: 'This is the answer to life, the univers, and everything',
|
||||
labelNames: ['test'],
|
||||
query: () => Promise.resolve(42),
|
||||
map: (result) => ({ value: result, labels: { test: 'case' } }),
|
||||
});
|
||||
|
||||
await dbMetrics.refreshDbMetrics();
|
||||
|
||||
const metrics = await prometheusRegister.metrics();
|
||||
expect(metrics).toMatch(
|
||||
/life_the_universe_and_everything\{test="case"\} 42/,
|
||||
);
|
||||
});
|
||||
|
||||
test('should collect multiple registered metrics with and without labels', async () => {
|
||||
dbMetrics.registerGaugeDbMetric({
|
||||
name: 'my_first_metric',
|
||||
help: 'This is the answer to life, the univers, and everything',
|
||||
labelNames: [],
|
||||
query: () => Promise.resolve(42),
|
||||
map: (result) => ({ value: result }),
|
||||
});
|
||||
|
||||
dbMetrics.registerGaugeDbMetric({
|
||||
name: 'my_other_metric',
|
||||
help: 'This is Eulers number',
|
||||
labelNames: ['euler'],
|
||||
query: () => Promise.resolve(Math.E),
|
||||
map: (result) => ({ value: result, labels: { euler: 'number' } }),
|
||||
});
|
||||
|
||||
await dbMetrics.refreshDbMetrics();
|
||||
|
||||
const metrics = await prometheusRegister.metrics();
|
||||
expect(metrics).toMatch(/my_first_metric 42/);
|
||||
expect(metrics).toMatch(/my_other_metric\{euler="number"\} 2.71828/);
|
||||
});
|
||||
|
||||
test('should support different label and value pairs', async () => {
|
||||
dbMetrics.registerGaugeDbMetric({
|
||||
name: 'multi_dimensional',
|
||||
help: 'This metric has different values for different labels',
|
||||
labelNames: ['version', 'range'],
|
||||
query: () => Promise.resolve(2),
|
||||
map: (result) => [
|
||||
{ value: result, labels: { version: '1', range: 'linear' } },
|
||||
{
|
||||
value: result * result,
|
||||
labels: { version: '2', range: 'square' },
|
||||
},
|
||||
{ value: result / 2, labels: { version: '3', range: 'half' } },
|
||||
],
|
||||
});
|
||||
|
||||
await dbMetrics.refreshDbMetrics();
|
||||
|
||||
const metrics = await prometheusRegister.metrics();
|
||||
expect(metrics).toMatch(
|
||||
/multi_dimensional\{version="1",range="linear"\} 2\nmulti_dimensional\{version="2",range="square"\} 4\nmulti_dimensional\{version="3",range="half"\} 1/,
|
||||
);
|
||||
expect(
|
||||
await dbMetrics.findValue('multi_dimensional', { range: 'linear' }),
|
||||
).toBe(2);
|
||||
expect(
|
||||
await dbMetrics.findValue('multi_dimensional', { range: 'half' }),
|
||||
).toBe(1);
|
||||
expect(
|
||||
await dbMetrics.findValue('multi_dimensional', { range: 'square' }),
|
||||
).toBe(4);
|
||||
expect(
|
||||
await dbMetrics.findValue('multi_dimensional', { range: 'x' }),
|
||||
).toBeUndefined();
|
||||
expect(await dbMetrics.findValue('multi_dimensional')).toBe(2); // first match
|
||||
expect(await dbMetrics.findValue('other')).toBeUndefined();
|
||||
});
|
94
src/lib/metrics-gauge.ts
Normal file
94
src/lib/metrics-gauge.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import type { Logger } from './logger';
|
||||
import type { IUnleashConfig } from './types';
|
||||
import { createGauge, type Gauge } from './util/metrics';
|
||||
|
||||
type Query<R> = () => Promise<R | undefined | null>;
|
||||
type MetricValue<L extends string> = {
|
||||
value: number;
|
||||
labels?: Record<L, string | number>;
|
||||
};
|
||||
type MapResult<R, L extends string> = (
|
||||
result: R,
|
||||
) => MetricValue<L> | MetricValue<L>[];
|
||||
|
||||
type GaugeDefinition<T, L extends string> = {
|
||||
name: string;
|
||||
help: string;
|
||||
labelNames: L[];
|
||||
query: Query<T>;
|
||||
map: MapResult<T, L>;
|
||||
};
|
||||
|
||||
type Task = () => Promise<void>;
|
||||
|
||||
interface GaugeUpdater {
|
||||
target: Gauge<string>;
|
||||
task: Task;
|
||||
}
|
||||
export class DbMetricsMonitor {
|
||||
private updaters: Map<string, GaugeUpdater> = new Map();
|
||||
private log: Logger;
|
||||
|
||||
constructor({ getLogger }: Pick<IUnleashConfig, 'getLogger'>) {
|
||||
this.log = getLogger('gauge-metrics');
|
||||
}
|
||||
|
||||
private asArray<T>(value: T | T[]): T[] {
|
||||
return Array.isArray(value) ? value : [value];
|
||||
}
|
||||
|
||||
registerGaugeDbMetric<T, L extends string>(
|
||||
definition: GaugeDefinition<T, L>,
|
||||
): Task {
|
||||
const gauge = createGauge(definition);
|
||||
const task = async () => {
|
||||
try {
|
||||
const result = await definition.query();
|
||||
if (result !== null && result !== undefined) {
|
||||
const results = this.asArray(definition.map(result));
|
||||
gauge.reset();
|
||||
for (const r of results) {
|
||||
if (r.labels) {
|
||||
gauge.labels(r.labels).set(r.value);
|
||||
} else {
|
||||
gauge.set(r.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
this.log.warn(`Failed to refresh ${definition.name}`, e);
|
||||
}
|
||||
};
|
||||
this.updaters.set(definition.name, { target: gauge, task });
|
||||
return task;
|
||||
}
|
||||
|
||||
refreshDbMetrics = async () => {
|
||||
const tasks = Array.from(this.updaters.entries()).map(
|
||||
([name, updater]) => ({ name, task: updater.task }),
|
||||
);
|
||||
for (const { name, task } of tasks) {
|
||||
this.log.debug(`Refreshing metric ${name}`);
|
||||
await task();
|
||||
}
|
||||
};
|
||||
|
||||
async findValue(
|
||||
name: string,
|
||||
labels?: Record<string, string | number>,
|
||||
): Promise<number | undefined> {
|
||||
const gauge = await this.updaters.get(name)?.target.gauge?.get();
|
||||
if (gauge && gauge.values.length > 0) {
|
||||
const values = labels
|
||||
? gauge.values.filter(({ labels: l }) => {
|
||||
return Object.entries(labels).every(
|
||||
([key, value]) => l[key] === value,
|
||||
);
|
||||
})
|
||||
: gauge.values;
|
||||
// return first value
|
||||
return values.map(({ value }) => value).shift();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
@ -15,7 +15,11 @@ import {
|
||||
FEATURE_UPDATED,
|
||||
PROJECT_ENVIRONMENT_REMOVED,
|
||||
} from './types/events';
|
||||
import { createMetricsMonitor } from './metrics';
|
||||
import {
|
||||
createMetricsMonitor,
|
||||
registerPrometheusMetrics,
|
||||
registerPrometheusPostgresMetrics,
|
||||
} from './metrics';
|
||||
import createStores from '../test/fixtures/store';
|
||||
import { InstanceStatsService } from './features/instance-stats/instance-stats-service';
|
||||
import VersionService from './services/version-service';
|
||||
@ -46,6 +50,7 @@ let schedulerService: SchedulerService;
|
||||
let featureLifeCycleStore: IFeatureLifecycleStore;
|
||||
let featureLifeCycleReadModel: IFeatureLifecycleReadModel;
|
||||
let db: ITestDb;
|
||||
let refreshDbMetrics: () => Promise<void>;
|
||||
|
||||
beforeAll(async () => {
|
||||
const config = createTestConfig({
|
||||
@ -102,16 +107,16 @@ beforeAll(async () => {
|
||||
},
|
||||
};
|
||||
|
||||
await monitor.startMonitoring(
|
||||
config,
|
||||
stores,
|
||||
'4.0.0',
|
||||
eventBus,
|
||||
statsService,
|
||||
schedulerService,
|
||||
// @ts-ignore - We don't want a full knex implementation for our tests, it's enough that it actually yields the numbers we want.
|
||||
metricsDbConf,
|
||||
);
|
||||
const { collectDbMetrics, collectStaticCounters } =
|
||||
registerPrometheusMetrics(
|
||||
config,
|
||||
stores,
|
||||
'4.0.0',
|
||||
eventBus,
|
||||
statsService,
|
||||
);
|
||||
refreshDbMetrics = collectDbMetrics;
|
||||
await collectStaticCounters();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@ -212,6 +217,7 @@ test('should collect metrics for function timings', async () => {
|
||||
});
|
||||
|
||||
test('should collect metrics for feature flag size', async () => {
|
||||
await refreshDbMetrics();
|
||||
const metrics = await prometheusRegister.metrics();
|
||||
expect(metrics).toMatch(/feature_toggles_total\{version="(.*)"\} 0/);
|
||||
});
|
||||
@ -222,12 +228,13 @@ test('should collect metrics for archived feature flag size', async () => {
|
||||
});
|
||||
|
||||
test('should collect metrics for total client apps', async () => {
|
||||
await statsService.refreshAppCountSnapshot();
|
||||
await refreshDbMetrics();
|
||||
const metrics = await prometheusRegister.metrics();
|
||||
expect(metrics).toMatch(/client_apps_total\{range="(.*)"\} 0/);
|
||||
});
|
||||
|
||||
test('Should collect metrics for database', async () => {
|
||||
registerPrometheusPostgresMetrics(db.rawDatabase, eventBus, '15.0.0');
|
||||
const metrics = await prometheusRegister.metrics();
|
||||
expect(metrics).toMatch(/db_pool_max/);
|
||||
expect(metrics).toMatch(/db_pool_min/);
|
||||
|
1693
src/lib/metrics.ts
1693
src/lib/metrics.ts
File diff suppressed because it is too large
Load Diff
@ -57,7 +57,7 @@ process.nextTick(async () => {
|
||||
unleashAI: true,
|
||||
webhookDomainLogging: true,
|
||||
addonUsageMetrics: true,
|
||||
releasePlans: true,
|
||||
releasePlans: false,
|
||||
},
|
||||
},
|
||||
authentication: {
|
||||
|
@ -6,10 +6,12 @@ import {
|
||||
import getLogger from '../../../fixtures/no-logger';
|
||||
import type { IUnleashStores } from '../../../../lib/types';
|
||||
import { ApiTokenType } from '../../../../lib/types/models/api-token';
|
||||
import { registerPrometheusMetrics } from '../../../../lib/metrics';
|
||||
|
||||
let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
let stores: IUnleashStores;
|
||||
let refreshDbMetrics: () => Promise<void>;
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('instance_admin_api_serial', getLogger);
|
||||
@ -26,6 +28,15 @@ beforeAll(async () => {
|
||||
},
|
||||
db.rawDatabase,
|
||||
);
|
||||
|
||||
const { collectDbMetrics } = registerPrometheusMetrics(
|
||||
app.config,
|
||||
stores,
|
||||
undefined as unknown as string,
|
||||
app.config.eventBus,
|
||||
app.services.instanceStatsService,
|
||||
);
|
||||
refreshDbMetrics = collectDbMetrics;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@ -39,6 +50,8 @@ test('should return instance statistics', async () => {
|
||||
createdByUserId: 9999,
|
||||
});
|
||||
|
||||
await refreshDbMetrics();
|
||||
|
||||
return app.request
|
||||
.get('/api/admin/instance-admin/statistics')
|
||||
.expect('Content-Type', /json/)
|
||||
|
235
website/docs/feature-flag-tutorials/use-cases/ai.md
Normal file
235
website/docs/feature-flag-tutorials/use-cases/ai.md
Normal file
@ -0,0 +1,235 @@
|
||||
---
|
||||
title: How to use feature flags with AI
|
||||
slug: /feature-flag-tutorials/use-cases/ai
|
||||
---
|
||||
|
||||
Hello,
|
||||
|
||||
Like many people in lots of tech companies today, you might be playing with generative AI and large language models (LLMs). You might even be integrating these AI technologies into your company's products, probably in the form of chatbots or content generators.
|
||||
|
||||
The main way to interact with LLMs today is via a set of APIs, usually either OpenAI, Anthropic or aggregators like Groq. Most of these APIs have similar parameters, like:
|
||||
|
||||
- `model`: The model and the specific version of the model to use.
|
||||
- `prompt`: The prompt to give to the LLM.
|
||||
|
||||
Given how much these models vary in their capabilities, chances are you'll be testing multiple models, multiple versions of the same model, and multiple prompts.
|
||||
|
||||
This is where feature flags are super useful. They allow you to easily switch between different configurations. In this tutorial, we’ll explore how to use feature flags with AI models.
|
||||
|
||||
We'll start with a basic chatbot. I know building a chatbot is a total cliché at this point, but it's a small, self-contained example that everyone knows how to interact with. You can take this example and apply it to any other use case where you need to interact with an LLM via an API.
|
||||
|
||||
First, clone the repo:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/alvinometric/feature-flags-ai
|
||||
cd feature-flags-ai
|
||||
```
|
||||
|
||||
Install the dependencies:
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
Copy the `.env.example` file to a new `.env` file, and add your OpenAI API key to it:
|
||||
|
||||
```sh
|
||||
OPENAI_API_KEY=sk-...
|
||||
```
|
||||
|
||||
Run the app:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
You should now see a chatbot UI, like this:
|
||||
|
||||

|
||||
|
||||
This is a simple SvelteKit app with a chatbot interface. If you’re not familiar with SvelteKit, it works similarly to frameworks like Next.js, Nuxt, or SolidStart, where your file structure defines the routing.
|
||||
|
||||
The most important file for this tutorial is the `src/routes/api/chat/+server.js` file.
|
||||
|
||||
It creates an API endpoint at `/api/chat`. When your frontend sends a POST request to `/api/chat`, this is the code that handles the request.
|
||||
|
||||
```javascript
|
||||
import { createOpenAI } from "@ai-sdk/openai";
|
||||
import { streamText } from "ai";
|
||||
import { env } from "$env/dynamic/private";
|
||||
|
||||
const openai = createOpenAI({
|
||||
apiKey: env.OPENAI_API_KEY ?? "",
|
||||
});
|
||||
|
||||
const variants = {
|
||||
variant1: {
|
||||
model: "gpt-3.5-turbo",
|
||||
max_tokens: 4096,
|
||||
temperature: 1,
|
||||
},
|
||||
variant2: {
|
||||
model: "gpt-4-turbo",
|
||||
max_tokens: 4096,
|
||||
temperature: 1,
|
||||
},
|
||||
variant3: {
|
||||
model: "gpt-4-vision-preview",
|
||||
max_tokens: 4096,
|
||||
temperature: 1,
|
||||
},
|
||||
};
|
||||
|
||||
export const POST = async ({ request }) => {
|
||||
const { messages } = await request.json();
|
||||
const variant = variants["variant1"];
|
||||
const result = await streamText({
|
||||
model: openai(variant.model),
|
||||
messages,
|
||||
max_tokens: variant.max_tokens,
|
||||
temperature: variant.temperature,
|
||||
});
|
||||
return result.toDataStreamResponse();
|
||||
};
|
||||
```
|
||||
|
||||
This file is doing a few key things:
|
||||
|
||||
1. Sets up our OpenAI client with an API key.
|
||||
2. Defines different AI model configurations in the `variants` object.
|
||||
3. Handles incoming chat requests with the `POST` function.
|
||||
|
||||
When a request comes in, it:
|
||||
|
||||
- Extracts the messages from the request body.
|
||||
- Selects a variant (currently hardcoded to 'variant1').
|
||||
- Uses the OpenAI API to generate a response.
|
||||
- Streams the response back to the client.
|
||||
|
||||
The `streamText` function is one of the utilities provided by [Vercel's AI SDK](https://sdk.vercel.ai/). It helps deal with the real-time streaming of the AI's responses.
|
||||
|
||||
## Creating a feature flag with AI variants
|
||||
|
||||
Instead of hardcoding `variant1`, we want to use feature flags to dynamically choose which AI model to use. This will let us easily switch between models, test different configurations, or even do some A/B testing to see which model performs better for which task. And we can do this without having to redeploy our app.
|
||||
|
||||
We can also disable it altogether if the upstream API stops working, or we run out of credits.
|
||||
|
||||
To implement this, we'll need to:
|
||||
|
||||
1. Set up a feature flag provider (we'll use Unleash).
|
||||
2. Replace our static objects with dynamic feature flag variants.
|
||||
3. Use the feature flag in our code to determine which AI model and settings to use for each request.
|
||||
|
||||
### Install a local feature flag provider
|
||||
|
||||
In this section, we’ll install Unleash, run the instance locally, log in, and create a feature flag. If you prefer, you can use other tools instead of Unleash, but you’ll need to update the code accordingly.
|
||||
|
||||
Use Git to clone the Unleash repository and Docker to build and run it. Open a terminal window and run the following commands:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/unleash/unleash.git
|
||||
cd unleash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
You now have Unleash installed on your machine and running in the background. You can access this instance in your web browser at [http://localhost:4242](http://localhost:4242).
|
||||
|
||||
Log in to the platform using these credentials:
|
||||
|
||||
```
|
||||
Username: admin
|
||||
Password: unleash4all
|
||||
```
|
||||
|
||||
Click **New feature flag** to create a new feature flag, called `ai-model`.
|
||||
|
||||
After that, and this is the most important part, we need to add a variant to our feature flag.
|
||||
|
||||
Add a strategy to the feature flag in the `development` environment, in that strategy, create a variant for each of these model configurations:
|
||||
|
||||
```javascript
|
||||
const variants = {
|
||||
variant1: {
|
||||
model: "gpt-3.5-turbo",
|
||||
max_tokens: 4096,
|
||||
temperature: 1,
|
||||
},
|
||||
variant2: {
|
||||
model: "gpt-4-turbo",
|
||||
max_tokens: 4096,
|
||||
temperature: 1,
|
||||
},
|
||||
variant3: {
|
||||
model: "gpt-4-vision-preview",
|
||||
max_tokens: 4096,
|
||||
temperature: 1,
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
What we'll do is move all the model configurations from the code to the feature flag variants.
|
||||
|
||||

|
||||
|
||||
## Querying AI feature flags
|
||||
|
||||
Now let's go back to the code and grab our AI config from the feature flag that we just created.
|
||||
|
||||
First, install the Unleash Node.js client:
|
||||
|
||||
```sh
|
||||
npm install unleash-client
|
||||
```
|
||||
|
||||
Now, let's modify our `+server.js` file to use Unleash:
|
||||
|
||||
```javascript
|
||||
import { initialize } from "unleash-client";
|
||||
import { createOpenAI } from "@ai-sdk/openai";
|
||||
import { streamText } from "ai";
|
||||
import { env } from "$env/dynamic/private";
|
||||
|
||||
const openai = createOpenAI({
|
||||
apiKey: env.OPENAI_API_KEY ?? "",
|
||||
});
|
||||
|
||||
const unleash = initialize({
|
||||
url: "http://localhost:4242/api/",
|
||||
appName: "my-ai-app",
|
||||
customHeaders: { Authorization: env.UNLEASH_API_KEY ?? "" },
|
||||
});
|
||||
|
||||
export const POST = async ({ request }) => {
|
||||
const { messages } = await request.json();
|
||||
|
||||
// Get the feature flag variant
|
||||
const variant = unleash.getVariant("ai-model");
|
||||
|
||||
const result = await streamText({
|
||||
model: openai(variant.model),
|
||||
messages,
|
||||
max_tokens: variant.max_tokens,
|
||||
temperature: variant.temperature,
|
||||
});
|
||||
|
||||
return result.toDataStreamResponse();
|
||||
};
|
||||
```
|
||||
|
||||
This setup uses the Unleash client to fetch the value of a feature flag called `ai-model`.
|
||||
|
||||
Now, instead of hardcoding `variant1`, we're dynamically choosing the AI model based on the feature flag variant.
|
||||
|
||||
This setup gives us a ton of flexibility.
|
||||
|
||||
Do you want to roll out GPT-4 to 10% of your users? Easy. Need to quickly switch everyone back to GPT-3.5 because of a bug? No problem.
|
||||
|
||||
You can do all of this from your Unleash dashboard without touching your code, and without needing to redeploy.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Thanks for following along!
|
||||
|
||||
In this guide, we covered how to use feature flags to help you manage AI models.
|
||||
|
||||
That approach lets you switch between different model configurations, experiment with variations, and even roll out updates without needing to touch your code or redeploy. This gives you more control when experimenting with LLMs, and more power to respond to the unexpected things that will inevitably happen, like running out of credits or discovering a bug.
|
BIN
website/docs/feature-flag-tutorials/use-cases/model-variant.png
Normal file
BIN
website/docs/feature-flag-tutorials/use-cases/model-variant.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 273 KiB |
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
@ -69,7 +69,7 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.25.2",
|
||||
"@babel/core": "7.25.8",
|
||||
"@docusaurus/module-type-aliases": "2.3.1",
|
||||
"@tsconfig/docusaurus": "2.0.3",
|
||||
"babel-loader": "9.1.3",
|
||||
|
@ -116,6 +116,11 @@ module.exports = {
|
||||
label: 'A/B Testing',
|
||||
id: 'feature-flag-tutorials/use-cases/a-b-testing',
|
||||
},
|
||||
{
|
||||
type: 'doc',
|
||||
label: 'Feature Flags for AI',
|
||||
id: 'feature-flag-tutorials/use-cases/ai',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
457
yarn.lock
457
yarn.lock
@ -104,13 +104,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/code-frame@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/code-frame@npm:7.24.7"
|
||||
"@babel/code-frame@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/code-frame@npm:7.25.7"
|
||||
dependencies:
|
||||
"@babel/highlight": "npm:^7.24.7"
|
||||
"@babel/highlight": "npm:^7.25.7"
|
||||
picocolors: "npm:^1.0.0"
|
||||
checksum: 10c0/ab0af539473a9f5aeaac7047e377cb4f4edd255a81d84a76058595f8540784cc3fbe8acf73f1e073981104562490aabfb23008cd66dc677a456a4ed5390fdde6
|
||||
checksum: 10c0/14825c298bdec914caf3d24d1383b6d4cd6b030714686004992f4fc251831ecf432236652896f99d5d341f17170ae9a07b58d8d7b15aa0df8cfa1c5a7d5474bc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -121,33 +121,33 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/compat-data@npm:^7.25.2":
|
||||
version: 7.25.2
|
||||
resolution: "@babel/compat-data@npm:7.25.2"
|
||||
checksum: 10c0/5bf1f14d6e5f0d37c19543e99209ff4a94bb97915e1ce01e5334a144aa08cd56b6e62ece8135dac77e126723d63d4d4b96fc603a12c43b88c28f4b5e070270c5
|
||||
"@babel/compat-data@npm:^7.25.7":
|
||||
version: 7.25.8
|
||||
resolution: "@babel/compat-data@npm:7.25.8"
|
||||
checksum: 10c0/8b81c17580e5fb4cbb6a3c52079f8c283fc59c0c6bd2fe14cfcf9c44b32d2eaab71b02c5633e2c679f5896f73f8ac4036ba2e67a4c806e8f428e4b11f526d7f4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/core@npm:7.25.2":
|
||||
version: 7.25.2
|
||||
resolution: "@babel/core@npm:7.25.2"
|
||||
"@babel/core@npm:7.25.8":
|
||||
version: 7.25.8
|
||||
resolution: "@babel/core@npm:7.25.8"
|
||||
dependencies:
|
||||
"@ampproject/remapping": "npm:^2.2.0"
|
||||
"@babel/code-frame": "npm:^7.24.7"
|
||||
"@babel/generator": "npm:^7.25.0"
|
||||
"@babel/helper-compilation-targets": "npm:^7.25.2"
|
||||
"@babel/helper-module-transforms": "npm:^7.25.2"
|
||||
"@babel/helpers": "npm:^7.25.0"
|
||||
"@babel/parser": "npm:^7.25.0"
|
||||
"@babel/template": "npm:^7.25.0"
|
||||
"@babel/traverse": "npm:^7.25.2"
|
||||
"@babel/types": "npm:^7.25.2"
|
||||
"@babel/code-frame": "npm:^7.25.7"
|
||||
"@babel/generator": "npm:^7.25.7"
|
||||
"@babel/helper-compilation-targets": "npm:^7.25.7"
|
||||
"@babel/helper-module-transforms": "npm:^7.25.7"
|
||||
"@babel/helpers": "npm:^7.25.7"
|
||||
"@babel/parser": "npm:^7.25.8"
|
||||
"@babel/template": "npm:^7.25.7"
|
||||
"@babel/traverse": "npm:^7.25.7"
|
||||
"@babel/types": "npm:^7.25.8"
|
||||
convert-source-map: "npm:^2.0.0"
|
||||
debug: "npm:^4.1.0"
|
||||
gensync: "npm:^1.0.0-beta.2"
|
||||
json5: "npm:^2.2.3"
|
||||
semver: "npm:^6.3.1"
|
||||
checksum: 10c0/a425fa40e73cb72b6464063a57c478bc2de9dbcc19c280f1b55a3d88b35d572e87e8594e7d7b4880331addb6faef641bbeb701b91b41b8806cd4deae5d74f401
|
||||
checksum: 10c0/8411ea506e6f7c8a39ab5c1524b00589fa3b087edb47389708f7fe07170929192171734666e3ea10b95a951643a531a6d09eedfe071572c9ea28516646265086
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -197,27 +197,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/generator@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/generator@npm:7.24.7"
|
||||
"@babel/generator@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/generator@npm:7.25.7"
|
||||
dependencies:
|
||||
"@babel/types": "npm:^7.24.7"
|
||||
"@babel/types": "npm:^7.25.7"
|
||||
"@jridgewell/gen-mapping": "npm:^0.3.5"
|
||||
"@jridgewell/trace-mapping": "npm:^0.3.25"
|
||||
jsesc: "npm:^2.5.1"
|
||||
checksum: 10c0/06b1f3350baf527a3309e50ffd7065f7aee04dd06e1e7db794ddfde7fe9d81f28df64edd587173f8f9295496a7ddb74b9a185d4bf4de7bb619e6d4ec45c8fd35
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/generator@npm:^7.25.0":
|
||||
version: 7.25.0
|
||||
resolution: "@babel/generator@npm:7.25.0"
|
||||
dependencies:
|
||||
"@babel/types": "npm:^7.25.0"
|
||||
"@jridgewell/gen-mapping": "npm:^0.3.5"
|
||||
"@jridgewell/trace-mapping": "npm:^0.3.25"
|
||||
jsesc: "npm:^2.5.1"
|
||||
checksum: 10c0/d0e2dfcdc8bdbb5dded34b705ceebf2e0bc1b06795a1530e64fb6a3ccf313c189db7f60c1616effae48114e1a25adc75855bc4496f3779a396b3377bae718ce7
|
||||
jsesc: "npm:^3.0.2"
|
||||
checksum: 10c0/c03a26c79864d60d04ce36b649c3fa0d6fd7b2bf6a22e22854a0457aa09206508392dd73ee40e7bc8d50b3602f9ff068afa47770cda091d332e7db1ca382ee96
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -236,16 +224,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-compilation-targets@npm:^7.25.2":
|
||||
version: 7.25.2
|
||||
resolution: "@babel/helper-compilation-targets@npm:7.25.2"
|
||||
"@babel/helper-compilation-targets@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/helper-compilation-targets@npm:7.25.7"
|
||||
dependencies:
|
||||
"@babel/compat-data": "npm:^7.25.2"
|
||||
"@babel/helper-validator-option": "npm:^7.24.8"
|
||||
browserslist: "npm:^4.23.1"
|
||||
"@babel/compat-data": "npm:^7.25.7"
|
||||
"@babel/helper-validator-option": "npm:^7.25.7"
|
||||
browserslist: "npm:^4.24.0"
|
||||
lru-cache: "npm:^5.1.1"
|
||||
semver: "npm:^6.3.1"
|
||||
checksum: 10c0/de10e986b5322c9f807350467dc845ec59df9e596a5926a3b5edbb4710d8e3b8009d4396690e70b88c3844fe8ec4042d61436dd4b92d1f5f75655cf43ab07e99
|
||||
checksum: 10c0/705be7e5274a3fdade68e3e2cf42e2b600316ab52794e13b91299a16f16c926f15886b6e9d6df20eb943ccc1cdba5a363d4766f8d01e47b8e6f4e01175f5e66c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -263,15 +251,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-environment-visitor@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/helper-environment-visitor@npm:7.24.7"
|
||||
dependencies:
|
||||
"@babel/types": "npm:^7.24.7"
|
||||
checksum: 10c0/36ece78882b5960e2d26abf13cf15ff5689bf7c325b10a2895a74a499e712de0d305f8d78bb382dd3c05cfba7e47ec98fe28aab5674243e0625cd38438dd0b2d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-function-name@npm:^7.23.0":
|
||||
version: 7.23.0
|
||||
resolution: "@babel/helper-function-name@npm:7.23.0"
|
||||
@ -282,16 +261,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-function-name@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/helper-function-name@npm:7.24.7"
|
||||
dependencies:
|
||||
"@babel/template": "npm:^7.24.7"
|
||||
"@babel/types": "npm:^7.24.7"
|
||||
checksum: 10c0/e5e41e6cf86bd0f8bf272cbb6e7c5ee0f3e9660414174435a46653efba4f2479ce03ce04abff2aa2ef9359cf057c79c06cb7b134a565ad9c0e8a50dcdc3b43c4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-hoist-variables@npm:^7.22.5":
|
||||
version: 7.22.5
|
||||
resolution: "@babel/helper-hoist-variables@npm:7.22.5"
|
||||
@ -301,15 +270,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-hoist-variables@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/helper-hoist-variables@npm:7.24.7"
|
||||
dependencies:
|
||||
"@babel/types": "npm:^7.24.7"
|
||||
checksum: 10c0/19ee37563bbd1219f9d98991ad0e9abef77803ee5945fd85aa7aa62a67c69efca9a801696a1b58dda27f211e878b3327789e6fd2a6f6c725ccefe36774b5ce95
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-module-imports@npm:^7.18.6":
|
||||
version: 7.18.6
|
||||
resolution: "@babel/helper-module-imports@npm:7.18.6"
|
||||
@ -319,13 +279,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-module-imports@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/helper-module-imports@npm:7.24.7"
|
||||
"@babel/helper-module-imports@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/helper-module-imports@npm:7.25.7"
|
||||
dependencies:
|
||||
"@babel/traverse": "npm:^7.24.7"
|
||||
"@babel/types": "npm:^7.24.7"
|
||||
checksum: 10c0/97c57db6c3eeaea31564286e328a9fb52b0313c5cfcc7eee4bc226aebcf0418ea5b6fe78673c0e4a774512ec6c86e309d0f326e99d2b37bfc16a25a032498af0
|
||||
"@babel/traverse": "npm:^7.25.7"
|
||||
"@babel/types": "npm:^7.25.7"
|
||||
checksum: 10c0/0fd0c3673835e5bf75558e184bcadc47c1f6dd2fe2016d53ebe1e5a6ae931a44e093015c2f9a6651c1a89f25c76d9246710c2b0b460b95ee069c464f2837fa2c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -345,17 +305,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-module-transforms@npm:^7.25.2":
|
||||
version: 7.25.2
|
||||
resolution: "@babel/helper-module-transforms@npm:7.25.2"
|
||||
"@babel/helper-module-transforms@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/helper-module-transforms@npm:7.25.7"
|
||||
dependencies:
|
||||
"@babel/helper-module-imports": "npm:^7.24.7"
|
||||
"@babel/helper-simple-access": "npm:^7.24.7"
|
||||
"@babel/helper-validator-identifier": "npm:^7.24.7"
|
||||
"@babel/traverse": "npm:^7.25.2"
|
||||
"@babel/helper-module-imports": "npm:^7.25.7"
|
||||
"@babel/helper-simple-access": "npm:^7.25.7"
|
||||
"@babel/helper-validator-identifier": "npm:^7.25.7"
|
||||
"@babel/traverse": "npm:^7.25.7"
|
||||
peerDependencies:
|
||||
"@babel/core": ^7.0.0
|
||||
checksum: 10c0/adaa15970ace0aee5934b5a633789b5795b6229c6a9cf3e09a7e80aa33e478675eee807006a862aa9aa517935d81f88a6db8a9f5936e3a2a40ec75f8062bc329
|
||||
checksum: 10c0/f37fa7d1d4df21690535b278468cbd5faf0133a3080f282000cfa4f3ffc9462a1458f866b04b6a2f2d1eec4691236cba9a867da61270dab3ab19846e62f05090
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -375,13 +335,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-simple-access@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/helper-simple-access@npm:7.24.7"
|
||||
"@babel/helper-simple-access@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/helper-simple-access@npm:7.25.7"
|
||||
dependencies:
|
||||
"@babel/traverse": "npm:^7.24.7"
|
||||
"@babel/types": "npm:^7.24.7"
|
||||
checksum: 10c0/7230e419d59a85f93153415100a5faff23c133d7442c19e0cd070da1784d13cd29096ee6c5a5761065c44e8164f9f80e3a518c41a0256df39e38f7ad6744fed7
|
||||
"@babel/traverse": "npm:^7.25.7"
|
||||
"@babel/types": "npm:^7.25.7"
|
||||
checksum: 10c0/eed1b499bfb4f613c18debd61517e3de77b6da2727ca025aa05ac81599e0269f1dddb5237db04e8bb598115d015874752e0a7f11ff38672d74a4976097417059
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -403,15 +363,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-split-export-declaration@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/helper-split-export-declaration@npm:7.24.7"
|
||||
dependencies:
|
||||
"@babel/types": "npm:^7.24.7"
|
||||
checksum: 10c0/0254577d7086bf09b01bbde98f731d4fcf4b7c3fa9634fdb87929801307c1f6202a1352e3faa5492450fa8da4420542d44de604daf540704ff349594a78184f6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-string-parser@npm:^7.19.4":
|
||||
version: 7.19.4
|
||||
resolution: "@babel/helper-string-parser@npm:7.19.4"
|
||||
@ -433,10 +384,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-string-parser@npm:^7.24.8":
|
||||
version: 7.24.8
|
||||
resolution: "@babel/helper-string-parser@npm:7.24.8"
|
||||
checksum: 10c0/6361f72076c17fabf305e252bf6d580106429014b3ab3c1f5c4eb3e6d465536ea6b670cc0e9a637a77a9ad40454d3e41361a2909e70e305116a23d68ce094c08
|
||||
"@babel/helper-string-parser@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/helper-string-parser@npm:7.25.7"
|
||||
checksum: 10c0/73ef2ceb81f8294678a0afe8ab0103729c0370cac2e830e0d5128b03be5f6a2635838af31d391d763e3c5a4460ed96f42fd7c9b552130670d525be665913bc4c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -475,6 +426,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-validator-identifier@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/helper-validator-identifier@npm:7.25.7"
|
||||
checksum: 10c0/07438e5bf01ab2882a15027fdf39ac3b0ba1b251774a5130917907014684e2f70fef8fd620137ca062c4c4eedc388508d2ea7a3a7d9936a32785f4fe116c68c0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-validator-option@npm:^7.18.6":
|
||||
version: 7.18.6
|
||||
resolution: "@babel/helper-validator-option@npm:7.18.6"
|
||||
@ -482,10 +440,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helper-validator-option@npm:^7.24.8":
|
||||
version: 7.24.8
|
||||
resolution: "@babel/helper-validator-option@npm:7.24.8"
|
||||
checksum: 10c0/73db93a34ae89201351288bee7623eed81a54000779462a986105b54ffe82069e764afd15171a428b82e7c7a9b5fec10b5d5603b216317a414062edf5c67a21f
|
||||
"@babel/helper-validator-option@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/helper-validator-option@npm:7.25.7"
|
||||
checksum: 10c0/12ed418c8e3ed9ed44c8c80d823f4e42d399b5eb2e423adccb975e31a31a008cd3b5d8eab688b31f740caff4a1bb28fe06ea2fa7d635aee34cc0ad6995d50f0a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -500,13 +458,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/helpers@npm:^7.25.0":
|
||||
version: 7.25.0
|
||||
resolution: "@babel/helpers@npm:7.25.0"
|
||||
"@babel/helpers@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/helpers@npm:7.25.7"
|
||||
dependencies:
|
||||
"@babel/template": "npm:^7.25.0"
|
||||
"@babel/types": "npm:^7.25.0"
|
||||
checksum: 10c0/b7fe007fc4194268abf70aa3810365085e290e6528dcb9fbbf7a765d43c74b6369ce0f99c5ccd2d44c413853099daa449c9a0123f0b212ac8d18643f2e8174b8
|
||||
"@babel/template": "npm:^7.25.7"
|
||||
"@babel/types": "npm:^7.25.7"
|
||||
checksum: 10c0/3b3ae9e373bd785414195ef8f59976a69d5a6ebe0ef2165fdcc5165e5c3ee09e0fcee94bb457df2ddb8c0532e4146d0a9b7a96b3497399a4bff4ffe196b30228
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -532,15 +490,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/highlight@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/highlight@npm:7.24.7"
|
||||
"@babel/highlight@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/highlight@npm:7.25.7"
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier": "npm:^7.24.7"
|
||||
"@babel/helper-validator-identifier": "npm:^7.25.7"
|
||||
chalk: "npm:^2.4.2"
|
||||
js-tokens: "npm:^4.0.0"
|
||||
picocolors: "npm:^1.0.0"
|
||||
checksum: 10c0/674334c571d2bb9d1c89bdd87566383f59231e16bcdcf5bb7835babdf03c9ae585ca0887a7b25bdf78f303984af028df52831c7989fecebb5101cc132da9393a
|
||||
checksum: 10c0/1f5894fdb0a0af6101fb2822369b2eeeae32cbeae2ef73ff73fc6a0a4a20471565cd9cfa589f54ed69df66adeca7c57266031ca9134b7bd244d023a488d419aa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -571,21 +529,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/parser@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/parser@npm:7.24.7"
|
||||
"@babel/parser@npm:^7.25.7, @babel/parser@npm:^7.25.8":
|
||||
version: 7.25.8
|
||||
resolution: "@babel/parser@npm:7.25.8"
|
||||
dependencies:
|
||||
"@babel/types": "npm:^7.25.8"
|
||||
bin:
|
||||
parser: ./bin/babel-parser.js
|
||||
checksum: 10c0/8b244756872185a1c6f14b979b3535e682ff08cb5a2a5fd97cc36c017c7ef431ba76439e95e419d43000c5b07720495b00cf29a7f0d9a483643d08802b58819b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/parser@npm:^7.25.0":
|
||||
version: 7.25.0
|
||||
resolution: "@babel/parser@npm:7.25.0"
|
||||
bin:
|
||||
parser: ./bin/babel-parser.js
|
||||
checksum: 10c0/4aecf13829fa6f4a66835429bd235458544d9cd14374b17c19bc7726f472727ca33f500e51e1298ddc72db93bdd77fcaa9ddc095200b0b792173069e6cf9742e
|
||||
checksum: 10c0/a1a13845b7e8dda4c970791814a4bbf60004969882f18f470e260ad822d2e1f8941948f851e9335895563610f240fa6c98481ce8019865e469502bbf21daafa4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -783,25 +734,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/template@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/template@npm:7.24.7"
|
||||
"@babel/template@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/template@npm:7.25.7"
|
||||
dependencies:
|
||||
"@babel/code-frame": "npm:^7.24.7"
|
||||
"@babel/parser": "npm:^7.24.7"
|
||||
"@babel/types": "npm:^7.24.7"
|
||||
checksum: 10c0/95b0b3ee80fcef685b7f4426f5713a855ea2cd5ac4da829b213f8fb5afe48a2a14683c2ea04d446dbc7f711c33c5cd4a965ef34dcbe5bc387c9e966b67877ae3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/template@npm:^7.25.0":
|
||||
version: 7.25.0
|
||||
resolution: "@babel/template@npm:7.25.0"
|
||||
dependencies:
|
||||
"@babel/code-frame": "npm:^7.24.7"
|
||||
"@babel/parser": "npm:^7.25.0"
|
||||
"@babel/types": "npm:^7.25.0"
|
||||
checksum: 10c0/4e31afd873215744c016e02b04f43b9fa23205d6d0766fb2e93eb4091c60c1b88897936adb895fb04e3c23de98dfdcbe31bc98daaa1a4e0133f78bb948e1209b
|
||||
"@babel/code-frame": "npm:^7.25.7"
|
||||
"@babel/parser": "npm:^7.25.7"
|
||||
"@babel/types": "npm:^7.25.7"
|
||||
checksum: 10c0/8ae9e36e4330ee83d4832531d1d9bec7dc2ef6a2a8afa1ef1229506fd60667abcb17f306d1c3d7e582251270597022990c845d5d69e7add70a5aea66720decb9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -823,36 +763,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/traverse@npm:^7.24.7":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/traverse@npm:7.24.7"
|
||||
"@babel/traverse@npm:^7.25.7":
|
||||
version: 7.25.7
|
||||
resolution: "@babel/traverse@npm:7.25.7"
|
||||
dependencies:
|
||||
"@babel/code-frame": "npm:^7.24.7"
|
||||
"@babel/generator": "npm:^7.24.7"
|
||||
"@babel/helper-environment-visitor": "npm:^7.24.7"
|
||||
"@babel/helper-function-name": "npm:^7.24.7"
|
||||
"@babel/helper-hoist-variables": "npm:^7.24.7"
|
||||
"@babel/helper-split-export-declaration": "npm:^7.24.7"
|
||||
"@babel/parser": "npm:^7.24.7"
|
||||
"@babel/types": "npm:^7.24.7"
|
||||
"@babel/code-frame": "npm:^7.25.7"
|
||||
"@babel/generator": "npm:^7.25.7"
|
||||
"@babel/parser": "npm:^7.25.7"
|
||||
"@babel/template": "npm:^7.25.7"
|
||||
"@babel/types": "npm:^7.25.7"
|
||||
debug: "npm:^4.3.1"
|
||||
globals: "npm:^11.1.0"
|
||||
checksum: 10c0/a5135e589c3f1972b8877805f50a084a04865ccb1d68e5e1f3b94a8841b3485da4142e33413d8fd76bc0e6444531d3adf1f59f359c11ffac452b743d835068ab
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/traverse@npm:^7.25.2":
|
||||
version: 7.25.2
|
||||
resolution: "@babel/traverse@npm:7.25.2"
|
||||
dependencies:
|
||||
"@babel/code-frame": "npm:^7.24.7"
|
||||
"@babel/generator": "npm:^7.25.0"
|
||||
"@babel/parser": "npm:^7.25.0"
|
||||
"@babel/template": "npm:^7.25.0"
|
||||
"@babel/types": "npm:^7.25.2"
|
||||
debug: "npm:^4.3.1"
|
||||
globals: "npm:^11.1.0"
|
||||
checksum: 10c0/1edcb602801d6ea577584e957a3f6ad48753c4ccb9373fce4c92ebfdee04833f5bd5f1b74758ab7d61fe66d6d83ffdd7c8d482f46199767feeaed6af7df2191e
|
||||
checksum: 10c0/75d73e52c507a7a7a4c7971d6bf4f8f26fdd094e0d3a0193d77edf6a5efa36fc3db91ec5cc48e8b94e6eb5d5ad21af0a1040e71309172851209415fd105efb1a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -900,7 +822,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/types@npm:^7.24.7, @babel/types@npm:^7.8.3":
|
||||
"@babel/types@npm:^7.25.7, @babel/types@npm:^7.25.8":
|
||||
version: 7.25.8
|
||||
resolution: "@babel/types@npm:7.25.8"
|
||||
dependencies:
|
||||
"@babel/helper-string-parser": "npm:^7.25.7"
|
||||
"@babel/helper-validator-identifier": "npm:^7.25.7"
|
||||
to-fast-properties: "npm:^2.0.0"
|
||||
checksum: 10c0/55ca2d6df6426c98db2769ce884ce5e9de83a512ea2dd7bcf56c811984dc14351cacf42932a723630c5afcff2455809323decd645820762182f10b7b5252b59f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/types@npm:^7.8.3":
|
||||
version: 7.24.7
|
||||
resolution: "@babel/types@npm:7.24.7"
|
||||
dependencies:
|
||||
@ -911,17 +844,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/types@npm:^7.25.0, @babel/types@npm:^7.25.2":
|
||||
version: 7.25.2
|
||||
resolution: "@babel/types@npm:7.25.2"
|
||||
dependencies:
|
||||
"@babel/helper-string-parser": "npm:^7.24.8"
|
||||
"@babel/helper-validator-identifier": "npm:^7.24.7"
|
||||
to-fast-properties: "npm:^2.0.0"
|
||||
checksum: 10c0/e489435856be239f8cc1120c90a197e4c2865385121908e5edb7223cfdff3768cba18f489adfe0c26955d9e7bbb1fb10625bc2517505908ceb0af848989bd864
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@bcoe/v8-coverage@npm:^0.2.3":
|
||||
version: 0.2.3
|
||||
resolution: "@bcoe/v8-coverage@npm:0.2.3"
|
||||
@ -1652,92 +1574,92 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-darwin-arm64@npm:1.7.26":
|
||||
version: 1.7.26
|
||||
resolution: "@swc/core-darwin-arm64@npm:1.7.26"
|
||||
"@swc/core-darwin-arm64@npm:1.7.35":
|
||||
version: 1.7.35
|
||||
resolution: "@swc/core-darwin-arm64@npm:1.7.35"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-darwin-x64@npm:1.7.26":
|
||||
version: 1.7.26
|
||||
resolution: "@swc/core-darwin-x64@npm:1.7.26"
|
||||
"@swc/core-darwin-x64@npm:1.7.35":
|
||||
version: 1.7.35
|
||||
resolution: "@swc/core-darwin-x64@npm:1.7.35"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-linux-arm-gnueabihf@npm:1.7.26":
|
||||
version: 1.7.26
|
||||
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.7.26"
|
||||
"@swc/core-linux-arm-gnueabihf@npm:1.7.35":
|
||||
version: 1.7.35
|
||||
resolution: "@swc/core-linux-arm-gnueabihf@npm:1.7.35"
|
||||
conditions: os=linux & cpu=arm
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-linux-arm64-gnu@npm:1.7.26":
|
||||
version: 1.7.26
|
||||
resolution: "@swc/core-linux-arm64-gnu@npm:1.7.26"
|
||||
"@swc/core-linux-arm64-gnu@npm:1.7.35":
|
||||
version: 1.7.35
|
||||
resolution: "@swc/core-linux-arm64-gnu@npm:1.7.35"
|
||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-linux-arm64-musl@npm:1.7.26":
|
||||
version: 1.7.26
|
||||
resolution: "@swc/core-linux-arm64-musl@npm:1.7.26"
|
||||
"@swc/core-linux-arm64-musl@npm:1.7.35":
|
||||
version: 1.7.35
|
||||
resolution: "@swc/core-linux-arm64-musl@npm:1.7.35"
|
||||
conditions: os=linux & cpu=arm64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-linux-x64-gnu@npm:1.7.26":
|
||||
version: 1.7.26
|
||||
resolution: "@swc/core-linux-x64-gnu@npm:1.7.26"
|
||||
"@swc/core-linux-x64-gnu@npm:1.7.35":
|
||||
version: 1.7.35
|
||||
resolution: "@swc/core-linux-x64-gnu@npm:1.7.35"
|
||||
conditions: os=linux & cpu=x64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-linux-x64-musl@npm:1.7.26":
|
||||
version: 1.7.26
|
||||
resolution: "@swc/core-linux-x64-musl@npm:1.7.26"
|
||||
"@swc/core-linux-x64-musl@npm:1.7.35":
|
||||
version: 1.7.35
|
||||
resolution: "@swc/core-linux-x64-musl@npm:1.7.35"
|
||||
conditions: os=linux & cpu=x64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-win32-arm64-msvc@npm:1.7.26":
|
||||
version: 1.7.26
|
||||
resolution: "@swc/core-win32-arm64-msvc@npm:1.7.26"
|
||||
"@swc/core-win32-arm64-msvc@npm:1.7.35":
|
||||
version: 1.7.35
|
||||
resolution: "@swc/core-win32-arm64-msvc@npm:1.7.35"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-win32-ia32-msvc@npm:1.7.26":
|
||||
version: 1.7.26
|
||||
resolution: "@swc/core-win32-ia32-msvc@npm:1.7.26"
|
||||
"@swc/core-win32-ia32-msvc@npm:1.7.35":
|
||||
version: 1.7.35
|
||||
resolution: "@swc/core-win32-ia32-msvc@npm:1.7.35"
|
||||
conditions: os=win32 & cpu=ia32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core-win32-x64-msvc@npm:1.7.26":
|
||||
version: 1.7.26
|
||||
resolution: "@swc/core-win32-x64-msvc@npm:1.7.26"
|
||||
"@swc/core-win32-x64-msvc@npm:1.7.35":
|
||||
version: 1.7.35
|
||||
resolution: "@swc/core-win32-x64-msvc@npm:1.7.35"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/core@npm:1.7.26":
|
||||
version: 1.7.26
|
||||
resolution: "@swc/core@npm:1.7.26"
|
||||
"@swc/core@npm:1.7.35":
|
||||
version: 1.7.35
|
||||
resolution: "@swc/core@npm:1.7.35"
|
||||
dependencies:
|
||||
"@swc/core-darwin-arm64": "npm:1.7.26"
|
||||
"@swc/core-darwin-x64": "npm:1.7.26"
|
||||
"@swc/core-linux-arm-gnueabihf": "npm:1.7.26"
|
||||
"@swc/core-linux-arm64-gnu": "npm:1.7.26"
|
||||
"@swc/core-linux-arm64-musl": "npm:1.7.26"
|
||||
"@swc/core-linux-x64-gnu": "npm:1.7.26"
|
||||
"@swc/core-linux-x64-musl": "npm:1.7.26"
|
||||
"@swc/core-win32-arm64-msvc": "npm:1.7.26"
|
||||
"@swc/core-win32-ia32-msvc": "npm:1.7.26"
|
||||
"@swc/core-win32-x64-msvc": "npm:1.7.26"
|
||||
"@swc/core-darwin-arm64": "npm:1.7.35"
|
||||
"@swc/core-darwin-x64": "npm:1.7.35"
|
||||
"@swc/core-linux-arm-gnueabihf": "npm:1.7.35"
|
||||
"@swc/core-linux-arm64-gnu": "npm:1.7.35"
|
||||
"@swc/core-linux-arm64-musl": "npm:1.7.35"
|
||||
"@swc/core-linux-x64-gnu": "npm:1.7.35"
|
||||
"@swc/core-linux-x64-musl": "npm:1.7.35"
|
||||
"@swc/core-win32-arm64-msvc": "npm:1.7.35"
|
||||
"@swc/core-win32-ia32-msvc": "npm:1.7.35"
|
||||
"@swc/core-win32-x64-msvc": "npm:1.7.35"
|
||||
"@swc/counter": "npm:^0.1.3"
|
||||
"@swc/types": "npm:^0.1.12"
|
||||
"@swc/types": "npm:^0.1.13"
|
||||
peerDependencies:
|
||||
"@swc/helpers": "*"
|
||||
dependenciesMeta:
|
||||
@ -1764,7 +1686,7 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
"@swc/helpers":
|
||||
optional: true
|
||||
checksum: 10c0/6c5ce3d048cb100cd545145b1701a857207b1aeecc8f6bc44ed10b0a1792052834d155a6fa651dad20f38d3fff595034649cc75618946be8da751fa86a9c75b7
|
||||
checksum: 10c0/aae11f2f311f16a21348e33768debe2295a7e0a04f4b37ffbbb15cf5303e0cd08cf0c72661b72f8e4e33cf530d82c15bb2cef090548c65c4bf3ab3854724465b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -1788,12 +1710,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@swc/types@npm:^0.1.12":
|
||||
version: 0.1.12
|
||||
resolution: "@swc/types@npm:0.1.12"
|
||||
"@swc/types@npm:^0.1.13":
|
||||
version: 0.1.13
|
||||
resolution: "@swc/types@npm:0.1.13"
|
||||
dependencies:
|
||||
"@swc/counter": "npm:^0.1.3"
|
||||
checksum: 10c0/f95fea7dee8fc07f8389afbb9578f3d0cd84b429b1d0dbff7fd99b2ef06ed88e96bc33631f36c3bc0505d5a783bee1374acd84b8fc2593001219b6c2caba241b
|
||||
checksum: 10c0/f85a850dead981ca9a26ae366529f2b383fa26324ffcbbee46d7b48399e6ed36d6a6a3d55398f17f87c65f550e28d642a35877d40f389c78765a31ecdfc88bd9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -2819,17 +2741,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"browserslist@npm:^4.23.1":
|
||||
version: 4.23.2
|
||||
resolution: "browserslist@npm:4.23.2"
|
||||
"browserslist@npm:^4.24.0":
|
||||
version: 4.24.0
|
||||
resolution: "browserslist@npm:4.24.0"
|
||||
dependencies:
|
||||
caniuse-lite: "npm:^1.0.30001640"
|
||||
electron-to-chromium: "npm:^1.4.820"
|
||||
node-releases: "npm:^2.0.14"
|
||||
caniuse-lite: "npm:^1.0.30001663"
|
||||
electron-to-chromium: "npm:^1.5.28"
|
||||
node-releases: "npm:^2.0.18"
|
||||
update-browserslist-db: "npm:^1.1.0"
|
||||
bin:
|
||||
browserslist: cli.js
|
||||
checksum: 10c0/0217d23c69ed61cdd2530c7019bf7c822cd74c51f8baab18dd62457fed3129f52499f8d3a6f809ae1fb7bb3050aa70caa9a529cc36c7478427966dbf429723a5
|
||||
checksum: 10c0/95e76ad522753c4c470427f6e3c8a4bb5478ff448841e22b3d3e53f89ecaf17b6984666d6c7e715c370f1e7fa0cf684f42e34e554236a8b2fab38ea76b9e4c52
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -2957,10 +2879,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"caniuse-lite@npm:^1.0.30001640":
|
||||
version: 1.0.30001644
|
||||
resolution: "caniuse-lite@npm:1.0.30001644"
|
||||
checksum: 10c0/96de82909f3ba9f44e5b261c42d3d8814ba99b7b8b48eb8f8eafb7015804ccb2bc2120c5fbc5e193e14e0c87bf1cd0d4de920d8f5a5b477e66e8f0c3972d0eb7
|
||||
"caniuse-lite@npm:^1.0.30001663":
|
||||
version: 1.0.30001669
|
||||
resolution: "caniuse-lite@npm:1.0.30001669"
|
||||
checksum: 10c0/f125f23440d3dbb6c25ffb8d55f4ce48af36a84d0932b152b3b74f143a4170cbe92e02b0a9676209c86609bf7bf34119ff10cc2bc7c1b7ea40e936cc16598408
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -3849,10 +3771,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"electron-to-chromium@npm:^1.4.820":
|
||||
version: 1.5.3
|
||||
resolution: "electron-to-chromium@npm:1.5.3"
|
||||
checksum: 10c0/acd4dad650dafa16c4bd19868fe79c58ae3208f666d868ef8d4c81012707b2855b1816241d1c243b50c75a6933817a9e33401a5a17bc4222c52a5ee8abf457e8
|
||||
"electron-to-chromium@npm:^1.5.28":
|
||||
version: 1.5.40
|
||||
resolution: "electron-to-chromium@npm:1.5.40"
|
||||
checksum: 10c0/3f97360627cf179b344a7d45b3d12fd3f18f1287529d9835a8e802c7a3b99f09e326b4ed3097be1b135e45a33e8497e758b0c101e38e5bb405eaa6aa887eca82
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -6066,6 +5988,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"jsesc@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "jsesc@npm:3.0.2"
|
||||
bin:
|
||||
jsesc: bin/jsesc
|
||||
checksum: 10c0/ef22148f9e793180b14d8a145ee6f9f60f301abf443288117b4b6c53d0ecd58354898dc506ccbb553a5f7827965cd38bc5fb726575aae93c5e8915e2de8290e1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"json-diff@npm:^1.0.6":
|
||||
version: 1.0.6
|
||||
resolution: "json-diff@npm:1.0.6"
|
||||
@ -7035,10 +6966,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-releases@npm:^2.0.14":
|
||||
version: 2.0.14
|
||||
resolution: "node-releases@npm:2.0.14"
|
||||
checksum: 10c0/199fc93773ae70ec9969bc6d5ac5b2bbd6eb986ed1907d751f411fef3ede0e4bfdb45ceb43711f8078bea237b6036db8b1bf208f6ff2b70c7d615afd157f3ab9
|
||||
"node-releases@npm:^2.0.18":
|
||||
version: 2.0.18
|
||||
resolution: "node-releases@npm:2.0.18"
|
||||
checksum: 10c0/786ac9db9d7226339e1dc84bbb42007cb054a346bd9257e6aa154d294f01bc6a6cddb1348fa099f079be6580acbb470e3c048effd5f719325abd0179e566fd27
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -9407,11 +9338,11 @@ __metadata:
|
||||
resolution: "unleash-server@workspace:."
|
||||
dependencies:
|
||||
"@apidevtools/swagger-parser": "npm:10.1.0"
|
||||
"@babel/core": "npm:7.25.2"
|
||||
"@babel/core": "npm:7.25.8"
|
||||
"@biomejs/biome": "npm:^1.8.3"
|
||||
"@cyclonedx/yarn-plugin-cyclonedx": "npm:^1.0.0-rc.7"
|
||||
"@slack/web-api": "npm:^7.3.4"
|
||||
"@swc/core": "npm:1.7.26"
|
||||
"@swc/core": "npm:1.7.35"
|
||||
"@swc/jest": "npm:0.2.36"
|
||||
"@types/bcryptjs": "npm:2.4.6"
|
||||
"@types/cors": "npm:2.8.17"
|
||||
|
Loading…
Reference in New Issue
Block a user