170 lines
4.1 KiB
TypeScript
170 lines
4.1 KiB
TypeScript
/**
|
|
* Generic entity store using Svelte 5's reactive SvelteMap
|
|
*
|
|
* Provides O(1) lookups by ID and granular reactivity for entity collections.
|
|
* Ideal for managing collections of objects with unique identifiers.
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* interface User extends Entity {
|
|
* id: string;
|
|
* name: string;
|
|
* }
|
|
*
|
|
* const store = createEntityStore<User>([
|
|
* { id: '1', name: 'Alice' },
|
|
* { id: '2', name: 'Bob' }
|
|
* ]);
|
|
*
|
|
* // Access is reactive in Svelte components
|
|
* const allUsers = store.all;
|
|
* const alice = store.getById('1');
|
|
* ```
|
|
*/
|
|
|
|
import { SvelteMap } from 'svelte/reactivity';
|
|
|
|
/**
|
|
* Base entity interface requiring an ID field
|
|
*/
|
|
export interface Entity {
|
|
/**
|
|
* Unique identifier for the entity
|
|
*/
|
|
id: string;
|
|
}
|
|
|
|
/**
|
|
* Reactive entity store with O(1) lookups
|
|
*
|
|
* Uses SvelteMap internally for reactive state that automatically
|
|
* triggers updates when entities are added, removed, or modified.
|
|
*/
|
|
export class EntityStore<T extends Entity> {
|
|
/**
|
|
* Reactive map of entities keyed by ID
|
|
*/
|
|
#entities = new SvelteMap<string, T>();
|
|
|
|
/**
|
|
* Creates a new entity store with optional initial data
|
|
* @param initialEntities - Initial entities to populate the store
|
|
*/
|
|
constructor(initialEntities: T[] = []) {
|
|
this.setAll(initialEntities);
|
|
}
|
|
|
|
/**
|
|
* Get all entities as an array
|
|
* @returns Array of all entities in the store
|
|
*/
|
|
get all() {
|
|
return Array.from(this.#entities.values());
|
|
}
|
|
|
|
/**
|
|
* Get a single entity by ID
|
|
* @param id - Entity ID to look up
|
|
* @returns The entity if found, undefined otherwise
|
|
*/
|
|
getById(id: string) {
|
|
return this.#entities.get(id);
|
|
}
|
|
|
|
/**
|
|
* Get multiple entities by their IDs
|
|
* @param ids - Array of entity IDs to look up
|
|
* @returns Array of found entities (undefined IDs are filtered out)
|
|
*/
|
|
getByIds(ids: string[]) {
|
|
return ids.map(id => this.#entities.get(id)).filter((e): e is T => !!e);
|
|
}
|
|
|
|
/**
|
|
* Add a single entity to the store
|
|
* @param entity - Entity to add (updates if ID already exists)
|
|
*/
|
|
addOne(entity: T) {
|
|
this.#entities.set(entity.id, entity);
|
|
}
|
|
|
|
/**
|
|
* Add multiple entities to the store
|
|
* @param entities - Array of entities to add
|
|
*/
|
|
addMany(entities: T[]) {
|
|
entities.forEach(e => this.addOne(e));
|
|
}
|
|
|
|
/**
|
|
* Update an existing entity by merging changes
|
|
* @param id - ID of entity to update
|
|
* @param changes - Partial changes to merge into existing entity
|
|
*/
|
|
updateOne(id: string, changes: Partial<T>) {
|
|
const entity = this.#entities.get(id);
|
|
if (entity) {
|
|
this.#entities.set(id, { ...entity, ...changes });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove a single entity by ID
|
|
* @param id - ID of entity to remove
|
|
*/
|
|
removeOne(id: string) {
|
|
this.#entities.delete(id);
|
|
}
|
|
|
|
/**
|
|
* Remove multiple entities by their IDs
|
|
* @param ids - Array of entity IDs to remove
|
|
*/
|
|
removeMany(ids: string[]) {
|
|
ids.forEach(id => this.#entities.delete(id));
|
|
}
|
|
|
|
/**
|
|
* Replace all entities in the store
|
|
* Clears existing entities and adds new ones
|
|
* @param entities - New entities to populate the store with
|
|
*/
|
|
setAll(entities: T[]) {
|
|
this.#entities.clear();
|
|
this.addMany(entities);
|
|
}
|
|
|
|
/**
|
|
* Check if an entity exists in the store
|
|
* @param id - Entity ID to check
|
|
* @returns true if entity exists, false otherwise
|
|
*/
|
|
has(id: string) {
|
|
return this.#entities.has(id);
|
|
}
|
|
|
|
/**
|
|
* Remove all entities from the store
|
|
*/
|
|
clear() {
|
|
this.#entities.clear();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a new entity store instance
|
|
* @param initialEntities - Initial entities to populate the store with
|
|
* @returns A new EntityStore instance
|
|
*
|
|
* @example
|
|
* ```ts
|
|
* const store = createEntityStore([
|
|
* { id: '1', name: 'Item 1' },
|
|
* { id: '2', name: 'Item 2' }
|
|
* ]);
|
|
* ```
|
|
*/
|
|
export function createEntityStore<T extends Entity>(initialEntities: T[] = []) {
|
|
return new EntityStore<T>(initialEntities);
|
|
}
|