import { ProjectData, ViewSettings, DatabaseModel, Settings, CustomFields, URLPreset, AutomationSettings, Rundown, ProjectRundowns, } from 'ontime-types'; import type { Low } from 'lowdb'; import { JSONFilePreset } from 'lowdb/node'; import { isPath } from '../../utils/fileManagement.js'; import { shouldCrashDev } from '../../utils/development.js'; import { isTest } from '../../setup/environment.js'; import { safeMerge } from './DataProvider.utils.js'; type ReadonlyPromise = Promise>; let db = {} as Low; /** * Initialises the JSON adapter to persist data to a file */ export async function initPersistence(filePath: string, fallbackData: DatabaseModel) { // eslint-disable-next-line no-unused-labels -- dev code path DEV: shouldCrashDev(!isPath(filePath), 'initPersistence should be called with a path'); const newDb = await JSONFilePreset(filePath, fallbackData); // Read the database to initialize it newDb.data = fallbackData; await newDb.write(); await newDb.read(); db = newDb; } export function getDataProvider() { if (db === null) throw new Error('Database not initialized'); return { getData, setProjectData, getProjectData, setCustomFields, getCustomFields, setRundown, mergeRundown, getSettings, setSettings, getUrlPresets, setUrlPresets, getViewSettings, setViewSettings, getAutomation, setAutomation, getRundown, mergeIntoData, }; } function getData(): Readonly { return db.data; } async function setProjectData(newData: Partial): ReadonlyPromise { db.data.project = { ...structuredClone(db.data.project), ...structuredClone(newData) }; // Performing deep copy as we're updating / merging data await persist(); return db.data.project; } function getProjectData(): Readonly { return db.data.project; } async function setCustomFields(newData: CustomFields): ReadonlyPromise { db.data.customFields = { ...newData }; await persist(); return db.data.customFields; } async function mergeRundown( newCustomFields: CustomFields, newRundowns: ProjectRundowns, ): ReadonlyPromise<{ rundowns: ProjectRundowns; customFields: CustomFields }> { db.data.customFields = { ...db.data.customFields, ...newCustomFields }; Object.entries(newRundowns).forEach(([id, rundown]) => { // Note that entries with the same key will be overridden db.data.rundowns[id] = rundown; }); await persist(); return { rundowns: db.data.rundowns, customFields: db.data.customFields }; } function getCustomFields(): Readonly { return db.data.customFields; } async function setRundown(rundownKey: string, newData: Rundown): ReadonlyPromise { db.data.rundowns[rundownKey] = newData; await persist(); return db.data.rundowns[rundownKey]; } function getSettings(): Readonly { return db.data.settings; } async function setSettings(newData: Settings): ReadonlyPromise { db.data.settings = { ...newData }; await persist(); return db.data.settings; } function getUrlPresets(): Readonly { return db.data.urlPresets; } async function setUrlPresets(newData: URLPreset[]): ReadonlyPromise { db.data.urlPresets = newData; await persist(); return db.data.urlPresets; } function getViewSettings(): Readonly { return db.data.viewSettings; } async function setViewSettings(newData: ViewSettings): ReadonlyPromise { db.data.viewSettings = { ...newData }; await persist(); return db.data.viewSettings; } function getAutomation(): Readonly { return db.data.automation; } async function setAutomation(newData: AutomationSettings): ReadonlyPromise { db.data.automation = { ...newData }; await persist(); return db.data.automation; } function getRundown(): Readonly { const firstRundown = Object.keys(db.data.rundowns)[0]; return db.data.rundowns[firstRundown]; } async function mergeIntoData(newData: Partial): ReadonlyPromise { const mergedData = safeMerge(db.data, newData); db.data.project = mergedData.project; db.data.settings = mergedData.settings; db.data.viewSettings = mergedData.viewSettings; db.data.automation = mergedData.automation; db.data.urlPresets = mergedData.urlPresets; db.data.customFields = mergedData.customFields; db.data.rundowns = mergedData.rundowns; await persist(); return db.data; } /** * Handles persisting data to file */ async function persist() { if (isTest) return; await db.write(); }