├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── cd.yml │ └── ci.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-push ├── .npmignore ├── LICENSE ├── README.md ├── package.json ├── src ├── index.test.ts └── index.ts ├── tsconfig.json └── yarn.lock /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [franky47] 2 | liberapay: francoisbest 3 | custom: ['https://paypal.me/francoisbest?locale.x=fr_FR'] 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: / 5 | target-branch: deps 6 | schedule: 7 | interval: weekly 8 | time: "09:00" 9 | timezone: Europe/Paris 10 | assignees: 11 | - franky47 12 | - package-ecosystem: github-actions 13 | directory: / 14 | target-branch: deps 15 | schedule: 16 | interval: monthly 17 | assignees: 18 | - franky47 19 | -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Delivery 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - beta 8 | 9 | jobs: 10 | cd: 11 | name: Continuous Delivery 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 15 | - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e 16 | with: 17 | node-version: 22.x 18 | cache: "yarn" 19 | - run: yarn install --ignore-scripts 20 | name: Install dependencies 21 | - run: yarn build 22 | name: Build package 23 | 24 | # Continuous Delivery Pipeline -- 25 | 26 | - uses: docker://ghcr.io/codfish/semantic-release-action@sha256:16ab6c16b1bff6bebdbcc6cfc07dfafff49d23c6818490500b8edb3babfff29e 27 | name: Semantic Release 28 | id: semantic 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 32 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - next 7 | - feature/* 8 | - dependabot/* 9 | pull_request: 10 | types: [opened, edited, reopened, synchronize] 11 | 12 | jobs: 13 | ci: 14 | name: Continuous Integration 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 18 | - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e 19 | with: 20 | node-version: 22.x 21 | cache: "yarn" 22 | - run: yarn install --ignore-scripts 23 | name: Install dependencies 24 | - run: yarn ci 25 | name: Run integration tests 26 | - uses: coverallsapp/github-action@9ba913c152ae4be1327bfb9085dc806cedb44057 27 | name: Report code coverage 28 | with: 29 | github-token: ${{ secrets.GITHUB_TOKEN }} 30 | - uses: 47ng/actions-slack-notify@main 31 | name: Notify on Slack 32 | if: always() 33 | with: 34 | status: ${{ job.status }} 35 | env: 36 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | coverage/ 4 | .env 5 | yarn-error.log 6 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn ci 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | **/*.test.ts 2 | tsconfig.json 3 | .env 4 | .volumes/ 5 | yarn-error.log 6 | .github/ 7 | src/ 8 | coverage/ 9 | .dependabot/ 10 | .husky/ 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 François Best 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

fastify-cron

