🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@fluojs/di

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fluojs/di - npm Package Compare versions

Comparing version
1.0.0-beta.5
to
1.0.0-beta.6
+13
-0
dist/container.d.ts

@@ -19,2 +19,6 @@ import { type Token } from '@fluojs/core';

private readonly forwardRefTokenCache;
private readonly providerLookupPlanCache;
private readonly multiProviderPlanCache;
private readonly requestScopeVerdictPlanCache;
private readonly effectiveProviderPlanCache;
private childScopes;

@@ -24,2 +28,3 @@ private disposePromise;

private trackedByRoot;
private graphRevision;
constructor(parent?: Container | undefined, requestScopeEnabled?: boolean, singletonCache?: Map<Token, Promise<unknown>>);

@@ -142,8 +147,16 @@ /**

private clearDisposalCaches;
private currentLineageRevision;
private readCachedPlan;
private writePlanCache;
private advanceGraphRevision;
private clearResolutionPlanCaches;
private waitForStaleDisposalTasks;
private scheduleStaleDisposal;
private throwDisposalErrors;
private collectDisposalError;
private isDisposable;
private instantiate;
private assertSingletonDependencyScopes;
private findRequestScopedDependency;
private findRequestScopedDependencyToken;
private resolveEffectiveProvider;

@@ -150,0 +163,0 @@ private resolveProviderDependencyToken;

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}

@@ -129,2 +129,6 @@ import { InvariantError, formatTokenName } from '@fluojs/core';

forwardRefTokenCache = new WeakMap();
providerLookupPlanCache = new Map();
multiProviderPlanCache = new Map();
requestScopeVerdictPlanCache = new Map();
effectiveProviderPlanCache = new Map();
childScopes;

@@ -134,2 +138,3 @@ disposePromise;

trackedByRoot = false;
graphRevision = 0;
constructor(parent, requestScopeEnabled = false, singletonCache) {

@@ -171,2 +176,3 @@ this.parent = parent;

existing.push(normalized);
this.advanceGraphRevision();
continue;

@@ -178,2 +184,3 @@ }

}
this.advanceGraphRevision();
}

@@ -212,2 +219,3 @@ return this;

this.multiOverriddenTokens.add(normalized.provide);
this.advanceGraphRevision();
continue;

@@ -217,2 +225,3 @@ }

this.registrations.set(normalized.provide, normalized);
this.advanceGraphRevision();
}

@@ -239,3 +248,7 @@ return this;

hasRequestScopedDependency(token) {
return this.providerGraphRequiresRequestScope(token, new Set());
const cached = this.readCachedPlan(this.requestScopeVerdictPlanCache, token);
if (cached) {
return cached.value;
}
return this.writePlanCache(this.requestScopeVerdictPlanCache, token, this.providerGraphRequiresRequestScope(token, new Set()));
}

@@ -290,2 +303,3 @@

this.disposed = true;
this.advanceGraphRevision();
this.disposePromise = this.disposeAll();

@@ -300,9 +314,20 @@ try {

async disposeAll() {
const errors = [];
try {
// Dispose all live request-scope children first (root only)
if (!this.parent && this.childScopes && this.childScopes.size > 0) {
await Promise.all(Array.from(this.childScopes).map(child => child.dispose()));
const childResults = await Promise.allSettled(Array.from(this.childScopes).map(child => child.dispose()));
for (const result of childResults) {
if (result.status === 'rejected') {
this.collectDisposalError(result.reason, errors);
}
}
this.childScopes.clear();
}
await this.disposeCache(this.disposalCacheEntries());
try {
await this.disposeCache(this.disposalCacheEntries());
} catch (error) {
this.collectDisposalError(error, errors);
}
this.throwDisposalErrors(errors);
} finally {

@@ -357,11 +382,16 @@ if (this.parent && this.trackedByRoot) {

collectMultiProviders(token) {
const cached = this.readCachedPlan(this.multiProviderPlanCache, token);
if (cached) {
return [...cached.value];
}
const local = this.multiRegistrations.get(token);
let providers;
if (this.multiOverriddenTokens.has(token)) {
return local ?? [];
providers = Object.freeze([...(local ?? [])]);
} else {
const fromParent = this.parent ? this.parent.collectMultiProviders(token) : [];
providers = Object.freeze(local ? [...fromParent, ...local] : [...fromParent]);
}
const fromParent = this.parent ? this.parent.collectMultiProviders(token) : [];
if (local) {
return [...fromParent, ...local];
}
return fromParent;
this.writePlanCache(this.multiProviderPlanCache, token, providers);
return [...providers];
}

@@ -572,7 +602,9 @@ providerGraphRequiresRequestScope(token, visited) {

lookupProvider(token) {
const cached = this.readCachedPlan(this.providerLookupPlanCache, token);
if (cached) {
return cached.value;
}
const local = this.registrations.get(token);
if (local) {
return local;
}
return this.parent?.lookupProvider(token);
const provider = local ?? this.parent?.lookupProvider(token);
return this.writePlanCache(this.providerLookupPlanCache, token, provider);
}

@@ -687,2 +719,3 @@

this.multiRequestCache?.clear();
this.clearResolutionPlanCaches();
return;

@@ -692,3 +725,32 @@ }

this.multiSingletonCache.clear();
this.clearResolutionPlanCaches();
}
currentLineageRevision() {
const parentRevision = this.parent?.currentLineageRevision();
return parentRevision ? `${parentRevision}/${this.graphRevision}` : String(this.graphRevision);
}
readCachedPlan(cache, token) {
const cached = cache.get(token);
if (!cached || cached.lineageRevision !== this.currentLineageRevision()) {
return undefined;
}
return cached;
}
writePlanCache(cache, token, value) {
cache.set(token, {
lineageRevision: this.currentLineageRevision(),
value
});
return value;
}
advanceGraphRevision() {
this.graphRevision += 1;
this.clearResolutionPlanCaches();
}
clearResolutionPlanCaches() {
this.providerLookupPlanCache.clear();
this.multiProviderPlanCache.clear();
this.requestScopeVerdictPlanCache.clear();
this.effectiveProviderPlanCache.clear();
}
async waitForStaleDisposalTasks() {

@@ -723,2 +785,9 @@ while (this.staleDisposalTasks.size > 0) {

}
collectDisposalError(error, errors) {
if (error instanceof AggregateError) {
errors.push(...error.errors);
return;
}
errors.push(error);
}
isDisposable(value) {

@@ -758,15 +827,57 @@ return typeof value === 'object' && value !== null && 'onDestroy' in value && typeof value.onDestroy === 'function';

}
for (const depEntry of provider.inject) {
const requestScopedDependency = this.findRequestScopedDependency(provider.inject, new Set([provider.provide]));
if (requestScopedDependency) {
throw new ScopeMismatchError(`Singleton provider ${formatTokenName(provider.provide)} depends on request-scoped provider ${formatTokenName(requestScopedDependency)}.`, {
token: provider.provide,
scope: 'singleton',
hint: `Singleton providers cannot depend on request-scoped providers. Either change ${formatTokenName(requestScopedDependency)} to singleton/transient scope, or change ${formatTokenName(provider.provide)} to request scope.`
});
}
}
findRequestScopedDependency(depEntries, visited) {
for (const depEntry of depEntries) {
const depToken = this.resolveProviderDependencyToken(depEntry);
const effectiveProvider = this.resolveEffectiveProvider(depToken);
if (effectiveProvider?.scope === 'request') {
throw new ScopeMismatchError(`Singleton provider ${formatTokenName(provider.provide)} depends on request-scoped provider ${formatTokenName(depToken)}.`, {
token: provider.provide,
scope: 'singleton',
hint: `Singleton providers cannot depend on request-scoped providers. Either change ${formatTokenName(depToken)} to singleton/transient scope, or change ${formatTokenName(provider.provide)} to request scope.`
});
if (isOptionalToken(depEntry) && !this.has(depToken)) {
continue;
}
const requestScopedToken = this.findRequestScopedDependencyToken(depToken, visited);
if (requestScopedToken) {
return requestScopedToken;
}
}
return undefined;
}
findRequestScopedDependencyToken(token, visited) {
if (visited.has(token)) {
return undefined;
}
visited.add(token);
try {
const provider = this.resolveEffectiveProvider(token);
if (provider) {
if (provider.scope === Scope.REQUEST) {
return provider.provide;
}
return this.findRequestScopedDependency(provider.inject, visited);
}
if (typeof token !== 'function') {
return undefined;
}
const metadata = getClassDiMetadata(token);
if (metadata?.scope === Scope.REQUEST) {
return token;
}
return this.findRequestScopedDependency(metadata?.inject ?? [], visited);
} finally {
visited.delete(token);
}
}
resolveEffectiveProvider(token, visited = new Set(), chain = []) {
const cacheable = visited.size === 0 && chain.length === 0;
if (cacheable) {
const cached = this.readCachedPlan(this.effectiveProviderPlanCache, token);
if (cached) {
return cached.value;
}
}
let currentToken = token;

@@ -780,5 +891,11 @@ while (true) {

if (!provider) {
if (cacheable) {
return this.writePlanCache(this.effectiveProviderPlanCache, token, undefined);
}
return undefined;
}
if (provider.type !== 'existing' || provider.useExisting === undefined) {
if (cacheable) {
return this.writePlanCache(this.effectiveProviderPlanCache, token, provider);
}
return provider;

@@ -785,0 +902,0 @@ }

@@ -12,3 +12,3 @@ {

],
"version": "1.0.0-beta.5",
"version": "1.0.0-beta.6",
"private": false,

@@ -40,3 +40,3 @@ "license": "MIT",

"dependencies": {
"@fluojs/core": "^1.0.0-beta.2"
"@fluojs/core": "^1.0.0-beta.3"
},

@@ -43,0 +43,0 @@ "devDependencies": {

@@ -79,2 +79,4 @@ # @fluojs/di

dispose 중에는 루트 컨테이너가 먼저 살아 있는 request scope 자식을 정리한 뒤, 자식 dispose 중 하나 이상이 실패하더라도 루트가 소유한 singleton 정리를 계속 수행합니다. 자식/루트 dispose 실패가 여러 개 발생하면 `dispose()`는 모든 shutdown 실패를 확인할 수 있도록 `AggregateError`로 보고합니다.
### request scope 분리

@@ -95,3 +97,3 @@

순환 의존성을 해결하려면 `forwardRef()`를 사용하여 의존성 토큰의 해석을 지연시키세요.
선언 순서 때문에 아직 정의되지 않은 토큰을 참조해야 한다면 `forwardRef()`를 사용하세요. `forwardRef()`는 선언 순서 문제를 위해 토큰 조회를 지연할 뿐이며, 실제 생성자 순환을 해소하지는 않습니다. 그런 순환은 여전히 `CircularDependencyError`로 거부됩니다.

@@ -104,8 +106,9 @@ ```typescript

class ServiceA {
constructor(private serviceB: any) {}
constructor(private readonly serviceB: ServiceB) {}
}
@Inject(forwardRef(() => ServiceA))
class ServiceB {
constructor(private serviceA: any) {}
getStatus() {
return 'ready';
}
}

@@ -137,3 +140,3 @@ ```

### CircularDependencyError
의존성 그래프에서 순환이 감지될 때 발생합니다. 생성자 주입 항목을 확인하고 필요한 경우 `forwardRef()`를 사용하여 순환을 끊으세요.
의존성 그래프에서 순환이 감지될 때 발생합니다. 생성자 주입 항목을 확인하고 공유 상태 추출, 중재자 도입, 수명 주기 경계 변경 등으로 순환을 제거하세요. `forwardRef()`는 선언 순서 문제를 위해 토큰 조회만 지연하며, 실제 생성자 순환을 끊지는 않습니다.

@@ -140,0 +143,0 @@ ### 토큰을 찾을 수 없음 (Token Not Found)

@@ -78,2 +78,4 @@ # @fluojs/di

During disposal, the root container first tears down live request-scope children and then continues with root-owned singleton cleanup even if one or more child disposals fail. When multiple child/root disposals fail, `dispose()` reports an `AggregateError` so callers can inspect every shutdown failure without losing cleanup progress.
### Request Scoping

@@ -95,3 +97,3 @@ Isolated containers can be created to handle per-request state without polluting the root container.

To resolve a circular dependency, use `forwardRef()` to defer the resolution of the dependent token.
Use `forwardRef()` when a token is referenced before its declaration. It defers token lookup for declaration-order issues, but it does not make true constructor cycles resolvable; those cycles are still rejected with `CircularDependencyError`.

@@ -104,8 +106,9 @@ ```typescript

class ServiceA {
constructor(private serviceB: any) {}
constructor(private readonly serviceB: ServiceB) {}
}
@Inject(forwardRef(() => ServiceA))
class ServiceB {
constructor(private serviceA: any) {}
getStatus() {
return 'ready';
}
}

@@ -137,3 +140,3 @@ ```

### CircularDependencyError
Thrown when the container detects a cycle in the dependency graph. Check your constructor injections and use `forwardRef()` where necessary to break the cycle.
Thrown when the container detects a cycle in the dependency graph. Check your constructor injections and remove the cycle by extracting shared state, introducing a mediator, or changing the lifetime boundary. `forwardRef()` only defers token lookup for declaration-order issues; it does not break true constructor cycles.

@@ -140,0 +143,0 @@ ### Token Not Found