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

@kuroi/syringe

v2.0.0

Published

TypeScript dependency injection framework

Downloads

5

Readme

Syringe Dependency Injection

Syringe is a custom dependency injection framework meant for TypeScript projects. It aims to require minimal effort from the developer by not requiring manual mapping code or any dependencies.

Syringe is best suited for applications with simple lifecycles and lots of singletons, as it was developed for use in a bot, and the code has not been production tested against more advanced use cases.

Usage

Importing

Use ES6 imports to access the Syringe namespace.

import { Syringe } from '@kuroi/syringe'

Creating Injectable Classes

Mark classes as injectable entities with the @Injectable decorator. If your class is a singleton, provide a scope of 'global', otherwise leave the decorator argument blank to allow Syringe to construct a new instance for each injection.

Singleton

@Syringe.Injectable({
  scope: 'global'
})
export class MyService implements IService {
  // ...
}

Non-singleton

@Syringe.Injectable()
export class MyInstance {
  // ...
}

Note: The @Injectable decorator will not work alongside class decorators that wrap the target class in an anonymous extending class. If your decorator returns a Proxy to the target constructor or manually preserves the prototype information (particularly the name), Syringe DI should still work, but it's advised that you put the @Injectable decorator first.

@Syringe.Injectable()
@Wrapper
export class MultiDecorated {
	// ...
}

Injecting Classes

To inject your @Injectable class as a dependency, use @Syringe.Inject to automatically supply the dependency instance to another @Injectable constructor. The decorator requires either the class definition for which you want to inject an instance or a direct reference to an InjectionToken as an argument.

@Syringe.Injectable()
export class MyComponent {
  constructor(@Syringe.Inject(MyService) private service: IService) {
    // ...
  }
}

Creating Injection Tokens

Sometimes, you might want to inject constants, function calls, or other non-class entities into your @Injectable class. In order to do this, you must manually construct an InjectionToken and return your value from its factory function.

const _someConstant: string = 'Jerry, hello! It\'s me, Uncle Leo!'
export const UNCLE_LEO = new InjectionToken('UncleLeo', {
  scope: 'global',
  factory: () => _someConstant
})

// inject an ID generator function
const GENERATE_ID = new InjectionToken('GenerateId', {
  scope: 'global',
  factory: () => () => Utilities.generateId()
})

Then, you can inject this token directly into an @Injectable class.

@Syringe.Injectable()
export class MyClass {
  public id: string;
  constructor(
    @Syringe.Inject(GENERATE_ID) idGenerator: () => string,
    @Syringe.Inject(UNCLE_LEO) private greeting: string
  ) {
    this.id = idGenerator()
  }
  public greet(): void {
    console.log(this.greeting) // Jerry, hello! It's me, Uncle Leo!
  }
}

Bootstrapping

To get all of your classes actually running, simply tell Syringe to inject the top level class(es) at the entry point(s) of your application, usually in index.ts or similar. Syringe will automatically construct all of its dependencies, and your instance is ready to go.

const app = Syringe.inject<MyApp>(MyApp)
app.start()

Providers

Sometimes, you may want to inject an abstract class and specify a concretion at a higher level context, or just substitute one implementation for another. Such is the beauty of dependency injection.

To do so, simply include a provider in the arguments to Syringe.inject when bootstrapping an entry point to your application.

// inject Abstraction
@Syringe.Injectable()
export class MyClass {
  constructor(@Syringe.Inject(AbstractService) service: AbstractService) {}
}

const app = Syringe.inject<MyApp>(MyApp, {
  providers: [
	NonDecoratedClass, // inject singletons that aren't decorated with @Injectable
	{
      for: AbstractService,
      use: ConcreteService // extension type of AbstractService to provide for MyApp
    },
	{
      for: AnotherAbstractService,
	  instance: PreConstructedValueType // extension value of AnotherAbstractService to provide for MyApp
	}
  ]
})

Lifecycles

There are two main lifecycle hooks available to all entities managed by Syringe: OnInit and OnDestroy. Syringe's container will automatically call these methods if implemented during construction and teardown.

To hook into these lifecycles, implement the interfaces Syringe.OnInit and/or Syringe.OnDestroy.

@Syringe.Injectable()
export class MyLifecycleClass implements Syringe.OnInit, Syringe.OnDestroy {
  public onInit(): void {
    // ...
  }
  public onDestroy(): void {
    // ...
  }
}

Credits / Considerations

This library is largely inspired by the feel of Google Angular's DI framework, minus the custom module pattern and any external dependencies. You can use it for UI/browser apps, but it's better suited for server side Node.js/TypeScript apps.