UNPKG

41 kBMarkdownView Raw
1<h1 align="center">
2 <a href="https://jobscheduler.net"><img src="https://d1i8ikybhfrv4r.cloudfront.net/bree/bree.png" alt="bree" /></a>
3</h1>
4<div align="center">
5 <a href="https://github.com/breejs/bree/actions/workflows/ci.yml"><img src="https://github.com/breejs/bree/actions/workflows/ci.yml/badge.svg" alt="build status" /></a>
6 <a href="https://github.com/sindresorhus/xo"><img src="https://img.shields.io/badge/code_style-XO-5ed9c7.svg" alt="code style" /></a>
7 <a href="https://github.com/prettier/prettier"><img src="https://img.shields.io/badge/styled_with-prettier-ff69b4.svg" alt="styled with prettier" /></a>
8 <a href="https://lass.js.org"><img src="https://img.shields.io/badge/made_with-lass-95CC28.svg" alt="made with lass" /></a>
9 <a href="LICENSE"><img src="https://img.shields.io/github/license/breejs/bree.svg" alt="license" /></a>
10</div>
11<br />
12<div align="center">
13 Bree is the best job scheduler for <a href="https://nodejs.org">Node.js</a> and JavaScript with <a href="https://en.wikipedia.org/wiki/Cron">cron</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date">dates</a>, <a href="https://github.com/vercel/ms">ms</a>, <a href="https://github.com/breejs/later">later</a>, and <a href="https://github.com/agenda/human-interval">human-friendly</a> support.
14</div>
15<hr />
16<div align="center">
17 Works in Node v12.11.0+, uses <a href="https://nodejs.org/api/worker_threads.html">worker threads</a> (Node.js) to spawn sandboxed processes, and supports <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function">async/await</a>, <a href="https://github.com/sindresorhus/p-retry">retries</a>, <a href="https://github.com/sindresorhus/p-throttle">throttling</a>, <a href="#concurrency">concurrency</a>, and <a href="#cancellation-retries-stalled-jobs-and-graceful-reloading">cancelable jobs with graceful shutdown</a>. Simple, fast, and lightweight. <strong>Made for <a href="https://forwardemail.net">Forward Email</a> and <a href="https://lad.js.org">Lad</a></strong>.
18</div>
19
20
21## Table of Contents
22
23* [Foreword](#foreword)
24* [Install](#install)
25* [Upgrading](#upgrading)
26* [Usage and Examples](#usage-and-examples)
27* [Instance Options](#instance-options)
28* [Job Options](#job-options)
29* [Job Interval and Timeout Values](#job-interval-and-timeout-values)
30* [Listening for events](#listening-for-events)
31* [Custom error/message handling](#custom-errormessage-handling)
32* [Cancellation, Retries, Stalled Jobs, and Graceful Reloading](#cancellation-retries-stalled-jobs-and-graceful-reloading)
33* [Interval, Timeout, Date, and Cron Validation](#interval-timeout-date-and-cron-validation)
34* [Writing jobs with Promises and async-await](#writing-jobs-with-promises-and-async-await)
35* [Callbacks, Done, and Completion States](#callbacks-done-and-completion-states)
36* [Long-running jobs](#long-running-jobs)
37* [Complex timeouts and intervals](#complex-timeouts-and-intervals)
38* [Custom Worker Options](#custom-worker-options)
39* [Using functions for jobs](#using-functions-for-jobs)
40* [Typescript and Usage with Bundlers](#typescript-and-usage-with-bundlers)
41* [Concurrency](#concurrency)
42* [Plugins](#plugins)
43 * [Available Plugins](#available-plugins)
44 * [Creating plugins for Bree](#creating-plugins-for-bree)
45* [Real-world usage](#real-world-usage)
46* [Contributors](#contributors)
47* [License](#license)
48
49
50## Foreword
51
52Bree was created to give you fine-grained control with simplicity, and has built-in support for workers, sandboxed processes, graceful reloading, cron jobs, dates, human-friendly time representations, and much more.
53
54We recommend you to query a persistent database in your jobs, to prevent specific operations from running more than once.
55
56Bree does not force you to use an additional database layer of [Redis][] or [MongoDB][] to manage job state.
57
58In doing so, you should manage boolean job states yourself using queries. For instance, if you have to send a welcome email to users, only send a welcome email to users that do not have a Date value set yet for `welcome_email_sent_at`.
59
60
61## Install
62
63[npm][]:
64
65```sh
66npm install bree
67```
68
69[yarn][]:
70
71```sh
72yarn add bree
73```
74
75
76## Upgrading
77
78To see details about upgrading from the last major version, check out this [doc](./UPGRADING.md).
79
80
81## Usage and Examples
82
83The example below assumes that you have a directory `jobs` in the root of the directory from which you run this example. For example, if the example below is at `/path/to/script.js`, then `/path/to/jobs/` must also exist as a directory. If you wish to disable this feature, then pass `root: false` as an option.
84
85Inside this `jobs` directory are individual scripts which are run using [Workers][] per optional timeouts, and additionally, an optional interval or cron expression. The example below contains comments, which help to clarify how this works.
86
87The option `jobs` passed to a new instance of `Bree` (as shown below) is an Array. It contains values which can either be a String (name of a job in the `jobs` directory, which is run on boot) OR it can be an Object with `name`, `path`, `timeout`, and `interval` properties. If you do not supply a `path`, then the path is created using the root directory (defaults to `jobs`) in combination with the `name`. If you do not supply values for `timeout` and/nor `interval`, then these values are defaulted to `0` (which is the default for both, see [index.js](src/index.js) for more insight into configurable default options).
88
89We have also documented all [Instance Options](#instance-options) and [Job Options](#job-options) in this README below. Be sure to read those sections so you have a complete understanding of how Bree works.
90
91> **NOTE:** [Bree v6.5.0](https://github.com/breejs/bree/releases/tag/v6.5.0) is the last version to support Node v10 and browsers.
92
93```js
94const path = require('path');
95
96// optional
97const ms = require('ms');
98const dayjs = require('dayjs');
99const Graceful = require('@ladjs/graceful');
100const Cabin = require('cabin');
101
102// required
103const Bree = require('bree');
104
105//
106// NOTE: see the "Instance Options" section below in this README
107// for the complete list of options and their defaults
108//
109const bree = new Bree({
110 //
111 // NOTE: by default the `logger` is set to `console`
112 // however we recommend you to use CabinJS as it
113 // will automatically add application and worker metadata
114 // to your log output, and also masks sensitive data for you
115 // <https://cabinjs.com>
116 //
117 // NOTE: You can also pass `false` as `logger: false` to disable logging
118 //
119 logger: new Cabin(),
120
121 //
122 // NOTE: instead of passing this Array as an option
123 // you can create a `./jobs/index.js` file, exporting
124 // this exact same array as `module.exports = [ ... ]`
125 // doing so will allow you to keep your job configuration and the jobs
126 // themselves all in the same folder and very organized
127 //
128 // See the "Job Options" section below in this README
129 // for the complete list of job options and configurations
130 //
131 jobs: [
132 // runs `./jobs/foo.js` on start
133 'foo',
134
135 // runs `./jobs/foo-bar.js` on start
136 {
137 name: 'foo-bar'
138 },
139
140 // runs `./jobs/some-other-path.js` on start
141 {
142 name: 'beep',
143 path: path.join(__dirname, 'jobs', 'some-other-path')
144 },
145
146 // runs `./jobs/worker-1.js` on the last day of the month
147 {
148 name: 'worker-1',
149 interval: 'on the last day of the month'
150 },
151
152 // runs `./jobs/worker-2.js` every other day
153 {
154 name: 'worker-2',
155 interval: 'every 2 days'
156 },
157
158 // runs `./jobs/worker-3.js` at 10:15am and 5:15pm every day except on Tuesday
159 {
160 name: 'worker-3',
161 interval: 'at 10:15 am also at 5:15pm except on Tuesday'
162 },
163
164 // runs `./jobs/worker-4.js` at 10:15am every weekday
165 {
166 name: 'worker-4',
167 cron: '15 10 ? * *',
168 cronValidate: {
169 override: {
170 useBlankDay: true
171 }
172 }
173 },
174
175 // runs `./jobs/worker-5.js` on after 10 minutes have elapsed
176 {
177 name: 'worker-5',
178 timeout: '10m'
179 },
180
181 // runs `./jobs/worker-6.js` after 1 minute and every 5 minutes thereafter
182 {
183 name: 'worker-6',
184 timeout: '1m',
185 interval: '5m'
186 // this is unnecessary but shows you can pass a Number (ms)
187 // interval: ms('5m')
188 },
189
190 // runs `./jobs/worker-7.js` after 3 days and 4 hours
191 {
192 name: 'worker-7',
193 // this example uses `human-interval` parsing
194 timeout: '3 days and 4 hours'
195 },
196
197 // runs `./jobs/worker-8.js` at midnight (once)
198 {
199 name: 'worker-8',
200 timeout: 'at 12:00 am'
201 },
202
203 // runs `./jobs/worker-9.js` every day at midnight
204 {
205 name: 'worker-9',
206 interval: 'at 12:00 am'
207 },
208
209 // runs `./jobs/worker-10.js` at midnight on the 1st of every month
210 {
211 name: 'worker-10',
212 cron: '0 0 1 * *'
213 },
214
215 // runs `./jobs/worker-11.js` at midnight on the last day of month
216 {
217 name: 'worker-11',
218 cron: '0 0 L * *',
219 cronValidate: {
220 useLastDayOfMonth: true
221 }
222 },
223
224 // runs `./jobs/worker-12.js` at a specific Date (e.g. in 3 days)
225 {
226 name: 'worker-12',
227 // <https://github.com/iamkun/dayjs>
228 date: dayjs().add(3, 'days').toDate()
229 // you can also use momentjs
230 // <https://momentjs.com/>
231 // date: moment('1/1/20', 'M/D/YY').toDate()
232 // you can pass Date instances (if it's in the past it will not get run)
233 // date: new Date()
234 },
235
236 // runs `./jobs/worker-13.js` on start and every 2 minutes
237 {
238 name: 'worker-13',
239 interval: '2m'
240 },
241
242 // runs `./jobs/worker-14.js` on start with custom `new Worker` options (see below)
243 {
244 name: 'worker-14',
245 // <https://nodejs.org/api/worker_threads.html#worker_threads_new_worker_filename_options>
246 worker: {
247 workerData: {
248 foo: 'bar',
249 beep: 'boop'
250 }
251 }
252 },
253
254 // runs `./jobs/worker-15.js` **NOT** on start, but every 2 minutes
255 {
256 name: 'worker-15',
257 timeout: false, // <-- specify `false` here to prevent default timeout (e.g. on start)
258 interval: '2m'
259 },
260
261 // runs `./jobs/worker-16.js` on January 1st, 2022
262 // and at midnight on the 1st of every month thereafter
263 {
264 name: 'worker-16',
265 date: dayjs('1-1-2022', 'M-D-YYYY').toDate(),
266 cron: '0 0 1 * *'
267 }
268 ]
269});
270
271// handle graceful reloads, pm2 support, and events like SIGHUP, SIGINT, etc.
272const graceful = new Graceful({ brees: [bree] });
273graceful.listen();
274
275// start all jobs (this is the equivalent of reloading a crontab):
276bree.start();
277
278/*
279// start only a specific job:
280bree.start('foo');
281
282// stop all jobs
283bree.stop();
284
285// stop only a specific job:
286bree.stop('beep');
287
288// run all jobs (this does not abide by timeout/interval/cron and spawns workers immediately)
289bree.run();
290
291// run a specific job (...)
292bree.run('beep');
293
294// add a job array after initialization:
295const added = bree.add(['boop']); // will return array of added jobs
296// this must then be started using one of the above methods
297
298// add a job after initialization:
299bree.add('boop');
300// this must then be started using one of the above methods
301
302// remove a job after initialization:
303bree.remove('boop');
304*/
305```
306
307For more examples - including setting up bree with TypeScript, ESModules, and implementing an Email Queue, see the [examples](./examples) folder.
308
309For a more complete demo using express see: [Bree Express Demo](https://github.com/breejs/express-example)
310
311
312## Instance Options
313
314Here is the full list of options and their defaults. See [index.js](index.js) for more insight if necessary.
315
316| Property | Type | Default Value | Description |
317| ----------------------- | -------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
318| `logger` | Object | `console` | This is the default logger. **We recommend using [Cabin][cabin]** instead of using `console` as your default logger. Set this value to `false` to disable logging entirely (uses noop function) |
319| `root` | String | `path.resolve('jobs')` | Resolves a jobs folder relative to where the project is ran (the directory you call `node` in). Set this value to `false` to prevent requiring a root directory of jobs (e.g. if your jobs are not all in one directory). Set this to `path.join(__dirname, 'jobs')` to keep your jobs directory relative to the file where Bree is set up. |
320| `silenceRootCheckError` | Boolean | `false` | Silences errors from requiring the root folder. Set this to `false` if you do not want to see errors from this operation |
321| `doRootCheck` | Boolean | `true` | Attempts to `require` the root directory, when `jobs` is empty or `null`. Set this to `false` to prevent requiring the root directory |
322| `removeCompleted` | Boolean | `false` | Removes job upon completion. Set this to `true` in order to remove jobs from the array upon completion. |
323| `timeout` | Number | `0` | Default timeout for jobs (e.g. a value of `0` means that jobs will start on boot by default unless a job has a property of `timeout` or `interval` defined. Set this to `false` if you do not wish for a default value to be set for jobs. **This value does not apply to jobs with a property of `date`.** |
324| `interval` | Number | `0` | Default interval for jobs (e.g. a value of `0` means that there is no interval, and a value greater than zero indicates a default interval will be set with this value). **This value does not apply to jobs with a property of `cron`**. |
325| `jobs` | Array | `[]` | Defaults to an empty Array, but if the `root` directory has a `index.js` file, then it will be used. This allows you to keep your jobs and job definition index in the same place. See [Job Options](#job-options) below, and [Usage and Examples](#usage-and-examples) above for more insight. |
326| `hasSeconds` | Boolean | `false` | This value is passed to `later` for parsing jobs, and can be overridden on a per job basis. See [later cron parsing](https://breejs.github.io/later/parsers.html#cron) documentation for more insight. Note that setting this to `true` will automatically set `cronValidate` defaults to have `{ preset: 'default', override: { useSeconds: true } }` |
327| `cronValidate` | Object | `{}` | This value is passed to `cron-validate` for validation of cron expressions. See the [cron-validate](https://github.com/Airfooox/cron-validate) documentation for more insight. |
328| `closeWorkerAfterMs` | Number | `0` | If you set a value greater than `0` here, then it will terminate workers after this specified time (in milliseconds). **As of v6.0.0, workers now terminate after they have been signaled as "online" (as opposed to previous versions which did not take this into account and started the timer when jobs were initially "run").** By default there is no termination done, and jobs can run for infinite periods of time. |
329| `defaultExtension` | String | `js` | This value can either be `js` or `mjs`. The default is `js`, and is the default extension added to jobs that are simply defined with a name and without a path. For example, if you define a job `test`, then it will look for `/path/to/root/test.js` as the file used for workers. |
330| `acceptedExtensions` | Array | `['.js', '.mjs']` | This defines all of the accepted extensions for file validation and job creation. Please note if you add to this list you must override the `createWorker` function to properly handle the new file types. |
331| `worker` | Object | `{}` | These are default options to pass when creating a `new Worker` instance. See the [Worker class](https://nodejs.org/api/worker_threads.html#worker_threads_new_worker_filename_options) documentation for more insight. |
332| `outputWorkerMetadata` | Boolean | `false` | By default worker metadata is not passed to the second Object argument of `logger`. However if you set this to `true`, then `logger` will be invoked internally with two arguments (e.g. `logger.info('...', { worker: ... })`). This `worker` property contains `isMainThread` (Boolean), `resourceLimits` (Object), and `threadId` (String) properties; all of which correspond to [Workers][] metadata. This can be overridden on a per job basis. |
333| `errorHandler` | Function | `null` | Set this function to receive a callback when an error is encountered during worker execution (e.g. throws an exception) or when it exits with non-zero code (e.g. `process.exit(1)`). The callback receives two parameters `error` and `workerMetadata`. Important note, when this callback is present default error logging will not be executed. |
334| `workerMessageHandler` | Function | `null` | Set this function to receive a callback when a worker sends a message through [parentPort.postMessage](https://nodejs.org/docs/latest-v14.x/api/worker_threads.html#worker_threads_port_postmessage_value_transferlist). The callback receives at least two parameters `name` (of the worker) and `message` (coming from `postMessage`), if `outputWorkerMetadata` is enabled additional metadata will be sent to this handler. |
335
336
337## Job Options
338
339See [Interval, Timeout, Date, and Cron Validate](#interval-timeout-date-and-cron-validation) below for more insight besides this table:
340
341| Property | Type | Description |
342| ---------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
343| `name` | String | The name of the job. This should match the base file path (e.g. `foo` if `foo.js` is located at `/path/to/jobs/foo.js`) unless `path` option is specified. A value of `index`, `index.js`, and `index.mjs` are reserved values and cannot be used here. |
344| `path` | String | The path of the job or function used for spawning a new [Worker][workers] with. If not specified, then it defaults to the value for `name` plus the default file extension specified under [Instance Options](#instance-options). |
345| `timeout` | Number, Object, String, or Boolean | Sets the duration in milliseconds before the job starts (it overrides the default inherited `timeout` as set in [Instance Options](#instance-options). A value of `0` indicates it will start immediately. This value can be a Number, String, or a Boolean of `false` (which indicates it will NOT inherit the default `timeout` from [Instance Options](#instance-options)). See [Job Interval and Timeout Values](#job-interval-and-timeout-values) below for more insight into how this value is parsed. |
346| `interval` | Number, Object, or String | Sets the duration in milliseconds for the job to repeat itself, otherwise known as its interval (it overrides the default inherited `interval` as set in [Instance Options](#instance-options)). A value of `0` indicates it will not repeat and there will be no interval. If the value is greater than `0` then this value will be used as the interval. See [Job Interval and Timeout Values](#job-interval-and-timeout-values) below for more insight into how this value is parsed. |
347| `date` | Date | This must be a valid JavaScript Date (we use `instance of Date` for comparison). If this value is in the past, then it is not run when jobs are started (or run manually). We recommend using [dayjs][] for creating this date, and then formatting it using the `toDate()` method (e.g. `dayjs().add('3, 'days').toDate()`). You could also use [moment][] or any other JavaScript date library, as long as you convert the value to a Date instance here. |
348| `cron` | String | A cron expression to use as the job's interval, which is validated against [cron-validate][] and parsed by [later][]. |
349| `hasSeconds` | Boolean | Overrides the [Instance Options](#instance-options) `hasSeconds` property if set. Note that setting this to `true` will automatically set `cronValidate` defaults to have `{ preset: 'default', override: { useSeconds: true } }` |
350| `cronValidate` | Object | Overrides the [Instance Options](#instance-options) `cronValidate` property if set. |
351| `closeWorkerAfterMs` | Number | Overrides the [Instance Options](#instance-options) `closeWorkerAfterMs` property if set. |
352| `worker` | Object | Overrides the [Instance Options](#instance-options) `worker` property if set. |
353| `outputWorkerMetadata` | Boolean | Overrides the [Instance Options](#instance-options) `outputWorkerMetadata` property if set. |
354
355
356## Job Interval and Timeout Values
357
358These values can include Number, Object, and String variable types:
359
360* Number values indicates the number of milliseconds for the timeout or interval
361* Object values must be a [later][] schedule object value (e.g. `later.parse.cron('15 10 * * ? *'))`)
362* String values can be either a [later][], [human-interval][], or [ms][] String values (e.g. [later][] supports Strings such as `every 5 mins`, [human-interval][] supports Strings such as `3 days and 4 hours`, and [ms][] supports Strings such as `4h` for four hours)
363
364
365## Listening for events
366
367Bree extends from [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) and emits two events:
368
369* `worker created` with an argument of `name`
370* `worker deleted` with an argument of `name`
371
372If you'd like to know when your workers are created (or deleted), you can do so through this example:
373
374```js
375bree.on('worker created', (name) => {
376 console.log('worker created', name);
377 console.log(bree.workers.get(name));
378});
379
380bree.on('worker deleted', (name) => {
381 console.log('worker deleted', name);
382 console.log(!bree.worker.has(name));
383});
384```
385
386
387## Custom error/message handling
388
389If you'd like to override default behavior for worker error/message handling, provide a callback function as `errorHandler` or `workerMessageHandler` parameter when creating a Bree instance.
390
391> **NOTE:** Any `console.log` calls, from within the worker, will not be sent to `stdout`/`stderr` until the main thread is available. Furthermore, any `console.log` calls, from within the worker, will not be sent if the process is terminated before the message is printed. You should use `parentPort.postMessage()` alongside `errorHandler` or `workerMessageHandler` to print to `stdout`/`stderr` during worker execution. This is a known [bug](https://github.com/nodejs/node/issues/30491) for workers.
392
393An example use-case. If you want to call an external service to record an error (like Honeybadger, Sentry, etc.) along with logging the error internally. You can do so with:
394
395```js
396const logger = ('../path/to/logger');
397const errorService = ('../path/to/error-service');
398
399new Bree({
400 jobs: [
401 {
402 name: 'job that sometimes throws errors',
403 path: jobFunction
404 }
405 ],
406 errorHandler: (error, workerMetadata) => {
407 // workerMetadata will be populated with extended worker information only if
408 // Bree instance is initialized with parameter `workerMetadata: true`
409 if (workerMetadata.threadId) {
410 logger.info(`There was an error while running a worker ${workerMetadata.name} with thread ID: ${workerMetadata.threadId}`)
411 } else {
412 logger.info(`There was an error while running a worker ${workerMetadata.name}`)
413 }
414
415 logger.error(error);
416 errorService.captureException(error);
417 }
418});
419```
420
421
422## Cancellation, Retries, Stalled Jobs, and Graceful Reloading
423
424We recommend that you listen for "cancel" event in your worker paths. Doing so will allow you to handle graceful cancellation of jobs. For example, you could use [p-cancelable][]
425
426Here's a quick example of how to do that (e.g. `./jobs/some-worker.js`):
427
428```js
429// <https://nodejs.org/api/worker_threads.html>
430const { parentPort } = require('worker_threads');
431
432// ...
433
434function cancel() {
435 // do cleanup here
436 // (if you're using @ladjs/graceful, the max time this can run by default is 5s)
437
438 // send a message to the parent that we're ready to terminate
439 // (you could do `process.exit(0)` or `process.exit(1)` instead if desired
440 // but this is a bit of a cleaner approach for worker termination
441 if (parentPort) parentPort.postMessage('cancelled');
442 else process.exit(0);
443}
444
445if (parentPort)
446 parentPort.once('message', message => {
447 if (message === 'cancel') return cancel();
448 });
449```
450
451If you'd like jobs to retry, simply wrap your usage of promises with [p-retry][].
452
453We leave it up to you to have as much fine-grained control as you wish.
454
455See [@ladjs/graceful][lad-graceful] for more insight into how this package works.
456
457
458## Interval, Timeout, Date, and Cron Validation
459
460If you need help writing cron expressions, you can reference [crontab.guru](https://crontab.guru/).
461
462We support [later][], [human-interval][], or [ms][] String values for both `timeout` and `interval`.
463
464If you pass a `cron` property, then it is validated against [cron-validate][].
465
466You can pass a Date as the `date` property, but you cannot combine both `date` and `timeout`.
467
468If you do pass a Date, then it is only run if it is in the future.
469
470See [Job Interval and Timeout Values](#job-interval-and-timeout-values) above for more insight.
471
472
473## Writing jobs with Promises and async-await
474
475If jobs are running with Node pre-[v14.8.0](https://nodejs.org/en/blog/release/v14.8.0/), which [enables top-level async-await](https://github.com/nodejs/node/commit/62bb2e757f) support, here is the working alternative:
476
477```js
478const { parentPort } = require('worker_threads');
479
480const delay = require('delay');
481const ms = require('ms');
482
483(async () => {
484 // wait for a promise to finish
485 await delay(ms('10s'));
486
487 // signal to parent that the job is done
488 if (parentPort) parentPort.postMessage('done');
489 else process.exit(0);
490})();
491```
492
493
494## Callbacks, Done, and Completion States
495
496To close out the worker and signal that it is done, you can simply `parentPort.postMessage('done');` and/or `process.exit(0)`.
497
498While writing your jobs (which will run in [worker][workers] threads), you should do one of the following:
499
500* Signal to the main thread that the process has completed by sending a "done" message (per the example above in [Writing jobs with Promises and async-await](#writing-jobs-with-promises-and-async-await))
501* Exit the process if there is NOT an error with code `0` (e.g. `process.exit(0);`)
502* Throw an error if an error occurs (this will bubble up to the worker event error listener and terminate it)
503* Exit the process if there IS an error with code `1` (e.g. `process.exit(1)`)
504
505
506## Long-running jobs
507
508If a job is already running, a new worker thread will not be spawned, instead `logger.error` will be invoked with an error message (no error will be thrown, don't worry). This is to prevent bad practices from being used. If you need something to be run more than one time, then make the job itself run the task multiple times. This approach gives you more fine-grained control.
509
510By default, workers run indefinitely and are not closed until they exit (e.g. via `process.exit(0)` or `process.exit(1)`, OR send to the parent port a "close" message, which will subsequently call `worker.close()` to close the worker thread.
511
512If you wish to specify a maximum time (in milliseconds) that a worker can run, then pass `closeWorkerAfterMs` (Number) either as a default option when creating a `new Bree()` instance (e.g. `new Bree({ closeWorkerAfterMs: ms('10s') })`) or on a per-job configuration, e.g. `{ name: 'beep', closeWorkerAfterMs: ms('5m') }`.
513
514As of v6.0.0 when you pass `closeWorkerAfterMs`, the timer will start once the worker is signaled as "online" (as opposed to previous versions which did not take this into account).
515
516
517## Complex timeouts and intervals
518
519Since we use [later][], you can pass an instance of `later.parse.recur`, `later.parse.cron`, or `later.parse.text` as the `timeout` or `interval` property values (e.g. if you need to construct something manually).
520
521You can also use [dayjs][] to construct dates (e.g. from now or a certain date) to millisecond differences using `dayjs().diff(new Date(), 'milliseconds')`. You would then pass that returned Number value as `timeout` or `interval` as needed.
522
523
524## Custom Worker Options
525
526You can pass a default worker configuration object as `new Bree({ worker: { ... } });`.
527
528These options are passed to the `options` argument when we internally invoke `new Worker(path, options)`.
529
530Additionally, you can pass custom worker options on a per-job basis through a `worker` property Object on the job definition.
531
532See [complete documentation](https://nodejs.org/api/worker_threads.html#worker_threads_new_worker_filename_options) for options (but you usually don't have to modify these).
533
534
535## Using functions for jobs
536
537It is highly recommended to use files instead of functions. However, sometimes it is necessary to use functions.
538
539You can pass a function to be run as a job:
540
541```js
542new Bree({ jobs: [someFunction] });
543```
544
545(or)
546
547```js
548new Bree({
549 jobs: [
550 {
551 name: 'job with function',
552 path: someFunction
553 }
554 ]
555});
556```
557
558The function will be run as if it's in its own file, therefore no variables or dependencies will be shared from the local context by default.
559
560You should be able to pass data via `worker.workerData` (see [Custom Worker Options](#custom-worker-options)).
561
562Note that you cannot pass a built-in nor bound function.
563
564
565## Typescript and Usage with Bundlers
566
567When working with a bundler or a tool that transpiles your code in some form or another, we recommend that your bundler is set up in a way that transforms both your application code and your jobs. Because your jobs are in their own files and are run in their own separate threads, they will not be part of your applications dependency graph and need to be setup as their own entry points. You need to ensure you have configured your tool to bundle your jobs into a jobs folder and keep them properly relative to your entry point folder.
568
569We recommend setting the `root` instance options to `path.join(__dirname,'jobs')` so that bree searches for your jobs folder relative to the file being ran. (by default it searches for jobs relative to where `node` is invoked). We recommend treating each job as an entry point and running all jobs through the same transformations as your app code.
570
571After an example transformation - you should expect the output in your `dist` folder to look like:
572
573```tree
574- dist
575 |-jobs
576 |-job.js
577 |-index.js
578```
579
580For some example TypeScript set ups - see the [examples folder](https://github.com/breejs/bree/tree/master/examples).
581
582For another alternative also see the [@breejs/ts-worker](https://github.com/breejs/ts-worker) plugin.
583
584
585## Concurrency
586
587We recommend using the following packages in your workers for handling concurrency:
588
589* <https://github.com/sindresorhus/p-all>
590* <https://github.com/sindresorhus/p-limit>
591* <https://github.com/sindresorhus/p-queue>
592* <https://github.com/sindresorhus/p-map>
593
594
595## Plugins
596
597Plugins can be added to Bree using a similar method to [Day.js](https://day.js.org/)
598
599To add a plugin use the following method:
600
601```js
602Bree.extend(plugin, options);
603```
604
605### Available Plugins
606
607* [API](https://github.com/breejs/api)
608* [TypeScript Worker](https://github.com/breejs/ts-worker)
609
610### Creating plugins for Bree
611
612Plugins should be a function that recieves an `options` object and the `Bree` class:
613
614```js
615 const plugin = (options, Bree) => {
616 /* plugin logic */
617 };
618```
619
620
621## Real-world usage
622
623More detailed examples can be found in [Forward Email][forward-email], [Lad][], and [Ghost][ghost].
624
625
626## Contributors
627
628| Name | Website |
629| ---------------- | --------------------------------- |
630| **Nick Baugh** | <http://niftylettuce.com/> |
631| **shadowgate15** | <https://github.com/shadowgate15> |
632
633
634## License
635
636[MIT](LICENSE) © [Nick Baugh](http://niftylettuce.com/)
637
638
639##
640
641<a href="#"><img src="https://d1i8ikybhfrv4r.cloudfront.net/bree/footer.png" alt="#" /></a>
642
643[ms]: https://github.com/vercel/ms
644
645[human-interval]: https://github.com/agenda/human-interval
646
647[npm]: https://www.npmjs.com/
648
649[yarn]: https://yarnpkg.com/
650
651[workers]: https://nodejs.org/api/worker_threads.html
652
653[lad]: https://lad.js.org
654
655[p-retry]: https://github.com/sindresorhus/p-retry
656
657[p-cancelable]: https://github.com/sindresorhus/p-cancelable
658
659[later]: https://breejs.github.io/later/parsers.html
660
661[cron-validate]: https://github.com/Airfooox/cron-validate
662
663[forward-email]: https://github.com/forwardemail/forwardemail.net
664
665[dayjs]: https://github.com/iamkun/dayjs
666
667[redis]: https://redis.io/
668
669[mongodb]: https://www.mongodb.com/
670
671[lad-graceful]: https://github.com/ladjs/graceful
672
673[cabin]: https://cabinjs.com
674
675[moment]: https://momentjs.com
676
677[ghost]: https://ghost.org/