npm.io
3.1.3 • Published 3 weeks ago

@biorate/inversion

Licence
MIT
Version
3.1.3
Deps
6
Size
22 kB
Vulns
0
Weekly
0
Stars
5

@biorate/inversion

IoC container and DI framework built on InversifyJS, extended with automatic lifecycle management ($run) via @biorate/lifecycled.

Features

  • @injectable() — marks a class for DI registration.
  • @inject() — lazy property injection (supports .multi, .named, .tagged).
  • Core() mixin — adds $run() lifecycle with logging.
  • container — global InversifyJS Container singleton.
  • **Types.*** — shared symbolic type identifiers for cross‑package DI.
  • @init() / @kill() — lifecycle decorators re‑exported from @biorate/lifecycled.
  • Async shutdown — graceful process exit via @biorate/shutdown-hook.

Installation

pnpm add @biorate/inversion

Peer dependency: reflect-metadata (make sure import 'reflect-metadata' is called before any @injectable() usage).

Quick start

import { Core, injectable, inject, container, kill, init } from '@biorate/inversion';

@injectable()
class Logger {
  @init() public initialize() {
    console.log('Logger ready');
  }

  public log(msg: string) { console.log(msg); }
}

@injectable()
class App {
  @inject(Logger) public logger: Logger;

  @init() public start() {
    this.logger.log('App started');
  }
}

class Root extends Core() {
  @inject(App) public app: App;
}

container.bind(Logger).toSelf().inSingletonScope();
container.bind(App).toSelf().inSingletonScope();
container.bind(Root).toSelf().inSingletonScope();

const root = container.get<Root>(Root);
await root.$run();
// Logger ready
// App started
// [Root] initialized [0.01s]
// @kill() methods will run automatically on process shutdown

API Reference

Core() mixin
function Core<T extends new (...args: any[]) => any>(Class?: T): typeof Core;

Creates an @injectable() class that extends Class (or an empty class) with:

Method Description
$run(root?, _parent?) Calls lifecycled() on the instance. _parent is used internally for recursion.
(none) Kill phase runs automatically via ShutdownHook on process exit.
class MyApp extends Core() { /* … */ }
// or
class MyApp extends Core(ExistingBase) { /* … */ }
@injectable()

Class decorator. Equivalent to InversifyJS @injectable() plus metadata storage.

@injectable()
class MyService {}
@inject()

Property decorator for lazy injection:

class Consumer {
  @inject(MyService) public service: MyService;
}

Variants:

@inject.multi(MyService)     // inject all bindings (array)
@inject.named(MyService, 'name')  // inject by named binding
@inject.tagged(MyService, 'tag', value) // inject by tagged binding
Types.*

Shared symbol identifiers registered globally via @biorate/symbolic:

import { Types } from '@biorate/inversion';

container.bind(Types.Config).to(Config).inSingletonScope();
container.bind(Types.Logger).to(Logger).inSingletonScope();
container

Global Container instance (skipBaseClassChecks enabled):

import { container } from '@biorate/inversion';

container.bind(Foo).toSelf();
container.bind(Bar).to(BarImpl);
Lifecycle decorators (re‑exported)
Decorator Description
@init() Marked method runs on $run().
@kill() Marked method runs on process shutdown via ShutdownHook.

Usage patterns

DI with config
import { inject, Core, container, Types } from '@biorate/inversion';
import { IConfig, Config } from '@biorate/config';

@injectable()
class DbService {
  @inject(Types.Config) protected config: IConfig;

  public connect() {
    const url = this.config.get<string>('db.url');
    // …
  }
}

class Root extends Core() {
  @inject(DbService) public db: DbService;
}

container.bind(Types.Config).to(Config).inSingletonScope();
container.bind(DbService).toSelf().inSingletonScope();
container.bind(Root).toSelf().inSingletonScope();
Custom logging

By default Core.log = console. Override to customise:

Core.log = {
  info: (msg: string) => myLogger.info(msg),
  warn: (msg: string) => myLogger.warn(msg),
};
Multi‑injection
@injectable()
class Aggregate {
  @inject.multi(Plugin) public plugins: Plugin[];
}

container.bind(Plugin).to(PluginA);
container.bind(Plugin).to(PluginB);
Named injection
@injectable()
class Cache {
  @inject.named(CacheAdapter, 'redis') public adapter: CacheAdapter;
}

container.bind(CacheAdapter).to(RedisAdapter).whenTargetNamed('redis');

Architecture

                     ┌───────────────────────┐
                     │    inversify           │
                     │    Container            │
                     └──────────┬────────────┘
                                │
                ┌───────────────┴───────────────┐
                │        @biorate/inversion      │
                ├────────────────────────────────┤
                │ container (global singleton)    │
                │ Types.* (global symbols)        │
                │ injectable() decorator           │
                │ inject() / .multi / .named / .tagged │
                │ Core() mixin                     │
                │   └── $run() → lifecycled()     │
                │         @init / @kill scanning  │
                └────────────────────────────────┘
                                │
                    ┌───────────┴───────────┐
                    │   @biorate/lifecycled  │
                    │   topological scan     │
                    │   @init / @kill / @on  │
                    └───────────────────────┘
Learn
  • Documentation can be found here - docs.
Release History

See the CHANGELOG

License

MIT

Copyright (c) 2021-present Leonid Levkin (llevkin)