2 | 3 |
4 | 5 | [![NPM](https://img.shields.io/npm/v/fastify-cron?color=red)](https://www.npmjs.com/package/fastify-cron) 6 | [![MIT License](https://img.shields.io/github/license/47ng/fastify-cron.svg?color=blue)](https://github.com/47ng/fastify-cron/blob/master/LICENSE) 7 | [![Continuous Integration](https://github.com/47ng/fastify-cron/workflows/Continuous%20Integration/badge.svg?branch=next)](https://github.com/47ng/fastify-cron/actions) 8 | [![Coverage Status](https://coveralls.io/repos/github/47ng/fastify-cron/badge.svg?branch=next)](https://coveralls.io/github/47ng/fastify-cron?branch=next) 9 | 10 |
11 | 12 |

13 | Run cron jobs alongside your Fastify server. 14 |

15 | 16 |
17 | 18 | While running cron jobs in the same process as a service is not the best 19 | recommended practice (according to the [Twelve Factor App](https://12factor.net/processes)), 20 | it can be useful when prototyping and when implementing low-criticality features 21 | on single-instance services (remember that when scaling horizontally, all your 22 | jobs may run in parallel too, see [Scaling](#scaling)). 23 | 24 | There is an existing discussion about this in [`fastify/fastify#1312`](https://github.com/fastify/fastify/issues/1312). 25 | 26 | ## Installation 27 | 28 | ```shell 29 | $ yarn add fastify-cron 30 | # or 31 | $ npm i fastify-cron 32 | ``` 33 | 34 | ## Usage 35 | 36 | Register the plugin with your Fastify server, and define a list of jobs to be 37 | created: 38 | 39 | > _If (like me) you always forget the syntax for cron times, 40 | > check out [crontab.guru](https://crontab.guru/)._ 41 | 42 | ```ts 43 | import Fastify from 'fastify' 44 | 45 | // Import it this way to benefit from TypeScript typings 46 | import fastifyCron from 'fastify-cron' 47 | 48 | const server = Fastify() 49 | 50 | server.register(fastifyCron, { 51 | jobs: [ 52 | { 53 | // Only these two properties are required, 54 | // the rest is from the node-cron API: 55 | // https://github.com/kelektiv/node-cron#api 56 | cronTime: '0 0 * * *', // Everyday at midnight UTC 57 | 58 | // Note: the callbacks (onTick & onComplete) take the server 59 | // as an argument, as opposed to nothing in the node-cron API: 60 | onTick: async server => { 61 | await server.db.runSomeCleanupTask() 62 | } 63 | } 64 | ] 65 | }) 66 | 67 | server.listen(() => { 68 | // By default, jobs are not running at startup 69 | server.cron.startAllJobs() 70 | }) 71 | ``` 72 | 73 | You can create other jobs later with `server.cron.createJob`: 74 | 75 | ```ts 76 | server.cron.createJob({ 77 | // Same properties as above 78 | cronTime: '0 0 * * *', // Everyday at midnight UTC 79 | onTick: () => {} 80 | }) 81 | ``` 82 | 83 | To interact with your jobs during the lifetime of your server, you can give 84 | them names: 85 | 86 | ```ts 87 | server.cron.createJob({ 88 | name: 'foo', 89 | cronTime: '0 * * * *', // Every hour at X o'clock 90 | onTick: () => {} 91 | }) 92 | 93 | // Later on, retrieve the job: 94 | const fooJob = server.cron.getJobByName('foo') 95 | fooJob.start() 96 | ``` 97 | 98 | Otherwise, you can access the list of jobs ordered by order of creation 99 | at `server.cron.jobs`. 100 | 101 | > **Warning**: if you mutate that list, you must take responsibility for manually 102 | > shutting down the jobs you pull out. 103 | 104 | ## Cron Jobs Lifecycle 105 | 106 | Cron jobs can be created either by passing properties to the `job` option when 107 | registering the plugin, or when explicitly calling `server.cron.createJob`. 108 | 109 | They are created by default in a stopped state, and are not automatically 110 | started (one good place to do so would be in a post-listening hook, but 111 | Fastify does not provide one). 112 | 113 | ### Starting jobs 114 | 115 | The recommended moment to start your jobs is when the server is listening 116 | (this way you can create test servers without cron jobs running around) : 117 | 118 | ```ts 119 | const server = Fastify() 120 | 121 | server.register(fastifyCron, { 122 | jobs: [ 123 | // ... 124 | ] 125 | }) 126 | 127 | server.listen(() => { 128 | server.cron.startAllJobs() 129 | }) 130 | ``` 131 | 132 | If you want to start a job immediately (synchronously) after its creation, 133 | set the `start` property to `true` (this is part of 134 | the [`cron` API](https://github.com/kelektiv/node-cron#api)): 135 | 136 | ```ts 137 | // When registering the plugin: 138 | server.register(fastifyCron, { 139 | jobs: [ 140 | { 141 | cronTime: '0 0 * * *', 142 | onTick: () => {}, 143 | start: true // Start job immediately 144 | } 145 | ] 146 | }) 147 | 148 | // You can also act directly on the job object being returned: 149 | const job = server.cron.createJob({ cronTime: '0 0 * * *', onTick: () => {} }) 150 | job.start() 151 | ``` 152 | 153 | If your job callback needs the server to be ready (all plugins loaded), it can 154 | be inappropriate to start the job straight away. You can have it start 155 | automatically when the server is ready by settings the `startWhenReady` 156 | property to `true`: 157 | 158 | ```ts 159 | server.register(fastifyCron, { 160 | jobs: [ 161 | { 162 | name: 'foo', 163 | cronTime: '0 0 * * *', 164 | onTick: server => { 165 | server.db.doStruff() 166 | }, 167 | startWhenReady: true 168 | } 169 | ] 170 | }) 171 | ``` 172 | 173 | ### Stopping jobs 174 | 175 | Jobs are stopped automatically when the server stops, in an `onClose` hook. 176 | 177 | If you have running cron jobs and need to stop them all (eg: in a test 178 | environment where the server is not listening): 179 | 180 | ```ts 181 | test('some test', () => { 182 | // ... 183 | 184 | // Stop all cron jobs to let the test runner exit cleanly: 185 | server.cron.stopAllJobs() 186 | }) 187 | ``` 188 | 189 | ## Scaling 190 | 191 | When horizontal-scaling your applications (running multiple identical instances 192 | in parallel), you'll probably want to make sure only one instance runs the cron 193 | tasks. 194 | 195 | If you have a way to uniquely identify an instance (eg: a number passed in the 196 | environment), you could use that to only enable crons for this instance. 197 | 198 | Example for [Clever Cloud](https://www.clever-cloud.com/doc/develop/env-variables/#what-is-the-instance_number-variable-used-for): 199 | 200 | ```ts 201 | if (process.env.INSTANCE_NUMBER === 0) { 202 | server.register(fastifyCron, { 203 | jobs: [ 204 | // ... 205 | ] 206 | }) 207 | } 208 | ``` 209 | 210 | ## Conditionally running jobs 211 | 212 | You may want to run certain jobs in development only, or under other conditions. 213 | 214 | `fastify-cron` will ignore any falsy values in the `jobs` array, so you can do: 215 | 216 | ```ts 217 | server.register(fastifyCron, { 218 | jobs: [ 219 | process.env.ENABLE_DEV_JOB === 'true' && { 220 | name: 'devJob', 221 | cronTime: '* * * * *', 222 | onTick: server => { 223 | // ... 224 | } 225 | } 226 | ] 227 | }) 228 | ``` 229 | 230 | ## Compatibility Notes 231 | 232 | Some compatibility issues may arise with the [`cron` API](https://github.com/kelektiv/node-cron#api). 233 | 234 | Possible issues (ticked if confirmed and unhandled): 235 | 236 | - [ ] Adding callbacks to a job via `addCallback` may not result in the server being passed as an argument 237 | - [ ] Using `fireOnTick` may lead to the same problem 238 | 239 | ## License 240 | 241 | [MIT](https://github.com/47ng/fastify-cron/blob/master/LICENSE) - Made with ❤️ by [François Best](https://francoisbest.com) 242 | 243 | Using this package at work ? [Sponsor me](https://github.com/sponsors/franky47) to help with support and maintenance. 244 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-cron", 3 | "version": "1.1.1", 4 | "description": "Run cron jobs alongside your Fastify server", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "license": "MIT", 8 | "author": { 9 | "name": "François Best", 10 | "email": "contact@francoisbest.com", 11 | "url": "https://francoisbest.com" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/47ng/fastify-cron" 16 | }, 17 | "keywords": [ 18 | "fastify", 19 | "fastify-plugin", 20 | "cron" 21 | ], 22 | "publishConfig": { 23 | "access": "public" 24 | }, 25 | "scripts": { 26 | "test": "jest --coverage", 27 | "test:watch": "jest --watch", 28 | "build:clean": "rm -rf ./dist", 29 | "build:ts": "tsc", 30 | "build": "run-s build:clean build:ts", 31 | "ci": "run-s test build", 32 | "prepare": "husky install" 33 | }, 34 | "dependencies": { 35 | "@types/cron": "^2.0.0", 36 | "cron": "^2.0.0", 37 | "fastify-plugin": "^3.0.0" 38 | }, 39 | "peerDependencies": { 40 | "fastify": "^4.1.0 || ^5.0.0" 41 | }, 42 | "devDependencies": { 43 | "@commitlint/config-conventional": "^17.0.3", 44 | "@types/jest": "^27.0.3", 45 | "@types/node": "^18.0.0", 46 | "commitlint": "^15.0.0", 47 | "fastify": "^4.1.0", 48 | "husky": "8.x", 49 | "jest": "^27.4.3", 50 | "jest-extended": "^2.0.0", 51 | "npm-run-all": "^4.1.5", 52 | "ts-jest": "^27.1.0", 53 | "ts-node": "^10.4.0", 54 | "typescript": "^4.5.2" 55 | }, 56 | "jest": { 57 | "verbose": true, 58 | "preset": "ts-jest/presets/js-with-ts", 59 | "setupFilesAfterEnv": [ 60 | "jest-extended/all" 61 | ], 62 | "testEnvironment": "node" 63 | }, 64 | "prettier": { 65 | "arrowParens": "avoid", 66 | "semi": false, 67 | "singleQuote": true, 68 | "tabWidth": 2, 69 | "trailingComma": "none", 70 | "useTabs": false 71 | }, 72 | "nodemon": { 73 | "verbose": false, 74 | "execMap": { 75 | "ts": "ts-node" 76 | }, 77 | "ignore": [ 78 | "./dist" 79 | ] 80 | }, 81 | "husky": { 82 | "hooks": { 83 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 84 | } 85 | }, 86 | "commitlint": { 87 | "extends": [ 88 | "@commitlint/config-conventional" 89 | ], 90 | "rules": { 91 | "type-enum": [ 92 | 2, 93 | "always", 94 | [ 95 | "build", 96 | "chore", 97 | "ci", 98 | "clean", 99 | "doc", 100 | "feat", 101 | "fix", 102 | "perf", 103 | "ref", 104 | "revert", 105 | "style", 106 | "test" 107 | ] 108 | ], 109 | "subject-case": [ 110 | 0, 111 | "always", 112 | "sentence-case" 113 | ], 114 | "body-leading-blank": [ 115 | 2, 116 | "always", 117 | true 118 | ] 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/index.test.ts: -------------------------------------------------------------------------------- 1 | import Fastify from 'fastify' 2 | import 'jest-extended' 3 | import fastifyCron from './index' 4 | 5 | describe('Creating jobs at plugin registration time', () => { 6 | test('no config', async () => { 7 | const server = Fastify({ logger: false }) 8 | await server.register(fastifyCron) 9 | await server.ready() 10 | expect(server.cron.jobs).toBeArrayOfSize(0) 11 | }) 12 | 13 | test('jobs are not started after plugins are loaded', async () => { 14 | const server = Fastify({ logger: false }) 15 | const spy = jest.fn() 16 | await server.register(fastifyCron, { 17 | jobs: [ 18 | { 19 | cronTime: '* * * * *', 20 | onTick: spy 21 | } 22 | ] 23 | }) 24 | await server.ready() // Wait for plugins to load 25 | expect(server.cron.jobs[0].running).toBeFalsy() 26 | expect(spy).not.toHaveBeenCalled() 27 | }) 28 | 29 | test('start job when startWhenReady = true', async () => { 30 | const server = Fastify({ logger: false }) 31 | const spy = jest.fn() 32 | await server.register(fastifyCron, { 33 | jobs: [ 34 | { 35 | cronTime: '* * * * *', 36 | onTick: spy, 37 | startWhenReady: true 38 | } 39 | ] 40 | }) 41 | await server.ready() 42 | expect(server.cron.jobs[0].running).toBeTrue() 43 | server.cron.stopAllJobs() 44 | }) 45 | 46 | test('retrieve job by name', async () => { 47 | const server = Fastify({ logger: false }) 48 | const spy = jest.fn() 49 | await server.register(fastifyCron, { 50 | jobs: [ 51 | { 52 | name: 'foo', 53 | cronTime: '* * * * *', 54 | onTick: spy 55 | } 56 | ] 57 | }) 58 | await server.ready() 59 | const job = server.cron.getJobByName('foo') 60 | expect(job).toBeDefined() 61 | expect(job!.running).toBeFalsy() 62 | }) 63 | }) 64 | 65 | describe('Creating jobs manually after registration', () => { 66 | test('jobs created after `ready` event should start automatically', async () => { 67 | const spy = jest.fn() 68 | const server = Fastify({ logger: false }) 69 | await server.register(fastifyCron) 70 | await server.ready() 71 | server.cron.createJob({ 72 | cronTime: '* * * * *', 73 | onTick: spy 74 | }) 75 | 76 | // We still need to re-await here as job startup is asynchronous, 77 | // but all plugins have already been loaded at this point. 78 | await server.ready() 79 | expect(server.cron.jobs).toBeArrayOfSize(1) 80 | expect(server.cron.jobs[0].running).toBeFalsy() 81 | server.cron.stopAllJobs() 82 | }) 83 | 84 | test('start job when startWhenReady = true', async () => { 85 | const server = Fastify({ logger: false }) 86 | const spy = jest.fn() 87 | await server.register(fastifyCron) 88 | await server.ready() // Needed to have the decoration 89 | server.cron.createJob({ 90 | cronTime: '* * * * *', 91 | onTick: spy, 92 | startWhenReady: true 93 | }) 94 | await server.ready() // Will resolve immediately 95 | expect(server.cron.jobs[0].running).toBeTrue() 96 | server.cron.stopAllJobs() 97 | }) 98 | 99 | test('retrieve job by name', async () => { 100 | jest.useFakeTimers('legacy') 101 | const server = Fastify({ logger: false }) 102 | const spy = jest.fn() 103 | await server.register(fastifyCron) 104 | await server.ready() 105 | server.cron.createJob({ 106 | name: 'foo', 107 | cronTime: '* * * * *', 108 | onTick: spy 109 | }) 110 | await server.ready() 111 | const job = server.cron.getJobByName('foo') 112 | expect(job).toBeDefined() 113 | expect(job!.running).toBeFalsy() 114 | }) 115 | 116 | test('falsy values are ignored in the jobs array', async () => { 117 | const server = Fastify({ logger: false }) 118 | const spy = jest.fn() 119 | await server.register(fastifyCron, { 120 | jobs: [ 121 | false && { 122 | name: 'foo', 123 | cronTime: '* * * * *', 124 | onTick: spy 125 | } 126 | ] 127 | }) 128 | await server.ready() 129 | const job = server.cron.getJobByName('foo') 130 | expect(job).toBeUndefined() 131 | }) 132 | }) 133 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { CronJob, CronJobParameters } from 'cron' 2 | import type { FastifyInstance, FastifyPluginAsync } from 'fastify' 3 | import fp from 'fastify-plugin' 4 | 5 | export type Job = CronJob & { 6 | readonly name?: string 7 | } 8 | 9 | declare module 'fastify' { 10 | interface FastifyInstance { 11 | cron: CronDecorator 12 | } 13 | } 14 | 15 | export type Params = Omit & { 16 | onTick: (server: FastifyInstance) => void 17 | onComplete?: (server: FastifyInstance) => void 18 | startWhenReady?: boolean 19 | name?: string 20 | } 21 | 22 | export interface CronDecorator { 23 | readonly jobs: Job[] 24 | createJob: (params: Params) => Job 25 | getJobByName: (name: string) => Job | undefined 26 | startAllJobs: () => void 27 | stopAllJobs: () => void 28 | } 29 | 30 | export type FalsyValue = undefined | null | false 31 | 32 | export interface Config { 33 | jobs?: (Params | FalsyValue)[] 34 | } 35 | 36 | const plugin: FastifyPluginAsync = async function fastifyCronPlugin( 37 | server, 38 | opts 39 | ) { 40 | const decorator: CronDecorator = { 41 | jobs: [], 42 | createJob: function createJob(params) { 43 | const innerParams: CronJobParameters = { 44 | ...params, 45 | onTick: () => params.onTick(server), 46 | onComplete: () => 47 | typeof params.onComplete === 'function' 48 | ? () => params.onComplete!(server) 49 | : undefined 50 | } 51 | const job: Job = Object.assign(new CronJob(innerParams), { 52 | name: params.name 53 | }) 54 | if (params.startWhenReady === true) { 55 | server.ready(() => job.start()) 56 | } 57 | decorator.jobs.push(job) 58 | return job 59 | }, 60 | getJobByName: function getJobByName(name) { 61 | return decorator.jobs.find(j => j.name === name) 62 | }, 63 | startAllJobs: function startAllJobs() { 64 | decorator.jobs.forEach(job => job.start()) 65 | }, 66 | stopAllJobs: function stopAllJobs() { 67 | decorator.jobs.forEach(job => job.stop()) 68 | } 69 | } 70 | for (const params of opts.jobs || []) { 71 | if (params) { 72 | decorator.createJob(params) 73 | } 74 | } 75 | 76 | server.decorate('cron', decorator) 77 | server.addHook('onClose', () => decorator.stopAllJobs()) 78 | } 79 | 80 | export default fp(plugin, { 81 | name: 'fastify-cron', 82 | fastify: '4.x || 5.x' 83 | }) 84 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "target": "es2018", 5 | "module": "commonjs", 6 | "lib": [], 7 | "declaration": true, 8 | "outDir": "./dist", 9 | "rootDir": "./src", 10 | "paths": { "*": ["types/*"] }, 11 | 12 | // Strict Type-Checking Options 13 | "strict": true, 14 | "noImplicitAny": true, 15 | "strictNullChecks": true, 16 | "strictFunctionTypes": true, 17 | "strictPropertyInitialization": true, 18 | "noImplicitThis": true, 19 | "alwaysStrict": true, 20 | 21 | // Additional Checks 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true, 24 | "noImplicitReturns": true, 25 | "noFallthroughCasesInSwitch": true, 26 | 27 | "esModuleInterop": true, 28 | 29 | // Experimental Options 30 | "experimentalDecorators": true, 31 | "emitDecoratorMetadata": true 32 | }, 33 | "exclude": ["./src/**/*.test.ts", "./dist", "./node_modules"] 34 | } 35 | --------------------------------------------------------------------------------