@codefast/di 0.5.0-canary.7 → 0.5.0-canary.8

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.
Files changed (100) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +3 -1
  3. package/dist/binding.d.ts +47 -5
  4. package/dist/binding.d.ts.map +1 -1
  5. package/dist/binding.js +44 -0
  6. package/dist/binding.js.map +1 -1
  7. package/dist/constructor-type.d.ts +4 -5
  8. package/dist/constructor-type.d.ts.map +1 -1
  9. package/dist/container/binding-builders.d.ts +32 -11
  10. package/dist/container/binding-builders.d.ts.map +1 -1
  11. package/dist/container/binding-builders.js +140 -191
  12. package/dist/container/binding-builders.js.map +1 -1
  13. package/dist/container/container.d.ts.map +1 -1
  14. package/dist/container/container.js +75 -92
  15. package/dist/container/container.js.map +1 -1
  16. package/dist/decorators/inject.d.ts +2 -4
  17. package/dist/decorators/inject.d.ts.map +1 -1
  18. package/dist/decorators/inject.js.map +1 -1
  19. package/dist/errors.d.ts +14 -0
  20. package/dist/errors.d.ts.map +1 -1
  21. package/dist/errors.js +17 -0
  22. package/dist/errors.js.map +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/introspection/inspector.js +1 -1
  28. package/dist/introspection/inspector.js.map +1 -1
  29. package/dist/metadata/metadata-keys.d.ts +3 -6
  30. package/dist/metadata/metadata-keys.d.ts.map +1 -1
  31. package/dist/metadata/metadata-keys.js +3 -6
  32. package/dist/metadata/metadata-keys.js.map +1 -1
  33. package/dist/registry.d.ts +14 -2
  34. package/dist/registry.d.ts.map +1 -1
  35. package/dist/registry.js +35 -45
  36. package/dist/registry.js.map +1 -1
  37. package/dist/resolution/activation-need.d.ts +25 -0
  38. package/dist/resolution/activation-need.d.ts.map +1 -0
  39. package/dist/resolution/activation-need.js +64 -0
  40. package/dist/resolution/activation-need.js.map +1 -0
  41. package/dist/resolution/binding-lookup-cache.d.ts +41 -0
  42. package/dist/resolution/binding-lookup-cache.d.ts.map +1 -0
  43. package/dist/resolution/binding-lookup-cache.js +102 -0
  44. package/dist/resolution/binding-lookup-cache.js.map +1 -0
  45. package/dist/resolution/binding-select.js +1 -4
  46. package/dist/resolution/binding-select.js.map +1 -1
  47. package/dist/resolution/class-introspector.d.ts +27 -0
  48. package/dist/resolution/class-introspector.d.ts.map +1 -0
  49. package/dist/resolution/class-introspector.js +60 -0
  50. package/dist/resolution/class-introspector.js.map +1 -0
  51. package/dist/resolution/diagnostics.d.ts +41 -0
  52. package/dist/resolution/diagnostics.d.ts.map +1 -0
  53. package/dist/resolution/diagnostics.js +18 -0
  54. package/dist/resolution/diagnostics.js.map +1 -0
  55. package/dist/resolution/environment.d.ts +22 -1
  56. package/dist/resolution/environment.d.ts.map +1 -1
  57. package/dist/resolution/environment.js +27 -1
  58. package/dist/resolution/environment.js.map +1 -1
  59. package/dist/resolution/instantiation-plan.d.ts +15 -15
  60. package/dist/resolution/instantiation-plan.d.ts.map +1 -1
  61. package/dist/resolution/instantiation-plan.js +68 -48
  62. package/dist/resolution/instantiation-plan.js.map +1 -1
  63. package/dist/resolution/lifecycle.d.ts +2 -0
  64. package/dist/resolution/lifecycle.d.ts.map +1 -1
  65. package/dist/resolution/lifecycle.js +16 -11
  66. package/dist/resolution/lifecycle.js.map +1 -1
  67. package/dist/resolution/resolution-path.d.ts +4 -13
  68. package/dist/resolution/resolution-path.d.ts.map +1 -1
  69. package/dist/resolution/resolution-path.js +3 -16
  70. package/dist/resolution/resolution-path.js.map +1 -1
  71. package/dist/resolution/resolver.d.ts +7 -2
  72. package/dist/resolution/resolver.d.ts.map +1 -1
  73. package/dist/resolution/resolver.js +116 -328
  74. package/dist/resolution/resolver.js.map +1 -1
  75. package/dist/resolution/scope.d.ts +7 -15
  76. package/dist/resolution/scope.d.ts.map +1 -1
  77. package/dist/resolution/scope.js +48 -44
  78. package/dist/resolution/scope.js.map +1 -1
  79. package/package.json +7 -97
  80. package/src/binding.ts +104 -5
  81. package/src/constructor-type.ts +4 -5
  82. package/src/container/binding-builders.ts +180 -283
  83. package/src/container/container.ts +90 -103
  84. package/src/decorators/inject.ts +3 -5
  85. package/src/errors.ts +21 -0
  86. package/src/index.ts +1 -0
  87. package/src/introspection/inspector.ts +1 -1
  88. package/src/metadata/metadata-keys.ts +3 -6
  89. package/src/registry.ts +38 -59
  90. package/src/resolution/activation-need.ts +81 -0
  91. package/src/resolution/binding-lookup-cache.ts +132 -0
  92. package/src/resolution/binding-select.ts +1 -4
  93. package/src/resolution/class-introspector.ts +74 -0
  94. package/src/resolution/diagnostics.ts +43 -0
  95. package/src/resolution/environment.ts +31 -1
  96. package/src/resolution/instantiation-plan.ts +113 -61
  97. package/src/resolution/lifecycle.ts +16 -11
  98. package/src/resolution/resolution-path.ts +5 -20
  99. package/src/resolution/resolver.ts +142 -371
  100. package/src/resolution/scope.ts +50 -49
