
- Quick Start
- Project Structure & Naming
- Task Triggers
- Task Event
- Calling a Task from Another Task
- Killing or Canceling Task
- Task Options
- Project Status
- To do
@xlsft/worker is a simple but powerful worker framework. Supported on node, deno and bun.
It automatically loads and schedules all *.task.[ts,js,mts,mjs] files from the tasks directory.
Tasks can run on a cron schedule or be triggered by events.
Install @xlsft/worker package
pnpm add jsr:@xlsft/worker # or...
yarn add jsr:@xlsft/worker # or...
npx jsr add @xlsft/worker # or...
bunx jsr add @xlsft/workerWarning
Do not use deno add jsr:@xlsft/worker! Due to JSR limitations with dynamic imports, it fails when registering tasks!
Create tasks folder and entry file
import{defineWorker}from"@xlsft/worker";defineWorker()You can pass custom logger or disable logging whatsoever
import{defineWorker}from"@xlsft/worker";constlog=useLogger()// Some loggerdefineWorker({ log })import{defineWorker}from"@xlsft/worker";defineWorker({log: false})// Disables loggingAlso you can change default tasks folder, be aware that it needs to be path from process.cwd()
import{defineWorker}from"@xlsft/worker";defineWorker({dir: 'src/tasks'})Then run your script with following commands
deno run -A main.ts # or...
bun run main.ts # or...
node main.jsAll tasks must be placed in the tasks directory (or directory of your choice).
File names follow the pattern:
<nn(optional)>.<name>.task.[ts,js,mts,mjs]// 01.some_job.task.ts || some_job.task.js || 03.job.task.mjsWhere:
nn(optional)— numeric prefix for ordering in directoryname— task name.task.[ts,js,mts,mjs]— required suffix
Example:
tasks/
│── 01.cron.task.ts │── 02.event.task.ts
│── other.task.ts
A task trigger can be:
- Cron based — using
TaskCronScheduleSchema-like type orcron syntax - Event based — for manual or cross-task triggering with
EventEmitter - Date based - for triggering task only once at some date
Examples:
- Cron based
cron syntax:
import{defineTask}from"@xlsft/worker";exportdefaultdefineTask((event)=>{console.log('Running every minute');},'* * * * *');- Cron based
TaskCronScheduleSchema:
import{defineTask}from"@xlsft/worker";exportdefaultdefineTask((event)=>{console.log('Running every 30 minutes');},{minute: {every: 30}});- Event based:
import{defineTask}from"@xlsft/worker";exportdefaultdefineTask((event)=>{console.log('Triggered by event.emit("event")');},'event');- Event based from generated trigger (from task name):
// name.task.tsimport{defineTask}from"@xlsft/worker";exportdefaultdefineTask((event)=>{console.log('Triggered by event.emit("name")');});// Works with cron tooimport{defineTask}from"@xlsft/worker";exportdefaultdefineTask((event)=>{console.log('Triggered by event.emit("name") and every 30 minutes');},{minute: {every: 30}});- Date based:
// name.task.tsimport{defineTask}from"@xlsft/worker";exportdefaultdefineTask((event)=>{console.log('Triggered at Tue Jan 01 2030 00:00:00');},newDate(2030,0,1));%%{init: {'flowchart': {'nodeSpacing': 12,'rankSpacing':24}}}%%
flowchart TD
Event[Event Emitter] --> TW
Cron[Cron-like Scheduler] --> TW[Task Workers]
Date[Date Scheduler] --> TW
subgraph TW[ ]
direction BT
TW1[Task Worker 1]
TW2[Task Worker 2]
TW3[Task Worker 3]
TW4[Task Worker ...n]
end
For every task event: TaskEvent class is passed for logging or some other purposes
// event.task.tsimport{defineTask}from"@xlsft/worker"exportdefaultdefineTask((event)=>{console.log(event);// TaskEvent {// worker: [Function: worker],// created: 2025-08-14T16:34:31.990Z,// state: { status: "running" },// data: { name: "event", trigger: "event", cron: false },// emit: [Function: emit]// }});Ensure you doesn`t modify event class, because event is readonly
import{defineTask}from"@xlsft/worker"exportdefaultdefineTask((event)=>{console.log(event);event.state.status='canceled';// TS error},{minute: {every: 1}});You can call (trigger) another task by emitting an event:
// 01.parent.task.tsimport{defineTask}from"@xlsft/worker";exportdefaultdefineTask((event)=>{console.log('Parent job running...');event.emit('children');},{minute: {every: 1}});// 02.children.task.tsimport{defineTask,events}from"tasks";exportdefaultdefineTask((event)=>{console.log('Children job running');},'children');%%{init: {'flowchart': {'nodeSpacing': 12,'rankSpacing':24}}}%%
flowchart LR
Parent[Parent Task] --> Bus[Event Bus]
Bus --> Child[Child Task]
You can kill or cancel task using event.kill() or event.cancel()
The difference between canceling and killing a job is that canceling stops only the current run and allows you to start a new one, whereas killing stops the job entirely and it cannot be restarted
// first_job.task.tsimport{defineTask}from"@xlsft/worker"exportdefaultdefineTask((event)=>{console.log('Job running');event.cancel();console.log('Unreachable');},'first_job');// second_job.task.tsimport{defineTask}from"@xlsft/worker"exportdefaultdefineTask((event)=>{console.log('Start first job');event.emit('first_job');// Works!},{minute: {every: 1}});// first_job.task.tsimport{defineTask}from"@xlsft/worker"exportdefaultdefineTask((event)=>{console.log('Job running');event.kill();console.log('Unreachable');},'first_job');// second_job.task.tsimport{defineTask}from"@xlsft/worker"exportdefaultdefineTask((event)=>{console.log('Start first job');event.emit('first_job');// Doesn`t work :(},{minute: {every: 1}});Now you can set 3 different modifications to your worker
retry– number of additional attempts the worker will make if the task failstimes– number of times the task should run in a single executiondelay– time in milliseconds to wait between task executions or retries
exportdefaultdefineTask((event)=>{console.log('Task starts, but fails');thrownewError('Error');},'event',{times: 2,delay: 2000,retry: 5});In this example:
- The task will run 2 times (times: 2), but, if it fails, job will retry with 2 times again
- It will wait 2 seconds between each run or retry (delay: 2000)
- If it fails, it will retry up to 5 times (retry: 5)
Testing in production — API and internal structure are stable, but needs testing for edge-cases
- Pass logger into event object with custom prefix
- Throw an error if try to emit cron string
- Add an coroutines to tasks (at least try to)
- Add coroutines docs
- Add log at start of the task
- Check for infinity in retry function
- Retry function in coroutines
- Emit working on cron triggers by name
- Depends option in task
- Add dependencies docs

