Add vitepress docs
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
# Deferred Promises
|
||||
|
||||
The Deferred module provides utilities for creating promises with exposed resolve/reject functions and typed error handling. This is particularly useful for managing asynchronous operations where you need external control over promise resolution.
|
||||
|
||||
## Types
|
||||
|
||||
### CustomPromise
|
||||
```typescript
|
||||
type CustomPromise<T, E> = Promise<T> & {
|
||||
__errorType: E
|
||||
}
|
||||
```
|
||||
A Promise type with strongly typed error information.
|
||||
|
||||
### Deferred
|
||||
```typescript
|
||||
type Deferred<T, E = T> = CustomPromise<T, E> & {
|
||||
resolve: (arg: T) => void
|
||||
reject: (arg: E) => void
|
||||
}
|
||||
```
|
||||
A Promise with exposed resolve/reject functions and typed error handling.
|
||||
|
||||
## Core Functions
|
||||
|
||||
### makePromise
|
||||
```typescript
|
||||
function makePromise<T, E>(
|
||||
executor: (
|
||||
resolve: (value: T | PromiseLike<T>) => void,
|
||||
reject: (reason: E) => void
|
||||
) => void
|
||||
): CustomPromise<T, E>
|
||||
```
|
||||
|
||||
Creates a Promise with strongly typed error information.
|
||||
|
||||
### defer
|
||||
```typescript
|
||||
function defer<T, E = T>(): Deferred<T, E>
|
||||
```
|
||||
|
||||
Creates a Deferred promise with resolve/reject methods exposed.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```typescript
|
||||
// Create a deferred promise
|
||||
const deferred = defer<string, Error>()
|
||||
|
||||
// Resolve later
|
||||
setTimeout(() => {
|
||||
deferred.resolve('Success!')
|
||||
}, 1000)
|
||||
|
||||
// Use like a regular promise
|
||||
await deferred // => 'Success!'
|
||||
```
|
||||
|
||||
### With Typed Errors
|
||||
|
||||
```typescript
|
||||
interface ApiError {
|
||||
code: number
|
||||
message: string
|
||||
}
|
||||
|
||||
const request = defer<Response, ApiError>()
|
||||
|
||||
try {
|
||||
const response = await fetch('/api')
|
||||
request.resolve(response)
|
||||
} catch (error) {
|
||||
request.reject({
|
||||
code: 500,
|
||||
message: error.message
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### External Promise Control
|
||||
|
||||
```typescript
|
||||
class AsyncOperation {
|
||||
private ready = defer<boolean>()
|
||||
|
||||
initialize() {
|
||||
// Setup async operation
|
||||
this.ready.resolve(true)
|
||||
}
|
||||
|
||||
async waitUntilReady() {
|
||||
return this.ready
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Timeout
|
||||
|
||||
```typescript
|
||||
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
||||
const timeout = defer<T>()
|
||||
|
||||
setTimeout(() => {
|
||||
timeout.reject(new Error('Timeout'))
|
||||
}, ms)
|
||||
|
||||
return Promise.race([promise, timeout])
|
||||
}
|
||||
|
||||
// Usage
|
||||
try {
|
||||
const result = await withTimeout(slowOperation(), 5000)
|
||||
} catch (error) {
|
||||
console.log('Operation timed out')
|
||||
}
|
||||
```
|
||||
|
||||
### Event to Promise
|
||||
|
||||
```typescript
|
||||
function eventToPromise<T>(
|
||||
emitter: EventEmitter,
|
||||
successEvent: string,
|
||||
errorEvent: string
|
||||
): Deferred<T, Error> {
|
||||
const deferred = defer<T, Error>()
|
||||
|
||||
emitter.once(successEvent, (data: T) => {
|
||||
deferred.resolve(data)
|
||||
})
|
||||
|
||||
emitter.once(errorEvent, (error: Error) => {
|
||||
deferred.reject(error)
|
||||
})
|
||||
|
||||
return deferred
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,17 @@
|
||||
# @welshman/lib
|
||||
|
||||
A lightweight TypeScript utility library with zero dependencies, providing essential tools for modern JavaScript development.
|
||||
|
||||
## What's Included
|
||||
|
||||
- **Deferred Promises** - Create promises with exposed resolve/reject methods
|
||||
- **LRU Cache** - Efficient caching with automatic eviction policies
|
||||
- **Utility Functions** - Helpers for arrays, objects, strings, and more
|
||||
- **Worker Queue** - Process tasks asynchronously with batching and throttling
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @welshman/lib
|
||||
```
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
# LRU Cache
|
||||
|
||||
The LRU (Least Recently Used) Cache implementation provides efficient caching with automatic eviction of least recently used items when the cache reaches its maximum size.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```typescript
|
||||
// Create cache with max size
|
||||
const cache = new LRUCache<string, number>(3)
|
||||
|
||||
// Add items
|
||||
cache.set('a', 1)
|
||||
cache.set('b', 2)
|
||||
cache.set('c', 3)
|
||||
|
||||
// Access items
|
||||
cache.get('a') // => 1
|
||||
|
||||
// Check if key exists
|
||||
cache.has('b') // => true
|
||||
|
||||
// Adding beyond max size evicts least recently used
|
||||
cache.set('d', 4) // Evicts oldest item
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Constructor
|
||||
|
||||
```typescript
|
||||
constructor(maxSize: number = Infinity)
|
||||
```
|
||||
|
||||
Creates a new LRU cache with specified maximum size.
|
||||
|
||||
### Methods
|
||||
|
||||
#### set(key: T, value: U)
|
||||
```typescript
|
||||
set(key: T, value: U): void
|
||||
```
|
||||
Adds or updates an item in the cache. If cache is at maximum size, evicts least recently used item.
|
||||
|
||||
#### get(key: T)
|
||||
```typescript
|
||||
get(key: T): U | undefined
|
||||
```
|
||||
Retrieves item from cache. Also marks item as recently used.
|
||||
|
||||
#### has(key: T)
|
||||
```typescript
|
||||
has(key: T): boolean
|
||||
```
|
||||
Checks if key exists in cache without affecting usage tracking.
|
||||
|
||||
## Cache Decorator
|
||||
|
||||
The package also provides a convenient decorator function for creating memoized functions with LRU caching:
|
||||
|
||||
```typescript
|
||||
function cached<T, V, Args extends any[]>({
|
||||
maxSize,
|
||||
getKey,
|
||||
getValue,
|
||||
}: {
|
||||
maxSize: number
|
||||
getKey: (args: Args) => T
|
||||
getValue: (args: Args) => V
|
||||
}): (...args: Args) => V
|
||||
```
|
||||
|
||||
### Usage Example
|
||||
|
||||
```typescript
|
||||
// Create cached function
|
||||
const getUser = cached({
|
||||
maxSize: 1000,
|
||||
getKey: (args) => args[0], // Use first argument as cache key
|
||||
getValue: async (args) => {
|
||||
const [id] = args
|
||||
return await fetchUser(id)
|
||||
}
|
||||
})
|
||||
|
||||
// Use cached function
|
||||
const user1 = await getUser(123)
|
||||
const user2 = await getUser(123) // Returns cached result
|
||||
```
|
||||
|
||||
### Simple Cache Helper
|
||||
|
||||
For basic caching needs, there's also a simplified cache creator:
|
||||
|
||||
```typescript
|
||||
function simpleCache<V, Args extends any[]>(
|
||||
getValue: (args: Args) => V
|
||||
) {
|
||||
return cached({
|
||||
maxSize: 100000,
|
||||
getKey: xs => xs.join(':'),
|
||||
getValue
|
||||
})
|
||||
}
|
||||
|
||||
// Usage
|
||||
const cachedFn = simpleCache(async (id: string) => {
|
||||
return await expensiveOperation(id)
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,256 @@
|
||||
# Utility Functions
|
||||
|
||||
The `Tools.ts` module provides a comprehensive collection of utility functions for common programming tasks. It includes functions for array manipulation, object handling, type checking, math operations, and more.
|
||||
|
||||
## Types
|
||||
|
||||
```typescript
|
||||
type Nil = null | undefined
|
||||
type Maybe<T> = T | undefined
|
||||
type Obj<T = any> = Record<string, T>
|
||||
```
|
||||
|
||||
## Categories
|
||||
|
||||
### Type Checking & Basic Operations
|
||||
|
||||
```typescript
|
||||
// Check if value is null or undefined
|
||||
isNil(x: any): boolean
|
||||
|
||||
// Execute function if value exists
|
||||
ifLet<T>(x: T | undefined, f: (x: T) => void)
|
||||
|
||||
// Return value unchanged
|
||||
identity<T>(x: T): T
|
||||
|
||||
// Create function that always returns same value
|
||||
always<T>(x: T): () => T
|
||||
|
||||
// Logical NOT
|
||||
not(x: any): boolean
|
||||
|
||||
// Create complement of a predicate function
|
||||
complement<T extends unknown[]>(f: (...args: T) => any): (...args: T) => boolean
|
||||
```
|
||||
|
||||
### Array Operations
|
||||
|
||||
```typescript
|
||||
// Get first element
|
||||
first<T>(xs: T[]): T | undefined
|
||||
|
||||
// Get first element of first array
|
||||
ffirst<T>(xs: T[][]): T | undefined
|
||||
|
||||
// Get last element
|
||||
last<T>(xs: T[]): T | undefined
|
||||
|
||||
// Drop first n elements
|
||||
drop<T>(n: number, xs: T[]): T[]
|
||||
|
||||
// Take first n elements
|
||||
take<T>(n: number, xs: T[]): T[]
|
||||
|
||||
// Remove duplicates
|
||||
uniq<T>(xs: T[]): T[]
|
||||
|
||||
// Remove duplicates by key function
|
||||
uniqBy<T>(f: (x: T) => any, xs: T[]): T[]
|
||||
|
||||
// Create array of n items using generator function
|
||||
initArray<T>(n: number, f: () => T): T[]
|
||||
|
||||
// Split array into chunks
|
||||
chunk<T>(chunkLength: number, xs: T[]): T[][]
|
||||
|
||||
// Split array into n chunks
|
||||
chunks<T>(n: number, xs: T[]): T[][]
|
||||
```
|
||||
|
||||
### Object Operations
|
||||
|
||||
```typescript
|
||||
// Create object excluding specified keys
|
||||
omit<T extends Obj>(ks: string[], x: T): T
|
||||
|
||||
// Create object excluding entries with specified values
|
||||
omitVals<T extends Obj>(xs: any[], x: T): T
|
||||
|
||||
// Create object with only specified keys
|
||||
pick<T extends Obj>(ks: string[], x: T): T
|
||||
|
||||
// Transform object keys
|
||||
mapKeys<T extends Obj>(f: (v: string) => string, x: T): T
|
||||
|
||||
// Transform object values
|
||||
mapVals<V, U>(f: (v: V) => U, x: Record<string, V>): Record<string, U>
|
||||
|
||||
// Merge objects (left priority)
|
||||
mergeLeft<T extends Obj>(a: T, b: T): T
|
||||
|
||||
// Merge objects (right priority)
|
||||
mergeRight<T extends Obj>(a: T, b: T): T
|
||||
|
||||
// Deep merge objects
|
||||
deepMergeLeft(a: Obj, b: Obj): Obj
|
||||
deepMergeRight(a: Obj, b: Obj): Obj
|
||||
```
|
||||
|
||||
### Number Operations
|
||||
|
||||
```typescript
|
||||
// Convert Maybe<number> to number
|
||||
num(x: Maybe<number>): number
|
||||
|
||||
// Basic arithmetic with Maybe<number>
|
||||
add(x: Maybe<number>, y: Maybe<number>): number
|
||||
sub(x: Maybe<number>, y: Maybe<number>): number
|
||||
mul(x: Maybe<number>, y: Maybe<number>): number
|
||||
div(x: Maybe<number>, y: number): number
|
||||
|
||||
// Increment/Decrement
|
||||
inc(x: Maybe<number>): number
|
||||
dec(x: Maybe<number>): number
|
||||
|
||||
// Comparisons
|
||||
lt(x: Maybe<number>, y: Maybe<number>): boolean
|
||||
lte(x: Maybe<number>, y: Maybe<number>): boolean
|
||||
gt(x: Maybe<number>, y: Maybe<number>): boolean
|
||||
gte(x: Maybe<number>, y: Maybe<number>): boolean
|
||||
|
||||
// Array number operations
|
||||
max(xs: Maybe<number>[]): number
|
||||
min(xs: Maybe<number>[]): number
|
||||
sum(xs: Maybe<number>[]): number
|
||||
avg(xs: Maybe<number>[]): number
|
||||
```
|
||||
|
||||
### String Operations
|
||||
|
||||
```typescript
|
||||
// Truncate string with ellipsis
|
||||
ellipsize(s: string, l: number, suffix = "..."): string
|
||||
|
||||
// URL operations
|
||||
stripProtocol(url: string): string
|
||||
displayUrl(url: string): string
|
||||
displayDomain(url: string): string
|
||||
|
||||
// Bech32 encoding/decoding
|
||||
hexToBech32(prefix: string, hex: string): string
|
||||
bech32ToHex(b32: string): string
|
||||
```
|
||||
|
||||
### Collection Operations
|
||||
|
||||
```typescript
|
||||
// Create union of arrays
|
||||
union<T>(a: T[], b: T[]): T[]
|
||||
|
||||
// Get intersection of arrays
|
||||
intersection<T>(a: T[], b: T[]): T[]
|
||||
|
||||
// Get difference of arrays
|
||||
difference<T>(a: T[], b: T[]): T[]
|
||||
|
||||
// Remove element from array
|
||||
remove<T>(a: T, xs: T[]): T[]
|
||||
|
||||
// Filter array by another array
|
||||
without<T>(a: T[], b: T[]): T[]
|
||||
|
||||
// Toggle element in array
|
||||
toggle<T>(x: T, xs: T[]): T[]
|
||||
|
||||
// Group array by key function
|
||||
groupBy<T, K>(f: (x: T) => K, xs: T[]): Map<K, T[]>
|
||||
|
||||
// Create map from array
|
||||
indexBy<T, K>(f: (x: T) => K, xs: T[]): Map<K, T>
|
||||
```
|
||||
|
||||
### Time Constants
|
||||
|
||||
```typescript
|
||||
const MINUTE = 60
|
||||
const HOUR = 60 * MINUTE
|
||||
const DAY = 24 * HOUR
|
||||
const WEEK = 7 * DAY
|
||||
const MONTH = 30 * DAY
|
||||
const QUARTER = 90 * DAY
|
||||
const YEAR = 365 * DAY
|
||||
|
||||
// Get current timestamp in seconds
|
||||
now(): number
|
||||
|
||||
// Get timestamp from ago in seconds
|
||||
ago(unit: number, count = 1): number
|
||||
|
||||
// Convert seconds to milliseconds
|
||||
ms(seconds: number): number
|
||||
```
|
||||
|
||||
### Function Utilities
|
||||
|
||||
```typescript
|
||||
// Create function that executes once
|
||||
once(f: (...args: any) => void): (...args: any) => void
|
||||
|
||||
// Memoize function results
|
||||
memoize<T>(f: (...args: any[]) => T): (...args: any[]) => T
|
||||
|
||||
// Create throttled function
|
||||
throttle<F extends (...args: any[]) => any>(
|
||||
ms: number,
|
||||
f: F
|
||||
): F
|
||||
|
||||
// Create batching function
|
||||
batch<T>(
|
||||
t: number,
|
||||
f: (xs: T[]) => void
|
||||
): (x: T) => void
|
||||
```
|
||||
|
||||
### Network Utilities
|
||||
|
||||
```typescript
|
||||
// Fetch JSON with options
|
||||
fetchJson(url: string, opts?: FetchOpts): Promise<any>
|
||||
|
||||
// Post JSON data
|
||||
postJson<T>(url: string, data: T, opts?: FetchOpts): Promise<any>
|
||||
|
||||
// Upload file
|
||||
uploadFile(url: string, file: File): Promise<any>
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```typescript
|
||||
// Array operations
|
||||
const nums = [1, 2, 2, 3, 3, 3]
|
||||
uniq(nums) // => [1, 2, 3]
|
||||
|
||||
// Object operations
|
||||
const obj = {a: 1, b: 2, c: 3}
|
||||
omit(['a', 'b'], obj) // => {c: 3}
|
||||
|
||||
// Number operations
|
||||
add(5, undefined) // => 5
|
||||
inc(undefined) // => 1
|
||||
|
||||
// Time operations
|
||||
ago(DAY, 2) // => timestamp from 2 days ago
|
||||
|
||||
// URL operations
|
||||
displayUrl('https://www.example.com/') // => 'example.com'
|
||||
|
||||
// Collection operations
|
||||
const users = [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}]
|
||||
indexBy(u => u.id, users) // => Map(1 => {id: 1, name: 'Alice'}, ...)
|
||||
|
||||
// Function utilities
|
||||
const throttledFn = throttle(1000, () => console.log('throttled'))
|
||||
```
|
||||
@@ -0,0 +1,117 @@
|
||||
# Worker
|
||||
|
||||
The Worker class provides a robust queue processing system with batched operations, throttling, and message routing capabilities. It's designed to handle asynchronous operations efficiently while maintaining control over processing rates and resource usage.
|
||||
|
||||
## Overview
|
||||
|
||||
```typescript
|
||||
class Worker<T> {
|
||||
constructor(readonly opts: WorkerOpts<T> = {})
|
||||
}
|
||||
```
|
||||
|
||||
The Worker class accepts messages of type `T` and processes them according to configured options and handlers.
|
||||
|
||||
## Configuration
|
||||
|
||||
```typescript
|
||||
type WorkerOpts<T> = {
|
||||
// Function to determine routing key for messages
|
||||
getKey?: (x: T) => any
|
||||
|
||||
// Function to check if message should be deferred
|
||||
shouldDefer?: (x: T) => boolean
|
||||
|
||||
// Maximum messages to process in one batch
|
||||
chunkSize?: number // default: 50
|
||||
|
||||
// Milliseconds between processing batches
|
||||
delay?: number // default: 50
|
||||
}
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```typescript
|
||||
// Create worker for processing messages
|
||||
const worker = new Worker<Message>({
|
||||
chunkSize: 10,
|
||||
delay: 100,
|
||||
getKey: msg => msg.type
|
||||
})
|
||||
|
||||
// Add message handlers
|
||||
worker.addHandler('email', async (msg) => {
|
||||
await sendEmail(msg)
|
||||
})
|
||||
|
||||
worker.addHandler('notification', async (msg) => {
|
||||
await sendNotification(msg)
|
||||
})
|
||||
|
||||
// Add messages to queue
|
||||
worker.push({
|
||||
type: 'email',
|
||||
content: 'Hello'
|
||||
})
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Message Routing
|
||||
|
||||
Messages can be routed to specific handlers based on a key:
|
||||
|
||||
```typescript
|
||||
const worker = new Worker<Task>({
|
||||
getKey: task => task.priority
|
||||
})
|
||||
|
||||
// Handle high priority tasks
|
||||
worker.addHandler('high', async (task) => {
|
||||
await processUrgent(task)
|
||||
})
|
||||
|
||||
// Handle normal priority tasks
|
||||
worker.addHandler('normal', async (task) => {
|
||||
await processNormal(task)
|
||||
})
|
||||
```
|
||||
|
||||
### Global Handlers
|
||||
|
||||
Handle all messages regardless of routing key:
|
||||
|
||||
```typescript
|
||||
worker.addGlobalHandler(async (message) => {
|
||||
console.log('Processing:', message)
|
||||
})
|
||||
```
|
||||
|
||||
### Message Deferral
|
||||
|
||||
Defer processing of messages that aren't ready:
|
||||
|
||||
```typescript
|
||||
const worker = new Worker<Task>({
|
||||
shouldDefer: (task) => !task.isReady(),
|
||||
delay: 1000
|
||||
})
|
||||
|
||||
worker.push(task) // Will retry until task.isReady()
|
||||
```
|
||||
|
||||
### Flow Control
|
||||
|
||||
Control message processing:
|
||||
|
||||
```typescript
|
||||
// Pause processing
|
||||
worker.pause()
|
||||
|
||||
// Resume processing
|
||||
worker.resume()
|
||||
|
||||
// Clear queue
|
||||
worker.clear()
|
||||
```
|
||||
Reference in New Issue
Block a user