Thingies: TypeScript Utility Library
Thingies is a versatile TypeScript utility library providing a collection of small, focused, and performant helpers for common programming tasks. It covers areas such as caching (LruCache, LruMap, LruTtlMap), promise management (Defer, of, promiseMap, until, tick, timeout), string manipulation (base64, hash, randomStr, normalizeEmail, dataUri), and concurrency control (Locks, codeMutex, concurrency, FanOut). The current stable version is 2.6.0, with frequent minor and patch releases introducing new features and performance improvements. Its key differentiator lies in its breadth of specific, often low-level, utilities, all designed with a TypeScript-first approach, offering strong typing and a comprehensive API for various Node.js and browser environments.
Common errors
-
ReferenceError: require is not defined in ES module scope
cause Attempting to use `require()` to import `thingies` after version 2.0.0, which switched to ES modules.fixChange `const { Symbol } = require('thingies');` to `import { Symbol } from 'thingies';` and ensure your environment supports ES modules (e.g., `"type": "module"` in `package.json` for Node.js). -
TypeError: Class extends value undefined is not a constructor or null
cause This error often indicates a problem with `tslib` not being found or an incompatible version being used, especially when TypeScript features like class extension or decorators are involved.fixVerify that `tslib` is installed in your project (`npm list tslib`) and that its version is compatible with your TypeScript compiler and runtime environment. Try `npm install tslib@^2`. -
TypeError: (0 , thingies_1.LruCache) is not a constructor
cause This typically occurs in transpiled JavaScript when an ES module's named export (like `LruCache`) is incorrectly handled during CommonJS interop, often due to mismatched module systems or incorrect bundler configuration.fixEnsure your build system (Webpack, Rollup, TypeScript compiler) is correctly configured for ES module output and consumption, especially when targeting older environments or using CommonJS. Prefer direct `import { LruCache } from 'thingies';` and verify `tsconfig.json` module settings.
Warnings
- breaking Version 2.0.0 introduced a breaking change by targeting `es2020` modules exclusively, meaning CommonJS environments will require additional transpilation or specific Node.js `--experimental-modules` flags for direct usage.
- gotcha The library declares `tslib` as a peer dependency. While npm/yarn often resolve peer dependencies automatically, an incompatible or missing `tslib` version can lead to runtime errors, especially with TypeScript features like decorators or async/await.
- gotcha Utilities like `codeMutex` and the `@mutex` decorator create mutexes that are scoped to the specific instance or function call. They do not provide global synchronization across different instances or processes.
Install
-
npm install thingies -
yarn add thingies -
pnpm add thingies
Imports
- LruCache
const { LruCache } = require('thingies');import { LruCache } from 'thingies'; - of
import of from 'thingies';
import { of } from 'thingies'; - codeMutex
import { codeMutex } from 'thingies'; - TimedQueue
import * as thingies from 'thingies'; const queue = new thingies.TimedQueue();
import { TimedQueue } from 'thingies';
Quickstart
import { LruCache, of, codeMutex, TimedQueue } from 'thingies';
// 1. Using LruCache for efficient data storage
const userCache = new LruCache<string, { id: string; name: string }>({
limit: 100, // Cache up to 100 users
ttl: 60 * 1000, // Items expire after 60 seconds
});
userCache.set('user-1', { id: 'user-1', name: 'Alice' });
console.log('Cached user:', userCache.get('user-1'));
// 2. Safely handling Promise results with `of`
async function fetchData(shouldSucceed: boolean) {
const [data, error] = await of(new Promise<string>((resolve, reject) => {
setTimeout(() => {
if (shouldSucceed) {
resolve('Data fetched successfully');
} else {
reject(new Error('Failed to fetch data'));
}
}, 100);
}));
if (error) {
console.error('Error fetching data:', error.message);
} else {
console.log('Data:', data);
}
}
fetchData(true); // Should log data
fetchData(false); // Should log error
// 3. Using codeMutex for synchronized execution
const mutex = codeMutex();
let sharedResource = 0;
async function incrementResource(id: number) {
await mutex(async () => {
const current = sharedResource;
await new Promise(resolve => setTimeout(resolve, Math.random() * 50)); // Simulate work
sharedResource = current + 1;
console.log(`Worker ${id}: sharedResource is now ${sharedResource}`);
});
}
Promise.all([incrementResource(1), incrementResource(2), incrementResource(3)]);
// 4. Batching operations with TimedQueue
const eventQueue = new TimedQueue<string>({
limit: 3, // Flush after 3 items
timeout: 100, // Or flush after 100ms
flush: async (items) => {
console.log(`Flushing ${items.length} events: ${items.join(', ')}`);
// In a real app, this would send to an external service or database
},
});
eventQueue.push('event-A');
eventQueue.push('event-B');
eventQueue.push('event-C'); // This push should trigger a flush immediately
eventQueue.push('event-D');
eventQueue.push('event-E');
// Wait for timeout to flush remaining items
setTimeout(() => eventQueue.flush(), 200);