npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ehsc/cls-context-wrapper

v1.3.0

Published

A wrapper of the cls-hooked and node:async_hooks libs to use as middleware

Downloads

585

Readme

NPM

cls-context-wrapper (Typescript)

Build Status


Continuous-Local-Storage Context Wrapper.

This is a Wrapper of the cls-hooked library (fixes included!) and AsyncLocalStorage.

The ContextWrapper class is a singleton instance that uses the cls-hooked or AsyncLocalStorage instance as its own Store.

This wrap is an easy plugin for web application libraries (middleware use) and other types of Node usage like service jobs, lambdas and different types of projects.

Example:

// App
import * as http from 'http';
import * as express from 'express';
import authentication from './middlewares/authentication';
import ContextWrapper from '@ehsc/cls-context-wrapper';

const app = express();

app.use(ContextWrapper.middleware);

app.use(authentication);

app.use((req, res, next) => {
  ContextWrapper.setUserSession(req.user || { id: 1, user: 'ecardoso' });
  ContextWrapper.set({ organization: 'EHSC' });

  const instance = ContextWrapper.getInstance();
  if (instance) instance.set({ foo: 'bar' });

  next();
});

app.get('/test', (_, res) => {
  res.json({
    reqId: ContextWrapper.getRequestId(),
    user: ContextWrapper.getUserSession(),
    organization: ContextWrapper.get('organization'),
    foo: ContextWrapper.getInstance().get('foo')
  });
});

const server = app.listen(8000, () => {
  ContextWrapper.getInstance({ name: 'MyApp', options: { correlationId: { enable: true } } });
});

// Request
http.get('http://localhost:8000/test', (res) => {
  server.close();
  let data: string = '';

  res.on('data', (chunk) => {
    data += chunk;
  });

  res.on('error', (err) => {
    console.error('error:', err);
  });
  
  res.on('close', () => {
    console.log('data:', JSON.parse(data));
  });
});

If you have a specific architecture or problem to solve, you can:

const { default: ContextWrapper } = require('@ehsc/cls-context-wrapper');

// Sync
(() => {
  const instance = ContextWrapper.getInstance({ name: 'MyApp' });

  instance.run(() => {
    ContextWrapper.set({ foo: 'bar' });
    // or
    instance.set({ foo: 'bar' });

    // some inner function...
    function doSomething() {
      console.log(ContextWrapper.get('foo'));
    }

    doSomething(); // print "bar"
  });

  // Warning - Inner Contexts!!!
  instance.run(() => {
    ContextWrapper.set({ foo: 'bar' });
    // or
    instance.set({ foo: 'bar' });

    instance.run(() => {
      ContextWrapper.set({ john: 'doe' });
      // some inner function...
      function doSomething() {
        console.log(ContextWrapper.get('foo'));
      }

      doSomething(); // print "bar"
      console.log(ContextWrapper.get('john')); // print "doe"
    });

    console.log(ContextWrapper.get('john')); // print "undefined"
  });
})();

// Async
(() => {
    const instance = ContextWrapper.getInstance({ name: 'MyApp' });
    const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

    instance.runPromise(async () => {

      ContextWrapper.set({ start: new Date() });
      await sleep(7000);
      const end = new Date();

      console.log('response time (ms):', Math.abs(end - ContextWrapper.get('start'))); // print "response time (ms): 700*"
    });
})();

Note: The Context class is also available for use in other scenarios.


