@codefast/di 0.3.15 → 0.3.16-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +4 -1
- package/dist/binding-scope.d.mts +2 -0
- package/dist/binding-scope.mjs +2 -0
- package/dist/binding-select.d.mts +6 -2
- package/dist/binding-select.mjs +4 -0
- package/dist/binding.d.mts +74 -7
- package/dist/binding.mjs +12 -0
- package/dist/constraints.d.mts +49 -9
- package/dist/constraints.mjs +71 -19
- package/dist/constructor-type.d.mts +6 -2
- package/dist/container.d.mts +15 -6
- package/dist/container.mjs +139 -55
- package/dist/decorators/inject.d.mts +27 -3
- package/dist/decorators/inject.mjs +28 -11
- package/dist/decorators/injectable.d.mts +13 -1
- package/dist/decorators/injectable.mjs +24 -14
- package/dist/decorators/lifecycle-decorators.d.mts +6 -0
- package/dist/decorators/lifecycle-decorators.mjs +9 -0
- package/dist/dependency-graph.d.mts +17 -2
- package/dist/dependency-graph.mjs +3 -0
- package/dist/environment.d.mts +27 -12
- package/dist/environment.mjs +12 -0
- package/dist/errors.d.mts +69 -8
- package/dist/errors.mjs +65 -1
- package/dist/graph-adapters/cytoscape.d.mts +12 -0
- package/dist/graph-adapters/cytoscape.mjs +3 -0
- package/dist/graph-adapters/dot.d.mts +3 -0
- package/dist/graph-adapters/dot.mjs +3 -0
- package/dist/graph-adapters/reactflow.d.mts +14 -2
- package/dist/graph-adapters/reactflow.mjs +3 -0
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +4 -3
- package/dist/inspector.d.mts +11 -3
- package/dist/inspector.mjs +8 -3
- package/dist/lifecycle.d.mts +8 -6
- package/dist/lifecycle.mjs +48 -52
- package/dist/metadata/metadata-keys.d.mts +42 -1
- package/dist/metadata/metadata-keys.mjs +32 -1
- package/dist/metadata/metadata-reader-token.d.mts +3 -0
- package/dist/metadata/metadata-reader-token.mjs +3 -0
- package/dist/metadata/metadata-types.d.mts +22 -6
- package/dist/metadata/symbol-metadata-reader.d.mts +6 -0
- package/dist/metadata/symbol-metadata-reader.mjs +37 -24
- package/dist/module.d.mts +25 -1
- package/dist/module.mjs +12 -0
- package/dist/registry.d.mts +8 -5
- package/dist/registry.mjs +38 -42
- package/dist/resolve-options.d.mts +4 -0
- package/dist/resolve-options.mjs +4 -0
- package/dist/resolver.d.mts +23 -8
- package/dist/resolver.mjs +67 -43
- package/dist/scope.d.mts +3 -0
- package/dist/scope.mjs +3 -0
- package/dist/token.d.mts +12 -0
- package/dist/token.mjs +9 -0
- package/dist/types.d.mts +44 -6
- package/package.json +11 -5
package/dist/container.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { effectiveBindingScope } from "./binding-scope.mjs";
|
|
2
|
-
import { DisposedContainerError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError } from "./errors.mjs";
|
|
2
|
+
import { AsyncModuleLoadError, CircularDependencyError, DisposedContainerError, InternalError, RebindUnboundTokenError, ScopeViolationError, SyncDisposalNotSupportedError } from "./errors.mjs";
|
|
3
3
|
import { DEFAULT_SLOT, generateBindingId } from "./binding.mjs";
|
|
4
4
|
import { tokenName } from "./token.mjs";
|
|
5
5
|
import { BindingRegistry } from "./registry.mjs";
|
|
6
6
|
import { ScopeManager } from "./scope.mjs";
|
|
7
7
|
import { LifecycleManager } from "./lifecycle.mjs";
|
|
8
|
-
import { slotKeyToResolveOptions } from "./resolve-options.mjs";
|
|
8
|
+
import { injectableSlotToResolveOptions, slotKeyToResolveOptions } from "./resolve-options.mjs";
|
|
9
9
|
import { DependencyResolver } from "./resolver.mjs";
|
|
10
10
|
import { Inspector } from "./inspector.mjs";
|
|
11
11
|
import { buildDependencyGraph } from "./dependency-graph.mjs";
|
|
@@ -142,6 +142,7 @@ var DefaultContainer = class DefaultContainer {
|
|
|
142
142
|
_loadSyncModules(modules) {
|
|
143
143
|
const toLoad = this._collectModuleDeps(modules);
|
|
144
144
|
for (const mod of toLoad) {
|
|
145
|
+
if (!isSyncModule(mod)) throw new AsyncModuleLoadError(mod.name);
|
|
145
146
|
const ref = mod;
|
|
146
147
|
const existing = this._moduleRefs.get(ref);
|
|
147
148
|
if (existing !== void 0) {
|
|
@@ -332,43 +333,116 @@ var DefaultContainer = class DefaultContainer {
|
|
|
332
333
|
const reader = this._getMetadataReader();
|
|
333
334
|
const allBindings = this._registry.allBindings();
|
|
334
335
|
for (const binding of allBindings) {
|
|
335
|
-
if (
|
|
336
|
-
|
|
337
|
-
if (scope !== "singleton") continue;
|
|
338
|
-
const deps = this._getDepsForBinding(binding, reader);
|
|
339
|
-
for (const dep of deps) this._validateDep(tokenName(binding.token), scope, dep, [tokenName(binding.token)], reader);
|
|
336
|
+
if (!this._isSingletonStaticAnalyzableBinding(binding)) continue;
|
|
337
|
+
this._validateSingletonBindingGraph(binding, reader);
|
|
340
338
|
}
|
|
341
339
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
340
|
+
_isSingletonStaticAnalyzableBinding(binding) {
|
|
341
|
+
if (effectiveBindingScope(binding) !== "singleton") return false;
|
|
342
|
+
return binding.kind === "class" || binding.kind === "resolved" || binding.kind === "resolved-async";
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* DFS over explicit constructor / `toResolved*` dependency edges. Follows `toAlias` chains to the
|
|
346
|
+
* terminal binding for scope checks (SPEC §6.9). Skips `toDynamic*` subtrees (opaque).
|
|
347
|
+
*/
|
|
348
|
+
_validateSingletonBindingGraph(root, reader) {
|
|
349
|
+
const rootName = tokenName(root.token);
|
|
350
|
+
const dfs = (current, pathNames, pathBindingIds) => {
|
|
351
|
+
if (pathBindingIds.has(current.id)) return;
|
|
352
|
+
const extendedPathIds = new Set(pathBindingIds);
|
|
353
|
+
extendedPathIds.add(current.id);
|
|
354
|
+
for (const edge of this._collectStaticDependencyEdges(current, reader)) {
|
|
355
|
+
const { terminal, depTokenName } = edge;
|
|
356
|
+
const depScope = this._validationScopeFromTerminal(terminal);
|
|
357
|
+
if (depScope === "opaque") continue;
|
|
358
|
+
if (depScope === "scoped" || depScope === "transient") throw new ScopeViolationError({
|
|
359
|
+
consumerToken: rootName,
|
|
360
|
+
consumerScope: "singleton",
|
|
361
|
+
dependencyToken: depTokenName,
|
|
362
|
+
dependencyScope: depScope,
|
|
363
|
+
path: [...pathNames, depTokenName]
|
|
351
364
|
});
|
|
365
|
+
if (terminal.kind === "class" || terminal.kind === "resolved" || terminal.kind === "resolved-async") dfs(terminal, [...pathNames, depTokenName], extendedPathIds);
|
|
352
366
|
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
367
|
+
};
|
|
368
|
+
dfs(root, [rootName], /* @__PURE__ */ new Set());
|
|
369
|
+
}
|
|
370
|
+
_validationScopeFromTerminal(terminal) {
|
|
371
|
+
switch (terminal.kind) {
|
|
372
|
+
case "constant": return "singleton";
|
|
373
|
+
case "dynamic":
|
|
374
|
+
case "dynamic-async": return "opaque";
|
|
375
|
+
case "class":
|
|
376
|
+
case "resolved":
|
|
377
|
+
case "resolved-async": return terminal.scope;
|
|
378
|
+
case "alias": throw new InternalError("validate: expected terminal binding after alias resolution");
|
|
379
|
+
default: return terminal;
|
|
359
380
|
}
|
|
360
|
-
return result;
|
|
361
381
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
382
|
+
_followAliasChainToTerminal(binding, hint) {
|
|
383
|
+
const cyclePath = [];
|
|
384
|
+
const seenAliasIds = /* @__PURE__ */ new Set();
|
|
385
|
+
let current = binding;
|
|
386
|
+
while (current !== void 0 && current.kind === "alias") {
|
|
387
|
+
if (seenAliasIds.has(current.id)) throw new CircularDependencyError(cyclePath);
|
|
388
|
+
seenAliasIds.add(current.id);
|
|
389
|
+
cyclePath.push(tokenName(current.token));
|
|
390
|
+
const nextToken = current.target;
|
|
391
|
+
const next = this._resolver.peekBindingForValidate(nextToken, hint);
|
|
392
|
+
if (next === void 0) return;
|
|
393
|
+
current = next.binding;
|
|
394
|
+
}
|
|
395
|
+
return current;
|
|
396
|
+
}
|
|
397
|
+
_collectStaticDependencyEdges(binding, reader) {
|
|
398
|
+
const edges = [];
|
|
399
|
+
const pushTerminal = (terminal, displayName) => {
|
|
400
|
+
if (terminal === void 0) return;
|
|
401
|
+
edges.push({
|
|
402
|
+
terminal,
|
|
403
|
+
depTokenName: displayName
|
|
370
404
|
});
|
|
405
|
+
};
|
|
406
|
+
if (binding.kind === "class") {
|
|
407
|
+
const meta = reader.getConstructorMetadata(binding.target);
|
|
408
|
+
if (meta === void 0) return edges;
|
|
409
|
+
for (const param of meta.params) {
|
|
410
|
+
const paramHint = injectableSlotToResolveOptions(param);
|
|
411
|
+
if (param.optional) continue;
|
|
412
|
+
const tokenRef = param.token;
|
|
413
|
+
if (param.multi) {
|
|
414
|
+
const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef, paramHint);
|
|
415
|
+
for (const cand of candidates) {
|
|
416
|
+
const term = this._followAliasChainToTerminal(cand, paramHint);
|
|
417
|
+
pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
|
|
418
|
+
}
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
const found = paramHint === void 0 ? this._resolver.peekBindingForValidate(tokenRef, void 0) : this._resolver.peekBindingForValidate(tokenRef, paramHint);
|
|
422
|
+
if (found === void 0) continue;
|
|
423
|
+
const term = this._followAliasChainToTerminal(found.binding, paramHint);
|
|
424
|
+
pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
|
|
425
|
+
}
|
|
426
|
+
return edges;
|
|
371
427
|
}
|
|
428
|
+
if (binding.kind === "resolved" || binding.kind === "resolved-async") for (const dep of binding.deps) {
|
|
429
|
+
const depHint = injectableSlotToResolveOptions(dep);
|
|
430
|
+
if (dep.optional) continue;
|
|
431
|
+
const tokenRef = dep.token;
|
|
432
|
+
if (dep.multi) {
|
|
433
|
+
const candidates = this._resolver.peekCandidateBindingsForValidate(tokenRef, depHint);
|
|
434
|
+
for (const cand of candidates) {
|
|
435
|
+
const term = this._followAliasChainToTerminal(cand, depHint);
|
|
436
|
+
pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
|
|
437
|
+
}
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
const found = depHint === void 0 ? this._resolver.peekBindingForValidate(tokenRef, void 0) : this._resolver.peekBindingForValidate(tokenRef, depHint);
|
|
441
|
+
if (found === void 0) continue;
|
|
442
|
+
const term = this._followAliasChainToTerminal(found.binding, depHint);
|
|
443
|
+
pushTerminal(term, term !== void 0 ? tokenName(term.token) : "");
|
|
444
|
+
}
|
|
445
|
+
return edges;
|
|
372
446
|
}
|
|
373
447
|
has(token, hint) {
|
|
374
448
|
this._assertNotDisposed();
|
|
@@ -394,7 +468,19 @@ var DefaultContainer = class DefaultContainer {
|
|
|
394
468
|
if (this._disposed) throw new DisposedContainerError();
|
|
395
469
|
}
|
|
396
470
|
};
|
|
471
|
+
function updateSlotTag(slot, tag, value) {
|
|
472
|
+
const existing = [...slot.tags];
|
|
473
|
+
const idx = existing.findIndex(([k]) => k === tag);
|
|
474
|
+
if (idx !== -1) existing[idx] = [tag, value];
|
|
475
|
+
else existing.push([tag, value]);
|
|
476
|
+
return {
|
|
477
|
+
...slot,
|
|
478
|
+
tags: existing
|
|
479
|
+
};
|
|
480
|
+
}
|
|
397
481
|
var BindToBuilderImpl = class {
|
|
482
|
+
_token;
|
|
483
|
+
_commit;
|
|
398
484
|
constructor(_token, _commit) {
|
|
399
485
|
this._token = _token;
|
|
400
486
|
this._commit = _commit;
|
|
@@ -454,6 +540,9 @@ var BindToBuilderImpl = class {
|
|
|
454
540
|
}
|
|
455
541
|
};
|
|
456
542
|
var BindingBuilderImpl = class {
|
|
543
|
+
_token;
|
|
544
|
+
_partial;
|
|
545
|
+
_commit;
|
|
457
546
|
_slot;
|
|
458
547
|
_predicate;
|
|
459
548
|
_committed;
|
|
@@ -492,14 +581,7 @@ var BindingBuilderImpl = class {
|
|
|
492
581
|
return this;
|
|
493
582
|
}
|
|
494
583
|
whenTagged(tag, value) {
|
|
495
|
-
|
|
496
|
-
const idx = existing.findIndex(([k]) => k === tag);
|
|
497
|
-
if (idx !== -1) existing[idx] = [tag, value];
|
|
498
|
-
else existing.push([tag, value]);
|
|
499
|
-
this._slot = {
|
|
500
|
-
...this._slot,
|
|
501
|
-
tags: existing
|
|
502
|
-
};
|
|
584
|
+
this._slot = updateSlotTag(this._slot, tag, value);
|
|
503
585
|
this._doCommit();
|
|
504
586
|
return this;
|
|
505
587
|
}
|
|
@@ -535,6 +617,13 @@ var BindingBuilderImpl = class {
|
|
|
535
617
|
}
|
|
536
618
|
};
|
|
537
619
|
var ScopeBuilderImpl = class {
|
|
620
|
+
_token;
|
|
621
|
+
_partial;
|
|
622
|
+
_slot;
|
|
623
|
+
_predicate;
|
|
624
|
+
_commit;
|
|
625
|
+
_scope;
|
|
626
|
+
_initialPreviousId;
|
|
538
627
|
_onActivation;
|
|
539
628
|
_onDeactivation;
|
|
540
629
|
_committed;
|
|
@@ -576,6 +665,9 @@ var ScopeBuilderImpl = class {
|
|
|
576
665
|
}
|
|
577
666
|
};
|
|
578
667
|
var ConstantBindingBuilderImpl = class {
|
|
668
|
+
_token;
|
|
669
|
+
_value;
|
|
670
|
+
_commit;
|
|
579
671
|
_slot;
|
|
580
672
|
_predicate;
|
|
581
673
|
_onActivation;
|
|
@@ -620,14 +712,7 @@ var ConstantBindingBuilderImpl = class {
|
|
|
620
712
|
return this;
|
|
621
713
|
}
|
|
622
714
|
whenTagged(tag, value) {
|
|
623
|
-
|
|
624
|
-
const idx = existing.findIndex(([k]) => k === tag);
|
|
625
|
-
if (idx !== -1) existing[idx] = [tag, value];
|
|
626
|
-
else existing.push([tag, value]);
|
|
627
|
-
this._slot = {
|
|
628
|
-
...this._slot,
|
|
629
|
-
tags: existing
|
|
630
|
-
};
|
|
715
|
+
this._slot = updateSlotTag(this._slot, tag, value);
|
|
631
716
|
this._doCommit();
|
|
632
717
|
return this;
|
|
633
718
|
}
|
|
@@ -649,6 +734,9 @@ var ConstantBindingBuilderImpl = class {
|
|
|
649
734
|
}
|
|
650
735
|
};
|
|
651
736
|
var AliasBindingBuilderImpl = class {
|
|
737
|
+
_token;
|
|
738
|
+
_target;
|
|
739
|
+
_commit;
|
|
652
740
|
_slot;
|
|
653
741
|
_predicate;
|
|
654
742
|
_committed;
|
|
@@ -688,14 +776,7 @@ var AliasBindingBuilderImpl = class {
|
|
|
688
776
|
return this;
|
|
689
777
|
}
|
|
690
778
|
whenTagged(tag, value) {
|
|
691
|
-
|
|
692
|
-
const idx = existing.findIndex(([k]) => k === tag);
|
|
693
|
-
if (idx !== -1) existing[idx] = [tag, value];
|
|
694
|
-
else existing.push([tag, value]);
|
|
695
|
-
this._slot = {
|
|
696
|
-
...this._slot,
|
|
697
|
-
tags: existing
|
|
698
|
-
};
|
|
779
|
+
this._slot = updateSlotTag(this._slot, tag, value);
|
|
699
780
|
this._doCommit();
|
|
700
781
|
return this;
|
|
701
782
|
}
|
|
@@ -706,6 +787,9 @@ var AliasBindingBuilderImpl = class {
|
|
|
706
787
|
return this._committed;
|
|
707
788
|
}
|
|
708
789
|
};
|
|
790
|
+
/**
|
|
791
|
+
* @since 0.3.16-canary.0
|
|
792
|
+
*/
|
|
709
793
|
const Container = {
|
|
710
794
|
create() {
|
|
711
795
|
return new DefaultContainer();
|
|
@@ -2,10 +2,16 @@ import { Constructor } from "../constructor-type.mjs";
|
|
|
2
2
|
import { Token } from "../token.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/decorators/inject.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* @since 0.3.16-canary.0
|
|
7
|
+
*/
|
|
5
8
|
interface InjectOptions {
|
|
6
9
|
name?: string;
|
|
7
10
|
tags?: ReadonlyArray<readonly [tag: string, value: unknown]>;
|
|
8
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* @since 0.3.16-canary.0
|
|
14
|
+
*/
|
|
9
15
|
interface InjectionDescriptor<Value = unknown> {
|
|
10
16
|
readonly token: Token<Value> | Constructor<Value>;
|
|
11
17
|
readonly optional: boolean;
|
|
@@ -13,12 +19,30 @@ interface InjectionDescriptor<Value = unknown> {
|
|
|
13
19
|
readonly name?: string;
|
|
14
20
|
readonly tags?: ReadonlyArray<readonly [string, unknown]>;
|
|
15
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* @since 0.3.16-canary.0
|
|
24
|
+
*/
|
|
16
25
|
type InjectableDependency<Value = unknown> = Token<Value> | Constructor<Value> | InjectionDescriptor<Value>;
|
|
26
|
+
/**
|
|
27
|
+
* @since 0.3.16-canary.0
|
|
28
|
+
*/
|
|
17
29
|
declare function isInjectionDescriptor(value: unknown): value is InjectionDescriptor;
|
|
30
|
+
/**
|
|
31
|
+
* @since 0.3.16-canary.0
|
|
32
|
+
*/
|
|
18
33
|
declare function normalizeToDescriptor(dep: InjectableDependency): InjectionDescriptor;
|
|
19
34
|
type ClassAccessorDecorator<This, Value> = (target: ClassAccessorDecoratorTarget<This, Value>, context: ClassAccessorDecoratorContext<This, Value>) => ClassAccessorDecoratorResult<This, Value> | void;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @since 0.3.16-canary.0
|
|
37
|
+
*/
|
|
38
|
+
declare function inject<const Value>(token: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value> & ClassAccessorDecorator<unknown, Value>;
|
|
39
|
+
/**
|
|
40
|
+
* @since 0.3.16-canary.0
|
|
41
|
+
*/
|
|
42
|
+
declare function optional<const Value>(token: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Value | undefined>;
|
|
43
|
+
/**
|
|
44
|
+
* @since 0.3.16-canary.0
|
|
45
|
+
*/
|
|
46
|
+
declare function injectAll<const Value>(token: Token<Value> | Constructor<Value>, options?: InjectOptions): InjectionDescriptor<Array<Value>>;
|
|
23
47
|
//#endregion
|
|
24
48
|
export { InjectOptions, InjectableDependency, InjectionDescriptor, inject, injectAll, isInjectionDescriptor, normalizeToDescriptor, optional };
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import { MissingContainerContextError } from "../errors.mjs";
|
|
1
|
+
import { InternalError, MissingContainerContextError } from "../errors.mjs";
|
|
2
2
|
import { getActiveContainer } from "../environment.mjs";
|
|
3
3
|
import { injectableSlotToResolveOptions } from "../resolve-options.mjs";
|
|
4
|
-
import { INJECT_ACCESSOR_KEY } from "../metadata/metadata-keys.mjs";
|
|
4
|
+
import { INJECT_ACCESSOR_KEY, accessorMetadataByMetadataObjectMap } from "../metadata/metadata-keys.mjs";
|
|
5
5
|
//#region src/decorators/inject.ts
|
|
6
|
+
/**
|
|
7
|
+
* @since 0.3.16-canary.0
|
|
8
|
+
*/
|
|
6
9
|
function isInjectionDescriptor(value) {
|
|
7
10
|
if (value === null || value === void 0) return false;
|
|
8
11
|
const type = typeof value;
|
|
9
12
|
if (type !== "object" && type !== "function") return false;
|
|
10
13
|
return "token" in value && "optional" in value && "multi" in value && typeof value.optional === "boolean" && typeof value.multi === "boolean";
|
|
11
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* @since 0.3.16-canary.0
|
|
17
|
+
*/
|
|
12
18
|
function normalizeToDescriptor(dep) {
|
|
13
19
|
if (isInjectionDescriptor(dep)) return materializeInjectionDescriptor(dep);
|
|
14
20
|
return {
|
|
@@ -48,9 +54,9 @@ function materializeInjectionDescriptor(dep) {
|
|
|
48
54
|
};
|
|
49
55
|
return base;
|
|
50
56
|
}
|
|
51
|
-
function buildInjectionDescriptor(
|
|
57
|
+
function buildInjectionDescriptor(token, options) {
|
|
52
58
|
const base = {
|
|
53
|
-
token
|
|
59
|
+
token,
|
|
54
60
|
optional: false,
|
|
55
61
|
multi: false
|
|
56
62
|
};
|
|
@@ -69,15 +75,20 @@ function buildInjectionDescriptor(t, options) {
|
|
|
69
75
|
};
|
|
70
76
|
return base;
|
|
71
77
|
}
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
/**
|
|
79
|
+
* @since 0.3.16-canary.0
|
|
80
|
+
*/
|
|
81
|
+
function inject(token, options) {
|
|
82
|
+
const descriptor = buildInjectionDescriptor(token, options);
|
|
74
83
|
const decoratorFn = (_target, context) => {
|
|
84
|
+
if (context.static === true) throw new InternalError("@inject() on static accessors is not supported; only instance accessors participate in runWithContainer-based property injection.");
|
|
75
85
|
const meta = context.metadata;
|
|
76
86
|
if (!Array.isArray(meta[INJECT_ACCESSOR_KEY])) meta[INJECT_ACCESSOR_KEY] = [];
|
|
77
87
|
meta[INJECT_ACCESSOR_KEY].push({
|
|
78
88
|
key: context.name,
|
|
79
89
|
descriptor
|
|
80
90
|
});
|
|
91
|
+
accessorMetadataByMetadataObjectMap.set(context.metadata, meta[INJECT_ACCESSOR_KEY]);
|
|
81
92
|
context.addInitializer(function() {
|
|
82
93
|
const container = getActiveContainer();
|
|
83
94
|
if (container === void 0) throw new MissingContainerContextError(String(context.name));
|
|
@@ -85,7 +96,7 @@ function inject(t, options) {
|
|
|
85
96
|
...options.name !== void 0 ? { name: options.name } : {},
|
|
86
97
|
...options.tags !== void 0 ? { tags: options.tags } : {}
|
|
87
98
|
});
|
|
88
|
-
const value = descriptor.optional ? container.resolveOptional(
|
|
99
|
+
const value = descriptor.optional ? container.resolveOptional(token, hint) : container.resolve(token, hint);
|
|
89
100
|
context.access.set(this, value);
|
|
90
101
|
});
|
|
91
102
|
return {};
|
|
@@ -100,9 +111,12 @@ function inject(t, options) {
|
|
|
100
111
|
Object.defineProperties(decoratorFn, props);
|
|
101
112
|
return decoratorFn;
|
|
102
113
|
}
|
|
103
|
-
|
|
114
|
+
/**
|
|
115
|
+
* @since 0.3.16-canary.0
|
|
116
|
+
*/
|
|
117
|
+
function optional(token, options) {
|
|
104
118
|
const base = {
|
|
105
|
-
token
|
|
119
|
+
token,
|
|
106
120
|
optional: true,
|
|
107
121
|
multi: false
|
|
108
122
|
};
|
|
@@ -121,9 +135,12 @@ function optional(t, options) {
|
|
|
121
135
|
};
|
|
122
136
|
return base;
|
|
123
137
|
}
|
|
124
|
-
|
|
138
|
+
/**
|
|
139
|
+
* @since 0.3.16-canary.0
|
|
140
|
+
*/
|
|
141
|
+
function injectAll(token, options) {
|
|
125
142
|
const base = {
|
|
126
|
-
token
|
|
143
|
+
token,
|
|
127
144
|
optional: false,
|
|
128
145
|
multi: true
|
|
129
146
|
};
|
|
@@ -3,6 +3,9 @@ import { BindingScope } from "../types.mjs";
|
|
|
3
3
|
import { InjectableDependency } from "./inject.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/decorators/injectable.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* @since 0.3.16-canary.0
|
|
8
|
+
*/
|
|
6
9
|
interface AutoRegisterRegistry {
|
|
7
10
|
register(target: Constructor, scope: BindingScope): void;
|
|
8
11
|
entries(): ReadonlyArray<{
|
|
@@ -10,11 +13,20 @@ interface AutoRegisterRegistry {
|
|
|
10
13
|
scope: BindingScope;
|
|
11
14
|
}>;
|
|
12
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* @since 0.3.16-canary.0
|
|
18
|
+
*/
|
|
13
19
|
declare function createAutoRegisterRegistry(): AutoRegisterRegistry;
|
|
20
|
+
/**
|
|
21
|
+
* @since 0.3.16-canary.0
|
|
22
|
+
*/
|
|
14
23
|
interface InjectableOptions {
|
|
15
24
|
autoRegister?: AutoRegisterRegistry;
|
|
16
25
|
scope?: BindingScope;
|
|
17
26
|
}
|
|
18
|
-
|
|
27
|
+
/**
|
|
28
|
+
* @since 0.3.16-canary.0
|
|
29
|
+
*/
|
|
30
|
+
declare function injectable(deps?: ReadonlyArray<InjectableDependency>, options?: InjectableOptions): (target: unknown, context: ClassDecoratorContext) => void;
|
|
19
31
|
//#endregion
|
|
20
32
|
export { AutoRegisterRegistry, type InjectableDependency, InjectableOptions, createAutoRegisterRegistry, injectable };
|
|
@@ -1,49 +1,59 @@
|
|
|
1
|
-
import { INJECTABLE_KEY, constructorMetadataMap } from "../metadata/metadata-keys.mjs";
|
|
1
|
+
import { INJECTABLE_KEY, INJECT_ACCESSOR_KEY, accessorMetadataByConstructorMap, constructorMetadataMap } from "../metadata/metadata-keys.mjs";
|
|
2
2
|
import { normalizeToDescriptor } from "./inject.mjs";
|
|
3
3
|
//#region src/decorators/injectable.ts
|
|
4
|
+
/**
|
|
5
|
+
* @since 0.3.16-canary.0
|
|
6
|
+
*/
|
|
4
7
|
function createAutoRegisterRegistry() {
|
|
5
|
-
const
|
|
8
|
+
const _registeredEntries = [];
|
|
6
9
|
return {
|
|
7
10
|
register(target, scope) {
|
|
8
|
-
|
|
11
|
+
_registeredEntries.push({
|
|
9
12
|
target,
|
|
10
13
|
scope
|
|
11
14
|
});
|
|
12
15
|
},
|
|
13
16
|
entries() {
|
|
14
|
-
return
|
|
17
|
+
return _registeredEntries;
|
|
15
18
|
}
|
|
16
19
|
};
|
|
17
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* @since 0.3.16-canary.0
|
|
23
|
+
*/
|
|
18
24
|
function injectable(deps, options) {
|
|
19
25
|
return function(target, context) {
|
|
20
|
-
const
|
|
21
|
-
const descriptor = normalizeToDescriptor(
|
|
22
|
-
const
|
|
26
|
+
const constructorMetadata = { params: (deps ?? []).map((dependency, index) => {
|
|
27
|
+
const descriptor = normalizeToDescriptor(dependency);
|
|
28
|
+
const baseParameterMetadata = {
|
|
23
29
|
index,
|
|
24
30
|
token: descriptor.token,
|
|
25
31
|
optional: descriptor.optional,
|
|
26
32
|
multi: descriptor.multi
|
|
27
33
|
};
|
|
28
34
|
if (descriptor.name !== void 0 && descriptor.tags !== void 0) return {
|
|
29
|
-
...
|
|
35
|
+
...baseParameterMetadata,
|
|
30
36
|
name: descriptor.name,
|
|
31
37
|
tags: descriptor.tags
|
|
32
38
|
};
|
|
33
39
|
if (descriptor.name !== void 0) return {
|
|
34
|
-
...
|
|
40
|
+
...baseParameterMetadata,
|
|
35
41
|
name: descriptor.name
|
|
36
42
|
};
|
|
37
43
|
if (descriptor.tags !== void 0) return {
|
|
38
|
-
...
|
|
44
|
+
...baseParameterMetadata,
|
|
39
45
|
tags: descriptor.tags
|
|
40
46
|
};
|
|
41
|
-
return
|
|
47
|
+
return baseParameterMetadata;
|
|
42
48
|
}) };
|
|
43
|
-
constructorMetadataMap.set(target,
|
|
49
|
+
constructorMetadataMap.set(target, constructorMetadata);
|
|
44
50
|
try {
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
51
|
+
const metadata = context.metadata;
|
|
52
|
+
if (metadata !== null && typeof metadata === "object") {
|
|
53
|
+
const accessorList = metadata[INJECT_ACCESSOR_KEY];
|
|
54
|
+
if (Array.isArray(accessorList) && accessorList.length > 0) accessorMetadataByConstructorMap.set(target, accessorList);
|
|
55
|
+
metadata[INJECTABLE_KEY] = constructorMetadata;
|
|
56
|
+
}
|
|
47
57
|
} catch {}
|
|
48
58
|
if (options?.autoRegister !== void 0) {
|
|
49
59
|
const scope = options.scope ?? "transient";
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
//#region src/decorators/lifecycle-decorators.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* @since 0.3.16-canary.0
|
|
4
|
+
*/
|
|
2
5
|
declare function postConstruct(): (target: unknown, context: ClassMethodDecoratorContext) => void;
|
|
6
|
+
/**
|
|
7
|
+
* @since 0.3.16-canary.0
|
|
8
|
+
*/
|
|
3
9
|
declare function preDestroy(): (target: unknown, context: ClassMethodDecoratorContext) => void;
|
|
4
10
|
//#endregion
|
|
5
11
|
export { postConstruct, preDestroy };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { InternalError } from "../errors.mjs";
|
|
1
2
|
import { LIFECYCLE_KEY, lifecycleByConstructorMetadataMap, lifecycleMetadataMap } from "../metadata/metadata-keys.mjs";
|
|
2
3
|
//#region src/decorators/lifecycle-decorators.ts
|
|
3
4
|
function appendUniqueMethod(metadata, phase, methodName) {
|
|
@@ -20,8 +21,12 @@ function registerByConstructor(target, phase, methodName) {
|
|
|
20
21
|
preDestroy: phase === "preDestroy" ? [methodName] : []
|
|
21
22
|
});
|
|
22
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* @since 0.3.16-canary.0
|
|
26
|
+
*/
|
|
23
27
|
function postConstruct() {
|
|
24
28
|
return function(target, context) {
|
|
29
|
+
if (context.static === true) throw new InternalError("@postConstruct() applies to instance methods only; static methods are not invoked during instance lifecycle.");
|
|
25
30
|
const methodName = String(context.name);
|
|
26
31
|
registerByConstructor(target, "postConstruct", methodName);
|
|
27
32
|
const existing = lifecycleMetadataMap.get(context.metadata);
|
|
@@ -53,8 +58,12 @@ function postConstruct() {
|
|
|
53
58
|
} catch {}
|
|
54
59
|
};
|
|
55
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* @since 0.3.16-canary.0
|
|
63
|
+
*/
|
|
56
64
|
function preDestroy() {
|
|
57
65
|
return function(target, context) {
|
|
66
|
+
if (context.static === true) throw new InternalError("@preDestroy() applies to instance methods only; static methods are not invoked during instance teardown.");
|
|
58
67
|
const methodName = String(context.name);
|
|
59
68
|
registerByConstructor(target, "preDestroy", methodName);
|
|
60
69
|
const existing = lifecycleMetadataMap.get(context.metadata);
|
|
@@ -3,6 +3,9 @@ import { BindingRegistry } from "./registry.mjs";
|
|
|
3
3
|
import { MetadataReader } from "./metadata/metadata-types.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/dependency-graph.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* @since 0.3.16-canary.0
|
|
8
|
+
*/
|
|
6
9
|
interface GraphNode {
|
|
7
10
|
readonly id: string;
|
|
8
11
|
readonly tokenName: string;
|
|
@@ -10,19 +13,31 @@ interface GraphNode {
|
|
|
10
13
|
readonly scope: BindingScope;
|
|
11
14
|
readonly fromParent: boolean;
|
|
12
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* @since 0.3.16-canary.0
|
|
18
|
+
*/
|
|
13
19
|
interface GraphEdge {
|
|
14
20
|
readonly from: string;
|
|
15
21
|
readonly to: string;
|
|
16
22
|
readonly label?: string;
|
|
17
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* @since 0.3.16-canary.0
|
|
26
|
+
*/
|
|
18
27
|
interface ContainerGraphJson {
|
|
19
|
-
readonly nodes:
|
|
20
|
-
readonly edges:
|
|
28
|
+
readonly nodes: ReadonlyArray<GraphNode>;
|
|
29
|
+
readonly edges: ReadonlyArray<GraphEdge>;
|
|
21
30
|
readonly includesParent: boolean;
|
|
22
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* @since 0.3.16-canary.0
|
|
34
|
+
*/
|
|
23
35
|
interface GraphOptions {
|
|
24
36
|
readonly includeParent?: boolean;
|
|
25
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* @since 0.3.16-canary.0
|
|
40
|
+
*/
|
|
26
41
|
declare function buildDependencyGraph(registry: BindingRegistry, metadataReader: MetadataReader, options: GraphOptions | undefined, parentRegistry?: BindingRegistry): ContainerGraphJson;
|
|
27
42
|
//#endregion
|
|
28
43
|
export { ContainerGraphJson, GraphEdge, GraphNode, GraphOptions, buildDependencyGraph };
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { effectiveBindingScope } from "./binding-scope.mjs";
|
|
2
2
|
import { tokenName } from "./token.mjs";
|
|
3
3
|
//#region src/dependency-graph.ts
|
|
4
|
+
/**
|
|
5
|
+
* @since 0.3.16-canary.0
|
|
6
|
+
*/
|
|
4
7
|
function buildDependencyGraph(registry, metadataReader, options, parentRegistry) {
|
|
5
8
|
const nodes = [];
|
|
6
9
|
const edges = [];
|