@@ -94,13 +95,14 @@ const EventBlockTimers = (props: EventBlockTimerProps) => {
submitHandler={handleSubmit}
validationHandler={handleValidation}
time={duration}
+ delay={0}
placeholder='Duration'
previousEnd={previousEnd}
warning={warning.duration}
/>
- {delay !== 0 && delay !== null && (
+ {delayTime && (
- {`${delayTime} minutes`}
+ {delayTime}
{`New start: ${newTime}`}
diff --git a/apps/client/src/features/table/OntimeTable.jsx b/apps/client/src/features/table/OntimeTable.jsx
index 27927c05d..0bb44e6a1 100644
--- a/apps/client/src/features/table/OntimeTable.jsx
+++ b/apps/client/src/features/table/OntimeTable.jsx
@@ -10,11 +10,7 @@ import {
useSensor,
useSensors,
} from '@dnd-kit/core';
-import {
- horizontalListSortingStrategy,
- SortableContext,
- sortableKeyboardCoordinates,
-} from '@dnd-kit/sortable';
+import { horizontalListSortingStrategy, SortableContext, sortableKeyboardCoordinates } from '@dnd-kit/sortable';
import PropTypes from 'prop-types';
import { TableSettingsContext } from '../../common/context/TableSettingsContext';
@@ -148,7 +144,7 @@ export default function OntimeTable({ tableData, userFields, selectedId, handleU
if (el) {
el.scrollIntoView({
behavior: 'smooth',
- block: 'start',
+ block: 'center',
inline: 'nearest',
});
}
diff --git a/apps/client/src/features/viewers/backstage/Backstage.scss b/apps/client/src/features/viewers/backstage/Backstage.scss
index b2e40b1d2..5fd1ae1ec 100644
--- a/apps/client/src/features/viewers/backstage/Backstage.scss
+++ b/apps/client/src/features/viewers/backstage/Backstage.scss
@@ -119,7 +119,7 @@
grid-area: schedule;
overflow: hidden;
height: 100%;
- margin-left: 16px;
+ margin-left: clamp(16px, 5vw, 64px);;
}
.schedule-nav-container {
@@ -131,6 +131,8 @@
grid-area: info;
display: flex;
gap: max(1vw, 16px);
+ align-self: flex-end;
+ overflow: hidden;
&__message {
font-size: clamp(16px, 1.5vw, 24px);
@@ -141,7 +143,7 @@
}
.qr {
- margin-left: auto;
+ margin-left: clamp(16px, 5vw, 64px);;
padding: 4px;
background-color: white;
}
diff --git a/apps/client/src/features/viewers/public/Public.scss b/apps/client/src/features/viewers/public/Public.scss
index 469b30b4f..1606d7288 100644
--- a/apps/client/src/features/viewers/public/Public.scss
+++ b/apps/client/src/features/viewers/public/Public.scss
@@ -99,7 +99,8 @@
grid-area: info;
display: flex;
gap: max(1vw, 16px);
- min-height: max(calc(100vh / 15), 128px);
+ align-self: flex-end;
+ overflow: hidden;
&__message {
font-size: clamp(16px, 1.5vw, 24px);
@@ -110,7 +111,7 @@
}
.qr {
- margin-left: auto;
+ margin-left: clamp(16px, 5vw, 64px);;
padding: 4px;
background-color: white;
}
diff --git a/apps/client/src/theme/_ontimeColours.scss b/apps/client/src/theme/_ontimeColours.scss
index d6a7f26ce..c1f74ab5f 100644
--- a/apps/client/src/theme/_ontimeColours.scss
+++ b/apps/client/src/theme/_ontimeColours.scss
@@ -6,6 +6,7 @@ $white-7: rgba(255, 255, 255, 0.07);
$white-9: rgba(255, 255, 255, 0.09);
$white-10: rgba(255, 255, 255, 0.10);
$white-13: rgba(255, 255, 255, 0.13);
+$white-20: rgba(255, 255, 255, 0.20);
$black-10: rgba(0, 0, 0, 0.10);
diff --git a/apps/client/src/theme/ontimeButton.ts b/apps/client/src/theme/ontimeButton.ts
index 4f543ff19..7d1be9a90 100644
--- a/apps/client/src/theme/ontimeButton.ts
+++ b/apps/client/src/theme/ontimeButton.ts
@@ -21,6 +21,9 @@ export const ontimeButtonOutlined = {
border: '1px solid rgba(255, 255, 255, 0.10)', // white-10
_hover: {
backgroundColor: '#404040', // $gray-1000
+ _disabled: {
+ backgroundColor: '#2d2d2d', // $gray-1100
+ },
},
_active: {
backgroundColor: '#2d2d2d', // $gray-1100
@@ -34,6 +37,9 @@ export const ontimeButtonSubtle = {
border: '1px solid transparent',
_hover: {
background: '#404040', // $gray-1000
+ _disabled: {
+ backgroundColor: '#303030', // $gray-1050
+ },
},
_active: {
backgroundColor: '#2d2d2d', // $gray-1100
@@ -47,6 +53,9 @@ export const ontimeButtonSubtleOnLight = {
border: '1px solid transparent',
_hover: {
backgroundColor: '#cfcfcf', // $gray-200
+ _disabled: {
+ backgroundColor: '#ececec', // $gray-100
+ },
},
_active: {
backgroundColor: '#ececec', // $gray-200
@@ -60,6 +69,9 @@ export const ontimeGhostOnLight = {
_hover: {
color: '#595959', // $gray-800
backgroundColor: '#ececec', // $gray-200
+ _disabled: {
+ backgroundColor: 'transparent',
+ },
},
_active: {
backgroundColor: 'transparent',
diff --git a/apps/client/src/theme/ontimeCheckbox.ts b/apps/client/src/theme/ontimeCheckbox.ts
index 7e31d29ce..003937d95 100644
--- a/apps/client/src/theme/ontimeCheckbox.ts
+++ b/apps/client/src/theme/ontimeCheckbox.ts
@@ -1,21 +1,21 @@
export const ontimeCheckboxOnDark = {
control: {
border: '1px',
- borderColor: '#2d2d2d', // $gray-1100
- backgroundColor: '#2d2d2d', // $gray-1100
+ borderColor: '#2d2d2d', // $gray-1100
+ backgroundColor: '#2d2d2d', // $gray-1100
_checked: {
borderColor: '#3182ce', // $action-blue
backgroundColor: '#3182ce', //$action-blue
},
_focus: {
- boxShadow: '0 0 0 1px #578AF4'
- }
+ boxShadow: '0 0 0 1px #578AF4', // $blue-500
+ },
},
label: {
fontWeight: '200',
color: '#9d9d9d', // $gray-500
_checked: {
color: '#cfcfcf', // $gray-300
- }
+ },
},
};
diff --git a/apps/client/src/theme/ontimeRadio.ts b/apps/client/src/theme/ontimeRadio.ts
new file mode 100644
index 000000000..9a56d8d3b
--- /dev/null
+++ b/apps/client/src/theme/ontimeRadio.ts
@@ -0,0 +1,27 @@
+export const ontimeBlockRadio = {
+ control: {
+ borderColor: '#262626', // $gray-1250
+ backgroundColor: '#262626', // $gray-1250
+ _checked: {
+ borderColor: '#262626', // $gray-1250
+ color: '#3182ce', // $action-blue
+ backgroundColor: '#3182ce', // $action-blue
+ },
+ _hover: {
+ color: '#3182ce', // $action-blue
+ backgroundColor: '#3182ce', // $action-blue
+ outline: 'none',
+ },
+ },
+ label: {
+ fontSize: '0.7em',
+ letterSpacing: '0.3px',
+ color: '#9d9d9d', // $gray-500
+ _checked: {
+ color: '#cfcfcf', // $gray-300
+ },
+ _hover: {
+ color: '#e2e2e2', // $gray-200
+ },
+ },
+};
diff --git a/apps/client/src/theme/theme.ts b/apps/client/src/theme/theme.ts
index 131f7e33b..87270cbba 100644
--- a/apps/client/src/theme/theme.ts
+++ b/apps/client/src/theme/theme.ts
@@ -12,6 +12,7 @@ import { ontimeCheckboxOnDark } from './ontimeCheckbox';
import { ontimeEditable } from './ontimeEditable';
import { ontimeMenuOnDark } from './ontimeMenu';
import { ontimeModal } from './ontimeModal';
+import { ontimeBlockRadio } from './ontimeRadio';
import { ontimeSelect } from './ontimeSelect';
import { lightSwitch, ontimeSwitch } from './ontimeSwitch';
import { ontimeTab } from './ontimeTab';
@@ -65,6 +66,11 @@ const theme = extendTheme({
ontime: { ...ontimeModal },
},
},
+ Radio: {
+ variants: {
+ 'ontime-block': { ...ontimeBlockRadio },
+ },
+ },
Tabs: {
variants: {
ontime: { ...ontimeTab },
diff --git a/apps/server/package.json b/apps/server/package.json
index 8dd6121da..235faaf0a 100644
--- a/apps/server/package.json
+++ b/apps/server/package.json
@@ -5,8 +5,6 @@
"version": "2.0.0-beta2",
"exports": "./src/index.js",
"dependencies": {
- "@sentry/node": "^7.47.0",
- "@sentry/tracing": "^7.47.0",
"body-parser": "^1.20.0",
"cors": "^2.8.5",
"dotenv": "^16.0.1",
diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts
index a12d924cf..6a7904eb6 100644
--- a/apps/server/src/app.ts
+++ b/apps/server/src/app.ts
@@ -6,7 +6,6 @@ import cors from 'cors';
// import utils
import { join, resolve } from 'path';
-import { initSentry, reportSentryException } from './modules/sentry.js';
import { currentDirectory, environment, externalsStartDirectory, isProduction, resolvedPath } from './setup.js';
import { ONTIME_VERSION } from './ONTIME_VERSION.js';
import { OSCSettings } from 'ontime-types';
@@ -39,8 +38,6 @@ if (!isProduction) {
console.log(`Ontime directory at ${currentDirectory} `);
}
-initSentry(isProduction);
-
// Create express APP
const app = express();
app.disable('x-powered-by');
@@ -209,14 +206,12 @@ export const shutdown = async (exitCode = 0) => {
process.on('exit', (code) => console.log(`Ontime exited with code: ${code}`));
-process.on('unhandledRejection', async (error) => {
- reportSentryException(error);
+process.on('unhandledRejection', async () => {
logger.error('SERVER', 'Error: unhandled rejection');
await shutdown(1);
});
-process.on('uncaughtException', async (error) => {
- reportSentryException(error);
+process.on('uncaughtException', async () => {
logger.error('SERVER', 'Error: uncaught exception');
await shutdown(1);
});
diff --git a/apps/server/src/classes/event-loader/EventLoader.ts b/apps/server/src/classes/event-loader/EventLoader.ts
index 9dd650da0..e39c7a53f 100644
--- a/apps/server/src/classes/event-loader/EventLoader.ts
+++ b/apps/server/src/classes/event-loader/EventLoader.ts
@@ -260,9 +260,11 @@ export class EventLoader {
* Handle side effects from event loading
*/
private _loadEvent() {
- eventStore.set('loaded', this.loaded);
- eventStore.set('titles', this.titles);
- eventStore.set('titlesPublic', this.titlesPublic);
+ eventStore.batchSet({
+ loaded: this.loaded,
+ titles: this.titles,
+ titlesPublic: this.titlesPublic,
+ });
}
/**
diff --git a/apps/server/src/modules/loadDb.ts b/apps/server/src/modules/loadDb.ts
index 35868e0f2..9de1cccd5 100644
--- a/apps/server/src/modules/loadDb.ts
+++ b/apps/server/src/modules/loadDb.ts
@@ -7,7 +7,6 @@ import { ensureDirectory } from '../utils/fileManagement.js';
import { validateFile } from '../utils/parserUtils.js';
import { dbModel } from '../models/dataModel.js';
import { parseJson } from '../utils/parser.js';
-import { reportSentryException } from './sentry.js';
import { pathToStartDb, resolveDbDirectory, resolveDbPath } from '../setup.js';
/**
@@ -22,8 +21,8 @@ const populateDb = () => {
if (!existsSync(dbInDisk)) {
try {
copyFileSync(pathToStartDb, dbInDisk);
- } catch (error) {
- reportSentryException(error);
+ } catch (_) {
+ /* we do not handle this */
}
}
diff --git a/apps/server/src/modules/loadStyles.ts b/apps/server/src/modules/loadStyles.ts
index fe4338fb5..7bf6bf817 100644
--- a/apps/server/src/modules/loadStyles.ts
+++ b/apps/server/src/modules/loadStyles.ts
@@ -1,7 +1,6 @@
import { copyFileSync, existsSync } from 'fs';
import { pathToStartStyles, resolveStylesDirectory, resolveStylesPath } from '../setup.js';
import { ensureDirectory } from '../utils/fileManagement.js';
-import { reportSentryException } from './sentry.js';
/**
* @description ensures directories exist and populates stylesheet
@@ -15,8 +14,8 @@ export const populateStyles = () => {
if (!existsSync(stylesInDisk)) {
try {
copyFileSync(pathToStartStyles, stylesInDisk);
- } catch (error) {
- reportSentryException(error);
+ } catch (_) {
+ /* we do not handle this */
}
}
diff --git a/apps/server/src/modules/sentry.js b/apps/server/src/modules/sentry.js
deleted file mode 100644
index fb4a8cf43..000000000
--- a/apps/server/src/modules/sentry.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import * as Sentry from '@sentry/node';
-
-let shouldReport;
-
-export function initSentry(doReport) {
- shouldReport = doReport;
- Sentry.init({
- dsn: 'https://ceb6abdce7374857bb50b65636cbaed1@o4504288369836032.ingest.sentry.io/4504288555565056',
- tracesSampleRate: 1.0,
- });
-}
-
-export function reportSentryException(e) {
- if (shouldReport) {
- Sentry.captureException(e);
- } else {
- console.error(e);
- }
-}
diff --git a/apps/server/src/services/RundownService.ts b/apps/server/src/services/RundownService.ts
index 9f6d9e66b..7e860803f 100644
--- a/apps/server/src/services/RundownService.ts
+++ b/apps/server/src/services/RundownService.ts
@@ -1,7 +1,17 @@
-import { OntimeBaseEvent, OntimeBlock, OntimeDelay, OntimeEvent, SupportedEvent } from 'ontime-types';
+import {
+ OntimeBaseEvent,
+ OntimeBlock,
+ OntimeDelay,
+ OntimeEvent,
+ SupportedEvent,
+} from 'ontime-types';
import { generateId } from 'ontime-utils';
import { DataProvider } from '../classes/data-provider/DataProvider.js';
-import { block as blockDef, delay as delayDef, event as eventDef } from '../models/eventsDefinition.js';
+import {
+ block as blockDef,
+ delay as delayDef,
+ event as eventDef,
+} from '../models/eventsDefinition.js';
import { MAX_EVENTS } from '../settings.js';
import { EventLoader, eventLoader } from '../classes/event-loader/EventLoader.js';
import { eventTimer } from './TimerService.js';
@@ -216,30 +226,27 @@ export async function reorderEvent(eventId, from, to) {
* @param eventId
* @returns {Promise
}
*/
-export async function applyDelay(eventId) {
+export async function applyDelay(eventId: string) {
const rundown = DataProvider.getRundown();
let delayIndex = null;
let delayValue = 0;
- for (const [index, e] of rundown.entries()) {
+ for (const [index, event] of rundown.entries()) {
// look for delay
if (delayIndex === null) {
- if (e.id === eventId && e.type === SupportedEvent.Delay) {
- delayValue = e.duration;
+ if (event.id === eventId && event.type === SupportedEvent.Delay) {
+ delayValue = event.duration;
delayIndex = index;
}
}
// apply delay value to all items until block or end
else {
- if (e.type === SupportedEvent.Event) {
- // update times
- e.timeStart += delayValue;
- e.timeEnd += delayValue;
-
- // increment revision
- e.revision += 1;
- } else if (e.type === SupportedEvent.Block) {
+ if (event.type === SupportedEvent.Event) {
+ event.timeStart = Math.max(0, event.timeStart + delayValue);
+ event.timeEnd = Math.max(event.duration, event.timeStart + delayValue);
+ event.revision += 1;
+ } else if (event.type === SupportedEvent.Block) {
break;
}
}
diff --git a/apps/server/src/services/TimerService.ts b/apps/server/src/services/TimerService.ts
index cd76f4408..1d1f4c108 100644
--- a/apps/server/src/services/TimerService.ts
+++ b/apps/server/src/services/TimerService.ts
@@ -141,8 +141,10 @@ export class TimerService {
* @private
*/
_onLoad() {
- eventStore.set('playback', this.playback);
- eventStore.set('timer', this.timer);
+ eventStore.batchSet({
+ playback: this.playback,
+ timer: this.timer,
+ });
integrationService.dispatch(TimerLifeCycle.onLoad);
}
@@ -182,8 +184,10 @@ export class TimerService {
* @private
*/
_onStart() {
- eventStore.set('playback', this.playback);
- eventStore.set('timer', this.timer);
+ eventStore.batchSet({
+ playback: this.playback,
+ timer: this.timer,
+ });
integrationService.dispatch(TimerLifeCycle.onStart);
}
@@ -199,8 +203,10 @@ export class TimerService {
}
_onPause() {
- eventStore.set('playback', this.playback);
- eventStore.set('timer', this.timer);
+ eventStore.batchSet({
+ playback: this.playback,
+ timer: this.timer,
+ });
integrationService.dispatch(TimerLifeCycle.onPause);
}
@@ -214,8 +220,10 @@ export class TimerService {
}
_onStop() {
- eventStore.set('playback', this.playback);
- eventStore.set('timer', this.timer);
+ eventStore.batchSet({
+ playback: this.playback,
+ timer: this.timer,
+ });
integrationService.dispatch(TimerLifeCycle.onStop);
}
diff --git a/apps/server/src/settings.js b/apps/server/src/settings.js
deleted file mode 100644
index adf098a4f..000000000
--- a/apps/server/src/settings.js
+++ /dev/null
@@ -1 +0,0 @@
-export const MAX_EVENTS = 255;
diff --git a/apps/server/src/settings.ts b/apps/server/src/settings.ts
new file mode 100644
index 000000000..d4831d8c5
--- /dev/null
+++ b/apps/server/src/settings.ts
@@ -0,0 +1 @@
+export const MAX_EVENTS = 32768;
diff --git a/apps/server/src/stores/EventStore.ts b/apps/server/src/stores/EventStore.ts
index c6da23fa4..933d8ff87 100644
--- a/apps/server/src/stores/EventStore.ts
+++ b/apps/server/src/stores/EventStore.ts
@@ -8,6 +8,10 @@ let store: Partial = {};
/**
* A runtime store that broadcasts its payload
+ * - init: allows for adding an initial payload to the store
+ * - batchSet: allows setting several keys with a single broadcast
+ * - poll: utility to return state
+ * - broadcast: send its payload as json object
*/
export const eventStore = {
init(payload: RuntimeStore) {
@@ -25,6 +29,12 @@ export const eventStore = {
// });
this.broadcast();
},
+ batchSet(values: Record) {
+ Object.entries(values).forEach(([key, value]) => {
+ store[key] = value;
+ });
+ this.broadcast();
+ },
poll() {
return store;
},
diff --git a/packages/utils/index.ts b/packages/utils/index.ts
index dbf9e00ed..18ccdd28e 100644
--- a/packages/utils/index.ts
+++ b/packages/utils/index.ts
@@ -1,2 +1,3 @@
+export { formatFromMillis } from './src/date-utils/formatFromMillis.js';
export { millisToString } from './src/date-utils/millisToString.js';
export { generateId } from './src/generate-id/generateId.js';
diff --git a/packages/utils/src/date-utils/formatFromMillis.ts b/packages/utils/src/date-utils/formatFromMillis.ts
new file mode 100644
index 000000000..6a649bf30
--- /dev/null
+++ b/packages/utils/src/date-utils/formatFromMillis.ts
@@ -0,0 +1,11 @@
+import { DateTime } from 'luxon';
+
+/**
+ * @description utility function to format a date in milliseconds using luxon
+ * @param {number} millis
+ * @param {string} format
+ * @return {string}
+ */
+export function formatFromMillis(millis: number, format: string) {
+ return DateTime.fromMillis(millis).toUTC().toFormat(format);
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 27bc4ad22..db3d3af91 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -70,7 +70,6 @@ importers:
eslint-plugin-testing-library: ^5.9.1
framer-motion: ^10.10.0
jsdom: ^21.1.0
- luxon: ^3.3.0
ontime-types: workspace:*
ontime-utils: workspace:*
prettier: ^2.8.3
@@ -112,7 +111,6 @@ importers:
csv-stringify: 6.2.3
deepmerge: 4.3.0
framer-motion: 10.11.2_biqbaboplfbrettd7655fr4n2y
- luxon: 3.3.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
react-fast-compare: 3.2.0
@@ -177,8 +175,6 @@ importers:
apps/server:
specifiers:
- '@sentry/node': ^7.47.0
- '@sentry/tracing': ^7.47.0
'@types/express': ^4.17.17
'@types/node': ^16.11.7
'@types/node-osc': ^6.0.0
@@ -210,8 +206,6 @@ importers:
vitest: ^0.29.8
ws: ^8.13.0
dependencies:
- '@sentry/node': 7.47.0
- '@sentry/tracing': 7.47.0
body-parser: 1.20.1
cors: 2.8.5
dotenv: 16.0.3
@@ -2497,6 +2491,7 @@ packages:
tslib: 1.14.1
transitivePeerDependencies:
- supports-color
+ dev: true
/@sentry/react/7.47.0_react@18.2.0:
resolution: {integrity: sha512-Qy6OnlE8FivKOLo0YE7tkr+G5fLmEOkpPxj179wbY/N8kp/ALkqbVdcOrZW7AL6HCc0lphhj+0SB+tpwoPEsiQ==}
@@ -2527,13 +2522,6 @@ packages:
dependencies:
'@sentry-internal/tracing': 7.46.0
- /@sentry/tracing/7.47.0:
- resolution: {integrity: sha512-hJCpKdekwaFNbCVXxfCz5IxfSEJIKnkPmRSVHITOm5VhKwq2e5kmy4Rn6bzSETwJFSDE8LGbR/3eSfGTqw37XA==}
- engines: {node: '>=8'}
- dependencies:
- '@sentry-internal/tracing': 7.47.0
- dev: false
-
/@sentry/types/7.46.0:
resolution: {integrity: sha512-2FMEMgt2h6u7AoELhNhu9L54GAh67KKfK2pJ1kEXJHmWxM9FSCkizjLs/t+49xtY7jEXr8qYq8bV967VfDPQ9g==}
engines: {node: '>=8'}
@@ -3484,6 +3472,7 @@ packages:
debug: 4.3.4
transitivePeerDependencies:
- supports-color
+ dev: true
/ajv-keywords/3.5.2_ajv@6.12.6:
resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
@@ -4352,6 +4341,7 @@ packages:
optional: true
dependencies:
ms: 2.1.2
+ dev: true
/decamelize-keys/1.1.1:
resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
@@ -5772,6 +5762,7 @@ packages:
debug: 4.3.4
transitivePeerDependencies:
- supports-color
+ dev: true
/iconv-corefoundation/1.1.7:
resolution: {integrity: sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==}
@@ -6392,6 +6383,7 @@ packages:
/lru_map/0.3.3:
resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==}
+ dev: true
/luxon/3.3.0:
resolution: {integrity: sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==}
@@ -6598,6 +6590,7 @@ packages:
/ms/2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+ dev: true
/ms/2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}