@wavezync/nestjs-pgboss 1.0.0-alpha

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/.editorconfig ADDED
@@ -0,0 +1,12 @@
1
+ # EditorConfig is awesome: https://EditorConfig.org
2
+
3
+ # top-most EditorConfig file
4
+ root = true
5
+
6
+ [*]
7
+ indent_style = space
8
+ indent_size = 2
9
+ end_of_line = lf
10
+ charset = utf-8
11
+ trim_trailing_whitespace = false
12
+ insert_final_newline = false
package/.eslintrc.js ADDED
@@ -0,0 +1,33 @@
1
+ /** @type {import('eslint').ESLint.ConfigData} */
2
+ module.exports = {
3
+ parser: '@typescript-eslint/parser',
4
+ parserOptions: {
5
+ project: 'tsconfig.json',
6
+ sourceType: 'module',
7
+ },
8
+ plugins: ['@typescript-eslint/eslint-plugin'],
9
+ extends: [
10
+ 'plugin:@typescript-eslint/recommended',
11
+ 'plugin:prettier/recommended',
12
+ ],
13
+ root: true,
14
+ env: {
15
+ node: true,
16
+ jest: true,
17
+ },
18
+ ignorePatterns: ['.eslintrc.js', 'tools/**'],
19
+ rules: {
20
+ '@typescript-eslint/interface-name-prefix': 'off',
21
+ '@typescript-eslint/explicit-function-return-type': 'off',
22
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
23
+ '@typescript-eslint/no-explicit-any': 'off',
24
+ "@typescript-eslint/no-unused-vars": [
25
+ "error",
26
+ {
27
+ "args": "all",
28
+ "argsIgnorePattern": "^_",
29
+ "varsIgnorePattern": "^_",
30
+ }
31
+ ],
32
+ },
33
+ };
@@ -0,0 +1,43 @@
1
+ name: Build
2
+
3
+ on:
4
+ push:
5
+ tags-ignore:
6
+ - "**"
7
+ branches:
8
+ - "**"
9
+ pull_request:
10
+
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ build:
17
+ runs-on: ubuntu-latest
18
+
19
+ steps:
20
+ - name: Check out Git repository
21
+ uses: actions/checkout@v4
22
+
23
+ - name: Setup Node
24
+ uses: actions/setup-node@v4
25
+ with:
26
+ node-version: "20"
27
+
28
+ - uses: actions/cache@v4
29
+ name: Setup npm cache
30
+ with:
31
+ path: ~/.npm
32
+ key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
33
+ restore-keys: |
34
+ ${{ runner.os }}-npm-
35
+
36
+ - name: Install dependencies
37
+ run: npm ci
38
+
39
+ - name: Lint
40
+ run: npm run lint
41
+
42
+ - name: Build
43
+ run: npm run build
@@ -0,0 +1,40 @@
1
+ name: Publish to NPM
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish-npm:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Check out Git repository
12
+ uses: actions/checkout@v4
13
+
14
+ - name: Setup Node
15
+ uses: actions/setup-node@v4
16
+ with:
17
+ node-version: "20"
18
+ registry-url: "https://registry.npmjs.org/"
19
+
20
+ - uses: actions/cache@v4
21
+ name: Setup npm cache
22
+ with:
23
+ path: ~/.npm
24
+ key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
25
+ restore-keys: |
26
+ ${{ runner.os }}-npm-
27
+
28
+ - name: Install dependencies
29
+ run: npm ci
30
+
31
+ - name: Lint
32
+ run: npm run lint
33
+
34
+ - name: Build
35
+ run: npm run build
36
+
37
+ - name: Publish to NPM
38
+ run: npm publish --access public
39
+ env:
40
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Wavezync
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.MD ADDED
@@ -0,0 +1,106 @@
1
+ # `@wavezync/nestjs-pgboss`
2
+
3
+ <p align="center">
4
+ Use <a href="https://github.com/timgit/pg-boss" target="_blank">pg-boss</a> in your Nest.js service!
5
+ <p align="center">
6
+
7
+ <p align="center">
8
+ <a href="https://github.com/wavezync/nestjs-pgboss/actions/workflows/build.yaml">
9
+ <img src="https://img.shields.io/github/actions/workflow/status/wavezync/nestjs-pgboss/build.yaml?branch=main" alt="Build Status">
10
+ </a>
11
+ <a href="https://github.com/wavezync/nestjs-pgboss/blob/main/LICENSE">
12
+ <img src="https://img.shields.io/badge/License-MIT-green" alt="License">
13
+ </a>
14
+ </p>
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @wavezync/nestjs-pgboss
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### Setup
25
+
26
+ To begin using `@wavezync/nestjs-pgboss`, initialize the root module:
27
+
28
+ ```ts
29
+ import { PGBossModule } from "@wavezync/nestjs-pgboss";
30
+
31
+ // app.module.ts
32
+ @Module({
33
+ imports: [
34
+ PgBossModule.forRootAsync({
35
+ imports: [ConfigModule],
36
+ useFactory: async (configService: ConfigService) => ({
37
+ connectionString: configService.get<string>('DATABASE_URL'),
38
+ }),
39
+ inject: [ConfigService],
40
+ }),
41
+ ],
42
+ })
43
+ export class AppModule {}
44
+ ```
45
+
46
+ Schedule a job using `PgBossService`:
47
+
48
+ ```ts
49
+ import { Injectable } from '@nestjs/common';
50
+ import { PgBossService } from '@wavezync/nestjs-pgboss';
51
+
52
+ @Injectable()
53
+ export class JobSchedulerService {
54
+ constructor(private readonly pgBossService: PgBossService) {}
55
+
56
+ async scheduleJob() {
57
+ await this.pgBossService.scheduleJob('my-job', { key: 'value' });
58
+ }
59
+ }
60
+
61
+ ```
62
+ Handle jobs using the `@Job` decorator:
63
+
64
+ ```ts
65
+ import { Injectable, Logger } from '@nestjs/common';
66
+ import { Job } from '@wavezync/nestjs-pgboss';
67
+
68
+ @Injectable()
69
+ export class MyJobHandler {
70
+ private readonly logger = new Logger(MyJobHandler.name);
71
+
72
+ @Job('my-job')
73
+ async handleMyJob(job: { data: any }) {
74
+ this.logger.log('Handling job with data:', job.data);
75
+ }
76
+ }
77
+
78
+ ```
79
+ Handle cron jobs using the `@CronJob` decorator:
80
+ ```ts
81
+ import { Injectable, Logger } from '@nestjs/common';
82
+ import { PgBossService, Cron } from '@wavezync/nestjs-pgboss';
83
+
84
+ @Injectable()
85
+ export class MyCronJobService {
86
+ private readonly logger = new Logger(MyCronJobService.name);
87
+
88
+ @CronJob('my-cron-job', '0 * * * *', { priority: 1 })
89
+ async handleCron() {
90
+ this.logger.log('Executing cron job: my-cron-job');
91
+ }
92
+ }
93
+
94
+ ```
95
+
96
+ ## Test
97
+
98
+ ```bash
99
+ # unit tests
100
+ $ npm run test
101
+
102
+ ```
103
+
104
+ ## License
105
+
106
+ `@wavezync/nestjs-pgboss` is [MIT licensed](LICENSE).
@@ -0,0 +1,7 @@
1
+ import { JobOptions } from "pg-boss";
2
+ export declare const JOB_NAME = "JOB_NAME";
3
+ export declare const JOB_OPTIONS = "JOB_OPTIONS";
4
+ export declare const CRON_EXPRESSION = "CRON_EXPRESSION";
5
+ export declare const CRON_OPTIONS = "CRON_OPTIONS";
6
+ export declare function Job<_TData extends object = any>(name: string, options?: JobOptions): (target: any, key: string, descriptor: PropertyDescriptor) => void;
7
+ export declare function Cron<_TData extends object = any>(name: string, cron: string, options?: JobOptions): (target: any, key: string, descriptor: PropertyDescriptor) => void;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CRON_OPTIONS = exports.CRON_EXPRESSION = exports.JOB_OPTIONS = exports.JOB_NAME = void 0;
4
+ exports.Job = Job;
5
+ exports.Cron = Cron;
6
+ const common_1 = require("@nestjs/common");
7
+ exports.JOB_NAME = "JOB_NAME";
8
+ exports.JOB_OPTIONS = "JOB_OPTIONS";
9
+ exports.CRON_EXPRESSION = "CRON_EXPRESSION";
10
+ exports.CRON_OPTIONS = "CRON_OPTIONS";
11
+ function Job(name, options = {}) {
12
+ return (target, key, descriptor) => {
13
+ (0, common_1.SetMetadata)(exports.JOB_NAME, name)(target, key, descriptor);
14
+ (0, common_1.SetMetadata)(exports.JOB_OPTIONS, options)(target, key, descriptor);
15
+ };
16
+ }
17
+ function Cron(name, cron, options = {}) {
18
+ return (target, key, descriptor) => {
19
+ (0, common_1.SetMetadata)(exports.JOB_NAME, name)(target, key, descriptor);
20
+ (0, common_1.SetMetadata)(exports.CRON_EXPRESSION, cron)(target, key, descriptor);
21
+ (0, common_1.SetMetadata)(exports.CRON_OPTIONS, options)(target, key, descriptor);
22
+ };
23
+ }
24
+ //# sourceMappingURL=job.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job.decorator.js","sourceRoot":"","sources":["../../lib/decorators/job.decorator.ts"],"names":[],"mappings":";;;AAQA,kBAQC;AAED,oBAUC;AA5BD,2CAA6C;AAGhC,QAAA,QAAQ,GAAG,UAAU,CAAC;AACtB,QAAA,WAAW,GAAG,aAAa,CAAC;AAC5B,QAAA,eAAe,GAAG,iBAAiB,CAAC;AACpC,QAAA,YAAY,GAAG,cAAc,CAAC;AAE3C,SAAgB,GAAG,CACjB,IAAY,EACZ,UAAsB,EAAE;IAExB,OAAO,CAAC,MAAW,EAAE,GAAW,EAAE,UAA8B,EAAE,EAAE;QAClE,IAAA,oBAAW,EAAC,gBAAQ,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;QACrD,IAAA,oBAAW,EAAC,mBAAW,EAAE,OAAO,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;IAC7D,CAAC,CAAC;AACJ,CAAC;AAED,SAAgB,IAAI,CAClB,IAAY,EACZ,IAAY,EACZ,UAAsB,EAAE;IAExB,OAAO,CAAC,MAAW,EAAE,GAAW,EAAE,UAA8B,EAAE,EAAE;QAClE,IAAA,oBAAW,EAAC,gBAAQ,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;QACrD,IAAA,oBAAW,EAAC,uBAAe,EAAE,IAAI,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;QAC5D,IAAA,oBAAW,EAAC,oBAAY,EAAE,OAAO,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;IAC9D,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from "./pgboss.module";
2
+ export * from "./pgboss.service";
3
+ export * from "./decorators/job.decorator";
4
+ export * from "./interfaces/pgboss-module-options.interface";
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./pgboss.module"), exports);
18
+ __exportStar(require("./pgboss.service"), exports);
19
+ __exportStar(require("./decorators/job.decorator"), exports);
20
+ __exportStar(require("./interfaces/pgboss-module-options.interface"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kDAAgC;AAChC,mDAAiC;AACjC,6DAA2C;AAC3C,+EAA6D"}
@@ -0,0 +1,12 @@
1
+ import { ModuleMetadata, Type } from "@nestjs/common";
2
+ import { ConstructorOptions } from "pg-boss";
3
+ export type PgBossModuleOptions = ConstructorOptions;
4
+ export interface PgBossOptionsFactory {
5
+ createPgBossOptions(): Promise<PgBossModuleOptions> | PgBossModuleOptions;
6
+ }
7
+ export interface PgBossModuleAsyncOptions extends Pick<ModuleMetadata, "imports"> {
8
+ useExisting?: Type<PgBossOptionsFactory>;
9
+ useClass?: Type<PgBossOptionsFactory>;
10
+ useFactory?: (...args: any[]) => Promise<PgBossModuleOptions> | PgBossModuleOptions;
11
+ inject?: any[];
12
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=pgboss-module-options.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pgboss-module-options.interface.js","sourceRoot":"","sources":["../../lib/interfaces/pgboss-module-options.interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import { DynamicModule } from "@nestjs/common";
2
+ import { PgBossModuleAsyncOptions } from "./interfaces/pgboss-module-options.interface";
3
+ export declare class PgBossModule {
4
+ static forRootAsync(options: PgBossModuleAsyncOptions): DynamicModule;
5
+ private static createAsyncProviders;
6
+ private static createAsyncOptionsProvider;
7
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ var PgBossModule_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.PgBossModule = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const pg_boss_1 = __importDefault(require("pg-boss"));
16
+ const pgboss_service_1 = require("./pgboss.service");
17
+ const consts_1 = require("./utils/consts");
18
+ let PgBossModule = PgBossModule_1 = class PgBossModule {
19
+ static forRootAsync(options) {
20
+ const logger = new common_1.Logger(PgBossModule_1.name);
21
+ const pgBossProvider = {
22
+ provide: consts_1.PGBOSS_TOKEN,
23
+ useFactory: async (pgBossOptions) => {
24
+ const boss = new pg_boss_1.default(pgBossOptions.connectionString);
25
+ await boss.start();
26
+ logger.log("PgBoss started successfully");
27
+ return boss;
28
+ },
29
+ inject: [consts_1.PGBOSS_OPTIONS],
30
+ };
31
+ const asyncProviders = this.createAsyncProviders(options);
32
+ return {
33
+ module: PgBossModule_1,
34
+ imports: options.imports || [],
35
+ providers: [...asyncProviders, pgBossProvider, pgboss_service_1.PgBossService],
36
+ exports: [pgboss_service_1.PgBossService],
37
+ };
38
+ }
39
+ static createAsyncProviders(options) {
40
+ if (options.useExisting || options.useFactory) {
41
+ return [this.createAsyncOptionsProvider(options)];
42
+ }
43
+ const useClass = options.useClass;
44
+ return [
45
+ this.createAsyncOptionsProvider(options),
46
+ {
47
+ provide: useClass,
48
+ useClass,
49
+ },
50
+ ];
51
+ }
52
+ static createAsyncOptionsProvider(options) {
53
+ if (options.useFactory) {
54
+ return {
55
+ provide: consts_1.PGBOSS_OPTIONS,
56
+ useFactory: options.useFactory,
57
+ inject: options.inject || [],
58
+ };
59
+ }
60
+ const inject = [(options.useClass || options.useExisting)];
61
+ return {
62
+ provide: consts_1.PGBOSS_OPTIONS,
63
+ useFactory: async (optionsFactory) => optionsFactory.createPgBossOptions(),
64
+ inject,
65
+ };
66
+ }
67
+ };
68
+ exports.PgBossModule = PgBossModule;
69
+ exports.PgBossModule = PgBossModule = PgBossModule_1 = __decorate([
70
+ (0, common_1.Global)(),
71
+ (0, common_1.Module)({})
72
+ ], PgBossModule);
73
+ //# sourceMappingURL=pgboss.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pgboss.module.js","sourceRoot":"","sources":["../lib/pgboss.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;AACA,2CAMwB;AACxB,sDAA6B;AAC7B,qDAAiD;AACjD,2CAA8D;AAQvD,IAAM,YAAY,oBAAlB,MAAM,YAAY;IACvB,MAAM,CAAC,YAAY,CAAC,OAAiC;QACnD,MAAM,MAAM,GAAG,IAAI,eAAM,CAAC,cAAY,CAAC,IAAI,CAAC,CAAC;QAE7C,MAAM,cAAc,GAAa;YAC/B,OAAO,EAAE,qBAAY;YACrB,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE;gBAClC,MAAM,IAAI,GAAG,IAAI,iBAAM,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;gBACxD,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;gBACnB,MAAM,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;gBAC1C,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,EAAE,CAAC,uBAAc,CAAC;SACzB,CAAC;QAEF,MAAM,cAAc,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAE1D,OAAO;YACL,MAAM,EAAE,cAAY;YACpB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,SAAS,EAAE,CAAC,GAAG,cAAc,EAAE,cAAc,EAAE,8BAAa,CAAC;YAC7D,OAAO,EAAE,CAAC,8BAAa,CAAC;SACzB,CAAC;IACJ,CAAC;IAEO,MAAM,CAAC,oBAAoB,CACjC,OAAiC;QAEjC,IAAI,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,QAEA,CAAC;QAE1B,OAAO;YACL,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC;YACxC;gBACE,OAAO,EAAE,QAAQ;gBACjB,QAAQ;aACT;SACF,CAAC;IACJ,CAAC;IAEO,MAAM,CAAC,0BAA0B,CACvC,OAAiC;QAEjC,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,OAAO;gBACL,OAAO,EAAE,uBAAc;gBACvB,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;aAC7B,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAQ,CAAC,CAAC;QAElE,OAAO;YACL,OAAO,EAAE,uBAAc;YACvB,UAAU,EAAE,KAAK,EAAE,cAAoC,EAAE,EAAE,CACzD,cAAc,CAAC,mBAAmB,EAAE;YACtC,MAAM;SACP,CAAC;IACJ,CAAC;CACF,CAAA;AAjEY,oCAAY;uBAAZ,YAAY;IAFxB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,YAAY,CAiExB"}
@@ -0,0 +1,8 @@
1
+ import PgBoss, { BatchWorkOptions, JobOptions, WorkHandler } from "pg-boss";
2
+ export declare class PgBossService {
3
+ private readonly boss;
4
+ constructor(boss: PgBoss);
5
+ registerJob<TData extends object>(name: string, handler: WorkHandler<TData>, options?: BatchWorkOptions): Promise<void>;
6
+ scheduleJob<TData extends object>(name: string, data: TData, options?: JobOptions): Promise<void>;
7
+ registerCronJob<TData extends object>(name: string, cron: string, handler: WorkHandler<TData>, data?: TData, options?: PgBoss.ScheduleOptions): Promise<void>;
8
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.PgBossService = void 0;
19
+ const common_1 = require("@nestjs/common");
20
+ const pg_boss_1 = __importDefault(require("pg-boss"));
21
+ const consts_1 = require("./utils/consts");
22
+ let PgBossService = class PgBossService {
23
+ constructor(boss) {
24
+ this.boss = boss;
25
+ }
26
+ async registerJob(name, handler, options) {
27
+ await this.boss.work(name, options, handler);
28
+ }
29
+ async scheduleJob(name, data, options) {
30
+ await this.boss.send(name, data, options);
31
+ }
32
+ async registerCronJob(name, cron, handler, data, options) {
33
+ await this.boss.schedule(name, cron, data ? data : {}, options ? options : {});
34
+ await this.boss.work(name, handler);
35
+ }
36
+ };
37
+ exports.PgBossService = PgBossService;
38
+ exports.PgBossService = PgBossService = __decorate([
39
+ (0, common_1.Injectable)(),
40
+ __param(0, (0, common_1.Inject)(consts_1.PGBOSS_TOKEN)),
41
+ __metadata("design:paramtypes", [pg_boss_1.default])
42
+ ], PgBossService);
43
+ //# sourceMappingURL=pgboss.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pgboss.service.js","sourceRoot":"","sources":["../lib/pgboss.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,sDAA4E;AAC5E,2CAA8C;AAGvC,IAAM,aAAa,GAAnB,MAAM,aAAa;IACxB,YAAmD,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAEnE,KAAK,CAAC,WAAW,CACf,IAAY,EACZ,OAA2B,EAC3B,OAA0B;QAE1B,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,WAAW,CACf,IAAY,EACZ,IAAW,EACX,OAAoB;QAEpB,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,eAAe,CACnB,IAAY,EACZ,IAAY,EACZ,OAA2B,EAC3B,IAAY,EACZ,OAAgC;QAEhC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CACtB,IAAI,EACJ,IAAI,EACJ,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAChB,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CACvB,CAAC;QACF,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;CACF,CAAA;AAlCY,sCAAa;wBAAb,aAAa;IADzB,IAAA,mBAAU,GAAE;IAEE,WAAA,IAAA,eAAM,EAAC,qBAAY,CAAC,CAAA;qCAAwB,iBAAM;GADpD,aAAa,CAkCzB"}
@@ -0,0 +1,2 @@
1
+ export declare const PGBOSS_OPTIONS = "PGBOSS_OPTIONS";
2
+ export declare const PGBOSS_TOKEN = "PGBOSS_TOKEN";
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PGBOSS_TOKEN = exports.PGBOSS_OPTIONS = void 0;
4
+ exports.PGBOSS_OPTIONS = "PGBOSS_OPTIONS";
5
+ exports.PGBOSS_TOKEN = "PGBOSS_TOKEN";
6
+ //# sourceMappingURL=consts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.js","sourceRoot":"","sources":["../../lib/utils/consts.ts"],"names":[],"mappings":";;;AAAa,QAAA,cAAc,GAAG,gBAAgB,CAAC;AAClC,QAAA,YAAY,GAAG,cAAc,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { SetMetadata } from "@nestjs/common";
2
+ import { JobOptions } from "pg-boss";
3
+
4
+ export const JOB_NAME = "JOB_NAME";
5
+ export const JOB_OPTIONS = "JOB_OPTIONS";
6
+ export const CRON_EXPRESSION = "CRON_EXPRESSION";
7
+ export const CRON_OPTIONS = "CRON_OPTIONS";
8
+
9
+ export function Job<_TData extends object = any>(
10
+ name: string,
11
+ options: JobOptions = {},
12
+ ) {
13
+ return (target: any, key: string, descriptor: PropertyDescriptor) => {
14
+ SetMetadata(JOB_NAME, name)(target, key, descriptor);
15
+ SetMetadata(JOB_OPTIONS, options)(target, key, descriptor);
16
+ };
17
+ }
18
+
19
+ export function CronJob<_TData extends object = any>(
20
+ name: string,
21
+ cron: string,
22
+ options: JobOptions = {},
23
+ ) {
24
+ return (target: any, key: string, descriptor: PropertyDescriptor) => {
25
+ SetMetadata(JOB_NAME, name)(target, key, descriptor);
26
+ SetMetadata(CRON_EXPRESSION, cron)(target, key, descriptor);
27
+ SetMetadata(CRON_OPTIONS, options)(target, key, descriptor);
28
+ };
29
+ }
package/lib/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./pgboss.module";
2
+ export * from "./pgboss.service";
3
+ export * from "./decorators/job.decorator";
4
+ export * from "./interfaces/pgboss-module-options.interface";
@@ -0,0 +1,18 @@
1
+ import { ModuleMetadata, Type } from "@nestjs/common";
2
+ import { ConstructorOptions } from "pg-boss";
3
+
4
+ export type PgBossModuleOptions = ConstructorOptions;
5
+
6
+ export interface PgBossOptionsFactory {
7
+ createPgBossOptions(): Promise<PgBossModuleOptions> | PgBossModuleOptions;
8
+ }
9
+
10
+ export interface PgBossModuleAsyncOptions
11
+ extends Pick<ModuleMetadata, "imports"> {
12
+ useExisting?: Type<PgBossOptionsFactory>;
13
+ useClass?: Type<PgBossOptionsFactory>;
14
+ useFactory?: (
15
+ ...args: any[]
16
+ ) => Promise<PgBossModuleOptions> | PgBossModuleOptions;
17
+ inject?: any[];
18
+ }
@@ -0,0 +1,84 @@
1
+ // src/pg-boss.module.ts
2
+ import {
3
+ Module,
4
+ DynamicModule,
5
+ Global,
6
+ Provider,
7
+ Logger,
8
+ } from "@nestjs/common";
9
+ import PgBoss from "pg-boss";
10
+ import { PgBossService } from "./pgboss.service";
11
+ import { PGBOSS_OPTIONS, PGBOSS_TOKEN } from "./utils/consts";
12
+ import {
13
+ PgBossModuleAsyncOptions,
14
+ PgBossOptionsFactory,
15
+ } from "./interfaces/pgboss-module-options.interface";
16
+
17
+ @Global()
18
+ @Module({})
19
+ export class PgBossModule {
20
+ static forRootAsync(options: PgBossModuleAsyncOptions): DynamicModule {
21
+ const logger = new Logger(PgBossModule.name);
22
+
23
+ const pgBossProvider: Provider = {
24
+ provide: PGBOSS_TOKEN,
25
+ useFactory: async (pgBossOptions) => {
26
+ const boss = new PgBoss(pgBossOptions.connectionString);
27
+ await boss.start();
28
+ logger.log("PgBoss started successfully");
29
+ return boss;
30
+ },
31
+ inject: [PGBOSS_OPTIONS],
32
+ };
33
+
34
+ const asyncProviders = this.createAsyncProviders(options);
35
+
36
+ return {
37
+ module: PgBossModule,
38
+ imports: options.imports || [],
39
+ providers: [...asyncProviders, pgBossProvider, PgBossService],
40
+ exports: [PgBossService],
41
+ };
42
+ }
43
+
44
+ private static createAsyncProviders(
45
+ options: PgBossModuleAsyncOptions,
46
+ ): Provider[] {
47
+ if (options.useExisting || options.useFactory) {
48
+ return [this.createAsyncOptionsProvider(options)];
49
+ }
50
+
51
+ const useClass = options.useClass as new (
52
+ ...args: any[]
53
+ ) => PgBossOptionsFactory;
54
+
55
+ return [
56
+ this.createAsyncOptionsProvider(options),
57
+ {
58
+ provide: useClass,
59
+ useClass,
60
+ },
61
+ ];
62
+ }
63
+
64
+ private static createAsyncOptionsProvider(
65
+ options: PgBossModuleAsyncOptions,
66
+ ): Provider {
67
+ if (options.useFactory) {
68
+ return {
69
+ provide: PGBOSS_OPTIONS,
70
+ useFactory: options.useFactory,
71
+ inject: options.inject || [],
72
+ };
73
+ }
74
+
75
+ const inject = [(options.useClass || options.useExisting) as any];
76
+
77
+ return {
78
+ provide: PGBOSS_OPTIONS,
79
+ useFactory: async (optionsFactory: PgBossOptionsFactory) =>
80
+ optionsFactory.createPgBossOptions(),
81
+ inject,
82
+ };
83
+ }
84
+ }
@@ -0,0 +1,40 @@
1
+ import { Inject, Injectable } from "@nestjs/common";
2
+ import PgBoss, { BatchWorkOptions, JobOptions, WorkHandler } from "pg-boss";
3
+ import { PGBOSS_TOKEN } from "./utils/consts";
4
+
5
+ @Injectable()
6
+ export class PgBossService {
7
+ constructor(@Inject(PGBOSS_TOKEN) private readonly boss: PgBoss) {}
8
+
9
+ async registerJob<TData extends object>(
10
+ name: string,
11
+ handler: WorkHandler<TData>,
12
+ options?: BatchWorkOptions,
13
+ ) {
14
+ await this.boss.work(name, options, handler);
15
+ }
16
+
17
+ async scheduleJob<TData extends object>(
18
+ name: string,
19
+ data: TData,
20
+ options?: JobOptions,
21
+ ) {
22
+ await this.boss.send(name, data, options);
23
+ }
24
+
25
+ async registerCronJob<TData extends object>(
26
+ name: string,
27
+ cron: string,
28
+ handler: WorkHandler<TData>,
29
+ data?: TData,
30
+ options?: PgBoss.ScheduleOptions,
31
+ ) {
32
+ await this.boss.schedule(
33
+ name,
34
+ cron,
35
+ data ? data : {},
36
+ options ? options : {},
37
+ );
38
+ await this.boss.work(name, handler);
39
+ }
40
+ }
@@ -0,0 +1,2 @@
1
+ export const PGBOSS_OPTIONS = "PGBOSS_OPTIONS";
2
+ export const PGBOSS_TOKEN = "PGBOSS_TOKEN";
package/nest-cli.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/nest-cli",
3
+ "collection": "@nestjs/schematics",
4
+ "sourceRoot": "lib",
5
+ "compilerOptions": {
6
+ "deleteOutDir": true
7
+ }
8
+ }
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@wavezync/nestjs-pgboss",
3
+ "version": "1.0.0-alpha",
4
+ "description": "A NestJS module that integrates pg-boss for job scheduling and handling.",
5
+ "license": "MIT",
6
+ "author": "samaratungajs@wavezync.com",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "scripts": {
10
+ "build": "nest build",
11
+ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12
+ "start": "nest start",
13
+ "start:dev": "nest start --watch",
14
+ "start:debug": "nest start --debug --watch",
15
+ "start:prod": "node dist/main",
16
+ "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
17
+ "test": "jest",
18
+ "test:watch": "jest --watch",
19
+ "test:cov": "jest --coverage",
20
+ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
21
+ "test:e2e": "jest --config ./test/jest-e2e.json"
22
+ },
23
+ "peerDependencies": {
24
+ "@nestjs/common": "^9 || ^10",
25
+ "@nestjs/core": "^9 || ^10",
26
+ "pg-boss": "^9",
27
+ "reflect-metadata": "^0.1.13 || ^0.2.0",
28
+ "rxjs": "^7.2.0"
29
+ },
30
+ "devDependencies": {
31
+ "@nestjs/cli": "^10.0.0",
32
+ "@nestjs/schematics": "^10.0.0",
33
+ "@nestjs/testing": "^10.0.0",
34
+ "@types/express": "^4.17.17",
35
+ "@types/jest": "^29.5.2",
36
+ "@types/node": "^20.3.1",
37
+ "@types/supertest": "^6.0.0",
38
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
39
+ "@typescript-eslint/parser": "^7.0.0",
40
+ "eslint": "^8.42.0",
41
+ "eslint-config-prettier": "^9.0.0",
42
+ "eslint-plugin-prettier": "^5.0.0",
43
+ "jest": "^29.5.0",
44
+ "prettier": "^3.0.0",
45
+ "source-map-support": "^0.5.21",
46
+ "supertest": "^7.0.0",
47
+ "ts-jest": "^29.1.0",
48
+ "ts-loader": "^9.4.3",
49
+ "ts-node": "^10.9.1",
50
+ "tsconfig-paths": "^4.2.0",
51
+ "typescript": "^5.1.3"
52
+ },
53
+ "jest": {
54
+ "roots": [
55
+ "<rootDir>/test"
56
+ ],
57
+ "transform": {
58
+ "^.+\\.(t|j)s$": "ts-jest"
59
+ },
60
+ "testEnvironment": "node",
61
+ "moduleFileExtensions": [
62
+ "ts",
63
+ "js"
64
+ ]
65
+ },
66
+ "repository": {
67
+ "type": "git",
68
+ "url": "https://github.com/wavezync/nestjs-pgboss"
69
+ }
70
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "moduleFileExtensions": ["js", "json", "ts"],
3
+ "rootDir": ".",
4
+ "testEnvironment": "node",
5
+ "testRegex": ".e2e-spec.ts$",
6
+ "transform": {
7
+ "^.+\\.(t|j)s$": "ts-jest"
8
+ }
9
+ }
@@ -0,0 +1,81 @@
1
+ import { Test, TestingModule } from "@nestjs/testing";
2
+ import PgBoss from "pg-boss";
3
+ import { PgBossService } from "../lib/pgboss.service";
4
+ import { PGBOSS_TOKEN } from "../lib/utils/consts";
5
+
6
+ describe("PgBossService", () => {
7
+ let service: PgBossService;
8
+ let mockPgBoss: jest.Mocked<PgBoss>;
9
+
10
+ beforeEach(async () => {
11
+ mockPgBoss = {
12
+ start: jest.fn(),
13
+ work: jest.fn(),
14
+ send: jest.fn(),
15
+ schedule: jest.fn(),
16
+ } as any;
17
+
18
+ const module: TestingModule = await Test.createTestingModule({
19
+ providers: [
20
+ PgBossService,
21
+ { provide: PGBOSS_TOKEN, useValue: mockPgBoss },
22
+ ],
23
+ }).compile();
24
+
25
+ service = module.get<PgBossService>(PgBossService);
26
+ });
27
+
28
+ it("should be defined", () => {
29
+ expect(service).toBeDefined();
30
+ });
31
+
32
+ describe("registerJob", () => {
33
+ it("should call PgBoss work with correct parameters", async () => {
34
+ const handler = jest.fn();
35
+ const options = { batchSize: 5 };
36
+
37
+ await service.registerJob("test-job", handler, options);
38
+
39
+ expect(mockPgBoss.work).toHaveBeenCalledWith(
40
+ "test-job",
41
+ { batchSize: 5 },
42
+ handler,
43
+ );
44
+ });
45
+ });
46
+
47
+ describe("scheduleJob", () => {
48
+ it("should call PgBoss send with correct parameters", async () => {
49
+ const data = { test: "data" };
50
+
51
+ await service.scheduleJob("test-job", data, {});
52
+
53
+ expect(mockPgBoss.send).toHaveBeenCalledWith("test-job", data, {});
54
+ });
55
+ });
56
+
57
+ describe("registerCronJob", () => {
58
+ it("should call PgBoss schedule and work with correct parameters", async () => {
59
+ const handler = jest.fn();
60
+ const cron = "* * * * *";
61
+ const data = { test: "data" };
62
+ const options = { tz: "UTC" };
63
+
64
+ await service.registerCronJob(
65
+ "test-cron-job",
66
+ cron,
67
+ handler,
68
+ data,
69
+ options,
70
+ );
71
+
72
+ expect(mockPgBoss.schedule).toHaveBeenCalledWith(
73
+ "test-cron-job",
74
+ cron,
75
+ data,
76
+ { tz: "UTC" },
77
+ );
78
+ expect(mockPgBoss.work).toHaveBeenCalledWith("test-cron-job", handler);
79
+ });
80
+ });
81
+ });
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "include": [
4
+ "lib/**/*.ts"
5
+ ],
6
+ "compilerOptions": {
7
+ "rootDir": "./lib",
8
+ "outDir": "./dist"
9
+ },
10
+ "exclude": [
11
+ "node_modules",
12
+ "test",
13
+ "dist",
14
+ "**/*spec.ts"
15
+ ]
16
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "declaration": true,
5
+ "removeComments": true,
6
+ "resolveJsonModule": true,
7
+ "emitDecoratorMetadata": true,
8
+ "experimentalDecorators": true,
9
+ "esModuleInterop": true,
10
+ "allowSyntheticDefaultImports": true,
11
+ "target": "es2017",
12
+ "sourceMap": true,
13
+ "outDir": "./dist",
14
+ "baseUrl": "lib",
15
+ "incremental": false,
16
+ "skipLibCheck": true,
17
+ "strictNullChecks": false,
18
+ "noImplicitAny": false,
19
+ "strictBindCallApply": false,
20
+ "forceConsistentCasingInFileNames": false,
21
+ "noFallthroughCasesInSwitch": false,
22
+ "types": [
23
+ "@types/node",
24
+ "jest"
25
+ ],
26
+ "strict": true
27
+ },
28
+ "include": [
29
+ "lib/**/*.ts",
30
+ "test/**/*.ts"
31
+ ],
32
+ "exclude": [
33
+ "node_modules",
34
+ "dist"
35
+ ]
36
+ }