npm package: @recordset/adonis7-scheduler
Description: Task scheduler provider for AdonisJS 7
Author: RecordSet
Latest version: 1.0.0

# @recordset/adonis7-scheduler

Task scheduler provider for **AdonisJS 7**. Run recurring tasks using cron expressions with optional file-based locking for multi-instance deployments.

Built on top of [node-schedule](https://github.com/node-schedule/node-schedule) and [cron-time-generator](https://github.com/xpresserjs/cron-time-generator).

## Features

- Cron-based task scheduling with human-readable helpers
- File-based locking to prevent overlapping task execution
- Auto-discovery of task files from a configurable directory
- AdonisJS 7 IoC container integration (dependency injection in tasks)
- `make:task` scaffolding command
- Graceful shutdown support

## Installation

```bash
npm install @recordset/adonis7-scheduler
node ace configure @recordset/adonis7-scheduler
```

The `configure` command will:

- Register the provider and commands in `adonisrc.ts`
- Create `config/scheduler.ts`

## Usage

### Creating a Task

```bash
node ace make:task cleanup_expired
```

This generates `app/tasks/cleanup_expired_task.ts`:

```typescript
import { BaseTask, CronTimeV2 } from "@recordset/adonis7-scheduler/task";

export default class CleanupExpiredTask extends BaseTask {
  static get schedule() {
    return CronTimeV2.everyMinute();
  }

  static get useLock() {
    return false;
  }

  async handle() {
    // Task logic here
  }
}
```

### Running the Scheduler

```bash
node ace scheduler:run
```

The scheduler auto-discovers all task files in the configured `tasksPath` directory.

### Schedule Helpers

`CronTimeV2` extends `cron-time-generator` with sub-minute intervals:

| Method                 | Cron Expression  |
| ---------------------- | ---------------- |
| `everySecond()`        | `* * * * * *`    |
| `everyFiveSeconds()`   | `*/5 * * * * *`  |
| `everyTenSeconds()`    | `*/10 * * * * *` |
| `everyThirtySeconds()` | `*/30 * * * * *` |
| `everyMinute()`        | `* * * * *`      |
| `everyFiveMinutes()`   | `*/5 * * * *`    |
| `everyThirtyMinutes()` | `*/30 * * * *`   |

You can also use any valid cron expression string directly:

```typescript
static get schedule() {
  return '0 9 * * 1-5' // Weekdays at 9:00 AM
}
```

All methods from [cron-time-generator](https://github.com/xpresserjs/cron-time-generator) are also available: `everyHour()`, `everyDay()`, `everyWeek()`, etc.

### File Locking

Enable `useLock` to prevent a task from running concurrently across multiple instances:

```typescript
export default class HeavyTask extends BaseTask {
  static get schedule() {
    return CronTimeV2.everyFiveMinutes();
  }

  static get useLock() {
    return true;
  }

  async handle() {
    console.log(this.duration()); // e.g. "1.2s"
  }
}
```

Lock files are stored in `tmp/adonis7-scheduler/locks/`.

### Dependency Injection

Tasks are resolved through the AdonisJS container, so you can use constructor injection:

```typescript
import { inject } from "@adonisjs/core";
import { BaseTask, CronTimeV2 } from "@recordset/adonis7-scheduler/task";
import UserService from "#services/user_service";

@inject()
export default class NotifyUsersTask extends BaseTask {
  static get schedule() {
    return CronTimeV2.everyHour();
  }

  constructor(private userService: UserService) {
    super();
  }

  async handle() {
    await this.userService.notifyInactiveUsers();
  }
}
```

## Configuration

```typescript
// config/scheduler.ts
import { defineConfig } from "@recordset/adonis7-scheduler/types";

export default defineConfig({
  tasksPath: "app/tasks",
});
```

| Option      | Type     | Default       | Description                            |
| ----------- | -------- | ------------- | -------------------------------------- |
| `tasksPath` | `string` | `'app/tasks'` | Directory where task files are located |

## Ace Commands

| Command            | Description                       |
| ------------------ | --------------------------------- |
| `make:task <name>` | Generate a new task class         |
| `scheduler:run`    | Start the scheduler (stays alive) |

## License

MIT