npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fluojs/cron

v1.0.0

Published

Decorator-based task scheduling for Fluo with cron, interval, and timeout triggers and optional distributed locking.

Downloads

671

Readme

@fluojs/cron

Decorator-based scheduling for fluo applications with lifecycle-managed startup/shutdown and optional Redis distributed locking.

Table of Contents

Installation

npm install @fluojs/cron croner

When to Use

  • When you need to run periodic background tasks (e.g., database cleanup, report generation).
  • When you want to schedule tasks using standard Cron expressions.
  • When running in a multi-instance environment and you need to ensure a task runs only on one instance at a time (Distributed Locking).
  • When you need simple one-off delayed tasks (Timeout) or fixed-rate intervals.

Quick Start

Register the CronModule and use decorators to schedule your methods.

Use CronModule.forRoot(...) to register scheduling for an application module. Cron expressions may use either five fields (minute hour day month weekday) or six fields (second minute hour day month weekday). The built-in CronExpression presets use six-field expressions when sub-minute precision is needed. Cron tasks start only after application bootstrap, dynamically registered cron tasks start when added to a started registry, and fluo forwards timezone plus no-overlap protection to the scheduler so one task instance does not overlap itself.

import { Module } from '@fluojs/core';
import { CronModule, Cron, CronExpression, Interval, Timeout } from '@fluojs/cron';

class BillingService {
  @Cron(CronExpression.EVERY_MINUTE, { name: 'billing.reconcile' })
  async reconcilePendingInvoices() {
    console.log('Reconciling invoices...');
  }

  @Interval(15_000) // 15 seconds
  async pollStatus() {
    console.log('Polling status...');
  }

  @Timeout(5_000) // 5 seconds after startup
  async initialSync() {
    console.log('Running initial sync...');
  }
}

@Module({
  imports: [CronModule.forRoot()],
  providers: [BillingService],
})
class AppModule {}

Common Patterns

Distributed Locking

To prevent scheduled tasks from running concurrently across multiple server instances, enable distributed mode. This requires @fluojs/redis.

import { Module } from '@fluojs/core';
import { CronModule } from '@fluojs/cron';
import { RedisModule } from '@fluojs/redis';

@Module({
  imports: [
    RedisModule.forRoot({ host: 'localhost', port: 6379 }),
    CronModule.forRoot({
      distributed: {
        enabled: true,
        keyPrefix: 'fluo:cron:lock',
        lockTtlMs: 30_000,
      },
    }),
  ],
})
class AppModule {}

Leave distributed.clientName unset to keep using the default Redis registration above. To use a non-default Redis connection for distributed locks, set distributed.clientName to the name registered through RedisModule.forRoot({ name, ... }).

distributed.lockTtlMs must stay at or above 1_000ms. fluo renews the Redis lock before that TTL expires, including the minimum supported 1_000ms boundary.

Each scheduler instance uses a platform-neutral default distributed.ownerId; set distributed.ownerId explicitly only when your deployment has a stronger stable-owner convention. Lock release runs in a finally path after task execution. If Redis release fails, fluo keeps local ownership in status snapshots and retries during shutdown; if Redis reports that another owner holds the key, local ownership is cleared because fencing has already moved elsewhere. Redis TTL and renewal timing are still drift-sensitive coordination primitives rather than hard fencing tokens, so long-running jobs should remain idempotent and use application-level fencing when stale work would be unsafe.

@Module({
  imports: [
    RedisModule.forRoot({ host: 'localhost', port: 6379 }),
    RedisModule.forRoot({ name: 'locks', host: 'localhost', port: 6380 }),
    CronModule.forRoot({
      distributed: {
        clientName: 'locks',
        enabled: true,
        keyPrefix: 'fluo:cron:lock',
        lockTtlMs: 30_000,
      },
    }),
  ],
})
class MultiRedisCronModule {}

Dynamic Scheduling

You can manage tasks at runtime using the SCHEDULING_REGISTRY.

import { Inject } from '@fluojs/core';
import { SCHEDULING_REGISTRY, type SchedulingRegistry } from '@fluojs/cron';

@Inject(SCHEDULING_REGISTRY)
class TaskManager {
  constructor(private readonly registry: SchedulingRegistry) {}

  addNewTask() {
    this.registry.addCron('dynamic-job', '0 * * * *', () => {
      console.log('Dynamic job running!');
    });
  }

  stopTask() {
    this.registry.remove('dynamic-job');
  }
}

The registry exposes addCron, addInterval, addTimeout, remove, enable, disable, get, getAll, and updateCronExpression. Timeout tasks run once, then disable themselves while remaining in the registry so they can be re-enabled deliberately.

Bounded Shutdown

CronModule drains active task executions during application shutdown with a bounded timeout so one hung task cannot block process termination forever.

By default the shutdown drain waits up to 10_000ms. If that timeout expires, the scheduler logs a warning and continues shutdown without waiting for the hung task to settle. When distributed locking is enabled, locks held by still-running tasks are not eagerly released on timeout; they remain owned by that task until it settles normally, or until Redis expires the lock after the process exits. This prevents another node from starting the same job while the original task is still running.

@Module({
  imports: [
    CronModule.forRoot({
      shutdown: {
        timeoutMs: 5_000,
      },
    }),
  ],
})
class AppModule {}

Only singleton providers/controllers are scheduled. Request-scoped and transient scheduled classes are skipped with a warning.

Public API Overview

Modules

  • CronModule.forRoot(options): Configures the scheduler and enables distributed locking if requested.

Decorators

  • @Cron(expression, options?): Schedules a method using a cron expression.
  • @Interval(ms, options?): Schedules a method to run at a fixed interval.
  • @Timeout(ms, options?): Schedules a method to run once after a delay.

Constants & Tokens

  • CronExpression: Enum-like object with common cron patterns, including sub-minute presets such as EVERY_SECOND, EVERY_5_SECONDS, and EVERY_30_SECONDS.
  • SCHEDULING_REGISTRY: Injection token for the SchedulingRegistry service.
  • normalizeCronModuleOptions(...): Normalizes module options and defaults.
  • createCronPlatformStatusSnapshot(...): Creates a status snapshot for health/readiness integrations.
  • Metadata helpers and symbols: defineSchedulingTaskMetadata, defineCronTaskMetadata, getSchedulingTaskMetadata, getCronTaskMetadata, getSchedulingTaskMetadataEntries, getCronTaskMetadataEntries, schedulingMetadataSymbol, cronMetadataSymbol.

Related Packages

  • @fluojs/redis: Required for distributed locking functionality.
  • @fluojs/core: Required for DI and Module management.
  • croner: The underlying scheduling engine.

Example Sources

  • packages/cron/src/module.test.ts: Comprehensive tests for decorators and module lifecycle.
  • packages/cron/src/service.ts: Runtime scheduling, registry, and shutdown behavior.
  • packages/cron/src/status.test.ts: Status snapshot behavior.
  • packages/cron/src/distributed-lock-manager.ts: Redis distributed lock behavior.