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

mediatr-ts

v2.0.1

Published

Mimic the famous MediatR csharp library see: (https://github.com/jbogard/MediatR)

Downloads

2,742

Readme

MediatR-TS

npm type definitions npm npm bundle size NPM

coverage stmt coverage fx coverage lines coverage branches

Porting to typescript of the famous MediatR for C#. It work out-of-the-box with an internal resolver, however it can be 'plugged in' with dependency injection frameworks like Inversify.

Supported concepts

Request handlers

Below the requestHandler pattern with internal resolver and with the inversify library

// request.ts -> Define the request
class Request extends RequestData<string> {
    name: string;
}

// handlertest.ts -> Add the attribute to the request handler
@requestHandler(Request)
class HandlerTest implements RequestHandler<Request, string> {
    handle(value: Request): Promise<string> {
        return Promise.resolve(`Value passed ${value.name}`);
    }
}

// main.ts -> Instantiate the mediator 
const mediator = new Mediator();

// Create the request
const r = new Request();
r.name = "Foo";

// Send the command
const result = await mediator.send(r);

// result = "Value passed Foo"

Notification handlers

import { Mediator } from "mediatr-ts";

const result: string[] = [];

// The notification class
class Ping extends NotificationData {
    constructor(public value?: string){ super(); }
}

// The notification handler
@notificationHandler(Ping)
class Pong1 implements NotificationHandler<Ping> {
    async handle(notification: Ping): Promise<void> {
        result.push(notification.value);
    }
}

const mediator = new Mediator();
mediator.publish(new Ping(message));

// result: [ "Foo" ]

Changing the order of execution

By default, the notification handlers will run in the order that they are loaded in. This might not be desirable, since it depends on the order of the imports. To change the order, you can set it explicitly.

import { Mediator, NotificationData, NotificationHandler } from "mediatr-ts";

const result: string[] = [];

// The notification class
class Ping extends NotificationData {
    constructor(public value?: string){}
}

// The notification handler
@notificationHandler(Ping)
class Pong2 implements NotificationHandler<Ping> {
    async handle(notification: Ping): Promise<void> {
        result.push(notification.value + " from 2");
    }
}

// The notification handler
@notificationHandler(Ping)
class Pong1 implements NotificationHandler<Ping> {
    async handle(notification: Ping): Promise<void> {
        result.push(notification.value + " from 1");
    }
}

const mediator = new Mediator();
mediator.publish(new Ping("Foo"));

// result: [ "Foo from 2", "Foo from 1" ]

// Set the order of the pipeline behaviors. PipelineBehaviorTest2 will be executed first, and then PipelineBehaviorTest1.
mediator.dispatcher.notifications.setOrder(Ping, [
    Pong2, 
    Pong1
]);

mediator.publish(new Ping("Foo"));

// result: [ "Foo from 1", "Foo from 2" ]

Pipeline behaviors

import { Mediator, PipelineBehavior, RequestHandler, RequestData } from "mediatr-ts";

class Request extends RequestData {
    name?: string;
}

@requestHandler(Request)
class HandlerTest implements RequestHandler<Request, string> {
    handle(value: Request): Promise<string> {
        return Promise.resolve(`Value passed ${value.name}`);
    }
}

@pipelineBehavior()
class PipelineBehaviorTest1 implements PipelineBehavior {
    async handle(request: IRequest<unknown>, next: () => unknown): Promise<unknown> {
        if(request instanceof Request) {
            request.name += " with stuff 1";
        }

        let result = await next();
        if(typeof result === "string") {
            result += " after 1";
        }

        return result;
    }
}

@pipelineBehavior()
class PipelineBehaviorTest2 implements PipelineBehavior {
    async handle(request: IRequest<unknown>, next: () => unknown): Promise<unknown> {
        if(request instanceof Request) {
            request.name += " with stuff 2";
        }

        let result = await next();
        if(typeof result === "string") {
            result += " after 2";
        }

        return result;
    }
}

const r = new Request();
r.name = "Foo";

const mediator = new Mediator();
const result = await mediator.send(r);

// result will be "Value passed Foo with stuff 2 with stuff 1 after 1 after 2"

Changing the order of execution

By default, the pipeline behaviors will run in the order that they are loaded in. This might not be desirable, since it depends on the order of the imports. To change the order, you can set it explicitly.

import { Mediator, PipelineBehavior } from "mediatr-ts";

@pipelineBehavior()
class PipelineBehaviorTest1 implements PipelineBehavior {
    async handle(request: IRequest<unknown>, next: () => unknown): Promise<unknown> {
        return result;
    }
}

@pipelineBehavior()
class PipelineBehaviorTest2 implements PipelineBehavior {
    async handle(request: IRequest<unknown>, next: () => unknown): Promise<unknown> {
        return result;
    }
}

const mediator = new Mediator();
// Set the order of the pipeline behaviors. PipelineBehaviorTest2 will be executed first, and then PipelineBehaviorTest1.
mediator.pipelineBehaviors.setOrder([
    PipelineBehaviorTest2, 
    PipelineBehaviorTest1
]);

Integrating with Dependency Injection containers

Inversify

At the very beginning of your app you MUST setup the resolver with Inversify, or at least BEFORE using the @requestHandler attribute and/or the Mediator class.

import { Container } from "inversify";
import { Mediator, Resolver, requestHandler, RequestData } from "mediatr-ts";

const container = new Container();

// inversify.resolver.ts -> Implement the resolver
class InversifyResolver implements Resolver {
    resolve<T>(type: Class<T>): T {
        return container.get(type);
    }

    add<T>(type: Class<T>): void {
        container.bind(type).toSelf();
    }
}

// You can later configure the inversify container
interface IWarrior {
    fight(): string;
}

const TYPES = {
    IWarrior: Symbol.for("IWarrior"),
};

@injectable()
class Ninja implements IWarrior {
    fight(): string {
        return "ninja fight";
    }
}

container.bind<IWarrior>(TYPES.IWarrior).to(Ninja);

// The request object
class Request extends RequestData<number> {
    public thenumber: number;

    constructor(thenumber: number) {
        super();
        this.thenumber = thenumber;
    }
}

// Decorate the handler request with Handler and injectable attribute, notice the warrior property
@requestHandler(Request)
@injectable()
class HandlerRequest implements RequestHandler<Request, string> {
    @inject(TYPES.IWarrior)
    public warrior: IWarrior; // Instantiated by the inversify

    public handle(value: Request): Promise<string> {
        return Promise.resolve(`We has ${value.thenumber} ${this.warrior.fight()}`);
    }
}

const mediator = new Mediator({
    resolver: new InversifyResolver()
});

const result = await mediator.send(new Request(99));

// result => "We has 99 ninja fight"