import {Signal, signal} from "@preact/signals-core"; export type Entity = { id: string; }; export type EntityAccessor = { id: string; value: T; } function createReactiveProxy(entitySignal: Signal): T { return new Proxy({} as T, { get(_target, prop) { const current = entitySignal.value; const value = current[prop as keyof T]; if (typeof value === 'function') { return value.bind(current); } return value; }, set(_target, prop, value) { const current = entitySignal.value; entitySignal.value = { ...current, [prop]: value }; return true; }, ownKeys(_target) { return Reflect.ownKeys(entitySignal.value); }, getOwnPropertyDescriptor(_target, prop) { return Reflect.getOwnPropertyDescriptor(entitySignal.value, prop); }, }); } function createReactiveAccessor(id: string, entitySignal: Signal): EntityAccessor { const proxy = createReactiveProxy(entitySignal); return { id, get value() { return proxy; }, set value(value: T) { entitySignal.value = value; } } as EntityAccessor; } export function createEntityCollection() { const collection = signal({} as Record>); const remove = (...ids: string[]) => { collection.value = Object.fromEntries( Object.entries(collection.value).filter(([id]) => !ids.includes(id)), ); }; const add = (...entities: T[]) => { collection.value = { ...collection.value, ...Object.fromEntries(entities.map((entity) => [entity.id, signal(entity)])), }; }; const get = (id: string): EntityAccessor => { const entitySignal = collection.value[id]; if (!entitySignal) { return { id, get value() { return undefined as unknown as T; }, set value(_value: T) {} } as EntityAccessor; } return createReactiveAccessor(id, entitySignal); } return { collection, remove, add, get } }