316 lines
9.5 KiB
Markdown
316 lines
9.5 KiB
Markdown
# @push.rocks/lik
|
|
|
|
A collection of lightweight utility classes for TypeScript/Node.js projects, providing efficient data structures and async helpers.
|
|
|
|
## Install
|
|
|
|
```bash
|
|
npm install @push.rocks/lik
|
|
```
|
|
|
|
## Usage
|
|
|
|
`@push.rocks/lik` provides a set of focused helper classes for common programming tasks: managing collections, controlling async execution, tracking interests, and more. All classes are fully typed and work in both Node.js and browser environments.
|
|
|
|
### AsyncExecutionStack
|
|
|
|
Controls execution of asynchronous tasks in two modes:
|
|
|
|
- **Exclusive**: tasks run one at a time, blocking all others until complete.
|
|
- **Non-exclusive**: tasks run in parallel with optional concurrency limits.
|
|
|
|
```typescript
|
|
import { AsyncExecutionStack } from '@push.rocks/lik';
|
|
|
|
const stack = new AsyncExecutionStack();
|
|
|
|
// Exclusive execution (sequential, blocks other tasks)
|
|
await stack.getExclusiveExecutionSlot(async () => {
|
|
// critical section work
|
|
}, 5000); // optional timeout in ms
|
|
|
|
// Non-exclusive execution (parallel)
|
|
const p1 = stack.getNonExclusiveExecutionSlot(async () => {
|
|
// concurrent work 1
|
|
});
|
|
const p2 = stack.getNonExclusiveExecutionSlot(async () => {
|
|
// concurrent work 2
|
|
});
|
|
await Promise.all([p1, p2]);
|
|
|
|
// Control concurrency
|
|
stack.setNonExclusiveMaxConcurrency(3);
|
|
console.log(stack.getNonExclusiveMaxConcurrency()); // 3
|
|
console.log(stack.getActiveNonExclusiveCount()); // currently running
|
|
console.log(stack.getPendingNonExclusiveCount()); // waiting for slots
|
|
```
|
|
|
|
### BackpressuredArray
|
|
|
|
An array with backpressure support using RxJS subjects. Useful for producer/consumer patterns where you need to throttle the producer when the consumer can't keep up.
|
|
|
|
```typescript
|
|
import { BackpressuredArray } from '@push.rocks/lik';
|
|
|
|
const buffer = new BackpressuredArray<string>(16); // high water mark of 16
|
|
|
|
// Producer: push items, returns false when full
|
|
const hasSpace = buffer.push('item1');
|
|
if (!hasSpace) {
|
|
await buffer.waitForSpace(); // wait until consumer frees space
|
|
}
|
|
|
|
// Consumer: shift items out
|
|
await buffer.waitForItems(); // wait until items are available
|
|
const item = buffer.shift();
|
|
|
|
// Check state
|
|
buffer.checkSpaceAvailable(); // true if below high water mark
|
|
buffer.checkHasItems(); // true if items exist
|
|
```
|
|
|
|
### FastMap
|
|
|
|
A high-performance key-value map optimized for rapid lookups and modifications.
|
|
|
|
```typescript
|
|
import { FastMap } from '@push.rocks/lik';
|
|
|
|
const map = new FastMap<string>();
|
|
|
|
map.addToMap('key1', 'value1');
|
|
map.addToMap('key2', 'value2');
|
|
|
|
const value = map.getByKey('key1'); // 'value1'
|
|
map.isUniqueKey('key1'); // false (already exists)
|
|
map.removeFromMap('key1');
|
|
|
|
// Merge maps
|
|
const otherMap = new FastMap<string>();
|
|
otherMap.addToMap('key3', 'value3');
|
|
const merged = map.concat(otherMap);
|
|
|
|
// Or merge in place
|
|
map.addAllFromOther(otherMap);
|
|
|
|
// Async find
|
|
const found = await map.find(async (item) => item === 'value2');
|
|
```
|
|
|
|
### InterestMap
|
|
|
|
Manages subscriptions/interests in events or entities. Multiple parties can express interest in the same thing; the interest is deduplicated and fulfilled once.
|
|
|
|
```typescript
|
|
import { InterestMap } from '@push.rocks/lik';
|
|
|
|
const interestMap = new InterestMap<string, number>(
|
|
(str) => str, // comparison function to deduplicate interests
|
|
{ markLostAfterDefault: 30000 } // optional: auto-mark lost after 30s
|
|
);
|
|
|
|
// Express interest
|
|
const interest = await interestMap.addInterest('event1');
|
|
|
|
// Wait for fulfillment
|
|
interest.interestFullfilled.then((result) => {
|
|
console.log('Got result:', result);
|
|
});
|
|
|
|
// Fulfill from elsewhere
|
|
const found = interestMap.findInterest('event1');
|
|
found.fullfillInterest(42);
|
|
|
|
// Check and manage interests
|
|
interestMap.checkInterest('event1'); // true/false
|
|
interestMap.informLostInterest('event1'); // starts destruction timer
|
|
|
|
// Observable stream of new interests
|
|
interestMap.interestObservable; // ObservableIntake<Interest>
|
|
```
|
|
|
|
### LimitedArray
|
|
|
|
An array that automatically enforces a maximum size, discarding oldest items when the limit is exceeded.
|
|
|
|
```typescript
|
|
import { LimitedArray } from '@push.rocks/lik';
|
|
|
|
const arr = new LimitedArray<number>(5);
|
|
|
|
arr.addMany([1, 2, 3, 4, 5, 6]);
|
|
console.log(arr.array.length); // 5 (oldest items dropped)
|
|
|
|
arr.addOne(7);
|
|
arr.setLimit(3); // dynamically adjust limit
|
|
|
|
// Compute average (for numeric arrays)
|
|
const numArr = new LimitedArray<number>(10);
|
|
numArr.addMany([10, 20, 30]);
|
|
console.log(numArr.getAverage()); // 20
|
|
```
|
|
|
|
### LoopTracker
|
|
|
|
Detects and prevents infinite loops by tracking object references during iterations.
|
|
|
|
```typescript
|
|
import { LoopTracker } from '@push.rocks/lik';
|
|
|
|
const tracker = new LoopTracker<object>();
|
|
const obj1 = {};
|
|
|
|
tracker.checkAndTrack(obj1); // true (first time, tracked)
|
|
tracker.checkAndTrack(obj1); // false (already seen - loop detected!)
|
|
```
|
|
|
|
### ObjectMap
|
|
|
|
A managed collection of objects with add/remove/find operations and event notifications via RxJS.
|
|
|
|
```typescript
|
|
import { ObjectMap } from '@push.rocks/lik';
|
|
|
|
interface IUser {
|
|
id: number;
|
|
name: string;
|
|
}
|
|
|
|
const users = new ObjectMap<IUser>();
|
|
|
|
// Add objects
|
|
const key = users.add({ id: 1, name: 'Alice' });
|
|
users.addArray([{ id: 2, name: 'Bob' }, { id: 3, name: 'Carol' }]);
|
|
|
|
// Find objects
|
|
const alice = users.findSync((u) => u.id === 1);
|
|
const bob = await users.find(async (u) => u.id === 2);
|
|
|
|
// Find and remove in one step
|
|
const removed = await users.findOneAndRemove(async (u) => u.id === 3);
|
|
const removedSync = users.findOneAndRemoveSync((u) => u.id === 2);
|
|
|
|
// Direct add/get by unique key
|
|
users.addMappedUnique('admin', { id: 99, name: 'Admin' });
|
|
const admin = users.getMappedUnique('admin');
|
|
|
|
// Get one and remove (FIFO-style)
|
|
const first = users.getOneAndRemove();
|
|
|
|
// Iterate, check, and manage
|
|
await users.forEach((u) => console.log(u.name));
|
|
users.checkForObject(alice); // true/false
|
|
users.isEmpty(); // true/false
|
|
users.getArray(); // cloned array of all objects
|
|
users.wipe(); // remove all
|
|
|
|
// Listen for changes
|
|
users.eventSubject.subscribe((event) => {
|
|
console.log(event.operation, event.payload); // 'add' | 'remove'
|
|
});
|
|
|
|
// Merge object maps
|
|
const merged = users.concat(otherObjectMap);
|
|
users.addAllFromOther(otherObjectMap);
|
|
```
|
|
|
|
### Stringmap
|
|
|
|
Manages a collection of strings with add/remove/query operations and minimatch pattern matching.
|
|
|
|
```typescript
|
|
import { Stringmap } from '@push.rocks/lik';
|
|
|
|
const strings = new Stringmap();
|
|
|
|
strings.addString('hello');
|
|
strings.addStringArray(['world', 'example']);
|
|
|
|
strings.checkString('hello'); // true
|
|
strings.checkMinimatch('hel*'); // true (glob matching)
|
|
strings.checkIsEmpty(); // false
|
|
|
|
strings.removeString('hello');
|
|
strings.getStringArray(); // ['world', 'example']
|
|
|
|
// Register trigger that fires when condition is met
|
|
await strings.registerUntilTrue((arr) => arr.length === 0);
|
|
strings.wipe(); // triggers the above
|
|
```
|
|
|
|
### TimedAggregator
|
|
|
|
Batches items over a time interval, then processes them in bulk. Useful for aggregating logs, metrics, or events.
|
|
|
|
```typescript
|
|
import { TimedAggregtor } from '@push.rocks/lik';
|
|
|
|
const aggregator = new TimedAggregtor<string>({
|
|
aggregationIntervalInMillis: 5000,
|
|
functionForAggregation: (items) => {
|
|
console.log('Batch:', items);
|
|
},
|
|
});
|
|
|
|
aggregator.add('event1');
|
|
aggregator.add('event2');
|
|
// After 5 seconds: Batch: ['event1', 'event2']
|
|
```
|
|
|
|
### Tree
|
|
|
|
A typed wrapper around `symbol-tree` for managing hierarchical data structures with parent/child/sibling relationships.
|
|
|
|
```typescript
|
|
import { Tree } from '@push.rocks/lik';
|
|
|
|
class TreeNode {
|
|
constructor(public value: string) {}
|
|
}
|
|
|
|
const tree = new Tree<TreeNode>();
|
|
const root = new TreeNode('root');
|
|
tree.initialize(root);
|
|
|
|
const child1 = new TreeNode('child1');
|
|
const child2 = new TreeNode('child2');
|
|
tree.appendChild(root, child1);
|
|
tree.appendChild(root, child2);
|
|
|
|
// Navigate
|
|
tree.hasChildren(root); // true
|
|
tree.firstChild(root); // child1
|
|
tree.lastChild(root); // child2
|
|
tree.nextSibling(child1); // child2
|
|
tree.parent(child1); // root
|
|
|
|
// Query
|
|
tree.childrenCount(root); // 2
|
|
tree.index(child2); // 1
|
|
tree.childrenToArray(root, {}); // [child1, child2]
|
|
tree.treeToArray(root, {}); // full tree as array
|
|
|
|
// Mutate
|
|
tree.insertBefore(child2, new TreeNode('between'));
|
|
tree.remove(child2);
|
|
```
|
|
|
|
## License and Legal Information
|
|
|
|
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
|
|
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
|
|
### Trademarks
|
|
|
|
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
|
|
|
|
### Company Information
|
|
|
|
Task Venture Capital GmbH
|
|
Registered at District court Bremen HRB 35230 HB, Germany
|
|
|
|
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
|
|
|
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|