Skip to content

Repository files navigation

collection

npm versionbuildnpm downloadslicensebundle size

🇺🇸 English | 🇷🇺 Русский

Managed collection of items with hooks, events, and strict type safety.


💎 Features

  • Strongly-typed collection with a configurable primary key
  • Lifecycle hooks (insert, patch, remove, clear) with before/after stages
  • Event-driven updates via onUpdate callback and addEventListener
  • Full support for string, number, and bigint primary keys
  • Zero runtime dependencies

📦 Installation

npm install @webeach/collection
pnpm add @webeach/collection
yarn add @webeach/collection

Browser via CDN

No build step needed — load directly in the browser via unpkg:

<scripttype="module">import{Collection}from'https://unpkg.com/@webeach/collection';constusers=newCollection({primaryKey: 'id'});users.appendItem({id: 1,name: 'Alice'});</script>

🚀 Quick Start

Adding items

import{Collection}from'@webeach/collection';constusers=newCollection({primaryKey: 'id',});users.appendItem({id: 1,firstName: 'Ivan',lastName: 'Petrov'});users.appendItem({id: 2,firstName: 'Jason',lastName: 'Statham'});console.log(users.numItems);// 2console.log(users.getItem(2)?.firstName);// 'Jason'

Replacing an item

import{Collection}from'@webeach/collection';constproducts=newCollection({primaryKey: 'sku'});products.appendItem({sku: 'A001',name: 'Laptop'});products.replaceItem('A001',{sku: 'A001',name: 'Laptop Pro'});console.log(products.getItem('A001')?.name);// 'Laptop Pro'

Bulk replacing items with setItems

import{Collection}from'@webeach/collection';consttasks=newCollection({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);// 2console.log(tasks.getItem(3)?.title);// 'New Task 3'

Listening for updates

import{Collection}from'@webeach/collection';constlist=newCollection({primaryKey: 'id'});list.onUpdate=(event)=>{console.log('Items updated:',event.detail);};// Or via addEventListenerlist.addEventListener('update',(event)=>{console.log('Items updated:',event.detail);});list.appendItem({id: 1,name: 'Alice'});

Using lifecycle hooks

import{Collection,$CollectionHookDispatcherSymbol,}from'@webeach/collection';constusers=newCollection({primaryKey: 'id'});// Block insertion of items with even idsconst{ unregister }=users[$CollectionHookDispatcherSymbol].register('insert:before',({ item })=>{if(item.id%2===0){returnfalse;// cancel insertion}},);users.appendItem({id: 1,name: 'Alice'});// succeedsusers.appendItem({id: 2,name: 'Bob'});// blockedconsole.log(users.numItems);// 1unregister();

🛠️ API

Collection

CollectionUpdateEvent


🧩 TypeScript

The collection is fully generic and infers types based on the primary key and item shape.

import{Collection}from'@webeach/collection';interfaceUser{id: number;name: string;role: 'admin'|'user';}constusers=newCollection<'id',number,User>({primaryKey: 'id',});users.appendItem({id: 1,name: 'Alice',role: 'admin'});constuser=users.getItem(1);// user: CollectionItem<'id', number, User> | null

📖 Real-world Examples

Tracking a list in React

import{FC,useEffect,useRef,useState}from'react';import{Collection}from'@webeach/collection';interfaceTask{id: number;title: string;done: boolean;}exportconstTaskList: FC=()=>{constcollectionRef=useRef(newCollection<'id',number,Task>({primaryKey: 'id'}),);const[tasks,setTasks]=useState<Task[]>([]);useEffect(()=>{constcollection=collectionRef.current;collection.onUpdate=(event)=>{setTasks([...event.detail]asTask[]);};collection.appendItem({id: 1,title: 'Buy groceries',done: false});collection.appendItem({id: 2,title: 'Write tests',done: false});},[]);consttoggle=(id: number)=>{constitem=collectionRef.current.getItem(id);if(item){collectionRef.current.patchItem(id,{done: !item.done});}};return(<ul>{tasks.map((task)=>(<likey={task.id}onClick={()=>toggle(task.id)}>{task.done ? '✓' : '○'}{task.title}</li>))}</ul>);};

Enforcing a max size via hook

import{Collection,$CollectionHookDispatcherSymbol,}from'@webeach/collection';functioncreateBoundedCollection<Textends{id: number}>(maxSize: number){constcollection=newCollection<'id',number,T>({primaryKey: 'id'});collection[$CollectionHookDispatcherSymbol].register('insert:before',()=>{if(collection.numItems>=maxSize){returnfalse;}});returncollection;}constlimited=createBoundedCollection(3);limited.appendItem({id: 1});// oklimited.appendItem({id: 2});// oklimited.appendItem({id: 3});// oklimited.appendItem({id: 4});// blocked — limit reachedconsole.log(limited.numItems);// 3

👨‍💻 Author

Development and support: Ruslan Martynov

If you have suggestions or found a bug, feel free to open an issue or submit a pull request.


📄 License

This package is distributed under the MIT License.

About

Managed collection of items with hooks, events, and strict type safety

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages