@tsed/agenda 8.11.2-rc.1 → 8.12.0-rc.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.
@@ -1,8 +1,12 @@
1
1
  import { StoreMerge, useDecorators } from "@tsed/core";
2
2
  import { Injectable } from "@tsed/di";
3
3
  import { PROVIDER_TYPE_AGENDA } from "../constants/constants.js";
4
- export function Agenda(options) {
4
+ export function JobsController(options) {
5
5
  return useDecorators(options?.namespace && StoreMerge("agenda", options), Injectable({
6
6
  type: PROVIDER_TYPE_AGENDA
7
7
  }));
8
8
  }
9
+ /**
10
+ * @deprecated Use `JobsController` instead.
11
+ */
12
+ export const Agenda = JobsController;
package/lib/esm/index.js CHANGED
@@ -1,11 +1,10 @@
1
1
  /**
2
2
  * @file Automatically generated by @tsed/barrels.
3
3
  */
4
- export * from "./AgendaModule.js";
5
4
  export * from "./constants/constants.js";
6
5
  export * from "./decorators/agenda.js";
7
6
  export * from "./decorators/define.js";
8
7
  export * from "./decorators/every.js";
9
8
  export * from "./interfaces/AgendaStore.js";
10
9
  export * from "./interfaces/interfaces.js";
11
- export * from "./services/AgendaFactory.js";
10
+ export * from "./services/AgendaService.js";
@@ -0,0 +1,121 @@
1
+ import { constant, DIContext, injectable, injector, logger, runInContext } from "@tsed/di";
2
+ import { $asyncEmit } from "@tsed/hooks";
3
+ import { Agenda } from "agenda";
4
+ import { v4 as uuid } from "uuid";
5
+ import { PROVIDER_TYPE_AGENDA } from "../constants/constants.js";
6
+ function getOpts() {
7
+ return constant("agenda", { enabled: false });
8
+ }
9
+ function addAgendaDefinitionsForProvider(agenda, provider) {
10
+ const store = provider.store.get("agenda", {});
11
+ if (!store.define) {
12
+ return;
13
+ }
14
+ Object.entries(store.define).forEach(([propertyKey, { name, ...options }]) => {
15
+ const instance = injector().get(provider.token);
16
+ const jobProcessor = instance[propertyKey].bind(instance);
17
+ const jobName = getNameForJob(propertyKey, store.namespace, name);
18
+ agenda.define(jobName, options, jobProcessor);
19
+ });
20
+ }
21
+ async function scheduleJobsForProvider(agenda, provider) {
22
+ const store = provider.store.get("agenda", {});
23
+ if (!store.every) {
24
+ return;
25
+ }
26
+ const promises = Object.entries(store.every).map(([propertyKey, { interval, name, ...options }]) => {
27
+ const jobName = getNameForJob(propertyKey, store.namespace, name);
28
+ return agenda.every(interval, jobName, {}, options);
29
+ });
30
+ await Promise.all(promises);
31
+ }
32
+ function getNameForJob(propertyKey, namespace, customName) {
33
+ const name = customName || propertyKey;
34
+ return namespace ? `${namespace}.${name}` : name;
35
+ }
36
+ async function afterListen(agenda) {
37
+ const opts = getOpts();
38
+ if (opts.enabled) {
39
+ const providers = injector().getProviders(PROVIDER_TYPE_AGENDA);
40
+ if (!opts.disableJobProcessing) {
41
+ logger().info({
42
+ event: "AGENDA_ADD_DEFINITIONS",
43
+ message: "Agenda add definitions"
44
+ });
45
+ providers.forEach((provider) => addAgendaDefinitionsForProvider(agenda, provider));
46
+ await $asyncEmit("$beforeAgendaStart");
47
+ }
48
+ await agenda.start();
49
+ if (!opts.disableJobProcessing) {
50
+ logger().info({
51
+ event: "AGENDA_ADD_JOBS",
52
+ message: "Agenda add scheduled jobs"
53
+ });
54
+ await Promise.all(providers.map((provider) => scheduleJobsForProvider(agenda, provider)));
55
+ await $asyncEmit("$afterAgendaStart");
56
+ }
57
+ }
58
+ }
59
+ async function onDestroy(agenda) {
60
+ const opts = getOpts();
61
+ if (opts.enabled) {
62
+ if (opts.drainJobsBeforeClose && "drain" in agenda) {
63
+ logger().info({
64
+ event: "AGENDA_DRAIN",
65
+ message: "Agenda is draining all jobs before close"
66
+ });
67
+ await agenda.drain();
68
+ }
69
+ else {
70
+ logger().info({
71
+ event: "AGENDA_STOP",
72
+ message: "Agenda is stopping"
73
+ });
74
+ await agenda.stop();
75
+ }
76
+ await agenda.close({ force: true });
77
+ logger().info({ event: "AGENDA_STOP", message: "Agenda stopped" });
78
+ }
79
+ }
80
+ export const AgendaService = injectable(Agenda)
81
+ .factory(() => {
82
+ const opts = getOpts();
83
+ if (opts.enabled) {
84
+ const agenda = new Agenda(opts);
85
+ return new Proxy(agenda, {
86
+ set(target, prop, value) {
87
+ return Reflect.set(target, prop, value);
88
+ },
89
+ get(target, prop) {
90
+ if (prop === "agenda") {
91
+ return target;
92
+ }
93
+ if (prop === "$define") {
94
+ return Reflect.get(target, "define", target);
95
+ }
96
+ if (prop === "define") {
97
+ return (name, options, processor) => {
98
+ return target["define"](name, options, (job) => {
99
+ const $ctx = new DIContext({
100
+ id: uuid()
101
+ });
102
+ $ctx.set("job", job);
103
+ return runInContext($ctx, () => processor(job));
104
+ });
105
+ };
106
+ }
107
+ return Reflect.get(target, prop, target);
108
+ }
109
+ });
110
+ }
111
+ return new Proxy({}, { get: () => () => Promise.resolve() });
112
+ })
113
+ .hooks({
114
+ $afterListen: afterListen,
115
+ $onDestroy: onDestroy
116
+ })
117
+ .token();
118
+ /**
119
+ * @deprecated Use `Agenda` from `agenda` instead.
120
+ */
121
+ export const AgendaModule = Agenda;
@@ -1,5 +1,9 @@
1
1
  interface AgendaOptions {
2
2
  namespace?: string;
3
3
  }
4
- export declare function Agenda(options?: AgendaOptions): ClassDecorator;
4
+ export declare function JobsController(options?: AgendaOptions): any;
5
+ /**
6
+ * @deprecated Use `JobsController` instead.
7
+ */
8
+ export declare const Agenda: typeof JobsController;
5
9
  export {};
@@ -1,11 +1,10 @@
1
1
  /**
2
2
  * @file Automatically generated by @tsed/barrels.
3
3
  */
4
- export * from "./AgendaModule.js";
5
4
  export * from "./constants/constants.js";
6
5
  export * from "./decorators/agenda.js";
7
6
  export * from "./decorators/define.js";
8
7
  export * from "./decorators/every.js";
9
8
  export * from "./interfaces/AgendaStore.js";
10
9
  export * from "./interfaces/interfaces.js";
11
- export * from "./services/AgendaFactory.js";
10
+ export * from "./services/AgendaService.js";
@@ -1,15 +1,13 @@
1
1
  import { AgendaConfig } from "agenda";
2
+ export type AgendaSettings = AgendaConfig & {
3
+ enabled?: boolean;
4
+ disableJobProcessing?: boolean;
5
+ drainJobsBeforeClose?: boolean;
6
+ };
2
7
  declare global {
3
8
  namespace TsED {
4
9
  interface Configuration {
5
- agenda?: {
6
- /**
7
- * Enable Agenda jobs. Default false.
8
- */
9
- enabled?: boolean;
10
- disableJobProcessing?: boolean;
11
- drainJobsBeforeClose?: boolean;
12
- } & AgendaConfig;
10
+ agenda?: AgendaSettings;
13
11
  }
14
12
  }
15
13
  }
@@ -0,0 +1,13 @@
1
+ import { Agenda } from "agenda";
2
+ export declare const AgendaService: {
3
+ readonly __type: "token_factory";
4
+ };
5
+ /**
6
+ * @deprecated Use `Agenda` from `agenda` instead.
7
+ */
8
+ export type AgendaService = Agenda;
9
+ /**
10
+ * @deprecated Use `Agenda` from `agenda` instead.
11
+ */
12
+ export declare const AgendaModule: typeof Agenda;
13
+ export type AgendaModule = Agenda;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tsed/agenda",
3
3
  "description": "Agenda package for Ts.ED framework",
4
4
  "type": "module",
5
- "version": "8.11.2-rc.1",
5
+ "version": "8.12.0-rc.1",
6
6
  "source": "./src/index.ts",
7
7
  "main": "./lib/esm/index.js",
8
8
  "module": "./lib/esm/index.js",
@@ -26,11 +26,11 @@
26
26
  "tslib": "2.7.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@tsed/barrels": "8.11.2-rc.1",
30
- "@tsed/core": "8.11.2-rc.1",
31
- "@tsed/di": "8.11.2-rc.1",
32
- "@tsed/platform-http": "8.11.2-rc.1",
33
- "@tsed/typescript": "8.11.2-rc.1",
29
+ "@tsed/barrels": "8.12.0-rc.1",
30
+ "@tsed/core": "8.12.0-rc.1",
31
+ "@tsed/di": "8.12.0-rc.1",
32
+ "@tsed/platform-http": "8.12.0-rc.1",
33
+ "@tsed/typescript": "8.12.0-rc.1",
34
34
  "agenda": "^5.0.0",
35
35
  "eslint": "9.12.0",
36
36
  "typescript": "5.4.5",
package/readme.md CHANGED
@@ -3,9 +3,9 @@
3
3
  </p>
4
4
 
5
5
  <div align="center">
6
-
6
+
7
7
  <h1>Agenda</h1>
8
-
8
+
9
9
  [![Build & Release](https://github.com/tsedio/tsed/workflows/Build%20&%20Release/badge.svg)](https://github.com/tsedio/tsed/actions?query=workflow%3A%22Build+%26+Release%22)
10
10
  [![PR Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/tsedio/tsed/blob/master/CONTRIBUTING.md)
11
11
  [![npm version](https://badge.fury.io/js/%40tsed%2Fcommon.svg)](https://badge.fury.io/js/%40tsed%2Fcommon)
@@ -84,10 +84,10 @@ Use the `@Define` decorator on methods that you would like to schedule
84
84
  programmatically via the AgendaService and Agenda instance access.
85
85
 
86
86
  ```ts
87
- import {Agenda, Every, Define} from "@tsed/agenda";
87
+ import {Every, Define, JobsController} from "@tsed/agenda";
88
88
  import {Job} from "agenda";
89
89
 
90
- @Agenda({namespace: "email"})
90
+ @JobsController({namespace: "email"})
91
91
  export class EmailJobService {
92
92
  @Every("60 minutes", {
93
93
  name: "maintenanceJob"
@@ -114,15 +114,17 @@ export class EmailJobService {
114
114
 
115
115
  ## Define a job processor manually
116
116
 
117
- Since Ts.ED 7.53.0, AgendaModule exposes methods to manually define a job processor. It can be useful to define a job processor when you need to fetch data beforehand and dynamically build job name / options.
117
+ Since Ts.ED 7.53.0, AgendaModule exposes methods to manually define a job processor. It can be useful to define a job
118
+ processor when you need to fetch data beforehand and dynamically build job name / options.
118
119
 
119
120
  ```typescript
120
- import {Agenda, AgendaModule, Define} from "@tsed/agenda";
121
+ import {Define, JobsController} from "@tsed/agenda";
122
+ import {Agenda} from "agenda";
121
123
 
122
- @Agenda({namespace: "email"})
124
+ @JobsController({namespace: "email"})
123
125
  export class EmailJobService {
124
126
  @Inject()
125
- agenda: AgendaModule;
127
+ agenda: Agenda;
126
128
 
127
129
  @Inject()
128
130
  httpClient: HttpClient;
@@ -163,15 +165,15 @@ export class EmailJobService {
163
165
  Inject the AgendaService instance to interact with it directly, e.g. to schedule
164
166
  a job manually.
165
167
 
166
- ```typescript
168
+ ```ts
167
169
  import {Service} from "@tsed/di";
168
170
  import {AfterRoutesInit} from "@tsed/platform-params";
169
- import {AgendaModule} from "@tsed/agenda";
171
+ import {Agenda} from "agenda";
170
172
 
171
173
  @Service()
172
174
  export class UsersService {
173
175
  @Inject()
174
- private agenda: AgendaModule;
176
+ private agenda: Agenda;
175
177
 
176
178
  async create(user: User): Promise<User> {
177
179
  // do something
@@ -187,7 +189,8 @@ export class UsersService {
187
189
 
188
190
  ## Using Agendash
189
191
 
190
- [Agendash](https://github.com/agenda/agendash) provides a job overview dashboard that makes it easy to manage, create and
192
+ [Agendash](https://github.com/agenda/agendash) provides a job overview dashboard that makes it easy to manage, create
193
+ and
191
194
  schedule your jobs.
192
195
 
193
196
  ::: tip Note
@@ -206,8 +209,7 @@ Afterward create the module `agendash.module.ts` in src/modules so that the dash
206
209
  import {AfterRoutesInit, PlatformApplication} from "@tsed/platform-http";
207
210
  import {Configuration, Inject, Module} from "@tsed/di";
208
211
  import {Agenda} from "agenda";
209
-
210
- const Agendash = require("agendash");
212
+ import Agendash from "agendash";
211
213
 
212
214
  @Module()
213
215
  export class AgendashModule implements AfterRoutesInit {
@@ -240,7 +242,8 @@ Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com
240
242
 
241
243
  ## Sponsors
242
244
 
243
- Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/tsed#sponsor)]
245
+ Support this project by becoming a sponsor. Your logo will show up here with a link to your
246
+ website. [[Become a sponsor](https://opencollective.com/tsed#sponsor)]
244
247
 
245
248
  ## License
246
249
 
@@ -248,8 +251,15 @@ The MIT License (MIT)
248
251
 
249
252
  Copyright (c) 2016 - 2021 Romain Lenzotti
250
253
 
251
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
254
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
255
+ documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
256
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
257
+ persons to whom the Software is furnished to do so, subject to the following conditions:
252
258
 
253
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
259
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
260
+ Software.
254
261
 
255
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
262
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
263
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
264
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
265
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,131 +0,0 @@
1
- import { __decorate, __metadata } from "tslib";
2
- import { Constant, DIContext, Inject, InjectorService, Module, runInContext } from "@tsed/di";
3
- import { Logger } from "@tsed/logger";
4
- import { v4 as uuid } from "uuid";
5
- import { PROVIDER_TYPE_AGENDA } from "./constants/constants.js";
6
- import { AgendaService } from "./services/AgendaFactory.js";
7
- let AgendaModule = class AgendaModule {
8
- async $afterListen() {
9
- if (this.enabled) {
10
- const providers = this.getProviders();
11
- if (!this.disableJobProcessing) {
12
- this.logger.info({
13
- event: "AGENDA_ADD_DEFINITIONS",
14
- message: "Agenda add definitions"
15
- });
16
- providers.forEach((provider) => this.addAgendaDefinitionsForProvider(provider));
17
- await this.injector.emit("$beforeAgendaStart");
18
- }
19
- await this.agenda.start();
20
- if (!this.disableJobProcessing) {
21
- this.logger.info({
22
- event: "AGENDA_ADD_JOBS",
23
- message: "Agenda add scheduled jobs"
24
- });
25
- await Promise.all(providers.map((provider) => this.scheduleJobsForProvider(provider)));
26
- await this.injector.emit("$afterAgendaStart");
27
- }
28
- }
29
- }
30
- async $onDestroy() {
31
- if (this.enabled) {
32
- if (this.drainJobsBeforeClose && "drain" in this.agenda) {
33
- this.logger.info({
34
- event: "AGENDA_DRAIN",
35
- message: "Agenda is draining all jobs before close"
36
- });
37
- await this.agenda.drain();
38
- }
39
- else {
40
- this.logger.info({
41
- event: "AGENDA_STOP",
42
- message: "Agenda is stopping"
43
- });
44
- await this.agenda.stop();
45
- }
46
- await this.agenda.close({ force: true });
47
- this.logger.info({ event: "AGENDA_STOP", message: "Agenda stopped" });
48
- }
49
- }
50
- define(name, options, processor) {
51
- return this.agenda.define(name, options, (job) => {
52
- const $ctx = new DIContext({
53
- injector: this.injector,
54
- id: uuid(),
55
- logger: this.injector.logger
56
- });
57
- $ctx.set("job", job);
58
- return runInContext($ctx, () => processor(job));
59
- });
60
- }
61
- every(interval, name, data, options) {
62
- return this.agenda.every(interval, name, data, options);
63
- }
64
- schedule(when, name, data) {
65
- return this.agenda.schedule(when, name, data);
66
- }
67
- now(name, data) {
68
- return this.agenda.now(name, data);
69
- }
70
- create(name, data) {
71
- return this.agenda.create(name, data);
72
- }
73
- getProviders() {
74
- return this.injector.getProviders(PROVIDER_TYPE_AGENDA);
75
- }
76
- addAgendaDefinitionsForProvider(provider) {
77
- const store = provider.store.get("agenda", {});
78
- if (!store.define) {
79
- return;
80
- }
81
- Object.entries(store.define).forEach(([propertyKey, { name, ...options }]) => {
82
- const instance = this.injector.get(provider.token);
83
- const jobProcessor = instance[propertyKey].bind(instance);
84
- const jobName = this.getNameForJob(propertyKey, store.namespace, name);
85
- this.define(jobName, options, jobProcessor);
86
- });
87
- }
88
- async scheduleJobsForProvider(provider) {
89
- const store = provider.store.get("agenda", {});
90
- if (!store.every) {
91
- return;
92
- }
93
- const promises = Object.entries(store.every).map(([propertyKey, { interval, name, ...options }]) => {
94
- const jobName = this.getNameForJob(propertyKey, store.namespace, name);
95
- return this.every(interval, jobName, {}, options);
96
- });
97
- await Promise.all(promises);
98
- }
99
- getNameForJob(propertyKey, namespace, customName) {
100
- const name = customName || propertyKey;
101
- return namespace ? `${namespace}.${name}` : name;
102
- }
103
- };
104
- __decorate([
105
- Inject(),
106
- __metadata("design:type", Logger)
107
- ], AgendaModule.prototype, "logger", void 0);
108
- __decorate([
109
- Inject(),
110
- __metadata("design:type", InjectorService)
111
- ], AgendaModule.prototype, "injector", void 0);
112
- __decorate([
113
- Inject(),
114
- __metadata("design:type", AgendaService)
115
- ], AgendaModule.prototype, "agenda", void 0);
116
- __decorate([
117
- Constant("agenda.enabled", false),
118
- __metadata("design:type", Boolean)
119
- ], AgendaModule.prototype, "enabled", void 0);
120
- __decorate([
121
- Constant("agenda.drainJobsBeforeClose", false),
122
- __metadata("design:type", Boolean)
123
- ], AgendaModule.prototype, "drainJobsBeforeClose", void 0);
124
- __decorate([
125
- Constant("agenda.disableJobProcessing", false),
126
- __metadata("design:type", Boolean)
127
- ], AgendaModule.prototype, "disableJobProcessing", void 0);
128
- AgendaModule = __decorate([
129
- Module()
130
- ], AgendaModule);
131
- export { AgendaModule };
@@ -1,12 +0,0 @@
1
- import { Configuration, registerProvider } from "@tsed/di";
2
- import { Agenda } from "agenda";
3
- // create Alias to avoid confusing import between Agenda from "agenda" and Agenda decorator
4
- export const AgendaService = Agenda;
5
- registerProvider({
6
- token: Agenda,
7
- deps: [Configuration],
8
- useFactory(settings) {
9
- const opts = settings.get("agenda", { enabled: false });
10
- return opts.enabled ? new Agenda(opts) : {};
11
- }
12
- });
@@ -1,24 +0,0 @@
1
- import { InjectorService, OnDestroy, Provider } from "@tsed/di";
2
- import { Logger } from "@tsed/logger";
3
- import { AfterListen } from "@tsed/platform-http";
4
- import { Job } from "agenda";
5
- import { AgendaService } from "./services/AgendaFactory.js";
6
- export declare class AgendaModule implements OnDestroy, AfterListen {
7
- protected logger: Logger;
8
- protected injector: InjectorService;
9
- protected agenda: AgendaService;
10
- private enabled;
11
- private drainJobsBeforeClose;
12
- private disableJobProcessing;
13
- $afterListen(): Promise<any>;
14
- $onDestroy(): Promise<any>;
15
- define(name: string, options?: any, processor?: any): void;
16
- every(interval: string, name: string, data?: any, options?: any): Promise<any>;
17
- schedule(when: Date | string, name: string, data?: any): Promise<Job<import("agenda").JobAttributesData>>;
18
- now(name: string, data?: any): Promise<Job<import("agenda").JobAttributesData>>;
19
- create(name: string, data?: any): Job<import("agenda").JobAttributesData>;
20
- protected getProviders(): Provider<any>[];
21
- protected addAgendaDefinitionsForProvider(provider: Provider): void;
22
- protected scheduleJobsForProvider(provider: Provider<any>): Promise<void>;
23
- protected getNameForJob(propertyKey: string, namespace?: string, customName?: string): string;
24
- }
@@ -1,3 +0,0 @@
1
- import { Agenda } from "agenda";
2
- export declare const AgendaService: typeof Agenda;
3
- export type AgendaService = Agenda;