@outloud/adonis-scheduler 1.1.0 → 1.1.1

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
@@ -56,9 +56,16 @@ export default class TestTask extends Task {
56
56
  ### Register a task
57
57
  For task to run it must be registered in the scheduler.
58
58
 
59
- > [!NOTE]
60
- > By default tasks are auto-discovered using the locations defined in config.
59
+ #### Auto-discovery (recommended)
60
+ By default tasks are auto-discovered using the locations defined in `config/scheduler.ts`.
61
+
62
+ ```ts
63
+ const schedulerConfig = defineConfig({
64
+ locations: ['./app/**/*.task.js'],
65
+ })
66
+ ```
61
67
 
68
+ #### Manual registration
62
69
  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.
63
70
 
64
71
  Using `start/scheduler.ts` file.
@@ -69,7 +76,7 @@ import scheduler from '@outloud/adonis-scheduler/services/main'
69
76
  scheduler.register(() => import('../app/tasks/test.task.js'))
70
77
  ```
71
78
 
72
- Or using a provider.
79
+ Or in a any provider's `start` method.
73
80
 
74
81
  ```ts
75
82
  import type { ApplicationService } from '@adonisjs/core/types'
@@ -117,11 +124,9 @@ You can also create a custom worker command that runs the scheduler. This is use
117
124
  ```ts [commands/worker.ts]
118
125
  import { createServer, type Server as HttpServer } from 'node:http'
119
126
  import { BaseCommand } from '@adonisjs/core/ace'
120
- import { Server } from '@adonisjs/core/http'
121
127
  import type { CommandOptions } from '@adonisjs/core/types/ace'
122
128
  import { inject } from '@adonisjs/core'
123
129
  import { Scheduler } from '@outloud/adonis-scheduler'
124
- import type { ApplicationService } from '@adonisjs/core/types'
125
130
 
126
131
  export default class extends BaseCommand {
127
132
  static commandName = 'worker'
@@ -136,7 +141,9 @@ export default class extends BaseCommand {
136
141
  private scheduler?: Scheduler
137
142
 
138
143
  prepare() {
139
- this.app.terminating(() => this.server?.close())
144
+ this.app.terminating(() => {
145
+ this.server?.close()
146
+ })
140
147
  this.app.terminating(() => this.scheduler?.stop())
141
148
  }
142
149
 
@@ -152,14 +159,14 @@ export default class extends BaseCommand {
152
159
 
153
160
  private async startServer() {
154
161
  const server = await this.makeServer()
155
- const httpServer = createServer(server.handle.bind(server))
156
- this.server = httpServer
157
162
  await server.boot()
158
163
 
164
+ const httpServer = createServer(server.handle.bind(server))
159
165
  server.setNodeServer(httpServer)
166
+ this.server = httpServer
160
167
 
161
168
  const host = process.env.HOST || '0.0.0.0'
162
- const port = Number(process.env.PORT || '3000')
169
+ const port = Number(process.env.PORT || 3000)
163
170
 
164
171
  httpServer.once('listening', () => this.logger.info(`listening to http server, host: ${host}, port: ${port}`))
165
172
 
@@ -167,15 +174,9 @@ export default class extends BaseCommand {
167
174
  }
168
175
 
169
176
  private async makeServer() {
170
- const server = new Server(
171
- this.app,
172
- await this.app.container.make('encryption'),
173
- await this.app.container.make('emitter'),
174
- await this.app.container.make('logger'),
175
- this.app.config.get<any>('app.http'),
176
- )
177
-
177
+ const server = await this.app.container.make('server')
178
178
  const router = server.getRouter()
179
+
179
180
  router.get('/health', () => ({ status: 'ok' }))
180
181
 
181
182
  return server
@@ -24,6 +24,7 @@ declare class Scheduler {
24
24
  private registerFromGlob;
25
25
  private load;
26
26
  private make;
27
+ private resolveLock;
27
28
  private run;
28
29
  private hasState;
29
30
  private setState;
@@ -22,7 +22,11 @@ var Scheduler = class {
22
22
  state: "created",
23
23
  jobs: []
24
24
  };
25
- if (typeof options === "object") {
25
+ if (Task.isTask(options)) {
26
+ definition.task = options;
27
+ Object.assign(definition, options.options ?? {});
28
+ } else if (typeof options === "function") definition.loader = options;
29
+ else {
26
30
  const command = Array.isArray(options.command) ? options.command : [options.command];
27
31
  Object.assign(definition, options);
28
32
  definition.loader = () => import("./command.task.js").then((module) => {
@@ -30,10 +34,7 @@ var Scheduler = class {
30
34
  static command = command;
31
35
  };
32
36
  });
33
- } else if (Task.isTask(options)) {
34
- definition.task = options;
35
- Object.assign(definition, options.options ?? {});
36
- } else definition.loader = options;
37
+ }
37
38
  if (!definition.task && !definition.loader) throw new Error("Task definition must have either a command or a task defined.");
38
39
  this.definitions.push(definition);
39
40
  if (this.hasState(["starting", "running"])) this.schedule(definition);
@@ -85,19 +86,25 @@ var Scheduler = class {
85
86
  async make(definition) {
86
87
  return await this.resolver.make(definition.task);
87
88
  }
89
+ resolveLock(task, definition) {
90
+ if (!definition.lock) return;
91
+ if (!this.locks) {
92
+ this.logger.warn("Lock is not available, install @adonisjs/lock to use task locking.");
93
+ return;
94
+ }
95
+ const lockDuration = definition.lock && typeof definition.lock !== "boolean" ? definition.lock : this.config.lockDuration;
96
+ return this.locks.createLock(`scheduler:${task.name}`, lockDuration);
97
+ }
88
98
  async run(definition) {
89
99
  const task = await this.make(definition);
90
- const lockDuration = definition.lock && typeof definition.lock !== "boolean" ? definition.lock : this.config.lockDuration;
91
- const lock = definition.lock ? this.locks?.createLock(`scheduler:${task.name}`, lockDuration) : void 0;
92
- if (definition.lock && !this.locks) this.logger.warn("Lock is not available, install @adonisjs/lock to use task locking.");
93
- if (lock) {
94
- if (!await lock.acquireImmediately()) {
100
+ const lock = this.resolveLock(task, definition);
101
+ try {
102
+ definition.jobs.push(task);
103
+ if (lock && !await lock.acquireImmediately()) {
95
104
  this.config.warnWhenLocked && this.logger.warn(`Task "${definition.task?.name}" is locked and cannot be run.`);
96
105
  return;
97
106
  }
98
- }
99
- try {
100
- definition.jobs.push(task);
107
+ if (task.isCanceled) return;
101
108
  const promise = this.resolver.call(task, "run").finally(() => lock?.release());
102
109
  task.promise = promise.catch(() => {});
103
110
  await promise;
@@ -117,12 +124,12 @@ var Scheduler = class {
117
124
  }
118
125
  async handleError(error, _, task) {
119
126
  await task.onError?.(error);
120
- this.emitter.emit("scheduler:error", {
127
+ await this.emitter.emit("scheduler:error", {
121
128
  error,
122
129
  task
123
130
  });
124
131
  await this.errorHandler?.(error, task);
125
- if (!this.emitter.hasListeners("scheduler:error") && !this.errorHandler) throw error;
132
+ if (!(task.onError || this.emitter.hasListeners("scheduler:error") || this.errorHandler)) throw error;
126
133
  }
127
134
  async cancel(job) {
128
135
  await job.cancel();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@outloud/adonis-scheduler",
3
3
  "type": "module",
4
- "version": "1.1.0",
4
+ "version": "1.1.1",
5
5
  "description": "Schedule cron jobs in AdonisJS 6/7.",
6
6
  "author": "Outloud <hello@outloud.co>",
7
7
  "contributors": [