mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-25 08:59:13 +00:00
Make createTransaction's rundown deep-readonly when not mutable
Overload createTransaction() on the literal mutableRundown option: with mutableRundown: true it returns rundown: Rundown as before; otherwise it returns rundown: DeepReadonly<Rundown> (ts-essentials, already a convention in this codebase for read-only snapshots). Previously a non-mutable transaction's rundown was typed as plain Rundown even though it's the live cachedRundown reference itself (or a background rundown read straight from disk) - nothing stopped a future mutation function from writing into it outside the commit() flow, since Readonly<T> (used elsewhere for the same purpose) only blocks top-level reassignment, not nested writes like array.push() or entry.field = x. Scoped to rundown.dao.ts only: every existing call site in rundown.service.ts passes a literal mutableRundown: true, so this changes no call-site types. The one mutableRundown: false site doesn't destructure rundown at all. Verified with a throwaway probe file (removed) that mutating a non-mutable transaction's rundown is now a compile error, and that a mutable one still compiles as before. Typecheck, lint and full test suite (695 tests) pass unchanged.
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
||||
isPlayableEvent,
|
||||
} from 'ontime-types';
|
||||
import { addToRundown, createGroup, customFieldLabelToKey, getInsertAfterId, insertAtIndex } from 'ontime-utils';
|
||||
import type { DeepReadonly } from 'ts-essentials';
|
||||
|
||||
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
|
||||
import { consoleError } from '../../utils/console.js';
|
||||
@@ -80,9 +81,16 @@ export const getRundownMetadata = (): Readonly<RundownMetadata> => rundownMetada
|
||||
export const getProjectCustomFields = (): Readonly<CustomFields> => projectCustomFields;
|
||||
export const getEntryWithId = (entryId: EntryId): OntimeEntry | undefined => cachedRundown.entries[entryId];
|
||||
|
||||
type Transaction = {
|
||||
/**
|
||||
* @param R the type callers see for `rundown` - a plain, mutable `Rundown` when the
|
||||
* transaction was opened with `mutableRundown: true`, otherwise a `DeepReadonly<Rundown>`
|
||||
* so that accidentally mutating an entry (or an order array) on a non-mutable transaction
|
||||
* - which would silently corrupt the live cache without going through commit() - is a
|
||||
* compile-time error instead of a runtime bug.
|
||||
*/
|
||||
type Transaction<R> = {
|
||||
customFields: CustomFields;
|
||||
rundown: Rundown;
|
||||
rundown: R;
|
||||
|
||||
commit: (shouldProcess?: boolean) => Promise<{
|
||||
rundown: Readonly<Rundown>;
|
||||
@@ -103,7 +111,13 @@ type TransactionOptions = {
|
||||
rundownId?: string;
|
||||
};
|
||||
|
||||
export function createTransaction(options: TransactionOptions): Transaction {
|
||||
export function createTransaction(options: TransactionOptions & { mutableRundown: true }): Transaction<Rundown>;
|
||||
export function createTransaction(
|
||||
options: TransactionOptions & { mutableRundown?: false },
|
||||
): Transaction<DeepReadonly<Rundown>>;
|
||||
export function createTransaction(
|
||||
options: TransactionOptions,
|
||||
): Transaction<Rundown> | Transaction<DeepReadonly<Rundown>> {
|
||||
const targetId = options.rundownId ?? cachedRundown.id;
|
||||
const isLoaded = targetId === cachedRundown.id;
|
||||
const sourceRundown: Rundown = isLoaded ? cachedRundown : (getDataProvider().getRundown(targetId) as Rundown);
|
||||
|
||||
Reference in New Issue
Block a user