rockbed
v0.1.0
Published
Small TypeScript primitives for errors, events, disposables, async helpers, locks, JSON, assertions, and responses.
Maintainers
Readme
rockbed
rockbed is a small TypeScript utility package extracted from template-commander/src/bedrock.
It keeps only the core primitives that are useful across projects.
The package is designed for subpath imports:
import { ILvErrorOr, makeOkWith, makeError } from 'rockbed/error';
import { Emitter } from 'rockbed/event';Module Map
| Import path | Main exports | Use it for |
| --- | --- | --- |
| rockbed/assert | lvAssert, lvAssertNotNil, lvAssertNever, lvAssertNotHere | Runtime invariants and TypeScript narrowing. |
| rockbed/async | wait, Barrier, makeBarrierByPromise, defer, makeCancelablePromise, parallelPromise, makePromiseWithTimeout, CancellationTokenSource | Timers, promise coordination, cancellation-aware promise helpers. |
| rockbed/dispose | IDisposable, Disposable, DisposableStore, MutableDisposable, SafeDisposable, makeSafeDisposable, disposeWithLog | Ownership and cleanup of resources or subscriptions. |
| rockbed/error | ILvErrorOr<T>, ILvErrorRef, makeOk, makeOkWith, makeError, makeErrorBy, lvErrorConst, isLvErrorRef, GenericError | Typed result objects instead of throwing for expected failures. |
| rockbed/event | Emitter, Event, listenOnce, listener error handlers | Lightweight typed event emitters that return disposables. |
| rockbed/json | safeJsonParse, safeJsonStringify | JSON helpers that never throw. |
| rockbed/lock | SharedMutex | Async read/write locking with writer priority. |
| rockbed/response | IResponse<T>, makeSuccessResponse, makeErrorResponse | Simple { code, msg, data } API response objects. |
Error Result Pattern
ILvErrorOr<T> is the central result type.
import { ILvErrorOr, makeError, makeOkWith } from 'rockbed/error';
function parsePort(value: string): ILvErrorOr<number> {
const port = Number(value);
if (!Number.isInteger(port)) {
return makeError(400, 'port must be an integer');
}
return makeOkWith(port);
}
const result = parsePort('8080');
if (result.ok) {
console.log(result.value);
} else {
console.error(result.code, result.msg);
}Every result also has pair() for tuple-style control flow:
const [err, value] = parsePort('8080').pair();
if (err) {
throw new Error(err.toString());
}
console.log(value);Event Pattern
Emitter<TArgs> produces an Event<TArgs>. Subscribing returns IDisposable.
import { Emitter, listenOnce } from 'rockbed/event';
const emitter = new Emitter<[message: string]>();
const subscription = emitter.event((message) => console.log(message));
emitter.fire('ready');
subscription.dispose();
listenOnce(emitter.event)((message) => console.log('only once', message));Dispose Pattern
Use DisposableStore when one owner needs to clean up multiple resources.
Use Disposable as a base class when a class owns a store.
import { Disposable, makeSafeDisposable } from 'rockbed/dispose';
class Session extends Disposable {
start() {
this._register(makeSafeDisposable(() => console.log('cleanup')));
}
}Async Pattern
import { Barrier, makePromiseWithTimeout, wait } from 'rockbed/async';
await wait(100);
const barrier = new Barrier();
queueMicrotask(() => barrier.open());
await barrier.wait();
const result = await makePromiseWithTimeout(async (token) => {
if (token.isCancellationRequested) {
return 'cancelled';
}
return 'done';
}, 1000);Lock Pattern
SharedMutex supports exclusive writes and shared reads.
import { SharedMutex } from 'rockbed/lock';
const mutex = new SharedMutex();
await mutex.lock();
try {
// write
} finally {
mutex.unlock();
}
await mutex.lockShared();
try {
// read
} finally {
mutex.unlockShared();
}Publishing
npm install
npm run typecheck
npm run build
npm publish --registry=https://registry.npmjs.orgThe npm package includes dist, src, and this README. Keeping source files in the published
package makes the available primitives easier for humans and AI coding agents to inspect.