ContextWrapper Methods

  • static getInstance(params?: { name: string; options: object }): IContextStrategy: create a singleton instance.
    • params?.name: string: name of the instance context.

    • params?.mode: 'legacy'|'modern': you can choose modern or legacy instance. If you don't want to choose, the lib will check the node version. 16.17.0 or lower will choose the legacy instance. The 'modern' instance only works from node version 12.20 (experimental / unstable).

    • params?.options?: object: options.

    • params?.options?.correlationId?: object: correlationId config. It is used to store a unique id per request.

    • params?.options?.correlationId?.enable: boolean: to enable automatic set of correlationId in middleware method. Default: true (if not passed params in the getInstance method in instance). Default value: true.

    • params?.options?.correlationId?.valuePath: string?: the path if a correlationId already exists in the req middleware param, e.g.: 'reqId' or 'headers.reqId'. (only available if middleware method is used). If the valuePath is not passed, the middleware method will try to fetch from headers['Correlation-ID'], headers['Request-ID'] or requestId (respectively). Default value: uuid.v4.

    • params?.options?.trackingFlowId?: object: trackingFlowId config. It is used to store the id of an entire flow (e.g.: business flow), ex: Signup (All steps). As it is returned in the http response headers, it can be written to localstorage or any other storage and be used in all requests of an entire flow (e.g.: business logic flow).

    • params?.options?.trackingFlowId?.enable: boolean: to enable automatic set of trackingFlowId in middleware method. Default: true (if not passed params in the getInstance method in instance). Default value: false.

    • params?.options?.trackingFlowId?.valuePath: string?: the path if a trackingFlowId already exists in the req middleware param, e.g.: 'reqsssssssssId' or 'headers.reqId'. (only available if middleware method is used). If the valuePath is not passed, the middleware method will try to fetch from headers['Tracking-Flow-ID'] or trackingFlowId (respectively). Default value: undefined'.

    • Returns an instance that implements the IContextStrategy interface, a super set of the AsyncLocalStorage from node:async_hooks(if the Node version is 14.20.0 or major) or Namespace from cls-hooked lib.

    • IContextStrategy Methods:

      • destroy(): void: whenever you are going to delete, remove or no longer use the Instance, call destroy to remove the instance context. If getInstance is called after destroy, will be created a new instance.
      • run(callback: () => void): void: Runs a function synchronously within a context and returns its return value. The storage is not accessible outside of the callback function. The store is accessible to any asynchronous operations created within the callback.
      • runPromise(callback: () => Promise<void>): void: Runs a function synchronously within a context and returns its return value. The storage is not accessible outside of the callback function. The store is accessible to any asynchronous operations created within the async callback.
      • set(store: { [prop: string]: any }): void: set a value in the context.
      • get(key?: string): any: retrieve a value from the context, if key not passed, it retrieve whole object from the context.
      • use(req: Request, res: Response, next: () => void): void: Use like a middleware (express, koa, etc.).
  • static destroy(): void: whenever you are going to delete, remove or no longer use the Instance, call destroy to remove the instance context. If getInstance is called after destroy, will be created a new instance.
  • static set(store: { [prop: string]: any }): void: set a key value in the context.
  • static get(key?: string): any: read a value previously recorded.
  • static setCorrelationId(value: string | number): void: set the correlation identifier, like ContextWrapper.set({ correlationId: uuid.v4() }) (see pattern).
  • static getCorrelationId(): string | number | undefined: retrieve correlation identifier value, like ContextWrapper.get('correlationId').
  • static setTrackingFlowId(value: string | number): void: set the tracking flow identifier, like ContextWrapper.set({ trackingFlowId: uuid.v4() }).
  • static getTrackingFlowId(): string | number | undefined: retrieve tracking flow identifier value, like ContextWrapper.get('trackingFlowId').
  • static setUserSession(value: { [prop: string]: any } | any): void : set user, like ContextWrapper.set({ user: {...} }).
  • static getUserSession(): { [prop: string]: any } | any: get user, like ContextWrapper.get('user').
  • middleware(): void: all of the middlewares and routes setup can set or read context values if it is called after this middleware, like asyncLocalStorage.run and Namespace.run.

Notes:

  • ContextWrapper.middleware will only create a request context if ContextWrapper.getInstance is called before.
  • Methods set, get, set/get CorrelationId, set/get UserSession only works if instance.run, instance.runPromise or ContextWrapper.middleware is called before.

License

This project is distributed under the MIT license.


For contact, feel free to email me: [email protected].

ps: sorry for any english mistakes. :)

Enjoy it!