@outloud/adonis-scheduler 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,6 +23,7 @@
23
23
  - Cancellation support for long-running tasks.
24
24
  - Graceful shutdown.
25
25
  - Global and task-level error handling.
26
+ - Auto-discovery of tasks.
26
27
 
27
28
  ## Getting Started
28
29
 
@@ -57,8 +58,12 @@ export default class TestTask extends Task {
57
58
  ```
58
59
 
59
60
  ### Register a task
61
+ For task to run it must be registered in the scheduler.
60
62
 
61
- For task to run it must be registered in the scheduler. You can register tasks in two ways: using the `start/scheduler.ts` preloaded file or in a provider's `start` method.
63
+ > [!NOTE]
64
+ > By default tasks are auto-discovered using the locations defined in config.
65
+
66
+ If you want to register tasks manually, you can register tasks in two ways: using the `start/scheduler.ts` preloaded file or in a provider's `start` method.
62
67
 
63
68
  Using `start/scheduler.ts` file.
64
69
 
@@ -1,4 +1,7 @@
1
+ import { Task } from './chunk-OIXFFNQU.js';
1
2
  import { __name } from './chunk-SHUYVCID.js';
3
+ import { glob } from 'fs/promises';
4
+ import { resolve } from 'path';
2
5
  import { Cron } from 'croner';
3
6
  import timers from 'timers/promises';
4
7
 
@@ -17,14 +20,16 @@ var Scheduler = class {
17
20
  config;
18
21
  resolver;
19
22
  logger;
23
+ emitter;
20
24
  locks;
21
25
  definitions = [];
22
26
  state = "created";
23
27
  errorHandler;
24
- constructor(config, resolver, logger, locks) {
28
+ constructor(config, resolver, logger, emitter, locks) {
25
29
  this.config = config;
26
30
  this.resolver = resolver;
27
31
  this.logger = logger;
32
+ this.emitter = emitter;
28
33
  this.locks = locks;
29
34
  }
30
35
  register(options) {
@@ -38,15 +43,18 @@ var Scheduler = class {
38
43
  options.command
39
44
  ];
40
45
  Object.assign(definition, options);
41
- definition.loader = () => import('./command.task-GUNK3QFY.js').then((module) => {
46
+ definition.loader = () => import('./command.task-GALKWNLQ.js').then((module) => {
42
47
  return class extends module.CommandTask {
43
48
  static command = command;
44
49
  };
45
50
  });
51
+ } else if (Task.isTask(options)) {
52
+ definition.task = options;
53
+ Object.assign(definition, options.options ?? {});
46
54
  } else {
47
55
  definition.loader = options;
48
56
  }
49
- if (!definition.loader) {
57
+ if (!definition.task && !definition.loader) {
50
58
  throw new Error("Task definition must have either a command or a task defined.");
51
59
  }
52
60
  this.definitions.push(definition);
@@ -67,6 +75,9 @@ var Scheduler = class {
67
75
  return;
68
76
  }
69
77
  this.setState("starting");
78
+ if (this.config.locations) {
79
+ await Promise.all(this.config.locations.map((location) => this.registerFromGlob(location)));
80
+ }
70
81
  await Promise.all(this.definitions.map((definition) => this.schedule(definition)));
71
82
  if (!this.definitions.length) {
72
83
  this.logger.warn("No tasks registered, scheduler will not run any jobs.");
@@ -92,6 +103,20 @@ var Scheduler = class {
92
103
  this.errorHandler = callback;
93
104
  return this;
94
105
  }
106
+ async registerFromGlob(pattern) {
107
+ const normalizedPattern = pattern.replace(/\.(?:js|ts)$/, ".{js,ts}");
108
+ for await (const file of glob(normalizedPattern)) {
109
+ try {
110
+ const absolutePath = resolve(file);
111
+ const module = await import(`file://${absolutePath}`);
112
+ if (Task.isTask(module.default)) {
113
+ this.register(module.default);
114
+ }
115
+ } catch (error) {
116
+ console.warn(`Failed to load task from ${file}:`, error);
117
+ }
118
+ }
119
+ }
95
120
  async load(definition) {
96
121
  if (definition.loader) {
97
122
  const module = await definition.loader();
@@ -145,15 +170,15 @@ var Scheduler = class {
145
170
  this.logger.info(`Scheduler: ${state}`);
146
171
  }
147
172
  async handleError(error, _, task) {
148
- if (task && task.onError) {
149
- await task.onError(error);
150
- return;
151
- }
152
- if (this.errorHandler) {
153
- await this.errorHandler(error, task);
154
- return;
173
+ await task.onError?.(error);
174
+ this.emitter.emit("scheduler:error", {
175
+ error,
176
+ task
177
+ });
178
+ await this.errorHandler?.(error, task);
179
+ if (!this.emitter.hasListeners("scheduler:error") && !this.errorHandler) {
180
+ throw error;
155
181
  }
156
- throw error;
157
182
  }
158
183
  async cancel(job) {
159
184
  await job.$cancel();
@@ -1,13 +1,16 @@
1
1
  import { __name } from './chunk-SHUYVCID.js';
2
2
 
3
3
  // src/task.ts
4
- var Task = class {
4
+ var Task = class _Task {
5
5
  static {
6
6
  __name(this, "Task");
7
7
  }
8
8
  isCanceled = false;
9
9
  promise;
10
10
  static options;
11
+ static isTask(obj) {
12
+ return typeof obj === "function" && obj.prototype instanceof _Task;
13
+ }
11
14
  // eslint-disable-next-line @typescript-eslint/no-useless-constructor
12
15
  constructor(..._) {
13
16
  }
@@ -1,4 +1,4 @@
1
- import { Task } from './chunk-7P6J5U6O.js';
1
+ import { Task } from './chunk-OIXFFNQU.js';
2
2
  import { __name } from './chunk-SHUYVCID.js';
3
3
  import ace from '@adonisjs/core/services/ace';
4
4
 
@@ -1,10 +1,11 @@
1
1
  import { BaseCommand } from '@adonisjs/core/ace';
2
2
  import { CommandOptions } from '@adonisjs/core/types/ace';
3
- import { a as Scheduler } from '../scheduler-BWT0Iqko.js';
3
+ import { a as Scheduler } from '../scheduler-vBDqaysB.js';
4
4
  import '@adonisjs/core/container';
5
5
  import '@adonisjs/core/types';
6
6
  import '@adonisjs/lock/types';
7
7
  import '@adonisjs/core/logger';
8
+ import '@adonisjs/core/events';
8
9
 
9
10
  declare class SchedulerRun extends BaseCommand {
10
11
  static commandName: string;
@@ -1,4 +1,5 @@
1
- import { Scheduler } from '../chunk-BBI2I5B5.js';
1
+ import { Scheduler } from '../chunk-6VW5PUDE.js';
2
+ import '../chunk-OIXFFNQU.js';
2
3
  import { __name } from '../chunk-SHUYVCID.js';
3
4
  import 'reflect-metadata';
4
5
  import { inject } from '@adonisjs/core';
package/build/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { S as SchedulerConfig } from './scheduler-BWT0Iqko.js';
2
- export { a as Scheduler, T as Task, b as TaskOptions } from './scheduler-BWT0Iqko.js';
1
+ import { S as SchedulerConfig } from './scheduler-vBDqaysB.js';
2
+ export { a as Scheduler, T as Task, b as TaskOptions } from './scheduler-vBDqaysB.js';
3
3
  import Configure from '@adonisjs/core/commands/configure';
4
4
  import '@adonisjs/core/container';
5
5
  import '@adonisjs/core/types';
6
6
  import '@adonisjs/lock/types';
7
7
  import '@adonisjs/core/logger';
8
+ import '@adonisjs/core/events';
8
9
 
9
10
  declare function configure(command: Configure): Promise<void>;
10
11
 
package/build/index.js CHANGED
@@ -1,5 +1,5 @@
1
- export { Scheduler } from './chunk-BBI2I5B5.js';
2
- export { Task } from './chunk-7P6J5U6O.js';
1
+ export { Scheduler } from './chunk-6VW5PUDE.js';
2
+ export { Task } from './chunk-OIXFFNQU.js';
3
3
  import { __name } from './chunk-SHUYVCID.js';
4
4
 
5
5
  // configure.ts
@@ -1,8 +1,9 @@
1
1
  import { ApplicationService } from '@adonisjs/core/types';
2
- import { a as Scheduler } from '../scheduler-BWT0Iqko.js';
2
+ import { a as Scheduler, c as SchedulerEvents } from '../scheduler-vBDqaysB.js';
3
3
  import '@adonisjs/core/container';
4
4
  import '@adonisjs/lock/types';
5
5
  import '@adonisjs/core/logger';
6
+ import '@adonisjs/core/events';
6
7
 
7
8
  declare class SchedulerProvider {
8
9
  protected app: ApplicationService;
@@ -18,6 +19,8 @@ declare module '@adonisjs/core/types' {
18
19
  interface ContainerBindings {
19
20
  scheduler: Scheduler;
20
21
  }
22
+ interface EventsList extends SchedulerEvents {
23
+ }
21
24
  }
22
25
 
23
26
  export { SchedulerProvider as default };
@@ -1,4 +1,5 @@
1
- import { Scheduler } from '../chunk-BBI2I5B5.js';
1
+ import { Scheduler } from '../chunk-6VW5PUDE.js';
2
+ import '../chunk-OIXFFNQU.js';
2
3
  import { __name } from '../chunk-SHUYVCID.js';
3
4
 
4
5
  // providers/scheduler.provider.ts
@@ -21,7 +22,7 @@ var SchedulerProvider = class {
21
22
  }
22
23
  register() {
23
24
  this.app.container.singleton(Scheduler, async () => {
24
- return new Scheduler(this.getConfig(), this.app.container.createResolver(), await this.app.container.make("logger"), await this.getLocks());
25
+ return new Scheduler(this.getConfig(), this.app.container.createResolver(), await this.app.container.make("logger"), await this.app.container.make("emitter"), await this.getLocks());
25
26
  });
26
27
  this.app.container.alias("scheduler", Scheduler);
27
28
  }
@@ -2,6 +2,7 @@ import { ContainerResolver } from '@adonisjs/core/container';
2
2
  import { ContainerBindings } from '@adonisjs/core/types';
3
3
  import { LockService } from '@adonisjs/lock/types';
4
4
  import { Logger } from '@adonisjs/core/logger';
5
+ import { Emitter } from '@adonisjs/core/events';
5
6
 
6
7
  interface SchedulerConfig {
7
8
  /**
@@ -16,6 +17,8 @@ interface SchedulerConfig {
16
17
  * The default ttl for the lock.
17
18
  */
18
19
  lockDuration: number | string;
20
+ /** Locations for task auto-discovery */
21
+ locations?: string[];
19
22
  }
20
23
  interface TaskOptions {
21
24
  /**
@@ -45,6 +48,12 @@ interface TaskRegisterOptions extends TaskOptions {
45
48
  command: string | string[];
46
49
  }
47
50
  type ErrorHandler = (error: Error, task: Task) => (void | Promise<void>);
51
+ interface SchedulerEvents {
52
+ 'scheduler:error': {
53
+ error: Error;
54
+ task: Task;
55
+ };
56
+ }
48
57
  type MaybePromise<T> = T | Promise<T>;
49
58
  type Factory<T> = () => MaybePromise<{
50
59
  default: T;
@@ -54,6 +63,7 @@ declare abstract class Task {
54
63
  isCanceled: boolean;
55
64
  promise?: Promise<any>;
56
65
  static options: TaskOptions;
66
+ static isTask(obj: unknown): obj is typeof Task;
57
67
  constructor(..._: any[]);
58
68
  get name(): string;
59
69
  abstract run(...args: any[]): Promise<void>;
@@ -68,15 +78,17 @@ declare class Scheduler {
68
78
  private config;
69
79
  private resolver;
70
80
  private logger;
81
+ private emitter;
71
82
  private locks?;
72
83
  private definitions;
73
84
  private state;
74
85
  private errorHandler?;
75
- constructor(config: SchedulerConfig, resolver: ContainerResolver<ContainerBindings>, logger: Logger, locks?: LockService | undefined);
76
- register(options: TaskRegisterOptions | Factory<typeof Task>): this;
86
+ constructor(config: SchedulerConfig, resolver: ContainerResolver<ContainerBindings>, logger: Logger, emitter: Emitter<any>, locks?: LockService | undefined);
87
+ register(options: TaskRegisterOptions | typeof Task | Factory<typeof Task>): this;
77
88
  start(wait?: boolean): Promise<void>;
78
89
  stop(): Promise<void>;
79
90
  onError(callback: ErrorHandler): this;
91
+ private registerFromGlob;
80
92
  private load;
81
93
  private make;
82
94
  private run;
@@ -88,4 +100,4 @@ declare class Scheduler {
88
100
  private schedule;
89
101
  }
90
102
 
91
- export { type SchedulerConfig as S, Task as T, Scheduler as a, type TaskOptions as b };
103
+ export { type SchedulerConfig as S, Task as T, Scheduler as a, type TaskOptions as b, type SchedulerEvents as c };
@@ -1,8 +1,9 @@
1
- import { a as Scheduler } from '../scheduler-BWT0Iqko.js';
1
+ import { a as Scheduler } from '../scheduler-vBDqaysB.js';
2
2
  import '@adonisjs/core/container';
3
3
  import '@adonisjs/core/types';
4
4
  import '@adonisjs/lock/types';
5
5
  import '@adonisjs/core/logger';
6
+ import '@adonisjs/core/events';
6
7
 
7
8
  declare let scheduler: Scheduler;
8
9
 
@@ -9,6 +9,7 @@ const schedulerConfig = defineConfig({
9
9
  httpServer: env.get('SCHEDULER_HTTP_SERVER', false),
10
10
  warnWhenLocked: false,
11
11
  lockDuration: '10m',
12
+ locations: ['./app/**/*.task.js'],
12
13
  })
13
14
 
14
15
  export default schedulerConfig
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@outloud/adonis-scheduler",
3
3
  "type": "module",
4
- "version": "1.0.5",
4
+ "version": "1.0.7",
5
5
  "description": "Schedule cron jobs in AdonisJS.",
6
6
  "author": "Outloud <hello@outloud.co>",
7
7
  "contributors": [
@@ -66,7 +66,6 @@
66
66
  "postcompile": "pnpm run copy:templates && pnpm run index:commands",
67
67
  "build": "rm -rf build && pnpm run compile",
68
68
  "release": "release-it",
69
- "index:commands": "adonis-kit index build/commands",
70
- "version": "pnpm run build"
69
+ "index:commands": "adonis-kit index build/commands"
71
70
  }
72
71
  }