Security News
Fluent Assertions Faces Backlash After Abandoning Open Source Licensing
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Lightweight and flexible dependency injection library for JavaScript and TypeScript, w/wo ECMAScript decorators.
Lightweight and flexible dependency injection library for JavaScript and TypeScript, w/wo ECMAScript decorators.
npm install di-wise
pnpm add di-wise
yarn add di-wise
Also available on JSR:
deno add jsr:@exuanbo/di-wise
reflect-metadata
experimentalDecorators
requiredExample:
import {createContainer, Inject, inject, Injectable, Scope, Scoped, Type} from "di-wise";
interface Spell {
cast(): void;
}
const Spell = Type<Spell>("Spell");
@Scoped(Scope.Container)
@Injectable(Spell)
class Fireball implements Spell {
cast() {
console.log("🔥");
}
}
class Wizard {
@Inject(Wand)
wand!: Wand;
// Equivalent to
wand = inject(Wand);
constructor(spell = inject(Spell)) {
// inject() can be used anywhere during construction
this.wand.store(spell);
}
}
const container = createContainer();
container.register(Fireball);
// Under the hood
[Fireball, Spell].forEach((token) => {
container.register(
token,
{useClass: Fireball},
{scope: Scope.Container},
);
});
const wizard = container.resolve(Wizard);
wizard.wand.activate(); // => 🔥
Build()
, Value()
Example:
import {Build, createContainer, inject, Value} from "di-wise";
class Wizard {
equipment = inject(
Cloak,
// Provide a default value
Value({
activate() {
console.log("👻");
},
}),
);
wand: Wand;
constructor(wand: Wand) {
this.wand = wand;
}
}
const container = createContainer();
const wizard = container.resolve(
Build(() => {
// inject() can be used in factory functions
const wand = inject(Wand);
return new Wizard(wand);
}),
);
wizard.equipment.activate(); // => 👻
Example:
import {createContainer, inject, Injectable, Type} from "di-wise";
const MagicSchool = Type<string>("MagicSchool");
const Spell = Type<{cast(): void}>("Spell");
// Parent container with shared config
const hogwarts = createContainer();
hogwarts.register(MagicSchool, {useValue: "Hogwarts"});
@Injectable(Spell)
class Fireball {
school = inject(MagicSchool);
cast() {
console.log(`🔥 from ${this.school}`);
}
}
// Child containers with isolated spells
const gryffindor = hogwarts.createChild();
gryffindor.register(Fireball);
const slytherin = hogwarts.createChild();
slytherin.register(Spell, {
useValue: {cast: () => console.log("🐍")},
});
gryffindor.resolve(Spell).cast(); // => 🔥 from Hogwarts
slytherin.resolve(Spell).cast(); // => 🐍
Inherited
(default), Transient
, Resolution
, Container
Example for singleton pattern:
import {createContainer, Scope} from "di-wise";
export const singletons = createContainer({
defaultScope: Scope.Container,
autoRegister: true,
});
// Always resolves to the same instance
const wizard = singletons.resolve(Wizard);
Inherits the scope from its dependent. If there is no dependent (top-level resolution), behaves like Transient
.
import {createContainer, Scope, Scoped} from "di-wise";
@Scoped(Scope.Container)
class Wizard {
wand = inject(Wand);
}
const container = createContainer();
container.register(
Wand,
{useClass: Wand},
{scope: Scope.Inherited},
);
container.register(Wizard);
// Dependency Wand will be resolved with "Container" scope
const wizard = container.resolve(Wizard);
Creates a new instance every time the dependency is requested. No caching occurs.
Creates one instance per resolution graph. The same instance will be reused within a single dependency resolution, but new instances are created for separate resolutions.
@Scoped(Scope.Resolution)
class Wand {}
class Inventory {
wand = inject(Wand);
}
class Wizard {
inventory = inject(Inventory);
wand = inject(Wand);
}
const container = createContainer();
const wizard = container.resolve(Wizard);
expect(wizard.inventory.wand).toBe(wizard.wand);
Creates one instance per container (singleton pattern). The instance is cached and reused for all subsequent resolutions within the same container.
Type.Null
and Type.Undefined
Example:
import {inject, Type} from "di-wise";
class Wizard {
wand = inject(Wand, Type.Null);
// ^? (property) Wizard.wand: Wand | null
spells = injectAll(Spell, Type.Null);
// ^? (property) Wizard.spells: Spell[]
// => []
}
@Inject()
or inject.by()
)Example:
import {createContainer, Inject, inject} from "di-wise";
class Wand {
owner = inject(Wizard);
}
class Wizard {
@Inject(Wand)
wand!: Wand;
// Equivalent to
wand = inject.by(this, Wand);
}
const container = createContainer();
const wizard = container.resolve(Wizard);
expect(wizard.wand.owner).toBe(wizard);
Injector
Example:
import {createContainer, inject, Injector} from "di-wise";
class Wizard {
private injector = inject(Injector);
private wand?: Wand;
getWand() {
// Lazy load wand only when needed
return (this.wand ??= this.injector.inject(Wand));
}
castAllSpells() {
// Get all registered spells
const spells = this.injector.injectAll(Spell);
spells.forEach((spell) => spell.cast());
}
}
const container = createContainer();
const wizard = container.resolve(Wizard);
wizard.getWand(); // => Wand
The injector maintains the same resolution context as its injection point, allowing proper handling of scopes and circular dependencies:
import {createContainer, inject, Injector} from "di-wise";
class Wand {
owner = inject(Wizard);
}
class Wizard {
private injector = inject.by(this, Injector);
getWand() {
return this.injector.inject(Wand);
}
}
const container = createContainer();
const wizard = container.resolve(Wizard);
const wand = wizard.getWand();
expect(wand.owner).toBe(wizard);
See discussion Does di-wise support constructor injection? #12
Example:
import {applyMiddleware, createContainer, type Middleware} from "di-wise";
const logger: Middleware = (composer, _api) => {
composer
.use("resolve", (next) => (token) => {
console.log("Resolving:", token.name);
const result = next(token);
console.log("Resolved:", token.name);
return result;
})
.use("resolveAll", (next) => (token) => {
console.log("Resolving all:", token.name);
const result = next(token);
console.log("Resolved all:", token.name);
return result;
});
};
const performanceTracker: Middleware = (composer, _api) => {
composer.use("resolve", (next) => (token) => {
const start = performance.now();
const result = next(token);
const end = performance.now();
console.log(`Resolution time for ${token.name}: ${end - start}ms`);
return result;
});
};
const container = applyMiddleware(createContainer(), [logger, performanceTracker]);
// Use the container with applied middlewares
const wizard = container.resolve(Wizard);
Middlewares are applied in array order but execute in reverse order, allowing outer middlewares to wrap and control the behavior of inner middlewares.
🏗️ WIP (PR welcome)
See API documentation.
Inspired by:
MIT License @ 2024-Present Xuanbo Cheng
FAQs
Lightweight and flexible dependency injection library for JavaScript and TypeScript, w/wo ECMAScript decorators.
We found that di-wise demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers 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
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Research
Security News
Socket researchers uncover the risks of a malicious Python package targeting Discord developers.
Security News
The UK is proposing a bold ban on ransomware payments by public entities to disrupt cybercrime, protect critical services, and lead global cybersecurity efforts.