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

@susisu/hokemi

v0.2.1

Published

Minimal type-safe dependency injection framework for TypeScript

Downloads

3

Readme

hokemi

CI

Minimal type-safe dependency injection framework for TypeScript, inspired by Cake Pattern in Scala.

Installation

# npm
npm i --save @susisu/hokemi
# yarn
yarn add @susisu/hokemi
# pnpm
pnpm add @susisu/hokemi

Usage

First, declare components of your application.

import type { Component } from "@susisu/hokemi";

export type Clock = {
  getTime: () => number;
};
export type ClockComponent = Component<"clock", Clock>;

export type Random = {
  getRandom: () => number;
};
export type RandomComponent = Component<"random", Random>;

export type MyService = {
  getTimeAndRandom: () => [number, number];
};
export type MyServiceComponent = Component<"myService", MyService>;

Each component has a name (e.g. "clock" for ClockComponent), which is used later to reference its instance.

Next, provide implementations for the components.

import { impl } from "@susisu/hokemi";

export const clockImpl = impl<ClockComponent>("clock", () => ({
  getTime: () => Date.now(),
}));

export const randomImpl = impl<RandomComponent>("random", () => ({
  getRandom: () => Math.random(),
}));

export const myServiceImpl = impl<MyServiceComponent, [ClockComponent, RandomComponent]>(
  "myService",
  ({ clock, random }) => ({
    getTimeAndRandom: () => [clock.getTime(), random.getRandom()],
  })
);

The impl function is a utility function to create an implementation of a component. It takes the component name and a factory function that creates an instance of the component.

impl also takes an optional second type argument (e.g. [ClockComponent, RandomComponent] for myServiceImpl), which specifies the dependencies of the implementation. Dependencies are passed to the factory function when the implementation is instantiated.

Finally, mix your implementations and create an instance of the application.

import { mixer } from "@susisu/hokemi";

const app = mixer(myServiceImpl, clockImpl, randomImpl).new();
console.log(app.myService.getTimeAndRandom()); // => [<time>, <random>]

You can reference each component instance by its name (e.g. app.myService).

If you forget to provide some dependencies, or provide mismatched dependencies, it will be detected at compile time with neat error messages.

const app = mixer(myServiceImpl, clockImpl).new();
//                                          ~~~
// TS2349: This expression is not callable.
//   Type '{ __missingDependenciesError?: { reason: "some dependencies are missing"; providerName: "myService"; dependencies: [{ name: "random"; expectedType: Random; }]; } | undefined; }' has no call signatures.

API

Component<Name, Type>

Declares a component.

impl<Component, Dependencies = []>(name, factory)

Creates an implementation of a component.

factory can be either a function or a class that creates an instance of the component.

mixer(...implementations)

Creates a mixer object, which has the following methods:

  • .with(...implementations): extends the mixer with more implementations.
  • .new(): creates a mixed instance.

Troubleshooting

Cannot export a mixer (TS7056) / type inference for a mixer is too slow

Adding a type annotation will solve the problem.

import { Mixer, mixer } from "@susisu/hokemi";

// Don't forget `as const`!
const impls = [myServiceImpl, clockImpl, randomImpl] as const;

export const myMixer: Mixer<[...typeof impls]> = mixer(...impls);

License

MIT License

Author

Susisu (GitHub, Twitter)