@procedures/collections
v0.1.0
Published
Collections for TypeScript: ArrayList, LinkedList, ArrayDeque, HashMap, LinkedHashMap, TreeMap (red-black), PriorityQueue, LRU and TTL maps, async queues, channels and collectors, tested with fast-check.
Maintainers
Readme
@procedures/collections
A comprehensive, transport-agnostic collections framework for TypeScript. Provides unified abstractions for data structures with pluggable storage backends, enabling seamless switching between local and remote storage.
Architecture Overview
┌─────────────────────────────────────────────────────────────────────────────┐
│ Application Layer │
│ │
│ ArrayList<T> HashMap<K,V> TreeSet<T> PriorityQueue<T> ... │
└─────────────────────────────────┬───────────────────────────────────────────┘
│
┌─────────────────────────────────▼───────────────────────────────────────────┐
│ Behavior Decorators │
│ │
│ Bounded LRU TTL Evented Synchronized ReadOnly Safe │
└─────────────────────────────────┬───────────────────────────────────────────┘
│
┌─────────────────────────────────▼───────────────────────────────────────────┐
│ CollectionStorage<T> │
│ │
│ get() set() delete() find() getAll() setBatch() deleteBatch() ... │
└─────────────────────────────────┬───────────────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ MemoryStorage │ │ ApiStorage │ │ HybridStorage │
│ │ │ │ │ │
│ In-memory Map │ │ Remote via RPC │ │ Local + Remote │
│ Fast, volatile │ │ Persistent │ │ With sync │
└─────────────────┘ └─────────────────┘ └─────────────────┘Key Design Principles
Transport Agnosticism
All storage methods return Promise<T>, ensuring client code cannot distinguish between local and remote storage:
// This code works identically whether storage is local or remote
const user = await storage.get("user-123");
await storage.set("user-456", { name: "Alice" });
const active = await storage.find(u => u.status === "active");Composable Behaviors
Behaviors can be stacked to add functionality:
import { ArrayList, withBounded, withLRU, withEvented } from "@procedures/collections";
// Create a bounded, LRU-evicting, event-emitting list
const list = withEvented(
withLRU(
withBounded(new ArrayList<User>(), { maxSize: 1000 }),
{ maxAge: 3600000 }
)
);
list.on("add", (item) => console.log("Added:", item));
list.on("evict", (item) => console.log("Evicted:", item));Installation
pnpm add @procedures/collectionsCore Interfaces
CollectionStorage
The foundational storage interface that all backends implement:
interface CollectionStorage<T> {
// Read Operations
get(id: string): Promise<T | undefined>;
getAll(): Promise<T[]>;
find(predicate: (item: T) => boolean): Promise<T[]>;
has(id: string): Promise<boolean>;
size(): Promise<number>;
// Write Operations
set(id: string, value: T): Promise<void>;
delete(id: string): Promise<boolean>;
clear(): Promise<void>;
// Bulk Operations
setBatch(items: Array<[string, T]>): Promise<void>;
deleteBatch(ids: string[]): Promise<number>;
getBatch(ids: string[]): Promise<Map<string, T>>;
// Lifecycle
close(): Promise<void>;
getMetadata?(): Promise<StorageMetadata>;
}Storage Backends
MemoryStorage
Fast, in-memory storage using JavaScript Map:
import { MemoryStorage } from "@procedures/collections";
const storage = new MemoryStorage<User>();
await storage.set("user-1", { name: "Alice", age: 30 });
const user = await storage.get("user-1");ApiStorage
Remote storage via RPC client:
import { ApiStorage } from "@procedures/collections";
import { Client } from "@procedures/client";
const client = new Client({ transport });
const storage = new ApiStorage<User>(client, {
collection: "users",
endpoints: {
get: { service: "users", operation: "get" },
set: { service: "users", operation: "set" },
// ... other endpoints
}
});HybridStorage
Local cache with remote synchronization:
import { HybridStorage, MemoryStorage, ApiStorage } from "@procedures/collections";
const hybrid = new HybridStorage<User>({
local: new MemoryStorage<User>(),
remote: apiStorage,
syncStrategy: "write-through", // or "write-behind"
conflictResolver: (local, remote) => remote.updatedAt > local.updatedAt ? remote : local
});Collection Implementations
Lists
import { ArrayList, LinkedList } from "@procedures/collections";
const arrayList = new ArrayList<number>(); // O(1) random access
const linkedList = new LinkedList<number>(); // O(1) insertionsMaps
import { HashMap, TreeMap, LinkedHashMap } from "@procedures/collections";
const hashMap = new HashMap<string, User>(); // O(1) access
const treeMap = new TreeMap<string, User>(); // Sorted keys
const linkedHashMap = new LinkedHashMap<string, User>(); // Insertion orderSets
import { HashSet, TreeSet } from "@procedures/collections";
const hashSet = new HashSet<string>(); // O(1) membership
const treeSet = new TreeSet<number>(); // Sorted, O(log n)Queues
import { PriorityQueue, ArrayDeque } from "@procedures/collections";
const pq = new PriorityQueue<Task>((a, b) => a.priority - b.priority);
const deque = new ArrayDeque<Message>(); // Double-ended queueBehavior Decorators
Bounded Collections
import { withBounded } from "@procedures/collections";
const bounded = withBounded(new ArrayList<Log>(), {
maxSize: 10000,
evictionPolicy: "oldest" // or "newest", "random"
});LRU Cache
import { withLRU } from "@procedures/collections";
const lru = withLRU(new HashMap<string, Session>(), {
maxSize: 1000,
maxAge: 3600000 // 1 hour TTL
});TTL (Time-To-Live)
import { withTTL } from "@procedures/collections";
const ttl = withTTL(new HashMap<string, Token>(), {
defaultTTL: 300000, // 5 minutes
checkInterval: 60000 // Check every minute
});Event Emission
import { withEvented } from "@procedures/collections";
const evented = withEvented(new ArrayList<Order>());
evented.on("add", (item, index) => audit.log("order_added", item));
evented.on("remove", (item) => audit.log("order_removed", item));
evented.on("clear", () => audit.log("orders_cleared"));Thread Safety (Synchronized)
import { withSynchronized } from "@procedures/collections";
const sync = withSynchronized(new HashMap<string, Counter>());
// All operations are automatically lockedAsync Utilities
AsyncQueue
import { AsyncQueue } from "@procedures/collections";
const queue = new AsyncQueue<Job>();
// Producer
await queue.enqueue({ id: 1, task: "process" });
// Consumer (blocks until item available)
const job = await queue.dequeue();Channels (Go-style)
import { Channel } from "@procedures/collections";
const ch = new Channel<Message>(10); // Buffered channel
// Send (blocks if buffer full)
await ch.send({ type: "hello" });
// Receive (blocks if empty)
const msg = await ch.receive();
// Close channel
ch.close();Functional Utilities
Iteration
import { iter } from "@procedures/collections";
const result = iter([1, 2, 3, 4, 5])
.filter(n => n % 2 === 0)
.map(n => n * 2)
.take(2)
.collect();
// [4, 8]Collectors
import { collect, toMap, groupBy, partition } from "@procedures/collections";
const users = [/* ... */];
const byId = collect(users, toMap(u => u.id));
const byRole = collect(users, groupBy(u => u.role));
const [admins, others] = collect(users, partition(u => u.isAdmin));Data Flow Diagram
┌──────────────────────────────────────────────────────────────────┐
│ Client Application │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Users │ │ Orders │ │ Products │ │
│ │ Collection │ │ Collection │ │ Collection │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ HybridStorage<T> │ │
│ │ ┌─────────────────┐ │ │
│ │ │ MemoryStorage │◄─┼── Fast reads │
│ │ │ (L1 Cache) │ │ │
│ │ └────────┬────────┘ │ │
│ │ │ sync │ │
│ │ ┌────────▼────────┐ │ │
│ │ │ ApiStorage │◄─┼── Persistent writes │
│ │ │ (Remote) │ │ │
│ │ └─────────────────┘ │ │
│ └───────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
│ HTTP/WebSocket
▼
┌──────────────────────────────────────────────────────────────────┐
│ Remote Server │
│ │
│ ┌───────────────────────┐ │
│ │ MongoStorage<T> │ (from client-mongo) │
│ │ │ │
│ │ Implements │ │
│ │ CollectionStorage<T> │ │
│ └───────────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ MongoDB │ │
│ └───────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘Integration with MongoDB
The client-mongo package provides MongoStorage<T> implementing CollectionStorage<T>:
import { MongoStorage } from "@procedures/mongo";
import { HybridStorage, MemoryStorage } from "@procedures/collections";
// MongoDB-backed storage
const mongoStorage = new MongoStorage<User>({ collection: "users" });
// Or hybrid with local cache
const hybrid = new HybridStorage<User>({
local: new MemoryStorage<User>(),
remote: mongoStorage
});API Reference
StorageMetadata
interface StorageMetadata {
type: "memory" | "api" | "hybrid" | "synced" | "custom";
size: number;
stats?: {
hitRate?: number; // Cache hit rate
lastSync?: number; // Last sync timestamp
pendingOps?: number; // Pending sync operations
memoryUsage?: number; // Memory in bytes
};
}License
MIT
