fix: apply delay (#494)

* fix: bug with applying delays

* fix: show delay data only in production screens

* chore: cover delay with feature test

* refactor: simplify type assertion
This commit is contained in:
Carlos Valente
2023-08-23 22:31:15 +02:00
committed by GitHub
parent 657cc22b44
commit e5fdf27f6c
16 changed files with 517 additions and 431 deletions
@@ -0,0 +1,249 @@
import { OntimeBlock, OntimeDelay, OntimeEvent, OntimeRundown, SupportedEvent } from 'ontime-types';
import { _applyDelay } from '../delayUtils.js';
describe('_applyDelay() ', () => {
describe('in a rundown without the delay field, persisted rundown', () => {
it('applies delays', () => {
const delayId = '1';
const testRundown: OntimeRundown = [
{ id: delayId, type: SupportedEvent.Delay, duration: 10 } as OntimeDelay,
{ id: '2', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
{ id: '3', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
{ id: '5', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
];
const expected = [
{ id: '2', type: SupportedEvent.Event, timeStart: 10, timeEnd: 20, duration: 10, revision: 2 } as OntimeEvent,
{ id: '3', type: SupportedEvent.Event, timeStart: 10, timeEnd: 20, duration: 10, revision: 2 } as OntimeEvent,
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
{ id: '5', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
];
const updatedRundown = _applyDelay(delayId, testRundown);
expect(updatedRundown).toStrictEqual(expected);
});
it('applies negative delays', () => {
const delayId = '1';
const testRundown: OntimeRundown = [
{ id: delayId, type: SupportedEvent.Delay, duration: -10 } as OntimeDelay,
{ id: '2', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
{ id: '3', type: SupportedEvent.Event, timeStart: 20, timeEnd: 40, duration: 20, revision: 1 } as OntimeEvent,
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
{ id: '5', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
];
const expected = [
{ id: '2', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 2 } as OntimeEvent,
{ id: '3', type: SupportedEvent.Event, timeStart: 10, timeEnd: 30, duration: 20, revision: 2 } as OntimeEvent,
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
{ id: '5', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
];
const updatedRundown = _applyDelay(delayId, testRundown);
expect(updatedRundown).toStrictEqual(expected);
});
it('maintains constant duration', () => {
const delayId = '1';
const testRundown: OntimeRundown = [
{ id: delayId, type: SupportedEvent.Delay, duration: -30 } as OntimeDelay,
{ id: '2', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 1 } as OntimeEvent,
{ id: '3', type: SupportedEvent.Event, timeStart: 20, timeEnd: 40, duration: 20, revision: 1 } as OntimeEvent,
];
const expected = [
{ id: '2', type: SupportedEvent.Event, timeStart: 0, timeEnd: 10, duration: 10, revision: 2 } as OntimeEvent,
{ id: '3', type: SupportedEvent.Event, timeStart: 0, timeEnd: 20, duration: 20, revision: 2 } as OntimeEvent,
];
const updatedRundown = _applyDelay(delayId, testRundown);
expect(updatedRundown).toStrictEqual(expected);
});
});
describe('in a rundown with the delay field, cached rundown', () => {
it('applies delays', () => {
const delayId = '1';
const testRundown: OntimeRundown = [
{ id: delayId, type: SupportedEvent.Delay, duration: 10 } as OntimeDelay,
{
id: '2',
type: SupportedEvent.Event,
timeStart: 0,
timeEnd: 10,
duration: 10,
revision: 1,
delay: 10,
} as OntimeEvent,
{
id: '3',
type: SupportedEvent.Event,
timeStart: 0,
timeEnd: 10,
duration: 10,
revision: 1,
delay: 10,
} as OntimeEvent,
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
{
id: '5',
type: SupportedEvent.Event,
timeStart: 0,
timeEnd: 10,
duration: 10,
revision: 1,
delay: 0,
} as OntimeEvent,
];
const expected = [
{
id: '2',
type: SupportedEvent.Event,
timeStart: 10,
timeEnd: 20,
duration: 10,
revision: 2,
delay: 0,
} as OntimeEvent,
{
id: '3',
type: SupportedEvent.Event,
timeStart: 10,
timeEnd: 20,
duration: 10,
revision: 2,
delay: 0,
} as OntimeEvent,
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
{
id: '5',
type: SupportedEvent.Event,
timeStart: 0,
timeEnd: 10,
duration: 10,
revision: 1,
delay: 0,
} as OntimeEvent,
];
const updatedRundown = _applyDelay(delayId, testRundown);
expect(updatedRundown).toStrictEqual(expected);
});
it('applies negative delays', () => {
const delayId = '1';
const testRundown: OntimeRundown = [
{ id: delayId, type: SupportedEvent.Delay, duration: -10 } as OntimeDelay,
{
id: '2',
type: SupportedEvent.Event,
timeStart: 0,
timeEnd: 10,
duration: 10,
revision: 1,
delay: -10,
} as OntimeEvent,
{
id: '3',
type: SupportedEvent.Event,
timeStart: 20,
timeEnd: 40,
duration: 20,
revision: 1,
delay: -10,
} as OntimeEvent,
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
{
id: '5',
type: SupportedEvent.Event,
timeStart: 0,
timeEnd: 10,
duration: 10,
revision: 1,
delay: 0,
} as OntimeEvent,
];
const expected = [
{
id: '2',
type: SupportedEvent.Event,
timeStart: 0,
timeEnd: 10,
duration: 10,
revision: 2,
delay: 0,
} as OntimeEvent,
{
id: '3',
type: SupportedEvent.Event,
timeStart: 10,
timeEnd: 30,
duration: 20,
revision: 2,
delay: 0,
} as OntimeEvent,
{ id: '4', type: SupportedEvent.Block } as OntimeBlock,
{
id: '5',
type: SupportedEvent.Event,
timeStart: 0,
timeEnd: 10,
duration: 10,
revision: 1,
delay: 0,
} as OntimeEvent,
];
const updatedRundown = _applyDelay(delayId, testRundown);
expect(updatedRundown).toStrictEqual(expected);
});
it('maintains constant duration', () => {
const delayId = '1';
const testRundown: OntimeRundown = [
{ id: delayId, type: SupportedEvent.Delay, duration: -30 } as OntimeDelay,
{
id: '2',
type: SupportedEvent.Event,
timeStart: 0,
timeEnd: 10,
duration: 10,
revision: 1,
delay: -30,
} as OntimeEvent,
{
id: '3',
type: SupportedEvent.Event,
timeStart: 20,
timeEnd: 40,
duration: 20,
revision: 1,
delay: -30,
} as OntimeEvent,
];
const expected = [
{
id: '2',
type: SupportedEvent.Event,
timeStart: 0,
timeEnd: 10,
duration: 10,
revision: 2,
delay: 0,
} as OntimeEvent,
{
id: '3',
type: SupportedEvent.Event,
timeStart: 0,
timeEnd: 20,
duration: 20,
revision: 2,
delay: 0,
} as OntimeEvent,
];
const updatedRundown = _applyDelay(delayId, testRundown);
expect(updatedRundown).toStrictEqual(expected);
});
});
});
+37
View File
@@ -0,0 +1,37 @@
import { isOntimeBlock, isOntimeEvent, OntimeRundown, SupportedEvent } from 'ontime-types';
import { deleteAtIndex } from '../utils/arrayUtils.js';
export function _applyDelay(eventId: string, rundown: OntimeRundown): OntimeRundown {
const delayIndex = rundown.findIndex((event) => event.id === eventId);
const delayEvent = rundown.at(delayIndex);
if (delayEvent.type !== SupportedEvent.Delay) {
throw new Error('Given event ID is not a delay');
}
const updatedRundown = [...rundown];
const delayValue = delayEvent.duration;
if (delayValue === 0 || delayIndex === rundown.length - 1) {
// nothing to apply
return updatedRundown;
}
for (let i = delayIndex + 1; i < rundown.length; i++) {
const currentEvent = updatedRundown[i];
if (isOntimeBlock(currentEvent)) {
break;
} else if (isOntimeEvent(currentEvent)) {
currentEvent.timeStart = Math.max(0, currentEvent.timeStart + delayValue);
currentEvent.timeEnd = Math.max(currentEvent.duration, currentEvent.timeEnd + delayValue);
if (currentEvent.delay) {
currentEvent.delay = currentEvent.delay - delayValue;
}
currentEvent.revision += 1;
}
}
return deleteAtIndex(delayIndex, updatedRundown);
}
@@ -4,7 +4,6 @@ import {
OntimeBlock,
OntimeDelay,
OntimeEvent,
OntimeRundown,
Playback,
SupportedEvent,
} from 'ontime-types';
@@ -18,6 +17,7 @@ import { sendRefetch } from '../../adapters/websocketAux.js';
import { runtimeCacheStore } from '../../stores/cachingStore.js';
import {
cachedAdd,
cachedApplyDelay,
cachedClear,
cachedDelete,
cachedEdit,
@@ -166,7 +166,7 @@ export async function addEvent(eventData: Partial<OntimeEvent> | Partial<OntimeD
const id = generateId();
let insertIndex = 0;
if (eventData?.after !== 'undefined') {
if (eventData?.after !== undefined) {
const index = DataProvider.getIndexOf(eventData.after);
if (index < 0) {
logger.warning(LogOrigin.Server, `Could not find event with id ${eventData.after}`);
@@ -268,46 +268,14 @@ export async function reorderEvent(eventId: string, from: number, to: number) {
return reorderedItem;
}
export function _applyDelay(
eventId: string,
rundown: OntimeRundown,
): {
delayIndex: number | null;
updatedRundown: OntimeRundown;
} {
const updatedRundown = [...rundown];
let delayIndex = null;
let delayValue = 0;
export async function applyDelay(eventId: string) {
await cachedApplyDelay(eventId);
for (const [index, event] of updatedRundown.entries()) {
// look for delay
if (delayIndex === null) {
if (event.type === SupportedEvent.Delay && event.id === eventId) {
delayValue = event.duration;
delayIndex = index;
// notify timer service of changed events
updateTimer();
if (delayValue === 0) {
// nothing to apply
break;
}
}
continue;
}
// once delay is found, apply delay value to all items until block or end
if (event.type === SupportedEvent.Event) {
updatedRundown[index] = {
...event,
timeStart: Math.max(0, event.timeStart + delayValue),
timeEnd: Math.max(event.duration, event.timeEnd + delayValue),
revision: event.revision + 1,
};
} else if (event.type === SupportedEvent.Block) {
break;
}
}
return { delayIndex, updatedRundown };
// advice socket subscribers of change
sendRefetch();
}
/**
@@ -326,22 +294,6 @@ export async function swapEvents(from: string, to: string) {
sendRefetch();
}
/**
* applies delay value for given event
* @param eventId
* @returns {Promise<void>}
*/
export async function applyDelay(eventId: string) {
const rundown: OntimeRundown = DataProvider.getRundown();
const { delayIndex, updatedRundown } = _applyDelay(eventId, rundown);
if (delayIndex === null) {
throw new Error(`Delay event with ID ${eventId} not found`);
}
await DataProvider.setRundown(updatedRundown);
await deleteEvent(eventId);
}
/**
* Forces update in the store
* Called when we make changes to the rundown object
@@ -1,319 +0,0 @@
import { EndAction, OntimeRundown, SupportedEvent, TimerType } from 'ontime-types';
import { _applyDelay } from '../RundownService.js';
describe('applyDelay()', () => {
it('applies its duration to following events', () => {
const rundown: OntimeRundown = [
{
title: '',
subtitle: '',
presenter: '',
note: '',
endAction: EndAction.None,
timerType: TimerType.CountDown,
timeStart: 600000,
timeEnd: 1200000,
duration: 600000,
isPublic: true,
skip: false,
colour: '',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
type: SupportedEvent.Event,
revision: 4,
id: '659e1',
},
{
duration: 600000,
type: SupportedEvent.Delay,
revision: 0,
id: '07986',
},
{
title: '',
subtitle: '',
presenter: '',
note: '',
endAction: EndAction.None,
timerType: TimerType.CountDown,
timeStart: 600000,
timeEnd: 1200000,
duration: 600000,
isPublic: true,
skip: false,
colour: '',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
type: SupportedEvent.Event,
revision: 4,
id: 'd48c2',
},
];
const eventId = rundown[1].id;
const { delayIndex, updatedRundown } = _applyDelay(eventId, rundown);
expect(delayIndex).toBe(1);
// we do not delay delays anymore
expect(updatedRundown.length).toBe(3);
expect(rundown.length).toBe(3);
expect(updatedRundown[0].timeStart).toBe(rundown[0].timeStart);
expect(updatedRundown[2].timeStart).toBe(rundown[1].duration + rundown[2].timeStart);
expect(updatedRundown[2].timeEnd).toBe(rundown[1].duration + rundown[2].timeEnd);
});
it('stops propagating on blocks', () => {
const rundown: OntimeRundown = [
{
title: '',
subtitle: '',
presenter: '',
note: '',
endAction: EndAction.None,
timerType: TimerType.CountDown,
timeStart: 600000,
timeEnd: 1200000,
duration: 600000,
isPublic: true,
skip: false,
colour: '',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
type: SupportedEvent.Event,
revision: 0,
id: '659e1',
},
{
duration: 600000,
type: SupportedEvent.Delay,
revision: 0,
id: '07986',
},
{
title: '',
subtitle: '',
presenter: '',
note: '',
endAction: EndAction.None,
timerType: TimerType.CountDown,
timeStart: 600000,
timeEnd: 1200000,
duration: 600000,
isPublic: true,
skip: false,
colour: '',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
type: SupportedEvent.Event,
revision: 0,
id: 'd48c2',
},
{
title: '',
type: SupportedEvent.Block,
id: '9870d',
},
{
title: '',
subtitle: '',
presenter: '',
note: '',
endAction: EndAction.None,
timerType: TimerType.CountDown,
timeStart: 1200000,
timeEnd: 1800000,
duration: 600000,
isPublic: true,
skip: false,
colour: '',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
type: SupportedEvent.Event,
revision: 2,
id: '2f185',
},
];
const eventId = rundown[1].id;
const { updatedRundown } = _applyDelay(eventId, rundown);
expect(updatedRundown[0].timeStart).toBe(rundown[0].timeStart);
expect(updatedRundown[2].timeStart).toBe(rundown[1].duration + rundown[2].timeStart);
expect(updatedRundown[4].timeStart).toBe(rundown[4].timeStart);
});
it('only applies given delay', () => {
const rundown: OntimeRundown = [
{
title: '',
subtitle: '',
presenter: '',
note: '',
endAction: EndAction.None,
timerType: TimerType.CountDown,
timeStart: 600000,
timeEnd: 1200000,
duration: 600000,
isPublic: true,
skip: false,
colour: '',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
type: SupportedEvent.Event,
revision: 0,
id: '659e1',
},
{
duration: 600000,
type: SupportedEvent.Delay,
revision: 0,
id: '07986',
},
{
title: '',
subtitle: '',
presenter: '',
note: '',
endAction: EndAction.None,
timerType: TimerType.CountDown,
timeStart: 1200000,
timeEnd: 1200000,
duration: 0,
isPublic: true,
skip: false,
colour: '',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
type: SupportedEvent.Event,
revision: 0,
id: '1c48f',
},
{
duration: 1200000,
type: SupportedEvent.Delay,
revision: 0,
id: '7db42',
},
{
title: '',
subtitle: '',
presenter: '',
note: '',
endAction: EndAction.None,
timerType: TimerType.CountDown,
timeStart: 600000,
timeEnd: 1200000,
duration: 600000,
isPublic: true,
skip: false,
colour: '',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
type: SupportedEvent.Event,
revision: 0,
id: 'd48c2',
},
{
title: '',
type: SupportedEvent.Block,
id: '9870d',
},
{
title: '',
subtitle: '',
presenter: '',
note: '',
endAction: EndAction.None,
timerType: TimerType.CountDown,
timeStart: 1200000,
timeEnd: 1800000,
duration: 600000,
isPublic: true,
skip: false,
colour: '',
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
type: SupportedEvent.Event,
revision: 0,
id: '2f185',
},
];
const eventId = rundown[1].id;
const { updatedRundown } = _applyDelay(eventId, rundown);
expect(updatedRundown[0].timeStart).toBe(rundown[0].timeStart);
expect(updatedRundown[2].timeStart).toBe(rundown[1].duration + rundown[2].timeStart);
expect(updatedRundown[4].timeStart).toBe(rundown[1].duration + rundown[4].timeStart);
});
});
@@ -1,10 +1,20 @@
import { OntimeBlock, OntimeDelay, OntimeEvent, OntimeRundown, SupportedEvent } from 'ontime-types';
import {
isOntimeBlock,
isOntimeDelay,
isOntimeEvent,
OntimeBlock,
OntimeDelay,
OntimeEvent,
OntimeRundown,
OntimeRundownEntry,
} from 'ontime-types';
import { swapOntimeEvents } from 'ontime-utils';
import { DataProvider } from '../../classes/data-provider/DataProvider.js';
import { getCached, runtimeCacheStore } from '../../stores/cachingStore.js';
import { isProduction } from '../../setup.js';
import { deleteAtIndex, insertAtIndex, reorderArray } from '../../utils/arrayUtils.js';
import { swapOntimeEvents } from 'ontime-utils';
import { _applyDelay } from '../delayUtils.js';
/**
* Key of rundown in cache
@@ -51,7 +61,7 @@ export async function cachedAdd(eventIndex: number, event: OntimeEvent | OntimeD
let newDelayedRundown = insertAtIndex(eventIndex, event, delayedRundown);
// update delay cache
if (event.type === SupportedEvent.Event) {
if (isOntimeEvent(event)) {
// if it is an event, we need its delay
(newDelayedRundown[eventIndex] as OntimeEvent).delay = getDelayAt(eventIndex, newDelayedRundown);
} else {
@@ -79,22 +89,20 @@ export async function cachedEdit(
}
const updatedRundown = DataProvider.getRundown();
const newEvent = { ...updatedRundown[indexInMemory], ...patchObject };
if (newEvent.type === SupportedEvent.Event) {
const newEvent = { ...updatedRundown[indexInMemory], ...patchObject } as OntimeRundownEntry;
if (isOntimeEvent(newEvent)) {
newEvent.revision++;
}
// @ts-expect-error -- this merge is safe
updatedRundown[indexInMemory] = newEvent;
let newDelayedRundown = getDelayedRundown();
if (newDelayedRundown?.[indexInMemory].id !== newEvent.id) {
invalidateFromError();
} else {
// @ts-expect-error -- this merge is safe
newDelayedRundown[indexInMemory] = newEvent;
if (newEvent.type === SupportedEvent.Event) {
if (isOntimeEvent(newEvent)) {
(newDelayedRundown[indexInMemory] as OntimeEvent).delay = getDelayAt(indexInMemory, newDelayedRundown);
} else if (newEvent.type === SupportedEvent.Delay) {
} else if (isOntimeDelay(newEvent)) {
// blocks have no reason to change the rundown, from delays we need to recalculate
newDelayedRundown = calculateRuntimeDelaysFromIndex(indexInMemory, newDelayedRundown);
}
@@ -124,13 +132,12 @@ export async function cachedDelete(eventId: string) {
}
let updatedRundown = DataProvider.getRundown();
const eventType = updatedRundown[eventIndex].type;
updatedRundown = deleteAtIndex(eventIndex, updatedRundown);
if (eventId !== delayedRundown[eventIndex].id) {
invalidateFromError();
} else {
delayedRundown = deleteAtIndex(eventIndex, delayedRundown);
if (eventType === SupportedEvent.Delay || eventType === SupportedEvent.Block) {
if (isOntimeDelay(updatedRundown[eventIndex]) || isOntimeBlock(updatedRundown[eventIndex])) {
// for events, we do not have to worry
// the following event, would have taken the place of the deleted event by now
delayedRundown = calculateRuntimeDelaysFromIndex(eventIndex, delayedRundown);
@@ -205,6 +212,19 @@ export async function cachedSwap(fromEventId: string, toEventId: string) {
await DataProvider.setRundown(rundownToUpdate);
}
export async function cachedApplyDelay(eventId: string) {
// update persisted rundown
const rundown: OntimeRundown = DataProvider.getRundown();
const persistedRundown = _applyDelay(eventId, rundown);
const delayedRundown = getDelayedRundown();
const cachedRundown = _applyDelay(eventId, delayedRundown);
// update
runtimeCacheStore.setCached(delayedRundownCacheKey, cachedRundown);
await DataProvider.setRundown(persistedRundown);
}
/**
* Calculates all delays in a given rundown
* @param rundown
@@ -214,11 +234,11 @@ export function calculateRuntimeDelays(rundown: OntimeRundown) {
const updatedRundown = [...rundown];
for (const [index, event] of updatedRundown.entries()) {
if (event.type === SupportedEvent.Delay) {
if (isOntimeDelay(event)) {
accumulatedDelay += event.duration;
} else if (event.type === SupportedEvent.Block) {
} else if (isOntimeBlock(event)) {
accumulatedDelay = 0;
} else if (event.type === SupportedEvent.Event) {
} else if (isOntimeEvent(event)) {
updatedRundown[index] = {
...event,
delay: accumulatedDelay,
@@ -243,15 +263,15 @@ export function calculateRuntimeDelaysFromIndex(eventIndex: number, rundown: Ont
for (let i = eventIndex; i < rundown.length; i++) {
const event = rundown[i];
if (event.type === SupportedEvent.Delay) {
if (isOntimeDelay(event)) {
accumulatedDelay += event.duration;
} else if (event.type === SupportedEvent.Block) {
} else if (isOntimeBlock(event)) {
if (i === eventIndex) {
accumulatedDelay = 0;
} else {
break;
}
} else if (event.type === SupportedEvent.Event) {
} else if (isOntimeEvent(event)) {
updatedRundown[i] = {
...event,
delay: accumulatedDelay,
@@ -284,11 +304,11 @@ export function getDelayAt(eventIndex: number, rundown: OntimeRundown): number {
// we need to check the event before
const event = rundown[eventIndex - 1];
if (event.type === SupportedEvent.Delay) {
if (isOntimeDelay(event)) {
return event.duration + getDelayAt(eventIndex - 1, rundown);
} else if (event.type === SupportedEvent.Block) {
} else if (isOntimeBlock(event)) {
return 0;
} else if (event.type === SupportedEvent.Event) {
} else if (isOntimeEvent(event)) {
return event.delay ?? 0;
}
return 0;