9.0 KiB
9.0 KiB
Taskbuffer Hints
Task Constraint System (v5.0.0+) — Breaking Changes
TaskRunnerremoved — replaced byTaskManager+TaskConstraintGroupblockingTasksremoved fromTask— useTaskConstraintGroupwithmaxConcurrent: 1execDelayremoved fromTask— useTaskConstraintGroupwithcooldownMsfinishedpromise removed fromTask— no longer neededTaskgeneric signature:Task<T, TSteps, TData>(3rd param added for typed data)
Task.data
Taskconstructor accepts optionaldata?: TData(defaults to{})- Typed data bag accessible as
task.data
TaskConstraintGroup
new TaskConstraintGroup<TData>({ name, constraintKeyForExecution, maxConcurrent?, cooldownMs?, shouldExecute?, rateLimit?, resultSharingMode? })constraintKeyForExecution(task, input?)returns a string key (constraint applies) ornull(skip). Receives both task and runtime input.shouldExecute(task, input?)— optional pre-execution check. Returnsfalseto skip (deferred resolvesundefined). Can be async.maxConcurrent(default:Infinity) — max concurrent tasks per keycooldownMs(default:0) — minimum ms gap between completions per keyrateLimit(optional) —{ maxPerWindow: number, windowMs: number }sliding window rate limiter. Counts both running + completed tasks in window.resultSharingMode(default:'none') —'none'|'share-latest'. When'share-latest', queued tasks for the same key resolve with the first task's result without executing.- Methods:
getConstraintKey(task, input?),checkShouldExecute(task, input?),canRun(key),acquireSlot(key),releaseSlot(key),getCooldownRemaining(key),getRateLimitDelay(key),getNextAvailableDelay(key),getRunningCount(key),recordResult(key, result),getLastResult(key),hasResultSharing(),reset() ITaskExecution<TData>type exported from index —{ task, input }tuple
Rate Limiting (v6.1.0+)
- Sliding window rate limiter:
rateLimit: { maxPerWindow: N, windowMs: ms } - Counts running + completed tasks against the window cap
- Per-key independence: saturating key A doesn't block key B
- Composable with
maxConcurrentandcooldownMs getNextAvailableDelay(key)returnsMath.max(cooldownRemaining, rateLimitDelay)— unified "how long until I can run" answer- Drain timer auto-schedules based on shortest delay across all constraints
Result Sharing (v6.1.0+)
resultSharingMode: 'share-latest'— queued tasks for the same key get the first task's result without executing- Only successful results are shared (errors from
catchErrors: trueor thrown errors are NOT shared) shouldExecuteis NOT called for shared results (the task's purpose was already fulfilled)lastResultspersists untilreset()— for time-bounded sharing, useshouldExecuteto control staleness- Composable with rate limiting: rate-limited waiters get shared result without waiting for the window
TaskManager Constraint Integration
manager.addConstraintGroup(group)/manager.removeConstraintGroup(name)triggerTaskByName(),triggerTask(),addExecuteRemoveTask(), cron callbacks all route throughtriggerTaskConstrained()triggerTaskConstrained(task, input?)— evaluates constraints, queues if blocked, drains after completion- Cooldown-blocked entries auto-drain via timer
Exported from index.ts
TaskConstraintGroupclassITaskConstraintGroupOptions,IRateLimitConfig,TResultSharingModetypes
Error Handling (v3.6.0+)
Tasknow hascatchErrorsconstructor option (default:false)- Default behavior:
trigger()rejects when taskFunction throws (breaking change from pre-3.6) - Set
catchErrors: trueto swallow errors (old behavior) - returnsundefinedon error - Error state tracked via
lastError?: Error,errorCount: number,clearError() getMetadata()status uses all four values:'idle'|'running'|'completed'|'failed'- All peripheral classes (Taskchain, Taskparallel, BufferRunner, TaskDebounced, TaskManager) have proper error propagation/handling
console.logcalls replaced withlogger.log()throughout
Error Context Improvements
- TaskChain: Errors now wrap the original with context: chain name, failing task name, and task index. Original error preserved via
.cause - BufferRunner: When
catchErrors: false, buffered task errors now reject the trigger promise (viaCycleCounter.informOfCycleError) instead of silently resolving withundefined - TaskChain stubs completed:
removeTask(task)returnsboolean,shiftTask()returnsTask | undefined
Task Labels (v4.1.0+)
Taskconstructor accepts optionallabels?: Record<string, string>- Helper methods:
setLabel(key, value),getLabel(key),removeLabel(key),hasLabel(key, value?) getMetadata()includeslabels(shallow copy)TaskManager.getTasksByLabel(key, value)returns matchingTask[]TaskManager.getTasksMetadataByLabel(key, value)returns matchingITaskMetadata[]
Push-Based Events (v4.1.0+)
Task.eventSubject: rxjsSubject<ITaskEvent>emitting'started','step','completed','failed'eventsTaskManager.taskSubject: aggregatedSubject<ITaskEvent>from all added tasksTaskManager.removeTask(task)unsubscribes and removes from mapTaskManager.stop()cleans up all event subscriptions- Exported types:
ITaskEvent,TTaskEventType
Project Structure
- Source in
ts/, web components ints_web/ - Tests in
test/- naming:*.node.ts,*.chromium.ts(preferred),*.both.ts- Note:
*.browser.tsis deprecated, use*.chromium.tsfor browser tests
- Note:
- Logger:
ts/taskbuffer.logging.tsexportslogger(ConsoleLog from smartlog) - Build:
pnpm build(tsbuild tsfolders) - Test:
pnpm testortstest test/test.XX.name.ts --verbose
Web Components (ts_web/)
- Uses
@design.estate/dees-elementwith TC39 decorators - Decorators require the
accessorkeyword:@property({ type: String }) accessor myProp = 'default'; @state() accessor count = 0;
Service Lifecycle System (v7.0.0+)
Service
new Service<T>(name)ornew Service<T>(options: IServiceOptions<T>)- Builder pattern:
.critical(),.optional(),.dependsOn(...),.withStart(fn),.withStop(fn),.withHealthCheck(fn, config?),.withRetry(config),.withStartupTimeout(ms),.withLabels(labels) - Subclass pattern: override
serviceStart(),serviceStop(),serviceHealthCheck() - State machine:
stopped→starting→running→degraded→failed→stopping service.instance— stores the value returned fromstart()(e.g. a database pool)withStop(fn)andwithHealthCheck(fn)receive the instance as argument:(instance: T) => Promise<void>waitForState(target, timeoutMs?),waitForRunning(timeoutMs?),waitForStopped(timeoutMs?)— programmatic readiness gates- Per-service startup timeout via
withStartupTimeout(ms)orstartupTimeoutMsin options - Labels:
setLabel,getLabel,removeLabel,hasLabel,withLabels - Health checks: configurable via
IHealthCheckConfigwithintervalMs,timeoutMs,failuresBeforeDegraded,failuresBeforeFailed - Auto-restart on health failure:
autoRestart: trueinIHealthCheckConfigwithmaxAutoRestarts,autoRestartDelayMs,autoRestartBackoffFactor - Events via
eventSubject:'started','stopped','failed','degraded','recovered','retrying','healthCheck','autoRestarting'
ServiceManager
manager.addService(service)/manager.addServiceFromOptions(options)/manager.removeService(name)- Dependency-ordered startup via topological sort (Kahn's algorithm), level-by-level parallel
- Critical service failure aborts startup with rollback; optional service failure continues
- Retry with exponential backoff + jitter
- Reverse-dependency-ordered shutdown with per-service timeout
restartService(name)— cascade stops dependents, restarts target, restarts dependentsgetHealth()— aggregated'healthy' | 'degraded' | 'unhealthy'statusgetServicesByLabel(key, value)/getServicesStatusByLabel(key, value)— label-based queries- Circular dependency detection
- Global startup timeout enforcement via
startupTimeoutMsinIServiceManagerOptions
Dependencies (as of v7.0.0)
@design.estate/dees-element^2.2.3 - TC39 decorators withaccessorkeyword@push.rocks/lik^6.3.1 - Data structures@push.rocks/smartdelay^3.0.5 - Delay utilities@push.rocks/smartlog^3.2.1 - Logging@push.rocks/smartpromise^4.2.3 - Promise utilities@push.rocks/smartrx^3.0.10 - RxJS wrapper@push.rocks/smarttime^4.2.3 - Time/cron utilities@push.rocks/smartunique^3.0.9 - Unique ID generation@git.zone/tsbuild^4.3.0 - Build tool@git.zone/tsbundle^2.9.1 - Bundler (for browser tests)@git.zone/tsrun^2.0.1 - TypeScript runner@git.zone/tstest^3.5.0 - Test runner (supports.chromium.tsfiles)@types/node^25.5.0 - Node.js type definitions