diff --git a/README.md b/README.md index 8f085f618..d458574f8 100644 --- a/README.md +++ b/README.md @@ -53,13 +53,13 @@ module.exports.pipe = function(cont, context, action) { action.logger.log("debug", "Constructing Custom Pipeline"); return pipeline() - .before(adjustContent) - .once(cont) // required: execute the continuation function - .after(cleanupContent) + .use(adjustContent) + .use(cont) // execute the continuation function + .use(cleanupContent) } ``` -In a typical pipeline, you will add additional processing steps as `.before(require('some-module'))` or as `.after(require('some-module'))`. +In a typical pipeline, you will add additional processing steps as `.use(require('some-module'))`. ### The Main Function @@ -138,11 +138,11 @@ Example: ```js new pipeline() - .before(doSomething) - .once(render) - .after(cleanup) + .use(doSomething) + .use(render) + .use(cleanup) .error(handleError) - .after(done); + .use(done); ``` If in the above example, the `doSomething` causes an error, subsequently, `render` and `cleanup` will not be invoked. but `handleError` will. If `handleError` clears the error state (i.e. sets `context.error = null`), the `done` function will be invoked again. @@ -151,16 +151,16 @@ If in the above example, none of the functions causes an error, the `handleError ### Extension Points -In addition to the (optional) wrapper function which can be invoked prior to the `once` function, pipeline creators can expose named extension points. These extension points allow users of a pipeline to inject additional functions that will be called right before or right after an extension point. To keep the extension points independent from the implementation (i.e. the name of the function), pipeline authors should use the `expose(name)` function to expose a particular extension point. +In addition to the (optional) wrapper function which can be invoked prior to the `once` function, pipeline creators can expose named extension points. These extension points allow users of a pipeline to inject additional functions that will be called right before, right after or instead of an extension point. To keep the extension points independent from the implementation (i.e. the name of the function), pipeline authors should use the `expose(name)` function to expose a particular extension point. Example: ```js new pipeline() - .before(doSomething).expose('init') - .once(render) - .after(cleanup).expose('cleanup') - .after(done); + .use(doSomething).expose('init') + .use(render) + .use(cleanup).expose('cleanup') + .use(done); ``` In this example, two extension points, `init` and `cleanup` have been defined. Note how the name of the extension point can be the same as the name of the function (i.e. `cleanup`), but does not have to be the same (i.e. `init` vs. `doSomething`). @@ -181,6 +181,7 @@ The easiest way to use extension points is by expanding on the [Wrapper Function - a `before` object - an `after` object +- a `replace` object Each of these objects can have keys that correspond to the named extension points defined for the pipeline. @@ -198,6 +199,12 @@ module.exports.after = { // will get called after the "fetch" pipeline step } } + +module.exports.replace = { + meta: (context, action) => { + // will get called instead of the "meta" pipeline step + } +} ``` All functions that are using the `before` and `after` extension points need to follow the same interface that all other pipeline functions follow, i.e. they have access to `context` and `action` and they should return a modified `context` object. diff --git a/src/defaults/default.js b/src/defaults/default.js index 48a5784cb..d41d39fcf 100644 --- a/src/defaults/default.js +++ b/src/defaults/default.js @@ -24,7 +24,7 @@ const Pipeline = require('../pipeline.js'); */ function pipe(next, context, action) { const mypipeline = new Pipeline(action); - mypipeline.once(next); + mypipeline.use(next); return mypipeline.run(context); } diff --git a/src/defaults/html.pipe.js b/src/defaults/html.pipe.js index 737de0c84..6bd4a4ad3 100644 --- a/src/defaults/html.pipe.js +++ b/src/defaults/html.pipe.js @@ -59,31 +59,31 @@ const htmlpipe = (cont, context, action) => { .every(dump.record) .every(validate).when(() => !production()) .every(timer.update) - .before(resolveRef).expose('resolve').when(hascontent) - .before(fetch).expose('fetch').when(hascontent) - .before(parse).expose('parse') - .before(parseFrontmatter) - .before(embeds) - .before(smartypants) - .before(sections) - .before(meta).expose('meta') - .before(unwrapSoleImages) - .before(selectstrain) - .before(selecttest) - .before(html).expose('html') - .before(sanitize).when(paranoid) - .once(cont) - .after(type('text/html')) - .after(cache).when(uncached) - .after(key) - .after(tovdom).expose('post') // start HTML post-processing - .after(removeHlxProps).when(() => production()) - .after(rewriteLinks).when(production) - .after(addHeaders) - .after(tohtml) // end HTML post-processing - .after(flag).expose('esi').when(esi) // flag ESI when there is ESI in the response - .after(debug) - .after(timer.report) + .use(resolveRef).expose('resolve').when(hascontent) + .use(fetch).expose('fetch').when(hascontent) + .use(parse).expose('parse') + .use(parseFrontmatter) + .use(embeds) + .use(smartypants) + .use(sections) + .use(meta).expose('meta') + .use(unwrapSoleImages) + .use(selectstrain) + .use(selecttest) + .use(html).expose('html') + .use(sanitize).when(paranoid) + .use(cont) + .use(type('text/html')) + .use(cache).when(uncached) + .use(key) + .use(tovdom).expose('post') // start HTML post-processing + .use(removeHlxProps).when(() => production()) + .use(rewriteLinks).when(production) + .use(addHeaders) + .use(tohtml) // end HTML post-processing + .use(flag).expose('esi').when(esi) // flag ESI when there is ESI in the response + .use(debug) + .use(timer.report) .error(dump.report) .error(selectStatus); diff --git a/src/defaults/json.pipe.js b/src/defaults/json.pipe.js index 5d0a9a55f..18df90503 100644 --- a/src/defaults/json.pipe.js +++ b/src/defaults/json.pipe.js @@ -37,16 +37,16 @@ const jsonpipe = (cont, context, action) => { .every(dump.record) .every(validate).when(() => !production()) .every(timer.update) - .before(fetch).expose('fetch') - .before(parse).expose('parse') - .before(parseFrontmatter) - .before(smartypants) - .before(sections) - .before(meta).expose('meta') - .once(cont) - .after(emit).expose('json') - .after(type('application/json')) - .after(timer.report) + .use(fetch).expose('fetch') + .use(parse).expose('parse') + .use(parseFrontmatter) + .use(smartypants) + .use(sections) + .use(meta).expose('meta') + .use(cont) + .use(emit).expose('json') + .use(type('application/json')) + .use(timer.report) .error(dump.report) .error(selectStatus(production())); diff --git a/src/defaults/xml.pipe.js b/src/defaults/xml.pipe.js index 73bb074cc..0b332412f 100644 --- a/src/defaults/xml.pipe.js +++ b/src/defaults/xml.pipe.js @@ -41,21 +41,20 @@ const xmlpipe = (cont, context, action) => { .every(dump.record) .every(validate).when(() => !production()) .every(timer.update) - .before(fetch).expose('fetch') - .before(parse).expose('parse') - .before(parseFrontmatter) - .before(smartypants) - .before(sections) - .before(meta).expose('meta') - .once(cont) - .after(emit).expose('xml') - .after(type('application/xml')) - .after(check) - .after(cache) - .when(uncached) - .after(key) - .after(flag).expose('esi').when(esi) // flag ESI when there is ESI in the response - .after(timer.report) + .use(fetch).expose('fetch') + .use(parse).expose('parse') + .use(parseFrontmatter) + .use(smartypants) + .use(sections) + .use(meta).expose('meta') + .use(cont) + .use(emit).expose('xml') + .use(type('application/xml')) + .use(check) + .use(cache).when(uncached) + .use(key) + .use(flag).expose('esi').when(esi) // flag ESI when there is ESI in the response + .use(timer.report) .error(dump.report) .error(selectStatus); diff --git a/src/pipeline.js b/src/pipeline.js index a72cdf2b3..3e606ef0b 100644 --- a/src/pipeline.js +++ b/src/pipeline.js @@ -69,10 +69,8 @@ function errorWrapper(fn) { */ /** - * Pipeline that allows to execute a list of functions in order. The pipeline consists of 3 - * major function lists: `pre`, `once` and, `post`. the functions added to the `pre` list are - * processed first, then the `once` function and finally the `post` functions. - * Using `when` and `unless` allows to conditionally execute the previously define function. + * Pipeline that allows to execute a list of functions in the order they have been added. + * Using `when` and `unless` allows to conditionally execute the previously defined function. * @class */ class Pipeline { @@ -90,53 +88,39 @@ class Pipeline { // function chain that was defined last. used for `when` and `unless` this._last = null; - // functions that are executed first - this._pres = []; - // function that is executed once - this._oncef = null; - // functions that are executed after - this._posts = []; + // object with the custom functions to attach to the pipeline + this._attachments = null; + // step functions to execute + this._steps = []; // functions that are executed before each step this._taps = []; - this.attach = (ext) => { - if (this.sealed) { - return; - } - if (ext && ext.before && typeof ext.before === 'object') { - Object.keys(ext.before).map((key) => this.attach.before(key, ext.before[key])); - } - if (ext && ext.after && typeof ext.after === 'object') { - Object.keys(ext.after).map((key) => this.attach.after(key, ext.after[key])); - } - this.sealed = true; - }; - /** * Registers an extension to the pipeline. * @param {String} name - name of the extension point (typically the function name). * @param {pipelineFunction} f - a new pipeline step that will be injected relative to `name`. - * @param {integer} before - where to insert the new function (true = before, false = after) + * @param {integer} offset - where to insert the new function (-1: before, 0: replace, 1: after) */ - const attachGeneric = (name, f, before) => { - const offset = before ? 0 : 1; + const attachGeneric = (name, f, offset) => { // find the index of the function where the resolved ext name - // matches the provided name by searching the list of pre and - // post functions - const foundpres = this._pres - .findIndex((pre) => pre && pre.ext && pre.ext === name); - const foundposts = this._posts - .findIndex((post) => post && post.ext && post.ext === name); - - // if something has been found in either lists, insert the + // matches the provided name by searching the list of step functions + const foundstep = this._steps + .findIndex((step) => step && step.ext && step.ext === name); + + // if something has been found in the list, insert the // new function into the list, with the correct offset - if (foundpres !== -1) { - this._pres.splice(foundpres + offset, 0, f); - } - if (foundposts !== -1) { - this._posts.splice(foundposts + offset, 0, f); - } - if (foundpres === -1 && foundposts === -1) { + if (foundstep !== -1) { + if (offset === 0) { + // replace + this._steps.splice(foundstep, 1, f); + } else if (offset > 0) { + // insert after + this._steps.splice(foundstep + 1, 0, f); + } else { + // insert before (default) + this._steps.splice(foundstep, 0, f); + } + } else { this._action.logger.warn(`Unknown extension point ${name}`); } }; @@ -147,7 +131,7 @@ class Pipeline { * @param {String} name - name of the extension point (typically the function name). * @param {pipelineFunction} f - a new pipeline step that will be injected relative to `name`. */ - this.attach.before = (name, f) => attachGeneric.bind(this)(name, f, true); + this.attach.before = (name, f) => attachGeneric.bind(this)(name, f, -1); /** * Registers an extension to the pipeline. The function `f` will be run in * the pipeline after the function called `name` will be executed. If `name` @@ -155,30 +139,33 @@ class Pipeline { * @param {String} name - name of the extension point (typically the function name). * @param {pipelineFunction} f - a new pipeline step that will be injected relative to `name`. */ - this.attach.after = (name, f) => attachGeneric.bind(this)(name, f, false); - } - - /** - * Adds a processing function to the `pre` list to this pipeline. - * @param {pipelineFunction} f function to add to the `post` list - * @returns {Pipeline} this - */ - before(f) { - this.describe(f); - this._pres.push(f); - this._last = this._pres; - return this; + this.attach.after = (name, f) => attachGeneric.bind(this)(name, f, 1); + /** + * Registers an extension to the pipeline. The function `f` will be executed in + * the pipeline instead of the function called `name`. If `name` does not exist, + * `f` will never be executed. + * @param {String} name - name of the extension point (typically the function name). + * @param {pipelineFunction} f - a new pipeline step that will replace `name`. + */ + this.attach.replace = (name, f) => attachGeneric.bind(this)(name, f, 0); } /** - * Adds a processing function to the `pre` list to this pipeline. - * @param {pipelineFunction} f function to add to the `post` list + * Adds a processing function to the `step` list of this pipeline. + * @param {pipelineFunction} f function to add to the `step` list * @returns {Pipeline} this */ - after(f) { + use(f) { this.describe(f); - this._posts.push(f); - this._last = this._posts; + this._steps.push(f); + this._last = this._steps; + // check for extensions + if (f && (f.before || f.replace || f.after)) { + if (typeof this._attachments === 'function') { + throw new Error(`Step '${this._attachments.alias}' already registered extensions for this pipeline, refusing to add more with '${f.alias}'.`); + } + this._attachments = f; + } return this; } @@ -204,7 +191,7 @@ class Pipeline { } /** - * Adds a condition to the previously defined `pre` or `post` function. The previously defined + * Adds a condition to the previously defined `step` function. The previously defined * function will only be executed if the predicate evaluates to something truthy or returns a * Promise that resolves to something truthy. * @param {function(context)} predicate Predicate function. @@ -241,7 +228,7 @@ class Pipeline { } /** - * Adds a condition to the previously defined `pre` or `post` function. The previously defined + * Adds a condition to the previously defined `step` function. The previously defined * function will only be executed if the predicate evaluates to something not-truthy or returns a * Promise that resolves to something not-truthy. * @param {function(context)} predicate Predicate function. @@ -254,15 +241,45 @@ class Pipeline { } /** - * Sets the `once` processing function. - * @param {pipelineFunction} f the `once` function to set - * @returns {Pipeline} this + * Attaches custom extensions to the pipeline. The expected argument is an object + * with a before object, an after object, and/or + * a replace object as its properties. + * Each of these objects can have keys that correspond to the named extension points + * defined for the pipeline, with the function to execute as values. For example: + *
+   * {
+   *   before: {
+   *     fetch: (context, action) => {
+   *       // do something before the fetch step
+   *       return context;
+   *     }
+   *   }
+   *   replace: {
+   *     html: (context, action) => {
+   *       // do this instead of the default html step
+   *       return context;
+   *     }
+   *   }
+   *   after: {
+   *     meta: (context, action) => {
+   *       // do something after the meta step
+   *       return context;
+   *     }
+   *   }
+   * }
+   * 
+ * @param {Object} att The object containing the attachments */ - once(f) { - this.describe(f); - this._oncef = f; - this._last = this._posts; - return this; + attach(att) { + if (att && att.before && typeof att.before === 'object') { + Object.keys(att.before).map((key) => this.attach.before(key, att.before[key])); + } + if (att && att.after && typeof att.after === 'object') { + Object.keys(att.after).map((key) => this.attach.after(key, att.after[key])); + } + if (att && att.replace && typeof att.replace === 'object') { + Object.keys(att.replace).map((key) => this.attach.replace(key, att.replace[key])); + } } /** @@ -305,7 +322,7 @@ class Pipeline { } /** - * Runs the pipline processor be executing the `pre`, `once`, and `post` functions in order. + * Runs the pipline processor be executing the `step` functions in order. * @param {Context} context Pipeline context * @returns {Promise} Promise that resolves to the final result of the accumulated * context. @@ -314,7 +331,7 @@ class Pipeline { const { logger } = this._action; // register all custom attachers to the pipeline - this.attach(this._oncef); + this.attach(this._attachments); /** * Executes the taps of the current function. @@ -378,7 +395,7 @@ class Pipeline { }; try { - await execFns([...this._pres, this._oncef, ...this._posts]); + await execFns(this._steps); } catch (e) { logger.error(`Unexpected error during pipeline execution: \n${e.stack}`); if (!context.error) { diff --git a/test/testPipeline.js b/test/testPipeline.js index fe126d53e..a5ec0acfa 100644 --- a/test/testPipeline.js +++ b/test/testPipeline.js @@ -26,17 +26,17 @@ describe('Testing Pipeline', () => { }); it('Executes without logger', async () => { - await new Pipeline().once(() => {}).run({}); + await new Pipeline().use(() => {}).run({}); }); it('Executes correct order', async () => { const order = []; await new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .after(() => { order.push('post0'); }) - .before(() => { order.push('pre1'); }) - .after(() => { order.push('post1'); }) - .once(() => { order.push('once'); }) + .use(() => { order.push('pre0'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .run(); assert.deepEqual(order, ['pre0', 'pre1', 'once', 'post0', 'post1']); }); @@ -44,11 +44,11 @@ describe('Testing Pipeline', () => { it('Can be run twice', async () => { const order = []; const pipe = new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .after(() => { order.push('post0'); }) - .before(() => { order.push('pre1'); }) - .after(() => { order.push('post1'); }) - .once(() => { order.push('once'); }); + .use(() => { order.push('pre0'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }); await pipe.run(); assert.deepEqual(order, ['pre0', 'pre1', 'once', 'post0', 'post1']); @@ -76,26 +76,31 @@ describe('Testing Pipeline', () => { order.push('four'); }; + const newFourth = function newFourth() { + order.push('4'); + }; + // inject explicit extension points [first, second, third, fourth].forEach((f) => { f.ext = f.name; }); const pipe = new Pipeline({ logger }) - .before(second) - .once(() => { + .use(second) + .use(() => { order.push('middle'); }) - .after(third); + .use(third); pipe.attach.before('second', first); pipe.attach.after('third', fourth); + pipe.attach.replace('fourth', newFourth); await pipe.run(); - assert.deepStrictEqual(order, ['one', 'two', 'middle', 'three', 'four']); + assert.deepStrictEqual(order, ['one', 'two', 'middle', 'three', '4']); }); - it('Can be extended using shorthand syntax', async () => { + it('Can be extended (once) using shorthand syntax', async () => { const order = []; const first = function first() { @@ -114,6 +119,10 @@ describe('Testing Pipeline', () => { order.push('four'); }; + const newFourth = function newFourth() { + order.push('4'); + }; + // inject explicit extension points [first, second, third, fourth].forEach((f) => { f.ext = f.name; @@ -131,13 +140,26 @@ describe('Testing Pipeline', () => { third: fourth, }; - const pipe = new Pipeline({ logger }) - .before(second) - .once(middle) - .after(third); + middle.replace = { + fourth: newFourth, + }; - await pipe.run(); - assert.deepStrictEqual(order, ['one', 'two', 'middle', 'three', 'four']); + const pipe1 = new Pipeline({ logger }) + .use(second) + .use(middle) + .use(third); + + await pipe1.run(); + assert.deepStrictEqual(order, ['one', 'two', 'middle', 'three', '4']); + + const pipe2 = new Pipeline({ logger }); + pipe2.use(middle); + try { + pipe2.use(middle); + assert.fail('only one step function can add extensions'); + } catch (e) { + // ignore expected error + } }); it('Logs correct names', async () => { @@ -158,21 +180,21 @@ describe('Testing Pipeline', () => { silly(msg, obj) { counter += 1; if (counter === 1) { - assert.ok(obj.function.match(/^before:pre0/)); + assert.ok(obj.function.match(/^use:pre0/)); } if (counter === 2) { - assert.ok(obj.function.match(/^once:anonymous/)); + assert.ok(obj.function.match(/^use:anonymous/)); } if (counter === 3) { - assert.ok(obj.function.match(/^after:post0/)); + assert.ok(obj.function.match(/^use:post0/)); } }, }; await new Pipeline({ logger: validatinglogger }) - .before(pre0) - .after(post0) - .once(() => { order.push('once'); }) + .use(pre0) + .use(() => { order.push('once'); }) + .use(post0) .run(); assert.deepEqual(order, ['pre0', 'once', 'post0']); }); @@ -180,13 +202,13 @@ describe('Testing Pipeline', () => { it('Disables pre before when', (done) => { const order = []; new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .before(() => { order.push('disabled'); }) + .use(() => { order.push('pre0'); }) + .use(() => { order.push('disabled'); }) .when(() => false) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .run() .then(() => { assert.deepEqual(order, ['pre0', 'pre1', 'once', 'post0', 'post1']); @@ -198,13 +220,13 @@ describe('Testing Pipeline', () => { it('Disables pre before when conditionally', (done) => { const order = []; new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .before(() => { order.push('enabled'); }) + .use(() => { order.push('pre0'); }) + .use(() => { order.push('enabled'); }) .when(() => true) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .run() .then(() => { assert.deepEqual(order, ['pre0', 'enabled', 'pre1', 'once', 'post0', 'post1']); @@ -216,13 +238,13 @@ describe('Testing Pipeline', () => { it('When works with promises resolving false pre before when', (done) => { const order = []; new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .before(() => { order.push('disabled'); }) + .use(() => { order.push('pre0'); }) + .use(() => { order.push('disabled'); }) .when(() => Promise.resolve(false)) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .run() .then(() => { assert.deepEqual(order, ['pre0', 'pre1', 'once', 'post0', 'post1']); @@ -235,13 +257,13 @@ describe('Testing Pipeline', () => { it('When works with promises resolving true pre before when', (done) => { const order = []; new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .before(() => { order.push('enabled'); }) + .use(() => { order.push('pre0'); }) + .use(() => { order.push('enabled'); }) .when(() => Promise.resolve(true)) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .run() .then(() => { assert.deepEqual(order, ['pre0', 'enabled', 'pre1', 'once', 'post0', 'post1']); @@ -250,16 +272,16 @@ describe('Testing Pipeline', () => { .catch(done); }); - it('Disables post before when', (done) => { + it('Disables step before when', (done) => { const order = []; new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('disabled'); }) + .use(() => { order.push('pre0'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('disabled'); }) .when(() => false) - .after(() => { order.push('post1'); }) + .use(() => { order.push('post1'); }) .run() .then(() => { assert.deepEqual(order, ['pre0', 'pre1', 'once', 'post0', 'post1']); @@ -268,22 +290,20 @@ describe('Testing Pipeline', () => { .catch(done); }); - it('when after once throws error', (done) => { + it('when after once throws error', () => { try { const order = []; new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .once(() => { order.push('once'); }) + .use(() => { order.push('pre0'); }) + .use(() => { order.push('once'); }) .when(() => false) - .after(() => { order.push('post1'); }) + .use(() => { order.push('post1'); }) .run() .then(() => { assert.fail('when after once should fail.'); - done(); }); } catch (err) { assert.equal(err.toString(), 'Error: when() needs function to operate on.'); - done(); } }); @@ -292,7 +312,7 @@ describe('Testing Pipeline', () => { const order = []; new Pipeline({ logger }) .when(() => false) - .after(() => { order.push('post1'); }) + .use(() => { order.push('post1'); }) .run() .then(() => { assert.fail('when after once should fail.'); @@ -307,13 +327,13 @@ describe('Testing Pipeline', () => { it('Disables pre before unless', (done) => { const order = []; new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .before(() => { order.push('disabled'); }) + .use(() => { order.push('pre0'); }) + .use(() => { order.push('disabled'); }) .unless(() => true) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .run() .then(() => { assert.deepEqual(order, ['pre0', 'pre1', 'once', 'post0', 'post1']); @@ -325,13 +345,13 @@ describe('Testing Pipeline', () => { it('Enables pre before when', (done) => { const order = []; new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .before(() => { order.push('enabled'); }) + .use(() => { order.push('pre0'); }) + .use(() => { order.push('enabled'); }) .when(() => true) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .run() .then(() => { assert.deepEqual(order, ['pre0', 'enabled', 'pre1', 'once', 'post0', 'post1']); @@ -343,13 +363,13 @@ describe('Testing Pipeline', () => { it('Enables pre before unless', (done) => { const order = []; new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .before(() => { order.push('enabled'); }) + .use(() => { order.push('pre0'); }) + .use(() => { order.push('enabled'); }) .unless(() => false) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .run() .then(() => { assert.deepEqual(order, ['pre0', 'enabled', 'pre1', 'once', 'post0', 'post1']); @@ -360,12 +380,12 @@ describe('Testing Pipeline', () => { it('Executes promises', async () => { await new Pipeline({ logger }) - .once((v) => new Promise((resolve) => { + .use((v) => new Promise((resolve) => { setTimeout(() => { v.foo = 'bar'; resolve(); }, 0.05); - })).after((v) => { + })).use((v) => { assert.equal(v.foo, 'bar'); }).run(); }); @@ -373,9 +393,9 @@ describe('Testing Pipeline', () => { it('Executes taps', async () => { let cnt = 0; await new Pipeline({ logger }) - .before(() => {}) - .once(() => {}) - .after(() => {}) + .use(() => {}) + .use(() => {}) + .use(() => {}) .every(() => { cnt += 1; }) @@ -386,9 +406,9 @@ describe('Testing Pipeline', () => { it('Does not executes taps when conditions fail', async () => { let cnt = 0; await new Pipeline({ logger }) - .before(() => ({ foo: 'bar' })) - .once(() => {}) - .after(() => ({ bar: 'baz' })) + .use(() => ({ foo: 'bar' })) + .use(() => {}) + .use(() => ({ bar: 'baz' })) .every(() => { assert.fail('this should not be invoked'); }) @@ -404,11 +424,11 @@ describe('Testing Pipeline', () => { it('Ignore error if no error', (done) => { const order = []; new Pipeline({ logger }) - .before(() => { order.push('pre0'); }) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('pre0'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .error(() => { order.push('error'); }) .run() .then(() => { @@ -421,14 +441,14 @@ describe('Testing Pipeline', () => { it('skip functions if context.error', (done) => { const order = []; new Pipeline({ logger }) - .before((ctx) => { + .use((ctx) => { order.push('pre0'); ctx.error = new Error(); }) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .error(() => { order.push('error'); }) .run() .then(() => { @@ -441,14 +461,14 @@ describe('Testing Pipeline', () => { it('skip functions if exception', (done) => { const order = []; new Pipeline({ logger }) - .before(() => { + .use(() => { order.push('pre0'); throw new Error('stop'); }) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .error(() => { order.push('error'); }) .run() .then(() => { @@ -461,18 +481,18 @@ describe('Testing Pipeline', () => { it('error handler can clear error', (done) => { const order = []; new Pipeline({ logger }) - .before(() => { + .use(() => { order.push('pre0'); throw new Error('stop'); }) - .before(() => { order.push('pre1'); }) + .use(() => { order.push('pre1'); }) .error((ctx) => { order.push('error0'); ctx.error = null; }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .error(() => { order.push('error1'); }) .run() .then(() => { @@ -485,14 +505,14 @@ describe('Testing Pipeline', () => { it('handles error in error', async () => { const order = []; await new Pipeline({ logger }) - .before(() => { + .use(() => { order.push('pre0'); throw new Error('stop'); }) - .before(() => { order.push('pre1'); }) - .once(() => { order.push('once'); }) - .after(() => { order.push('post0'); }) - .after(() => { order.push('post1'); }) + .use(() => { order.push('pre1'); }) + .use(() => { order.push('once'); }) + .use(() => { order.push('post0'); }) + .use(() => { order.push('post1'); }) .error(() => { throw Error('in error handler'); }) .run(); const output = await logger.getOutput(); @@ -503,8 +523,8 @@ describe('Testing Pipeline', () => { it('handles generic pipeline error', async () => { const order = []; await new Pipeline({ logger }) - .before(() => { order.push('pre1'); }) - .once({ + .use(() => { order.push('pre1'); }) + .use({ get errorHandler() { throw new Error('generic error'); },