
Research
Security News
Malicious PyPI Package Exploits Deezer API for Coordinated Music Piracy
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
continuation-local-storage
Advanced tools
userland implementation of https://github.com/joyent/node/issues/5243
The continuation-local-storage (CLS) npm package provides a way to maintain context across asynchronous calls in Node.js. It allows you to create a namespace to store data that is accessible across different asynchronous operations, which is useful for tasks like logging, error tracking, and request tracing.
Creating a Namespace
This feature allows you to create a namespace where you can store and retrieve data across asynchronous calls.
const cls = require('continuation-local-storage');
const namespace = cls.createNamespace('myNamespace');
Setting and Getting Data
Within a namespace, you can set and get data that will be available throughout the asynchronous call chain.
namespace.run(() => {
namespace.set('key', 'value');
console.log(namespace.get('key')); // Outputs: value
});
Using with Asynchronous Functions
This feature demonstrates how to use CLS with asynchronous functions, ensuring that the context is maintained across asynchronous boundaries.
const asyncFunction = async () => {
namespace.set('key', 'value');
await new Promise(resolve => setTimeout(resolve, 1000));
console.log(namespace.get('key')); // Outputs: value
};
namespace.run(() => {
asyncFunction();
});
The async_hooks module is a core Node.js module that provides an API to track asynchronous resources. It is more low-level compared to continuation-local-storage and offers more control but requires more boilerplate code.
cls-hooked is a modern alternative to continuation-local-storage that uses the async_hooks module under the hood. It provides a similar API to CLS but with better performance and support for newer Node.js versions.
zone.js is a library that provides execution contexts (zones) that persist across asynchronous operations. It is more commonly used in Angular applications but can be used in Node.js as well. It offers more features but is also more complex.
Stability: 1 - Experimental
Continuation-local storage provides a mechanism similar to thread-local storage in threaded programming, with closures wrapped around portions of a continuation chain taking the place of mutable cells bound to thread data structures. Contexts are created on namespaces and can be be nested.
Every namespace is created with a default context. The currently active
context on a namespace is available via namespace.active
.
var cls = require('contination-local-storage');
var writer = cls.createNamespace('writer');
writer.set('value', 0);
function requestHandler() {
writer.run(function (outer) {
// writer.get('value') returns 0
// outer.value is 0
writer.set('value', 1);
// writer.get('value') returns 1
// outer.value is 1
process.nextTick(function () {
// writer.get('value') returns 1
// outer.value is 1
writer.run(function (inner) {
// writer.get('value') returns 1
// outer.value is 1
// inner.value is 1
writer.set('value', 2);
// writer.get('value') returns 2
// outer.value is 1
// inner.value is 2
});
});
});
setTimeout(function () {
// runs with the default context, because nested contexts have ended
console.log(writer.get('value')); // prints 0
}, 1000);
}
Each application that wants to use continuation-local attributes should create its own namespace. Reading from (or, more significantly, writing to) namespaces that don't belong to you should be considered a faux pas.
Look up an existing namespace.
Set of available namespaces.
Application-specific namespaces provide access to continuation-local attributes, and may have specialized behavior on a per-namespace basis (custom nesting behavior). Once the execution of a handler chain begins, creating new contexts and changing local values should be considered mutating the value of a given attribute on that particular continuation chain.
Returns the currently active context on a namespace.
Set a value on the current continuation context. Shorthand for
namespace.active.set(key, value)
.
Returns the value.
Look up a value on the current continuation context. Recursively searches from the innermost to outermost nested continuation context for a value associated with a given key.
Create a new scope to which attributes can be bound or mutated. Run the continuation in this new scope (passing in the new context into the continuation). It also returns the context that just exited in case you want to read data from it.
Bind a function to the specified continuation context. Works analagously to
Function.bind()
or domain.bind()
. If context is omitted, it will default to
the currently active context in the namespace.
A context is just a plain object that inherits from it's parent context.
The domains mechanism is a useful tool for adding context to errors raised in asynchronous call chains (or, if you like living dangerously / tempting the wrath of @isaacs, to recover from errors without restarting your services). It also almost serves the purposes of developers who want to annotate async call chains with metadata or extra state (examples: logging, tracing / profiling, generic instrumentation), but due to the needs of error-handling, it doesn't quite generalize enough to be truly useful in this regard. There are modules that allow developers to do similar things when they have full control over their stacks (CrabDude/trycatch and Gozala/reducers, among many others), but none of these modules are a good fit for developers writing tooling meant to be dropped transparently into user code.
See also joyent/node#3733.
Here is a sketch at what the user-visible API might look like. My
original attempt at this used a slightly modified version of the domains
API with some special-purpose logic for dealing with nested contexts,
but allowing multiple distinct namespaces is actually simpler and trades
memory for execution time. It also makes it possible to special-case
behavior for specific namespaces (i.e. my hope would be that domains
would just become a specialized namespace, and _tickDomainCallback
and _nextDomainTick
would be all that would be required to deal with
namespaces), although that isn't included here.
Here's an example of how the API might be used:
var cls = require('continuation-local-storage');
// multiple contexts in use
var tracer = cls.createNamespace('tracer');
function Trace(harvester) {
this.harvester = harvester;
}
Trace.prototype.runHandler = function (callback) {
var trace = tracer.run(callback);
this.harvester.emit('finished', trace.transaction);
};
Trace.prototype.annotateState = function (name, value) {
tracer.set(name, value);
};
FAQs
userland implementation of https://github.com/joyent/node/issues/5243
We found that continuation-local-storage demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
Research
The Socket Research Team discovered a malicious npm package, '@ton-wallet/create', stealing cryptocurrency wallet keys from developers and users in the TON ecosystem.
Security News
Newly introduced telemetry in devenv 1.4 sparked a backlash over privacy concerns, leading to the removal of its AI-powered feature after strong community pushback.