
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
phantomdi is a no-boilerplate DI framework for classes and functions which can optionally leverage typescript-rtti.
typescript-rtti)OptionA | OptionB)emitDecoratorMetadata-style injection (a la Angular, injection-js,
@alterior/di, etc)import { injector, provide } from 'phantomdi';
import { reify } from 'typescript-rtti';
interface Foobar {
version : number;
}
class A {
constructor(readonly foobar : Foobar) {
}
get version() { return this.foobar.version; }
}
let a = injector([ provide(reify<Foobar>, { version: 123 }), provide(A) ]).provide(A)
expect(a.version).to.equal(123);
Functions:
import { injector, provide } from 'phantomdi';
class A { foo = 123 }
class B { bar = 321 }
function foobar(a : A, b : B) {
return a.foo + b.bar;
}
expect(injector([ provide(A), provide(B) ]).invoke(globalThis, foobar))
.to.equal(123 + 321);
Optional:
import { injector, provide } from 'phantomdi';
class A { foo: 123 }
class B { bar: 321 }
function foobar(a : A, b? : B) {
return a.foo + (b?.bar ?? 555);
}
expect(injector([ provide(A) ]).invoke(globalThis, foobar))
.to.equal(123 + 555);
Initializers:
import { injector, provide } from 'phantomdi';
class A { foo: 123 }
class B { bar: 321 }
function foobar(a : A, b = new B(555)) {
return a.foo + (b?.bar);
}
expect(injector([ provide(A) ]).invoke(globalThis, foobar))
.to.equal(123 + 555);
Heirarchical injection:
import { injector, provide } from 'phantomdi';
class A {
constructor(readonly foo = 123) {
}
}
class B {
bar = 321;
}
let parent = injector([ provide(A), provide(B) ]);
let injector = injector([ provide(A, () => new A(555))], parent)
expect(injector.provide(A).foo).to.equal(555);
expect(injector.provide(B).bar).to.equal(321);
Alterations:
import { provide, alter, injector } from 'phantomdi';
class A {
bar = 123;
foo() {
return 'original';
}
}
let i = injector([ provide(A), alter(A, {
beforeFoo() {
console.log(`foo() is about to run`);
}
afterFoo() {
console.log(`foo() is finished running`);
}
aroundFoo(foo : () => string) {
return function () {
return `around(${foo.call(this)})`
}
}
})]);
let a = i.provide(A);
expect(a.bar).to.equal(123);
expect(a.foo()).to.equal('around(original)');
The injector() function (and the Injector constructor) accept an array of providers. Each provider is a tuple of two values: a token and a function which provides the value for that token.
let i = injector([
['foo', () => 123],
['bar', () => 321]
]);
expect(i.provide('foo')).to.equal(123);
expect(i.provide('bar')).to.equal(321);
The provide() function provides syntactic sugar for defining these:
let i = injector([
provide('foo', () => 123),
provide('bar', () => 321)
]);
expect(i.provide('foo')).to.equal(123);
expect(i.provide('bar')).to.equal(321);
Provider functions are also subject to dependency injection:
let i = injector([
provide(Number, () => 123),
provide('bar', (num : number) => num + 1)
])
expect(i.provide('bar').to.equal(124));
Calling provide() with a class constructor will provide that class using its constructor as the token:
class Foo { }
injector([ provide(Foo) ]);
This is done using the construct(constructor) function. It returns a provider function which constructs the
given class using the dependency injector.
You can provide a class token using another class:
class Foo { }
class Bar extends Foo { }
injector([ provide(Foo, Bar) ]);
You can also invoke a function by injecting its parameters based on the available metadata:
class Foo { bar = 123; }
let result = injector([ provide(Foo) ]).invoke((foo : Foo) => foo.bar);
expect(result).to.equal(123);
In addition to parameter injection, you can do property injection:
class Foo {
baz = 123;
}
class Bar {
@Inject() foo : Foo;
}
let result = inject([ provide(Foo), provide(Bar) ]).provide(Bar).foo.baz;
expect(result).to.equal(123);
You can define an onInjectionCompleted() method which will get called after all injection is resolved:
class Foo {
baz = 123;
}
class Bar {
@Inject() foo : Foo;
onInjectionCompleted() {
expect(this.foo.baz).to.equal(123);
}
}
let bar = inject([ provide(Foo), provide(Bar) ]).provide(Bar);
If you specify that a parameter or property is optional, it will be treated as optional. If you specify an initializer for a property or parameter it will automatically be considered "optional", with its value set automatically to the initializer.
When using typescript-rtti, no decorators are required, the library will automatically determine all relevant Typescript types and do the right thing. However you can still use the library without it- the library provides @Injectable() along with @Inject() and @Optional(), and it supports emitDecoratorMetadata:
@Injectable()
class Foo {
baz = 123;
}
@Injectable()
class Bar {
constructor(readonly foo : Foo) {
}
}
let result = inject([ provide(Foo), provide(Bar) ]).provide(Bar).foo.baz;
expect(result).to.equal(123);
As with other dependency injection libraries, technically any decorator on the class being injected is fine, the specific use of @Injectable() is not enforced.
Alterations are special providers. Use alter(token, provider) to define an alteration provider.
The provider function is invoked in a special child injector where token is already provided,
and the provider is expected to return a new value for token. An alteration provider usually uses
Proxy to modify the value injected for token in some way, but it could also completely replace
the object.
You can also pass a special alteration definition object (Alteration<T>) to alter() which will
create the Proxy for you. That definition supports adding functions before (ie beforeMethod()), after (ie afterMethod()), and around (ie aroundMethod()) the
original function value. You can completely replace the method by providing a function property with the
same name as the method (ie method())
FAQs
A dependency injection framework
We found that phantomdi demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.