import type { Draft } from 'immer' import { current, isDraft } from 'immer' import type { DraftableEntityState, EntityId, IdSelector, Update, } from './models' export function selectIdValue( entity: T, selectId: IdSelector, ) { const key = selectId(entity) if (process.env.NODE_ENV !== 'production' && key === undefined) { console.warn( 'The entity passed to the `selectId` implementation returned undefined.', 'You should probably provide your own `selectId` implementation.', 'The entity that was passed:', entity, 'The `selectId` implementation:', selectId.toString(), ) } return key } export function ensureEntitiesArray( entities: readonly T[] | Record, ): readonly T[] { if (!Array.isArray(entities)) { entities = Object.values(entities) } return entities } export function getCurrent(value: T | Draft): T { return (isDraft(value) ? current(value) : value) as T } export function splitAddedUpdatedEntities( newEntities: readonly T[] | Record, selectId: IdSelector, state: DraftableEntityState, ): [T[], Update[], Id[]] { newEntities = ensureEntitiesArray(newEntities) const existingIdsArray = getCurrent(state.ids) const existingIds = new Set(existingIdsArray) const added: T[] = [] const addedIds = new Set([]) const updated: Update[] = [] for (const entity of newEntities) { const id = selectIdValue(entity, selectId) if (existingIds.has(id) || addedIds.has(id)) { updated.push({ id, changes: entity }) } else { addedIds.add(id) added.push(entity) } } return [added, updated, existingIdsArray] }