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

ts-aspect

v3.1.1

Published

Simplistic library for Aspect Oriented Programming in TypeScript

Downloads

1,165

Readme

ts-aspect

A simplistic library for Aspect Oriented Programming (AOP) in TypeScript. Aspects can be injected on pointcuts via regular expressions for a class or object.

One application of AOP is the encapsulation of cross-cutting concerns, like logging, and keep the original business logic clean. Although a powerful tool, it should be used with care as it hides logic and complexity.

Build Status

Installation

To get started, install ts-aspect with npm.

npm install ts-aspect

Using with NestJS

ts-aspect can also be used together with NestJS. An example can be seen here.

Usage

An aspect can be injected to the target class instance or object via

function addAspect(target: any, methodName: string, advice: Advice, aspect: Aspect): void

or

function addAspectToPointcut(target: any, pointcut: string, advice: Advice, aspect: Aspect): void

The aspect parameter is the actual behavior that extends the target code. When the aspect is about to be executed is defined by the advice parameter. Currently, the following advices are available:

  • Before
  • After
  • Around
  • AfterReturn
  • TryCatch
  • TryFinally

For example, the AfterReturn enables you to access the return value of the original function and execute additional logic on it (like logging). Finally, the pointcut parameter describes the where - so basically for which functions the aspect should be executed. For this a regular expression can be used.

If an aspect is called, it creates a new context. The context itself is defined as

export type AspectContext = {
    target: any;            // injected object
    methodName: string;     // the name of the injected function
    functionParams: any[];  // parameters passed to the call of the injected function
    returnValue: any;       // only set for the AfterReturn-Aspect
    error: any;             // only set for the TryCatch-Aspect when an error is thrown
};

Aspects await the execution of asynchronous functions - and are asynchronous themselves in this case. This enables an aspect for the advices Advice.After, Advice.AfterReturn and at the second execution of Advice.Around to work with the resolved return value of the injected function.

Also, ts-aspect provides a method decorator to attach an aspect to a all instances of a class in a declarative manner:

function UseAspect(advice: Advice, aspect: Aspect | (new () => Aspect)): MethodDecorator

Example

Assume the following aspect class which simply logs the current aspect context passed to it to the console:

class LogAspect implements Aspect{
    function execute(ctx: AspectContext): void {
        console.log(ctx);
    }
};

Also, we create the following Calculator class:

class Calculator {
    public add(a: number, b: number) {
        return a + b;
    }

    public subtract(a: number, b: number) {
        return a - b;
    }

    public divide(a: number, b: number) {
        if(b === 0){
            throw new Error('Division by zero!');
        }
        return a / b;
    }

    public multiply(a: number, b: number) {
        return a * b;
    }
};

Now the logArgsAspect can be injected to an instance of Calculator. In the following example, the aspect is supposed to be executed before running the actual business logic:

const calculator = new Calculator();
addAspectToPointcut(calculator, '.*', Advice.Before, new LogAspect());

By defining the pointcut as '.*', the aspect is executed when calling any of the functions of the respective Calculator instance. Therefore, a call to

calculator.add(1, 2);

should output

{
  target: Calculator {},
  methodName: 'add',
  functionParams: [1, 2],
  returnValue: null,
  error: null
}

And further calls to other functions like

calculator.subtract(1, 2);
calculator.divide(1, 2);
calculator.multiply(1, 2);

should result in the same output (except for the changing methodName).

An aspect can also be applied in case an exception occurs in the target code:

const calculator = new Calculator();
addAspect(calculator, 'divide', Advice.TryCatch, new LogAspect());
calculator.divide(1, 0);

In this case, the divide function throws the division by zero exception. Due to Advice.TryCatch the error is being caught and control is handed over to the aspect, which logs the error as well as both input parameters of the divide function call.
Note: Because the aspect does not rethrow the exception implicitly, the handling will stop here. Rethrowing the error in the aspect is necessary if it is supposed to be handled elsewhere.

UseAspect

In addition, aspects can be added to a all class instances in a declarative manner by using the decorator UseAspect. Based on the Calculator example above, lets add another LogAspect to the add method so that the result gets logged to the console as well:

class Calculator {
    @UseAspect(Advice.AfterReturn, LogAspect)
    public add(a: number, b: number) {
        return a + b;
    }
    // ...
}

const calculator = new Calculator();
calculator.add(1300, 37);

The aspect passed to the decorator can be either a class which provides a constructor with no arguments or an instance of an aspect.