Skip to content

xlsft/worker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation



@xlsft/worker

@xlsft/worker – Lightweight, cron, date and event-based task framework with zero-boilerplate setup


Made with <3 and TS

🔗 Table of Contents


⭐ Quick Start

@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/worker

Warning

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";
const log = useLogger() // Some logger

defineWorker({ log })
import { defineWorker } from "@xlsft/worker";

defineWorker({ log: false }) // Disables logging

Also 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.js

📂 Project Structure & Naming

All 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.mjs

Where:

  • nn(optional) — numeric prefix for ordering in directory
  • name — task name
  • .task.[ts,js,mts,mjs] — required suffix

Example:

tasks/
│── 01.cron.task.ts 
│── 02.event.task.ts
│── other.task.ts

🕒 Task Triggers

A task trigger can be:

  1. Cron based — using TaskCronScheduleSchema-like type or cron syntax
  2. Event based — for manual or cross-task triggering with EventEmitter
  3. Date based - for triggering task only once at some date

Examples:

  1. Cron based cron syntax:
import { defineTask } from "@xlsft/worker";

export default defineTask((event) => {
    console.log('Running every minute');
}, '* * * * *');
  1. Cron based TaskCronScheduleSchema:
import { defineTask } from "@xlsft/worker";

export default defineTask((event) => {
    console.log('Running every 30 minutes');
}, { minute: { every: 30 } });
  1. Event based:
import { defineTask } from "@xlsft/worker";

export default defineTask((event) => {
    console.log('Triggered by event.emit("event")');
}, 'event');
  1. Event based from generated trigger (from task name):
// name.task.ts

import { defineTask } from "@xlsft/worker";

export default defineTask((event) => {
    console.log('Triggered by event.emit("name")');
}); 

// Works with cron too

import { defineTask } from "@xlsft/worker";

export default defineTask((event) => {
    console.log('Triggered by event.emit("name") and every 30 minutes');
}, { minute: { every: 30 } }); 
  1. Date based:
// name.task.ts

import { defineTask } from "@xlsft/worker";

export default defineTask((event) => {
    console.log('Triggered at Tue Jan 01 2030 00:00:00');
}, new Date(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
Loading

📣 Task Event

For every task event: TaskEvent class is passed for logging or some other purposes

// event.task.ts

import { defineTask } from "@xlsft/worker"

export default defineTask((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"

export default defineTask((event) => {
    console.log(event);
    event.state.status = 'canceled'; // TS error
}, { minute: { every: 1 }});

📡 Calling a Task from Another Task

You can call (trigger) another task by emitting an event:

// 01.parent.task.ts

import { defineTask } from "@xlsft/worker";

export default defineTask((event) => {
    console.log('Parent job running...');
    event.emit('children');
}, { minute: { every: 1 } });

// 02.children.task.ts

import { defineTask, events } from "tasks";

export default defineTask((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]
Loading

🔪 Killing or Canceling 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.ts

import { defineTask } from "@xlsft/worker"

export default defineTask((event) => {
    console.log('Job running');
    event.cancel();
    console.log('Unreachable');
}, 'first_job');

// second_job.task.ts

import { defineTask } from "@xlsft/worker"

export default defineTask((event) => {
    console.log('Start first job');
    event.emit('first_job'); // Works!
}, { minute: { every: 1 }});
// first_job.task.ts

import { defineTask } from "@xlsft/worker"

export default defineTask((event) => {
    console.log('Job running');
    event.kill();
    console.log('Unreachable');
}, 'first_job');

// second_job.task.ts

import { defineTask } from "@xlsft/worker"

export default defineTask((event) => {
    console.log('Start first job');
    event.emit('first_job'); // Doesn`t work :(
}, { minute: { every: 1 }});

⚙️ Task Options

Now you can set 3 different modifications to your worker

  1. retry – number of additional attempts the worker will make if the task fails
  2. times – number of times the task should run in a single execution
  3. delay – time in milliseconds to wait between task executions or retries
export default defineTask((event) => {
    console.log('Task starts, but fails');
    throw new Error('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)

🚧 Project Status

Testing in production — API and internal structure are stable, but needs testing for edge-cases

✅ To do

  • 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

About

@xlsft/worker – Lightweight, cron, date and event-based task framework with zero-boilerplate setup

Topics

Resources

License

Stars

Watchers

Forks