From 20b58ddcb5fbdb797fb2ae478551e013fd2c789f Mon Sep 17 00:00:00 2001 From: KernelDeimos Date: Thu, 25 Jul 2024 15:32:16 -0400 Subject: [PATCH] dev: add Invoker --- src/puter-js-common/src/libs/invoker.js | 53 +++++++++++++++++++++++++ src/puter-js-common/test/test.js | 38 ++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/puter-js-common/src/libs/invoker.js diff --git a/src/puter-js-common/src/libs/invoker.js b/src/puter-js-common/src/libs/invoker.js new file mode 100644 index 00000000..3a2ebd4a --- /dev/null +++ b/src/puter-js-common/src/libs/invoker.js @@ -0,0 +1,53 @@ +const { AdvancedBase } = require("../.."); + +class Invoker extends AdvancedBase { + static create ({ + decorators, + delegate, + }) { + const invoker = new Invoker(); + invoker.decorators = decorators; + invoker.delegate = delegate; + return invoker; + } + async run (args) { + let fn = this.delegate; + const decorators = this.decorators; + for ( let i = decorators.length-1 ; i >= 0 ; i-- ) { + const dec = decorators[i]; + fn = this.add_dec_(dec, fn); + } + return await fn(args); + } + add_dec_ (dec, fn) { + return async (args) => { + try { + if ( dec.on_call ) { + args = await dec.on_call(args); + } + let result = await fn(args); + if ( dec.on_return ) { + result = await dec.on_return(result); + } + return result; + } catch (e) { + if ( ! dec.on_error ) throw e; + + let cancel = false; + const a = { + error () { return e }, + cancel_error () { cancel = true; }, + }; + const result = await dec.on_error(a); + if ( cancel ) { + return result; + } + throw result ?? e; + } + } + } +} + +module.exports = { + Invoker, +}; diff --git a/src/puter-js-common/test/test.js b/src/puter-js-common/test/test.js index 34a36f4d..e5f2bb00 100644 --- a/src/puter-js-common/test/test.js +++ b/src/puter-js-common/test/test.js @@ -19,6 +19,7 @@ const { expect } = require('chai'); const { BasicBase } = require('../src/bases/BasicBase'); const { AdvancedBase } = require('../src/AdvancedBase'); +const { Invoker } = require('../src/libs/invoker'); class ClassA extends BasicBase { static STATIC_OBJ = { @@ -70,3 +71,40 @@ describe('AdvancedBase', () => { }); }); +describe('lib:invoker', () => { + it('works', async () => { + const invoker = Invoker.create({ + decorators: [ + { + name: 'uphill both ways', + on_call: (args) => { + return { + ...args, + n: args.n + 1, + }; + }, + on_return: (result) => { + return { + n: result.n + 1, + }; + }, + }, + { + name: 'error number five', + on_error: a => { + a.cancel_error(); + return { n: 5 }; + }, + } + ], + async delegate (args) { + const { n } = args; + if ( n === 3 ) { + throw new Error('test error'); + } + return { n: 'oops' }; + } + }); + expect(await invoker.run({ n: 2 })).to.deep.equal({ n: 6 }); + }); +});