
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.
mini-di-cocos
Advanced tools
Lightweight DI container for Cocos Creator 3.x - inspired by VContainer and Zenject, optimized for Playable Ads
Lightweight Dependency Injection container for Cocos Creator 3.x, inspired by VContainer and Zenject.
Optimized for Playable Ads with minimal bundle size impact.
container.instantiate(prefab) with auto-injectionnpm install mini-di-cocos --legacy-peer-deps
Or add to your .npmrc:
legacy-peer-deps=true
import { Injectable, IInitializable, IDisposable } from 'mini-di-cocos';
// Simple service
@Injectable([])
class ConfigService {
readonly maxScore = 1000;
readonly soundEnabled = true;
}
// Service with dependencies
@Injectable([ConfigService])
class AudioService implements IInitializable, IDisposable {
constructor(private config: ConfigService) {}
initialize(): void {
console.log('AudioService initialized');
}
playSound(name: string): void {
if (this.config.soundEnabled) {
// play sound
}
}
dispose(): void {
console.log('AudioService disposed');
}
}
@Injectable([AudioService, ConfigService])
class GameService {
constructor(
private audio: AudioService,
private config: ConfigService
) {}
onScore(): void {
this.audio.playSound('score');
}
}
import { _decorator } from 'cc';
import { LifetimeScope, ContainerBuilder } from 'mini-di-cocos';
const { ccclass } = _decorator;
@ccclass('GameScope')
export class GameScope extends LifetimeScope {
protected configure(builder: ContainerBuilder): void {
builder.register(ConfigService);
builder.register(AudioService);
builder.register(GameService);
}
}
import { _decorator, Component } from 'cc';
import { NeedInject, InjectProperty } from 'mini-di-cocos';
@NeedInject()
@ccclass('PlayerController')
export class PlayerController extends Component {
@InjectProperty(GameService)
private gameService!: GameService;
@InjectProperty(AudioService)
private audio!: AudioService;
onCollectCoin(): void {
this.gameService.onScore();
this.audio.playSound('coin');
}
}
Scene Hierarchy:
├── GameScope (LifetimeScope) ← Add your scope here
├── Player
│ └── PlayerController ← @NeedInject components
├── UI
│ └── ScoreView ← @NeedInject components
└── ...
@Injectable(dependencies: Token[])Marks a class for constructor injection.
@Injectable([DepA, DepB])
class MyService {
constructor(private depA: DepA, private depB: DepB) {}
}
@NeedInject()Marks a Cocos Component for property injection.
@NeedInject()
@ccclass('MyComponent')
class MyComponent extends Component {}
@InjectProperty(token: Token)Injects a dependency into a property.
@InjectProperty(AudioService)
private audio!: AudioService;
// Registration
container.register(Token, options?)
container.registerSingleton(Token, useClass?)
container.registerTransient(Token, useClass?)
container.registerInstance(Token, instance)
container.registerFactory(Token, factory, lifetime?)
// Resolution
container.resolve<T>(Token): T
container.tryResolve<T>(Token): T | null
container.isRegistered(Token): boolean
// Cocos Integration
container.injectComponent(component)
container.injectNode(node)
container.instantiate(prefab, parent?): Node
container.instantiateAndGet<T>(prefab, type, parent?): T
// Lifecycle
container.initializeAll()
container.startAll()
container.disposeAll()
container.getAllSingletons(): any[]
const builder = new ContainerBuilder();
builder
.register(ServiceA)
.register(ServiceB, { useClass: ServiceBImpl })
.registerSingleton(ServiceC)
.registerTransient(ServiceD)
.registerInstance(Config, configInstance)
.registerFactory(ServiceE, (c) => new ServiceE(c.resolve(ServiceA)));
const container = builder.build();
| Property | Default | Description |
|---|---|---|
autoInjectScene | true | Auto-inject all @NeedInject components in scene |
autoInitialize | true | Auto-call IInitializable.initialize() |
autoStart | true | Auto-call IStartable.start() |
enableTickManager | false | Enable ITickable support |
@ccclass('GameScope')
class GameScope extends LifetimeScope {
protected configure(builder: ContainerBuilder): void {
// Register your services here
}
}
// Static access
const scope = LifetimeScope.findInScene();
const service = scope?.resolve(MyService);
For interface-based injection (TypeScript interfaces are erased at runtime):
interface IAudioService {
playSound(name: string): void;
}
const IAudioService = new InjectionToken<IAudioService>('IAudioService');
// Registration
builder.register(IAudioService, {
useFactory: (c) => new AudioServiceImpl(c.resolve(ConfigService))
});
// Injection
@InjectProperty(IAudioService)
private audio!: IAudioService;
interface IInitializable {
initialize(): void | Promise<void>;
}
interface IStartable {
start(): void;
}
interface ITickable {
tick(deltaTime: number): void;
}
interface IFixedTickable {
fixedTick(fixedDeltaTime: number): void;
}
interface ILateTickable {
lateTick(deltaTime: number): void;
}
interface IDisposable {
dispose(): void;
}
Execution Order:
onLoad()
└── Container.build()
└── IInitializable.initialize() (registration order)
└── Scene injection
start()
└── IStartable.start() (registration order)
update(dt)
└── ITickable.tick(dt)
└── IFixedTickable.fixedTick(fixedDt) (fixed timestep)
lateUpdate(dt)
└── ILateTickable.lateTick(dt)
onDestroy()
└── IDisposable.dispose() (reverse order)
@Injectable([Container])
class EnemySpawner {
@property(Prefab)
private enemyPrefab!: Prefab;
constructor(private container: Container) {}
spawn(parent: Node): Enemy {
// Instantiate with auto-injection
return this.container.instantiateAndGet(
this.enemyPrefab,
Enemy,
parent
);
}
}
| Type | Description |
|---|---|
Lifetime.Singleton | One instance per container (default) |
Lifetime.Transient | New instance on each resolve |
| Feature | MiniDI | VContainer | Zenject |
|---|---|---|---|
| Platform | Cocos Creator | Unity | Unity |
| reflect-metadata | ❌ Not required | ✅ Required | ✅ Required |
| Bundle size | ~5KB | ~50KB | ~200KB |
| Constructor injection | ✅ | ✅ | ✅ |
| Property injection | ✅ | ✅ | ✅ |
| Scene injection | ✅ | ❌ | ✅ |
| Prefab instantiate | ✅ | ✅ | ✅ |
| Lifecycle hooks | ✅ | ✅ | ✅ |
| Scoped containers | ❌ | ✅ | ✅ |
// ✅ Services — constructor injection
@Injectable([ConfigService])
class AudioService {
constructor(private config: ConfigService) {}
}
// ✅ Cocos Components — property injection
@NeedInject()
@ccclass('PlayerController')
class PlayerController extends Component {
@InjectProperty(AudioService)
private audio!: AudioService;
}
// Define interfaces
interface IAudioService {
playSound(name: string): void;
}
// Create tokens
const IAudioService = new InjectionToken<IAudioService>('IAudioService');
// Register implementation
builder.register(IAudioService, { useClass: AudioServiceImpl });
builder.registerFactory(ComplexService, (container) => {
const config = container.resolve(ConfigService);
const audio = container.resolve(AudioService);
return new ComplexService(config, audio, {
debug: true,
maxRetries: 3
});
});
Ensure the dependency is registered before resolving:
protected configure(builder: ContainerBuilder): void {
builder.register(DependencyFirst); // Register dependencies first
builder.register(ServiceThatNeedsIt);
}
Break the cycle with factory or lazy resolution:
@Injectable([])
class ServiceA {
private _serviceB: ServiceB | null = null;
setServiceB(b: ServiceB): void {
this._serviceB = b;
}
}
// In configure:
builder.registerFactory(ServiceA, (c) => {
const a = new ServiceA();
const b = c.resolve(ServiceB);
a.setServiceB(b);
return a;
});
@NeedInject() decorator is presentcontainer.instantiate()MIT
FAQs
Lightweight DI container for Cocos Creator 3.x - inspired by VContainer and Zenject, optimized for Playable Ads
The npm package mini-di-cocos receives a total of 8 weekly downloads. As such, mini-di-cocos popularity was classified as not popular.
We found that mini-di-cocos demonstrated a healthy version release cadence and project activity because the last version was released less than 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.