diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts index 03fffcde3..4d31c1943 100644 --- a/apps/server/src/app.ts +++ b/apps/server/src/app.ts @@ -208,8 +208,9 @@ export const startServer = async ( expressServer.listen(serverPort, '0.0.0.0', () => { const nif = getNetworkInterfaces(); consoleSuccess(`Local: http://localhost:${serverPort}/editor`); - for (const key of Object.keys(nif)) { - consoleSuccess(`Network: http://${nif[key].address}:${serverPort}/editor`); + for (const key in nif) { + const address = nif[key].address; + consoleSuccess(`Network: http://${address}:${serverPort}/editor`); } }); diff --git a/apps/server/src/classes/Logger.ts b/apps/server/src/classes/Logger.ts index 5c29f7b36..972f1b152 100644 --- a/apps/server/src/classes/Logger.ts +++ b/apps/server/src/classes/Logger.ts @@ -8,7 +8,7 @@ import { consoleSubdued, consoleRed } from '../utils/console.js'; class Logger { private queue: Log[]; - private escalateErrorFn: (error: string) => void | null; + private escalateErrorFn: ((error: string) => void) | null; private canLog = false; constructor() { @@ -20,7 +20,7 @@ class Logger { /** * Enabling setup logger after init */ - init(escalateErrorFn: (error: string) => void) { + init(escalateErrorFn?: (error: string) => void) { // flush logs from queue this.queue.forEach((log) => { this._push(log); diff --git a/apps/server/src/classes/data-provider/__tests__/DataProvider.test.ts b/apps/server/src/classes/data-provider/__tests__/DataProvider.test.ts index 56bdd2d0b..f4fc625ea 100644 --- a/apps/server/src/classes/data-provider/__tests__/DataProvider.test.ts +++ b/apps/server/src/classes/data-provider/__tests__/DataProvider.test.ts @@ -1,4 +1,4 @@ -import { DatabaseModel, OntimeRundown, Settings, URLPreset } from 'ontime-types'; +import { DatabaseModel, OntimeRundown, Settings, URLPreset, ViewSettings } from 'ontime-types'; import { safeMerge } from '../DataProvider.utils.js'; describe('safeMerge', () => { @@ -49,7 +49,7 @@ describe('safeMerge', () => { } as DatabaseModel; it('returns existing data if new data is not provided', () => { - const mergedData = safeMerge(existing, undefined); + const mergedData = safeMerge(existing, {}); expect(mergedData).toEqual(existing); }); @@ -136,6 +136,7 @@ describe('safeMerge', () => { rundown: [], project: { title: '', + description: '', publicUrl: '', publicInfo: '', backstageUrl: '', @@ -153,8 +154,9 @@ describe('safeMerge', () => { viewSettings: { overrideStyles: false, endMessage: '', - }, + } as ViewSettings, urlPresets: [], + customFields: {}, osc: { portIn: 8888, portOut: 9999, @@ -163,6 +165,10 @@ describe('safeMerge', () => { enabledOut: false, subscriptions: [], }, + http: { + enabledOut: false, + subscriptions: [], + }, } as DatabaseModel; const newData = { diff --git a/apps/server/src/services/rundown-service/__tests__/rundownCache.test.ts b/apps/server/src/services/rundown-service/__tests__/rundownCache.test.ts index a8205b314..3b9e729f6 100644 --- a/apps/server/src/services/rundown-service/__tests__/rundownCache.test.ts +++ b/apps/server/src/services/rundown-service/__tests__/rundownCache.test.ts @@ -361,7 +361,7 @@ describe('remove() mutation', () => { ]; const { newRundown } = remove({ eventIds: ['1', '2', '3'], persistedRundown: testRundown }); expect(newRundown.length).toBe(3); - expect(newRundown.at(0).id).toBe('4'); + expect(newRundown.at(0)?.id).toBe('4'); }); }); diff --git a/apps/server/src/services/runtime-service/RuntimeService.ts b/apps/server/src/services/runtime-service/RuntimeService.ts index 65d5f24ef..bfff3f4a4 100644 --- a/apps/server/src/services/runtime-service/RuntimeService.ts +++ b/apps/server/src/services/runtime-service/RuntimeService.ts @@ -85,7 +85,7 @@ class RuntimeService { integrationService.dispatch(TimerLifeCycle.onUpdate); }); - this.lastIntegrationTimerValue = newState.timer.current; + this.lastIntegrationTimerValue = newState.timer.current ?? -1; } const shouldUpdateClock = getShouldClockUpdate(this.lastIntegrationClockUpdate, newState.clock); diff --git a/apps/server/src/services/sheet-service/SheetService.ts b/apps/server/src/services/sheet-service/SheetService.ts index 5a796ee86..0b7950283 100644 --- a/apps/server/src/services/sheet-service/SheetService.ts +++ b/apps/server/src/services/sheet-service/SheetService.ts @@ -5,6 +5,7 @@ */ import { AuthenticationStatus, CustomFields, LogOrigin, MaybeString, OntimeRundown } from 'ontime-types'; +import { ImportMap, getErrorMessage } from 'ontime-utils'; import { sheets, sheets_v4 } from '@googleapis/sheets'; import { Credentials, OAuth2Client } from 'google-auth-library'; @@ -13,7 +14,6 @@ import got from 'got'; import { resolveSheetsDirectory } from '../../setup/index.js'; import { ensureDirectory } from '../../utils/fileManagement.js'; import { cellRequestFromEvent, type ClientSecret, getA1Notation, validateClientSecret } from './sheetUtils.js'; -import { ImportMap } from 'ontime-utils'; import { parseExcel } from '../../utils/parser.js'; import { logger } from '../../classes/Logger.js'; import { parseCustomFields, parseRundown } from '../../utils/parserFunctions.js'; @@ -208,7 +208,8 @@ async function verifySheet( }); return { worksheetOptions: spreadsheets.data.sheets.map((i) => i.properties.title) }; } catch (error) { - throw new Error(`Failed to verify sheet: ${error.message}`); + const errorMessage = getErrorMessage(error); + throw new Error(`Failed to verify sheet: ${errorMessage}`); } } diff --git a/apps/server/src/services/sheet-service/__tests__/sheetUtils.test.ts b/apps/server/src/services/sheet-service/__tests__/sheetUtils.test.ts index 08b0d68af..3304c74c1 100644 --- a/apps/server/src/services/sheet-service/__tests__/sheetUtils.test.ts +++ b/apps/server/src/services/sheet-service/__tests__/sheetUtils.test.ts @@ -60,7 +60,7 @@ describe('cellRequestFromEvent()', () => { timeDanger: { row: 1, col: 41 }, }; const result = cellRequestFromEvent(event, 1, 1234, metadata); - expect(result.updateCells.rows[0].values[5].userEnteredValue.stringValue).toStrictEqual(event.note); + expect(result.updateCells?.rows?.at(0)?.values?.at(5)?.userEnteredValue?.stringValue).toStrictEqual(event.note); }); test('number to timer', () => { @@ -103,9 +103,10 @@ describe('cellRequestFromEvent()', () => { timeWarning: { row: 1, col: 40 }, timeDanger: { row: 1, col: 41 }, }; - const result = cellRequestFromEvent(event, 1, 1234, metadata).updateCells.rows[0].values[10].userEnteredValue - .stringValue; - expect(result).toStrictEqual(millisToString(event.duration)); + const result = cellRequestFromEvent(event, 1, 1234, metadata); + expect(result.updateCells?.rows?.at(0)?.values?.at(10)?.userEnteredValue?.stringValue).toStrictEqual( + millisToString(event.duration), + ); }); test('boolean to TRUE', () => { @@ -149,8 +150,8 @@ describe('cellRequestFromEvent()', () => { timeDanger: { row: 1, col: 41 }, }; const result = cellRequestFromEvent(event, 1, 1234, metadata); - expect(result.updateCells.rows[0].values[11].userEnteredValue.boolValue).toStrictEqual(true); - expect(result.updateCells.rows[0].values[12].userEnteredValue.boolValue).toStrictEqual(false); + expect(result.updateCells?.rows?.at(0)?.values?.at(11)?.userEnteredValue?.boolValue).toStrictEqual(true); + expect(result.updateCells?.rows?.at(0)?.values?.at(12)?.userEnteredValue?.boolValue).toStrictEqual(false); }); test('spacing in metadata', () => { @@ -180,8 +181,8 @@ describe('cellRequestFromEvent()', () => { title: { row: 1, col: 6 }, }; const result = cellRequestFromEvent(event, 1, 1234, metadata); - expect(result.updateCells.rows[0].values[0].userEnteredValue.stringValue).toStrictEqual(event.cue); - expect(result.updateCells.rows[0].values[6].userEnteredValue.stringValue).toStrictEqual(event.title); + expect(result.updateCells?.rows?.at(0)?.values?.at(0)?.userEnteredValue?.stringValue).toStrictEqual(event.cue); + expect(result.updateCells?.rows?.at(0)?.values?.at(6)?.userEnteredValue?.stringValue).toStrictEqual(event.title); }); test('metadata offset from zero', () => { @@ -212,8 +213,8 @@ describe('cellRequestFromEvent()', () => { user0: { row: 1, col: 16 }, }; const result = cellRequestFromEvent(event, 1, 1234, metadata); - expect(result.updateCells.rows[0].values[0].userEnteredValue.stringValue).toStrictEqual(event.cue); - expect(result.updateCells.rows[0].values[1].userEnteredValue.stringValue).toStrictEqual(event.title); + expect(result.updateCells?.rows?.at(0)?.values?.at(0)?.userEnteredValue?.stringValue).toStrictEqual(event.cue); + expect(result.updateCells?.rows?.at(0)?.values?.at(1)?.userEnteredValue?.stringValue).toStrictEqual(event.title); }); test('sheet setup', () => { @@ -243,10 +244,10 @@ describe('cellRequestFromEvent()', () => { title: { row: 10, col: 6 }, }; const result1 = cellRequestFromEvent(event, 1, 1234, metadata); - expect(result1.updateCells.start.sheetId).toStrictEqual(1234); + expect(result1.updateCells?.start?.sheetId).toStrictEqual(1234); const result2 = cellRequestFromEvent(event, 10, 1234, metadata); - expect(result2.updateCells.start.rowIndex).toStrictEqual(21); - expect(result2.updateCells.start.columnIndex).toStrictEqual(5); - expect(result2.updateCells.fields).toStrictEqual('userEnteredValue'); + expect(result2.updateCells?.start?.rowIndex).toStrictEqual(21); + expect(result2.updateCells?.start?.columnIndex).toStrictEqual(5); + expect(result2.updateCells?.fields).toStrictEqual('userEnteredValue'); }); }); diff --git a/apps/server/src/stores/__tests__/runtimeState.test.ts b/apps/server/src/stores/__tests__/runtimeState.test.ts index 8cbe7bdf8..2e4e06052 100644 --- a/apps/server/src/stores/__tests__/runtimeState.test.ts +++ b/apps/server/src/stores/__tests__/runtimeState.test.ts @@ -160,6 +160,10 @@ describe('mutation on runtimeState', () => { start(); newState = getState(); const firstStart = newState.clock; + if (newState.runtime.offset === null) { + throw new Error('Value cannot be null at this stage'); + } + expect(newState.runtime.actualStart).toBe(newState.clock); expect(newState.runtime.offset).toBe(event1.timeStart - newState.clock); expect(newState.runtime.expectedEnd).toBe(event2.timeEnd - newState.runtime.offset); @@ -167,7 +171,11 @@ describe('mutation on runtimeState', () => { // 3. Next event load(event2, [event1, event2]); start(); + newState = getState(); + if (newState.runtime.actualStart === null || newState.runtime.offset === null) { + throw new Error('Value cannot be null at this stage'); + } // there is a case where the calculation time overflows the millisecond which makes // tests fail @@ -182,6 +190,10 @@ describe('mutation on runtimeState', () => { // 4. Add time addTime(10); newState = getState(); + if (newState.runtime.offset === null) { + throw new Error('Value cannot be null at this stage'); + } + expect(newState.runtime.offset).toBe(delayBefore - 10); expect(newState.runtime.expectedEnd).toBe(event2.timeEnd - newState.runtime.offset); diff --git a/apps/server/src/utils/__tests__/parserUtils.test.ts b/apps/server/src/utils/__tests__/parserUtils.test.ts index 79f8f5db5..d5d1430d4 100644 --- a/apps/server/src/utils/__tests__/parserUtils.test.ts +++ b/apps/server/src/utils/__tests__/parserUtils.test.ts @@ -30,7 +30,7 @@ describe('mergeObject()', () => { test('it handles falsy values', () => { const a = { first: 'yes', - second: 'yes', + second: 'yes' as string | null, third: 'yes', }; const b = { diff --git a/apps/server/src/utils/networkInterfaces.ts b/apps/server/src/utils/networkInterfaces.ts index b1dfdd3d0..138db62c9 100644 --- a/apps/server/src/utils/networkInterfaces.ts +++ b/apps/server/src/utils/networkInterfaces.ts @@ -9,7 +9,11 @@ export function getNetworkInterfaces(): { name: string; address: string }[] { const results: { name: string; address: string }[] = []; for (const name of Object.keys(nets)) { - for (const net of nets[name]) { + const netObjects = nets[name]; + if (!netObjects) { + continue; + } + for (const net of netObjects) { // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses if (net.family === 'IPv4' && !net.internal) { results.push({ diff --git a/apps/server/src/utils/parserUtils.ts b/apps/server/src/utils/parserUtils.ts index 90a128083..02ff56d2b 100644 --- a/apps/server/src/utils/parserUtils.ts +++ b/apps/server/src/utils/parserUtils.ts @@ -53,7 +53,7 @@ export function mergeObject(a: T, b: Partial): T { } if (typeof bValue === 'object' && bValue !== null && typeof aValue === 'object' && aValue !== null) { - // @ts-expect-error -- library side, ignore for now + // @ts-expect-error -- not sure how to type this merged[key] = deepmerge(aValue, bValue); } else if (bValue !== undefined) { merged[key] = bValue; diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json index 0e9b4333b..8afa826ae 100644 --- a/apps/server/tsconfig.json +++ b/apps/server/tsconfig.json @@ -1,5 +1,16 @@ { "compilerOptions": { + "alwaysStrict": true, + "strictNullChecks": false, + "strictBindCallApply": true, + "strictFunctionTypes": false, + "strictPropertyInitialization": false, + "noImplicitAny": false, + "noImplicitThis": false, + "useUnknownInCatchVariables": true, + "noImplicitOverride": true, + "noUncheckedIndexedAccess": true, + "noFallthroughCasesInSwitch": true, "target": "ESNext", "module": "Node16", "moduleResolution": "Node16",