@@ -1,24 +1,19 @@
1
+ import type { Binding } from "#/binding";
2
+ import { NO_INSTANCE } from "#/binding";
1
3
  import { MissingScopeContextError } from "#/errors";
2
4
  import type { BindingIdentifier } from "#/types";
3
5
 
4
- /**
5
- * Sentinel returned by {@link ScopeManager.peekSingleton} when nothing is cached —
6
- * lets hot paths read the cache with a single Map.get even for `undefined` values.
7
- *
8
- * @since 0.5.0-canary.7
9
- */
10
- export const SINGLETON_MISS: unique symbol = Symbol("di:singleton-miss");
11
-
12
6
  /**
13
7
  * @since 0.3.16-canary.0
14
8
  */
15
9
  export class ScopeManager {
16
- // Singleton cache: bindingId -> instance
17
- readonly #singletons = new Map<BindingIdentifier, unknown>();
18
- // In-flight promises for async singleton creation
19
- readonly #inflight = new Map<BindingIdentifier, Promise<unknown>>();
20
- // Scoped cache (for child containers): bindingId -> instance
21
- readonly #scoped = new Map<BindingIdentifier, unknown>();
10
+ // Instances live on their binding (see ARCHITECTURE.md); this list only lets disposal and
11
+ // `inspect()` enumerate them.
12
+ #singletonBindings: Array<Binding<unknown>> | undefined;
13
+ // In-flight promises for async singleton creation — only an async resolve ever needs it.
14
+ #inflight: Map<BindingIdentifier, Promise<unknown>> | undefined;
15
+ // Scoped cache — only a child container resolving a `scoped` binding ever needs it.
16
+ #scoped: Map<BindingIdentifier, unknown> | undefined;
22
17
 
23
18
  readonly isChild: boolean;
24
19
 
@@ -26,69 +21,75 @@ export class ScopeManager {
26
21
  this.isChild = isChild;
27
22
  }
28
23
 
29
- hasSingleton(id: BindingIdentifier): boolean {
30
- return this.#singletons.has(id);
31
- }
32
-
33
- getSingleton<Value>(id: BindingIdentifier): Value {
34
- return this.#singletons.get(id) as Value;
35
- }
36
-
37
- /** Cached value, or {@link SINGLETON_MISS} — one Map.get on the hot path. */
38
- peekSingleton(id: BindingIdentifier): unknown {
39
- const value = this.#singletons.get(id);
40
- if (value === undefined && !this.#singletons.has(id)) {
41
- return SINGLETON_MISS;
24
+ setSingleton<const Value>(binding: Binding<Value>, instance: unknown): void {
25
+ if (binding.instance === NO_INSTANCE) {
26
+ (this.#singletonBindings ??= []).push(binding as Binding<unknown>);
42
27
  }
43
- return value;
44
- }
45
-
46
- setSingleton(id: BindingIdentifier, instance: unknown): void {
47
- this.#singletons.set(id, instance);
28
+ binding.instance = instance;
48
29
  }
49
30
 
50
- deleteSingleton(id: BindingIdentifier): boolean {
51
- return this.#singletons.delete(id);
31
+ /** Every binding in this container holding a cached singleton. */
32
+ cachedSingletons(): ReadonlyArray<Binding<unknown>> {
33
+ return this.#singletonBindings ?? EMPTY_BINDINGS;
52
34
  }
53
35
 
54
- getAllSingletons(): ReadonlyMap<BindingIdentifier, unknown> {
55
- return this.#singletons;
36
+ deleteSingleton<const Value>(binding: Binding<Value>): boolean {
37
+ if (binding.instance === NO_INSTANCE) {
38
+ return false;
39
+ }
40
+ binding.instance = NO_INSTANCE;
41
+ const tracked = this.#singletonBindings;
42
+ if (tracked !== undefined) {
43
+ const index = tracked.indexOf(binding as Binding<unknown>);
44
+ if (index !== -1) {
45
+ tracked.splice(index, 1);
46
+ }
47
+ }
48
+ return true;
56
49
  }
57
50
 
58
51
  getInflight(id: BindingIdentifier): Promise<unknown> | undefined {
59
- return this.#inflight.get(id);
52
+ return this.#inflight?.get(id);
60
53
  }
61
54
 
62
55
  setInflight(id: BindingIdentifier, p: Promise<unknown>): void {
63
- this.#inflight.set(id, p);
56
+ (this.#inflight ??= new Map()).set(id, p);
64
57
  }
65
58
 
66
59
  clearInflight(id: BindingIdentifier): void {
67
- this.#inflight.delete(id);
60
+ this.#inflight?.delete(id);
68
61
  }
69
62
 
70
63
  hasScoped(id: BindingIdentifier): boolean {
71
- return this.#scoped.has(id);
64
+ return this.#scoped !== undefined && this.#scoped.has(id);
72
65
  }
73
66
 
74
67
  getScoped<Value>(id: BindingIdentifier): Value {
75
- return this.#scoped.get(id) as Value;
68
+ return this.#scoped?.get(id) as Value;
76
69
  }
77
70
 
78
71
  setScoped(id: BindingIdentifier, instance: unknown): void {
79
72
  if (!this.isChild) {
80
73
  throw new MissingScopeContextError("(unknown)");
81
74
  }
82
- this.#scoped.set(id, instance);
83
- }
84
-
85
- getAllScoped(): ReadonlyMap<BindingIdentifier, unknown> {
86
- return this.#scoped;
75
+ (this.#scoped ??= new Map()).set(id, instance);
87
76
  }
88
77
 
89
78
  clearAll(): void {
90
- this.#singletons.clear();
91
- this.#inflight.clear();
92
- this.#scoped.clear();
79
+ const tracked = this.#singletonBindings;
80
+ if (tracked !== undefined) {
81
+ for (const binding of tracked) {
82
+ binding.instance = NO_INSTANCE;
83
+ }
84
+ tracked.length = 0;
85
+ }
86
+ this.#inflight?.clear();
87
+ this.#scoped?.clear();
88
+ }
89
+ /** Whether the deferred table behind `#scoped` has had to be built. */
90
+ get isBuilt(): boolean {
91
+ return this.#scoped !== undefined;
93
92
  }
94
93
  }
94
+
95
+ const EMPTY_BINDINGS: ReadonlyArray<Binding<unknown>> = [];