Gathering detailed insights and metrics for monkey-around
Gathering detailed insights and metrics for monkey-around
Gathering detailed insights and metrics for monkey-around
Gathering detailed insights and metrics for monkey-around
@contrast/patcher
Advanced monkey patching--registers hooks to run in and around functions
primus-headless-cookie
A monkey-patch around http for headless Primus.createSocket to handle sticky sessions & cookies
gorilla-build
Gorilla: Stop monkeying around and build better scripts.
@aps.king/gorilla
Gorilla: Stop monkeying around and build better scripts.
Co-operatively, removably, monkeypatch around JS methods, or serialize their execution
npm install monkey-around
Typescript
Module System
Node Version
NPM Version
72.9
Supply Chain
98.8
Quality
75.4
Maintenance
100
Vulnerability
100
License
JavaScript (54.91%)
TypeScript (45.09%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
54 Stars
8 Commits
2 Forks
3 Watchers
1 Branches
1 Contributors
Updated on Jul 16, 2025
Latest Version
3.0.0
Package Id
monkey-around@3.0.0
Unpacked Size
15.91 kB
Size
4.19 kB
File Count
6
NPM Version
8.19.4
Node Version
16.20.0
Published on
Feb 05, 2024
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
5
monkey-around
Ever needed to monkeypatch a method someplace? It's not hard to do if you're the only one doing it, or if you can leave the patch in place for the life of your program. But it's not quite as easy if multiple independently-written bits of code need to patch the same method(s), and might need to uninstall their patches in arbitrary order.
That's what monkey-around
is for. Let's say you have an object that looks like this:
1// Unwrapped object 2var anObject = { 3 someMethod(...args) { 4 console.log("someMethod", args); 5 return 42; 6 } 7}; 8 9anObject.someMethod(27);
someMethod [ 27 ]
Then you can import the around
function:
1import {around} from 'monkey-around';
And wrap one or more methods like so:
1// Add a wrapper
2var uninstall1 = around(anObject, {
3 someMethod(oldMethod) {
4 return function(...args) {
5 console.log("wrapper 1 before someMethod", args);
6 const result = oldMethod && oldMethod.apply(this, args);
7 console.log("wrapper 1 after someMethod", result);
8 return result;
9 }
10 }
11});
12
13anObject.someMethod(23);
wrapper 1 before someMethod [ 23 ] someMethod [ 23 ] wrapper 1 after someMethod 42
The around()
function takes an object, and an object whose own-methods are factory functions that receive the old method (which may be undefined
) and return a replacement method. Each old method on the original object is then replaced with a wrapper that delegates to the newly-created method. The around()
function then returns an "uninstaller" -- a function that can be called to disable or remove all of the wrappers it created.
The wrapper functions are set up to inherit properties from the newly-created method, which in turn are configured to inherit from the original method(s), so that any properties or methods attached to them will also be visible on the wrappers (and any wrappers added around those wrappers).
Multiple wrappers can be applied to the same method of the same object:
1// Add a second wrapper
2var uninstall2 = around(anObject, {
3 someMethod(oldMethod) {
4 return function(...args) {
5 console.log("wrapper 2 before someMethod", args);
6 const result = oldMethod && oldMethod.apply(this, args);
7 console.log("wrapper 2 after someMethod", result);
8 return result;
9 }
10 }
11});
12
13anObject.someMethod(); // runs both wrappers
wrapper 2 before someMethod [] wrapper 1 before someMethod [] someMethod [] wrapper 1 after someMethod 42 wrapper 2 after someMethod 42
And the uninstallers can be called in any order:
1// Uninstall wrappers 2uninstall1(); // remove the first wrapper 3anObject.someMethod(); // runs only the second wrapper 4uninstall2(); // remove the second wrapper 5anObject.someMethod(); // runs only the original method
wrapper 2 before someMethod [] someMethod [] wrapper 2 after someMethod 42 someMethod []
However, when uninstallation is requested, the wrapper for each method is removed... unless another wrapper has since been added for that method, in which case the wrapper will delegate to the original method instead of the new version. (Until such time as it detects it is once again safe to remove itself entirely.)
1import {dedupe} from 'monkey-around';
When multiple clients may require the same patch of a target library or platform (as is often the case with Obsidian plugins), it may be important to perform the new behavior only once, no matter how many clients are currently active (e.g. to avoid triggering duplicate events). For this purpose, monkey-around
offers a dedupe()
function, that takes a string or symbol as a "key" to ensure that one (and only one) version of the patch is applied. The de-duplication is done at runtime rather than patch time, so that patches can be added or removed at any time and still only one version of the patch will run for a given invocation of the wrapped method.
To use it, you should choose a globally unique string (or Symbol.for()
that string) that will stand for the functionality provided by the patch. Then, return dedupe(key, oldMethod, newMethod)
from your around wrapper method, like this:
1// Add a wrapper
2var demo_key = "demo-wrapper@github.com/pjeby/monkey-around";
3var uninstall1 = around(anObject, {
4 someMethod(oldMethod) {
5 return dedupe(demo_key, oldMethod, function(...args) {
6 console.log("wrapper 1 before someMethod", args);
7 const result = oldMethod && oldMethod.apply(this, args);
8 console.log("wrapper 1 after someMethod", result);
9 return result;
10 });
11 }
12});
13
14anObject.someMethod(23);
wrapper 1 before someMethod [ 23 ] someMethod [ 23 ] wrapper 1 after someMethod 42
As you can see, the first wrapper applied works the same as any other patch. The second, however, does not:
1// Add a second wrapper 2var uninstall2 = around(anObject, { 3 someMethod(oldMethod) { 4 return dedupe(demo_key, oldMethod, function(...args) { 5 console.log("wrapper 2 before someMethod", args); 6 const result = oldMethod && oldMethod.apply(this, args); 7 console.log("wrapper 2 after someMethod", result); 8 return result; 9 }); 10 } 11}); 12 13anObject.someMethod(); // Second wrapper won't be called while first is in place!
wrapper 1 before someMethod [] someMethod [] wrapper 1 after someMethod 42
But if the first wrapper is removed, then the second wrapper will run:
1uninstall1(); 2anObject.someMethod(99); //
wrapper 2 before someMethod [ 99 ] someMethod [ 99 ] wrapper 2 after someMethod 42
Thus, no matter how many independent clients/plugins/etc. apply the "same" patch, only the oldest currently-active such patch will be run: all others will be bypassed in the call chain, so long as only monkey-around
is used to do the patching.
1import {serialize} from 'monkey-around';
Async methods that manipulate the state of an object can sometimes be subject to race conditions if the method can be called (e.g. from an event handler) while another invocation is already occurring. In such cases, it can be desirable to defer the execution of a method until the promise from its previous invocation has settled.
For this purpose, monkey-around
offers a serialize()
function, that takes an async function and returns a new async function that only calls the old function after its most-recent invocation has settled. You can thus use it as a method factory argument to around()
, e.g. around(anObject, {asyncMethod: serialize})
, to prevent re-entry of the method while an invocation is pending.
1function sleep(ms) { return new Promise(res => setTimeout(res, ms)); } 2 3const aService = { 4 async method(msg) { 5 console.log(` before ${msg}`); 6 await sleep(15); 7 console.log(` after ${msg}`); 8 } 9} 10 11async function demo() { 12 console.log("Without serialization:"); 13 aService.method("Without 1"); 14 await sleep(5); 15 aService.method("Without 2"); 16 await sleep(20); 17 18 around(aService, {method: serialize}); 19 console.log(); 20 21 console.log("With serialization:"); 22 aService.method("With 1"); 23 await sleep(5); 24 aService.method("With 2"); 25 await sleep(35); 26 27 console.log(); 28 console.log("Using .after():"); 29 aService.method("After 1"); 30 await aService.method.after(); 31 console.log("done"); 32} 33 34wait(demo())
Without serialization: before Without 1 before Without 2 after Without 1 after Without 2 With serialization: before With 1 after With 1 before With 2 after With 2 Using .after(): before After 1 after After 1 done
The wrapped function returned by serialize()
(and thus the wrapper(s) created by around()
) will have an after()
method that returns a promise that will resolve when there are no in-progress invocations of the wrapped function that were requested beofre after()
was called.
(Note: if you patch a class prototype rather than an instance, this will make invocation of the method serialized across all instances of the class that share that method... which may or may not be what you want! If you want different instances to be able to run that method at the same time, you need to patch each instance instead of patching the prototype. The .after()
method is similarly either shared or not: if you patch the prototype, then .after()
means "after the current invocation of this method on any instance", not "the current invocation on this instance". So, most of the time, you probably want to serialize the instance methods, not prototype methods.)
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
9 existing vulnerabilities detected
Details
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no SAST tool detected
Details
Reason
Found 0/8 approved changesets -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
license file not detected
Details
Reason
branch protection not enabled on development/release branches
Details
Score
Last Scanned on 2025-07-07
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More