πΊπΈ English | π·πΊ Π ΡΡΡΠΊΠΈΠΉ
Managed collection of items with hooks, events, and strict type safety.
- Strongly-typed collection with a configurable primary key
- Lifecycle hooks (
insert,patch,remove,clear) with before/after stages - Event-driven updates via
onUpdatecallback andaddEventListener - Full support for
string,number, andbigintprimary keys - Zero runtime dependencies
npm install @webeach/collectionpnpm add @webeach/collectionyarn add @webeach/collectionNo build step needed β load directly in the browser via unpkg:
<script type="module">
import { Collection } from 'https://unpkg.com/@webeach/collection';
const users = new Collection({ primaryKey: 'id' });
users.appendItem({ id: 1, name: 'Alice' });
</script>import { Collection } from '@webeach/collection';
const users = new Collection({
primaryKey: 'id',
});
users.appendItem({ id: 1, firstName: 'Ivan', lastName: 'Petrov' });
users.appendItem({ id: 2, firstName: 'Jason', lastName: 'Statham' });
console.log(users.numItems); // 2
console.log(users.getItem(2)?.firstName); // 'Jason'import { Collection } from '@webeach/collection';
const products = new Collection({ primaryKey: 'sku' });
products.appendItem({ sku: 'A001', name: 'Laptop' });
products.replaceItem('A001', { sku: 'A001', name: 'Laptop Pro' });
console.log(products.getItem('A001')?.name); // 'Laptop Pro'import { Collection } from '@webeach/collection';
const tasks = new Collection({
primaryKey: 'id',
initialItems: [
{ id: 1, title: 'Task 1' },
{ id: 2, title: 'Task 2' },
],
});
tasks.setItems([
{ id: 3, title: 'New Task 3' },
{ id: 4, title: 'New Task 4' },
]);
console.log(tasks.numItems); // 2
console.log(tasks.getItem(3)?.title); // 'New Task 3'import { Collection } from '@webeach/collection';
const list = new Collection({ primaryKey: 'id' });
list.onUpdate = (event) => {
console.log('Items updated:', event.detail);
};
// Or via addEventListener
list.addEventListener('update', (event) => {
console.log('Items updated:', event.detail);
});
list.appendItem({ id: 1, name: 'Alice' });import {
Collection,
$CollectionHookDispatcherSymbol,
} from '@webeach/collection';
const users = new Collection({ primaryKey: 'id' });
// Block insertion of items with even ids
const { unregister } = users[$CollectionHookDispatcherSymbol].register(
'insert:before',
({ item }) => {
if (item.id % 2 === 0) {
return false; // cancel insertion
}
},
);
users.appendItem({ id: 1, name: 'Alice' }); // succeeds
users.appendItem({ id: 2, name: 'Bob' }); // blocked
console.log(users.numItems); // 1
unregister();- constructor
- Methods
- Properties
- Hooks
- constructor
- Inherits CustomEvent API
The collection is fully generic and infers types based on the primary key and item shape.
import { Collection } from '@webeach/collection';
interface User {
id: number;
name: string;
role: 'admin' | 'user';
}
const users = new Collection<'id', number, User>({
primaryKey: 'id',
});
users.appendItem({ id: 1, name: 'Alice', role: 'admin' });
const user = users.getItem(1);
// user: CollectionItem<'id', number, User> | nullimport { FC, useEffect, useRef, useState } from 'react';
import { Collection } from '@webeach/collection';
interface Task {
id: number;
title: string;
done: boolean;
}
export const TaskList: FC = () => {
const collectionRef = useRef(
new Collection<'id', number, Task>({ primaryKey: 'id' }),
);
const [tasks, setTasks] = useState<Task[]>([]);
useEffect(() => {
const collection = collectionRef.current;
collection.onUpdate = (event) => {
setTasks([...event.detail] as Task[]);
};
collection.appendItem({ id: 1, title: 'Buy groceries', done: false });
collection.appendItem({ id: 2, title: 'Write tests', done: false });
}, []);
const toggle = (id: number) => {
const item = collectionRef.current.getItem(id);
if (item) {
collectionRef.current.patchItem(id, { done: !item.done });
}
};
return (
<ul>
{tasks.map((task) => (
<li key={task.id} onClick={() => toggle(task.id)}>
{task.done ? 'β' : 'β'} {task.title}
</li>
))}
</ul>
);
};import {
Collection,
$CollectionHookDispatcherSymbol,
} from '@webeach/collection';
function createBoundedCollection<T extends { id: number }>(maxSize: number) {
const collection = new Collection<'id', number, T>({ primaryKey: 'id' });
collection[$CollectionHookDispatcherSymbol].register('insert:before', () => {
if (collection.numItems >= maxSize) {
return false;
}
});
return collection;
}
const limited = createBoundedCollection(3);
limited.appendItem({ id: 1 }); // ok
limited.appendItem({ id: 2 }); // ok
limited.appendItem({ id: 3 }); // ok
limited.appendItem({ id: 4 }); // blocked β limit reached
console.log(limited.numItems); // 3Development and support: Ruslan Martynov
If you have suggestions or found a bug, feel free to open an issue or submit a pull request.
This package is distributed under the MIT License.