// Generated from types/*.ts — do not edit. // Regenerate with: npm run generate:typescript /** * Annotations Channel Reducer — Pure reducer for `AnnotationsState`. * * @module channels-annotations/reducer */ import { ActionType } from '../common/actions.js'; import type { AnnotationEntry, Annotation, AnnotationsState } from './state.js'; import type { AnnotationsAction } from '../action-origin.generated.js'; import { softAssertNever } from '../common/reducer-helpers.js'; /** * Pure reducer for annotations state. Handles every {@link AnnotationsAction} * variant. * * Per the spec, every annotations action is client-dispatchable; the reducer * runs identically on the client (optimistic, write-ahead) and the server. It * preserves the dispatch order of annotations (and of entries within an * annotation): new entries are appended; `*Set` actions with a matching id * replace in place, while actions whose target id is unknown are no-ops * (mirroring `changeset/fileRemoved` semantics). The single-entry * minimum invariant is enforced by producers, not the reducer — removing an * annotation's last entry via {@link AnnotationsEntryRemovedAction} (instead * of {@link AnnotationsRemovedAction}) would leave an empty annotation, * which is observable but not catastrophic. */ export function annotationsReducer(state: AnnotationsState, action: AnnotationsAction, log?: (msg: string) => void): AnnotationsState { switch (action.type) { case ActionType.AnnotationsSet: { const idx = state.annotations.findIndex(t => t.id === action.annotation.id); if (idx < 0) { return { ...state, annotations: [...state.annotations, action.annotation] }; } const next: Annotation[] = [...state.annotations]; next[idx] = action.annotation; return { ...state, annotations: next }; } case ActionType.AnnotationsUpdated: { const idx = state.annotations.findIndex(t => t.id === action.annotationId); if (idx < 0) { return state; } const annotation = state.annotations[idx]; const updated: Annotation = { ...annotation }; if (action.origin !== undefined) { updated.origin = action.origin; } if (action.resource !== undefined) { updated.resource = action.resource; } if (action.range !== undefined) { updated.range = action.range; } if (action.resolved !== undefined) { updated.resolved = action.resolved; } const next: Annotation[] = [...state.annotations]; next[idx] = updated; return { ...state, annotations: next }; } case ActionType.AnnotationsRemoved: { const idx = state.annotations.findIndex(t => t.id === action.annotationId); if (idx < 0) { return state; } const next: Annotation[] = [...state.annotations]; next.splice(idx, 1); return { ...state, annotations: next }; } case ActionType.AnnotationsEntrySet: { const tIdx = state.annotations.findIndex(t => t.id === action.annotationId); if (tIdx < 0) { return state; } const annotation = state.annotations[tIdx]; const cIdx = annotation.entries.findIndex(c => c.id === action.entry.id); let nextEntries: AnnotationEntry[]; if (cIdx < 0) { nextEntries = [...annotation.entries, action.entry]; } else { nextEntries = [...annotation.entries]; nextEntries[cIdx] = action.entry; } const nextAnnotations: Annotation[] = [...state.annotations]; nextAnnotations[tIdx] = { ...annotation, entries: nextEntries }; return { ...state, annotations: nextAnnotations }; } case ActionType.AnnotationsEntryRemoved: { const tIdx = state.annotations.findIndex(t => t.id === action.annotationId); if (tIdx < 0) { return state; } const annotation = state.annotations[tIdx]; const cIdx = annotation.entries.findIndex(c => c.id === action.entryId); if (cIdx < 0) { return state; } const nextEntries: AnnotationEntry[] = [...annotation.entries]; nextEntries.splice(cIdx, 1); const nextAnnotations: Annotation[] = [...state.annotations]; nextAnnotations[tIdx] = { ...annotation, entries: nextEntries }; return { ...state, annotations: nextAnnotations }; } default: softAssertNever(action, log); return state; } }