New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

@lindorm/worker

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lindorm/worker

Interval-based background worker with retry, jitter, lifecycle events, graceful shutdown, and structured logging.

latest
Source
npmnpm
Version
0.3.0
Version published
Maintainers
1
Created
Source

@lindorm/worker

Interval-based background worker with retry, jitter, lifecycle events, graceful shutdown, and structured logging.

Installation

npm install @lindorm/worker

Quick Start

import { LindormWorker } from "@lindorm/worker";

const worker = new LindormWorker({
  alias: "cleanup",
  interval: "10m",
  jitter: "30s",
  logger,

  callback: async (ctx) => {
    ctx.logger.info("Running cleanup", { seq: ctx.seq });
    await db.deleteExpired();
  },
});

worker.start();

Options

const worker = new LindormWorker({
  // Required
  alias: "my-worker",                 // identifier used in logs
  interval: "5m",                     // execution interval (ms or readable time)
  callback: async (ctx) => { ... },   // main work
  logger: ILogger,                    // @lindorm/logger instance

  // Optional
  jitter: "30s",                      // +/- spread around interval (default: 0)
  callbackTimeout: "2m",              // abort callback after this duration (default: disabled)
  errorCallback: async (ctx, err) => { ... },  // runs after all retries exhausted
  retry: {                            // override retry defaults
    strategy: "exponential",          // "exponential" | "linear" (default: "exponential")
    timeout: 250,                     // initial retry delay ms (default: 250)
    timeoutMax: 30_000,               // retry delay cap ms (default: 30000)
    maxAttempts: 10,                  // retries per interval (default: 10)
  },
  listeners: [                        // pre-register event listeners
    { event: "error", listener: (err) => alerting.notify(err) },
  ],
});

All time values accept milliseconds or human-readable strings ("30s", "5m", "1h", "1d", "1w").

Lifecycle

start() ──> callback ──> [success] ──> wait(interval +/- jitter) ──> callback ──> ...
                │
                └── [error] ──> retry(1) ──> retry(2) ──> ... ──> retry(max)
                        │            │                                │
                     warning      warning                           error
                     emitted      emitted                          emitted
                                                                      │
                                                                errorCallback
                                                                      │
                                                          wait(interval +/- jitter)

Jitter

Jitter is centered around the base interval: interval + random(-jitter, +jitter). With interval: "10m" and jitter: "30s", the actual wait is between 9m30s and 10m30s.

Retry

When the callback throws, the worker retries with configurable backoff before moving to the next interval. Each retry emits a warning event. When all retries are exhausted, an error event is emitted and the optional errorCallback runs.

Callback Timeout

When callbackTimeout is set, the callback is raced against a deadline. If the callback exceeds the timeout, it is treated as a failure and enters the retry flow.

Events

worker.on("start", () => { ... });            // worker started
worker.on("stop", () => { ... });             // worker stopped
worker.on("success", () => { ... });          // callback succeeded
worker.on("warning", (err) => { ... });       // callback failed, retrying
worker.on("error", (err) => { ... });         // all retries exhausted

Use off() to remove a listener and once() for one-shot listeners.

Callback Context

The callback receives a context object:

type LindormWorkerContext = {
  logger: ILogger; // child logger scoped to this execution
  seq: number; // monotonic counter (1, 2, 3, ...)
  latestSuccess: Date | null;
  latestError: Date | null;
  latestTry: Date | null;
};

Methods

MethodDescription
start()Begin interval execution. No-op if already started.
stop()Graceful shutdown. Awaits running callback, then stops.
destroy()Stop + remove all listeners. Methods throw after destroy.
trigger()Run the callback immediately (outside the interval).
health()Returns a LindormWorkerHealth snapshot.

Health Check

const h = worker.health();
// {
//   alias: "cleanup",
//   started: true,
//   running: false,
//   destroyed: false,
//   seq: 42,
//   latestSuccess: Date,
//   latestError: null,
//   latestTry: Date,
// }

State Getters

worker.alias; // string
worker.started; // boolean
worker.running; // boolean — true while callback is executing
worker.seq; // number — incremented each interval
worker.latestStart; // Date | null
worker.latestStop; // Date | null
worker.latestSuccess; // Date | null
worker.latestError; // Date | null
worker.latestTry; // Date | null

File Scanner

LindormWorkerScanner discovers worker configs from files. Each file exports uppercase named constants:

// workers/CleanupWorker.ts
import type { LindormWorkerCallback } from "@lindorm/worker";

export const CALLBACK: LindormWorkerCallback = async (ctx) => {
  await db.deleteExpired();
};

export const INTERVAL = "15m";
export const JITTER = "1m";
export const RETRY = { maxAttempts: 3, strategy: "linear" };

Scan a directory to get an array of LindormWorkerConfig:

import { LindormWorkerScanner } from "@lindorm/worker";

const configs = LindormWorkerScanner.scan([
  "./src/workers", // directories are scanned recursively
  existingConfig, // LindormWorkerConfig objects pass through
]);

Recognised Exports

ExportTypeRequired
CALLBACKLindormWorkerCallbackYes
ALIASstringNo (defaults to filename)
INTERVALReadableTime | numberNo
JITTERReadableTime | numberNo
CALLBACK_TIMEOUTReadableTime | numberNo
ERROR_CALLBACKLindormWorkerErrorCallbackNo
LISTENERSLindormWorkerListenerConfig[]No
RETRYRetryOptionsNo

Errors

ErrorThrown when
LindormWorkerErrorValidation failure, callback timeout, or method called after destroy
LindormWorkerScannerErrorScanner finds a file without a CALLBACK export

Both extend LindormError from @lindorm/errors.

Testing

A mock factory is available at @lindorm/worker/mocks:

import { createMockWorker } from "@lindorm/worker/mocks";

const mock = createMockWorker();

mock.trigger.mockResolvedValue(undefined);
expect(mock.start).toHaveBeenCalled();
expect(mock.health).toHaveReturnedWith(expect.objectContaining({ started: false }));

All methods are jest.fn() instances. trigger(), stop(), and destroy() return resolved promises.

License

AGPL-3.0-or-later

FAQs

Package last updated on 15 Apr 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts