From 426d42ab630e8a0ebc95540e791402f09349b9da Mon Sep 17 00:00:00 2001 From: Carlos Valente Date: Thu, 11 Dec 2025 17:59:46 +0100 Subject: [PATCH] refactor: add guards around trivial index checks --- .../server/src/api-data/automation/automation.dao.ts | 12 +++++++++--- apps/server/src/api-data/rundown/rundown.dao.ts | 3 +++ apps/server/src/api-data/sheets/googleApi.utils.ts | 5 +++-- .../server/src/classes/data-provider/DataProvider.ts | 4 +++- .../src/services/project-service/ProjectService.ts | 4 ++++ apps/server/src/services/rollUtils.ts | 4 +++- apps/server/src/stores/runtimeState.ts | 11 ++++++++++- 7 files changed, 35 insertions(+), 8 deletions(-) diff --git a/apps/server/src/api-data/automation/automation.dao.ts b/apps/server/src/api-data/automation/automation.dao.ts index 63bbcd61a..409571b1a 100644 --- a/apps/server/src/api-data/automation/automation.dao.ts +++ b/apps/server/src/api-data/automation/automation.dao.ts @@ -72,9 +72,13 @@ export async function editTrigger(id: string, newTrigger: TriggerDTO): Promise trigger.automationId === automationId); if (triggers.length) { + const firstTrigger = triggers[0]; + const triggerTitle = firstTrigger?.title ?? 'Unknown trigger'; throw new Error( - `Unable to delete automation used in trigger ${triggers[0].title}${triggers.length > 1 ? ` and ${triggers.length - 1} more` : ''}`, + `Unable to delete automation used in trigger ${triggerTitle}${triggers.length > 1 ? ` and ${triggers.length - 1} more` : ''}`, ); } diff --git a/apps/server/src/api-data/rundown/rundown.dao.ts b/apps/server/src/api-data/rundown/rundown.dao.ts index 95dd7231e..323add503 100644 --- a/apps/server/src/api-data/rundown/rundown.dao.ts +++ b/apps/server/src/api-data/rundown/rundown.dao.ts @@ -213,6 +213,9 @@ function add(rundown: Rundown, entry: OntimeEntry, afterId: EntryId | null, pare */ function edit(rundown: Rundown, patch: PatchWithId): { entry: OntimeEntry; didInvalidate: boolean } { const entry = rundown.entries[patch.id]; + if (!entry) { + throw new Error(`Entry with id ${patch.id} not found`); + } // apply the patch and replace the entry const newEntry = applyPatchToEntry(entry, patch); diff --git a/apps/server/src/api-data/sheets/googleApi.utils.ts b/apps/server/src/api-data/sheets/googleApi.utils.ts index f4d3ff846..7f06d550a 100644 --- a/apps/server/src/api-data/sheets/googleApi.utils.ts +++ b/apps/server/src/api-data/sheets/googleApi.utils.ts @@ -31,8 +31,9 @@ export function catchCommonImportXlsxError(error: any) { isGoogleApiError(error) && error.code === 400 && Array.isArray(error.errors) && - error.errors[0].reason === 'failedPrecondition' && - error.errors[0].message === 'This operation is not supported for this document' + error.errors.length > 0 && + error.errors[0]?.reason === 'failedPrecondition' && + error.errors[0]?.message === 'This operation is not supported for this document' ) { throw new Error('Cannot read the linked file as a Google Sheet. It may be an .xlsx file instead.'); } diff --git a/apps/server/src/classes/data-provider/DataProvider.ts b/apps/server/src/classes/data-provider/DataProvider.ts index c7c762e1f..915b10bf2 100644 --- a/apps/server/src/classes/data-provider/DataProvider.ts +++ b/apps/server/src/classes/data-provider/DataProvider.ts @@ -150,7 +150,9 @@ async function setAutomation(newData: AutomationSettings): ReadonlyPromise { if (!(rundownKey in db.data.rundowns)) throw new Error(`Rundown with id: ${rundownKey} not found`); - return db.data.rundowns[rundownKey]; + const rundown = db.data.rundowns[rundownKey]; + if (!rundown) throw new Error(`Rundown with id: ${rundownKey} not found`); + return rundown; } async function deleteRundown(rundownKey: string): Promise { diff --git a/apps/server/src/services/project-service/ProjectService.ts b/apps/server/src/services/project-service/ProjectService.ts index 6bfbee029..73ace8b7f 100644 --- a/apps/server/src/services/project-service/ProjectService.ts +++ b/apps/server/src/services/project-service/ProjectService.ts @@ -94,6 +94,10 @@ async function loadProject(projectData: DatabaseModel, fileName: string, rundown ? projectData.rundowns[rundownId] : getFirstRundown(projectData.rundowns); + if (!rundown) { + throw new Error('No rundown found in project'); + } + await initRundown(rundown, projectData.customFields, true); // persist the project selection diff --git a/apps/server/src/services/rollUtils.ts b/apps/server/src/services/rollUtils.ts index 5c075d5cb..b20d5bb3a 100644 --- a/apps/server/src/services/rollUtils.ts +++ b/apps/server/src/services/rollUtils.ts @@ -28,7 +28,9 @@ export function loadRoll( let daySpan = 0; for (let i = 0; i < metadata.playableEventOrder.length; i++) { - const event = rundown.entries[metadata.playableEventOrder[i]] as PlayableEvent; + const eventId = metadata.playableEventOrder[i]; + if (!eventId) continue; + const event = rundown.entries[eventId] as PlayableEvent; // rolling into events of 0 duration would make the playback be stuck if (event.duration === 0) { continue; diff --git a/apps/server/src/stores/runtimeState.ts b/apps/server/src/stores/runtimeState.ts index 14dd6eda5..f54f5b5fa 100644 --- a/apps/server/src/stores/runtimeState.ts +++ b/apps/server/src/stores/runtimeState.ts @@ -276,7 +276,12 @@ export function loadNow( return; } - const event = rundown.entries[metadata.timedEventOrder[eventIndex]] as PlayableEvent; + const eventId = metadata.timedEventOrder[eventIndex]; + if (!eventId) { + runtimeState.eventNow = null; + return; + } + const event = rundown.entries[eventId] as PlayableEvent; runtimeState.rundown.selectedEventIndex = eventIndex; runtimeState.eventNow = event; } @@ -302,6 +307,10 @@ export function loadNext( return; } const nextId = metadata.playableEventOrder[nowPlayableIndex + 1]; + if (!nextId) { + runtimeState.eventNext = null; + return; + } runtimeState.eventNext = rundown.entries[nextId] as PlayableEvent; }