Gathering detailed insights and metrics for iplug
Gathering detailed insights and metrics for iplug
Gathering detailed insights and metrics for iplug
Gathering detailed insights and metrics for iplug
npm install iplug
Typescript
Module System
Node Version
NPM Version
TypeScript (57.72%)
JavaScript (42.28%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
3 Stars
74 Commits
1 Watchers
9 Branches
1 Contributors
Updated on Jul 07, 2025
Latest Version
1.0.0-rc1
Package Id
iplug@1.0.0-rc1
Unpacked Size
23.41 kB
Size
7.29 kB
File Count
19
NPM Version
8.19.3
Node Version
18.13.0
Published on
Mar 07, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
The lightest JavaScript plugin system for the Map/Reduce World
1npm install --save iplug
Extend your JavaScript application with declarative plugins
Your application loads your plugins and creates one or more message buses to communicate with them through a number of topics. Plugins can register their interest to any number of topics within the scope of a message bus. There can be multiple message buses in your application. When you want to defer control to your plugins you emit a message on a topic/channel and all registered plugins will be invoked, either sequentially or in parallel.
1// main.js 2import iplug from 'iplug' 3 4import module1 from './plugins/module1.js' 5import module2 from './plugins/module2.js' 6 7const modules = { 8 module1, 9 module2, 10} 11 12const plugins = iplug(modules) 13 14function main() { 15 const input = 'test payload' 16 const output = plugins.serial('test:message', input) 17 console.log(output) 18} 19 20main()
Each plugin module can export either: A "manifest object":
1export default { 2 'message': data => doSomethingWith(data), 3 // ... 4}
Or a function that takes (messagebus, config) and returns a manifest object
1export default (messagebus: IPlugMessagebus, config: IPlugConfig): PluginManifest => { 2 // keep state here for better testability 3 4 return { 5 'message': data => doSomethingWith(data), 6 // ... 7 } 8}
A manifest object is one whose keys are messagebus topics (typically strings) we want to register to and corresponding handler functions
example-plugin.js
1export default { 2 'category1:message1': data => 3 `result for a call to category1:message1 - payload: ${data}`, 4 5 'category2:message2': data => 6 `result for a call to category1:message1 - payload: ${data}`, 7}
There are two ways you can call plugins: in sequence, or in parallel.
Calling plugins in sequence means starting with an optional initial value, and chaining it through each, so the output of one becomes the input of the next, and the final result is the output of the last. This is useful when your plugins can be somewhat aware of each-other and the output of any may be the final one.
1// main.js 2// this returns the output of the last plugin in the chain 3plugins.serial(<message> [, initial data]) 4plugins.reduce(<message> [, initial data]) 5plugins(<message> [, initial data])
If no plugins are registered for a topic, emitting a message returns the initial data, if provided, undefined otherwise.
1// main.js 2// no plugin handles 'empty topic' 3plugins.serial('empty topic', 'default data') 4 5// returns 'default data'
You can call plugins in parallel, sync or async ones. Initial data will be passed to each as an argument.
1// main.js 2// this returns an array of your plugins' output 3plugins.map(<message> [, initial data]) 4plugins.parallel(<message> [, initial data])
Occasionally, you may need to only allow one plugin to handle a topic
1// main.js 2// this only runs at most one plugin and returns its output. If more than one are registered, only the first is run, the others are ignored. 3// If no handler is registered, the initial data is returned 4plugins.one(<message> [, initial data])
You want to use plugins to moderate content before rendering it, by passing it through a number of plugins, each of which has to approve the content.
1// module.js 2import {moderation} from './moderation.js 3const config = { 4 moderation: { enabled: true }, 5}; 6 7const plugins = await iplug({moderation}, config); 8 9const initialData = await fetch('/api/getMessages').then(x=>x.json()); 10const result = plugins('moderate', data);
1// moderation.js 2export default async () => { 3 const blackList = await fetch('/word-blacklist') 4 .then(x=>x.json()); 5 6 return { 7 'moderate': data => data.map(str => 8 blackList.forEach(word => 9 str.replace(word, '###redacted###') 10 ) 11 ) 12 } 13};
You may want each plugin to emit more data over time, effectively exposing an Observable interface back to the main application
1// plugin.js 2import { Observable } from 'rxjs' 3const sourceStream = Observable(...); 4 5export default { 6 'getdata': data => sourceStream, 7}
1// app.js 2import { merge } from 'rxjs'; 3 4// Get an Observable from each plugin. 5const plugins = await iplug({}); 6const streams = streamingPlugins.map('getdata'); 7merge(streams).pipe( 8 subscribe(doSomething) 9);
You can pass an observable to each of your plugins and get one back to enable two-way communication over time
1// echo.js 2import { map } from 'rxjs' 3 4export default { 5 'duplex': ({ inputStream }) => inputStream.pipe( 6 map(inputMessage=>`This is a reply to ${inputMessage}.`) 7 ), 8}
1// app.js 2import { Subject, merge } from 'rxjs' 3 4const outputStream = new Subject(); 5 6const allStreams = merge(streamingPlugins.pipe( 7 map('duplex', outputStream) 8)); 9 10allStreams.subscribe(doSomething); 11
Writing unit tests for your plugins should be just as simple as calling the function they export for a particular event/topic.
1import module from '/plugins/double-it.js' 2 3describe('plugin1', () => { 4 describe('when handling a "test:topic"', () => { 5 6 it('doubles its input', () => { 7 const plugin = module(); 8 const fn = plugin['test:topic'](); 9 expect(fn(2)).toEqual(4); 10 }); 11 12 }); 13});
Following is an example unit test for a hypothetical plugin that returns 0, written for Jest, but easily adaptable to other test frameworks.
1import module from '/plugins/plugin2.js' 2import fixture from '/plugins/plugin2.fixture.js' 3 4describe('plugin2', () => { 5 describe('when handling a "test:event"', () => { 6 7 it('returns 0', () => { 8 const plugin = module(); 9 const fn = plugin['test:event'](); 10 const result = fn(fixture); 11 expect(result).toEqual(0); 12 }); 13 14 }); 15});
Global variables, even inside an ES6 module, can pose various challenges to unit testing. Consider the follwing plugin:
1const globalState = get_some_state(); 2 3export default { 4 'message:handler': config => data => globalState(data), 5}
The problem here is sometimes it can be hard for test frameworks to mock or stub globalState
in order to force a certain behaviour.
What you may experience is the first time a unit test runs, the globalState may be mocked as expected, but at subsequent runs, re-mocking or re-stubbing may just not work, failing the tests.
A solution to this problem is creating a plugin that exports a function, which in turn will return everything else.
1export default function() { 2 const globalState = get_some_state(); 3 4 return { 5 'message:handler': config => data => globalState(data), 6 } 7}
This way, no global state will remain between test runs.
1import initModule from '/plugins/plugin.js' 2 3describe('plugin', () => { 4 describe('when handling a "test:event"', () => { 5 6 it('returns 0', () => { 7 // Loading the plugin from a test will need this one extra line 8 const plugin = initModule(); 9 10 const fn = plugin['test:event'](); 11 const result = fn(fixture); 12 expect(result).toEqual(0); 13 }); 14 15 }); 16});
You can find more examples in the respective folder
No vulnerabilities found.
No security vulnerabilities found.