| @@ -1,144 +1,138 | |||
|
|
1 | 1 | import { Descriptor, ILifetime, RegistrationMap, LifetimeContainer, ConfigurableKeys } from "./interfaces"; |
|
|
2 | 2 | import { argumentNotNull } from "./traits"; |
|
|
3 | 3 | |
|
|
4 | 4 | export interface ActivationContextInfo { |
|
|
5 | 5 | name: string; |
|
|
6 | 6 | |
|
|
7 | 7 | service: string; |
|
|
8 | 8 | |
|
|
9 | 9 | } |
|
|
10 | 10 | |
|
|
11 | 11 | let nextId = 1; |
|
|
12 | 12 | |
|
|
13 | 13 | /** This class is created once per `Container.resolve` method call and used to |
|
|
14 | 14 | * cache dependencies and to track created instances. The activation context |
|
|
15 | 15 | * tracks services with `context` activation type. |
|
|
16 | 16 | */ |
|
|
17 | 17 | export class ActivationContext<S extends object> { |
|
|
18 |
private _cache |
|
|
|
18 | private readonly _cache: Record<string, unknown>; | |
|
|
19 | 19 | |
|
|
20 | private _services: Partial<RegistrationMap<S>>; | |
|
|
20 | private readonly _services: Partial<RegistrationMap<S>>; | |
|
|
21 | 21 | |
|
|
22 | private _name: string; | |
|
|
22 | private readonly _name: string; | |
|
|
23 | 23 | |
|
|
24 | private _service: Descriptor<S, unknown>; | |
|
|
24 | private readonly _service: Descriptor<S, unknown>; | |
|
|
25 | 25 | |
|
|
26 | 26 | private readonly _containerLifetimeManager: LifetimeContainer; |
|
|
27 | 27 | |
|
|
28 | private _parent: ActivationContext<S> | undefined; | |
|
|
28 | private readonly _parent: ActivationContext<S> | undefined; | |
|
|
29 | 29 | |
|
|
30 | 30 | /** Creates a new activation context with the specified parameters. |
|
|
31 | 31 | * @param containerLifetimeManager the container which starts the activation process |
|
|
32 | 32 | * @param services the initial service registrations |
|
|
33 | 33 | * @param name the name of the service being activated, this parameter is |
|
|
34 | 34 | * used for the debug purpose. |
|
|
35 | 35 | * @param service the service to activate, this parameter is used for the |
|
|
36 | 36 | * debug purpose. |
|
|
37 | 37 | */ |
|
|
38 | constructor(containerLifetimeManager: LifetimeContainer, services: Partial<RegistrationMap<S>>, name: string, service: Descriptor<S, unknown>) { | |
|
|
38 | constructor(containerLifetimeManager: LifetimeContainer, services: Partial<RegistrationMap<S>>, name: string, service: Descriptor<S, unknown>, cache = {}) { | |
|
|
39 | 39 | this._name = name; |
|
|
40 | 40 | this._service = service; |
|
|
41 |
this._cache = |
|
|
|
41 | this._cache = cache; | |
|
|
42 | 42 | this._services = services; |
|
|
43 | 43 | this._containerLifetimeManager = containerLifetimeManager; |
|
|
44 | 44 | } |
|
|
45 | 45 | |
|
|
46 | 46 | /** the name of the current resolving dependency */ |
|
|
47 | 47 | getName() { |
|
|
48 | 48 | return this._name; |
|
|
49 | 49 | } |
|
|
50 | 50 | |
|
|
51 | 51 | createContainerLifetime<T>() { |
|
|
52 | 52 | return this._containerLifetimeManager.createLifetime<T>(); |
|
|
53 | 53 | } |
|
|
54 | 54 | |
|
|
55 | 55 | /** Resolves the specified dependency in the current context |
|
|
56 | 56 | * @param name The name of the dependency being resolved |
|
|
57 | 57 | */ |
|
|
58 | 58 | resolve<K extends keyof S>(name: K): NonNullable<S[K]>; |
|
|
59 | 59 | /** Resolves the specified dependency with the specified default value if |
|
|
60 | 60 | * the dependency is missing. |
|
|
61 | 61 | * |
|
|
62 | 62 | * @param name The name of the dependency being resolved |
|
|
63 | 63 | * @param def A default value to return in case of the specified dependency |
|
|
64 | 64 | * is missing. |
|
|
65 | 65 | */ |
|
|
66 | 66 | resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T; |
|
|
67 | 67 | resolve<K extends keyof S, T>(name: K, def?: T): S[K] | T | undefined { |
|
|
68 | 68 | const d = this._services[name]; |
|
|
69 | 69 | |
|
|
70 | 70 | if (d !== undefined) { |
|
|
71 | 71 | return this.activate(d, name.toString()); |
|
|
72 | 72 | } else { |
|
|
73 | 73 | if (arguments.length > 1) |
|
|
74 | 74 | return def; |
|
|
75 | 75 | else |
|
|
76 | 76 | throw new Error(`Service ${String(name)} not found`); |
|
|
77 | 77 | } |
|
|
78 | 78 | } |
|
|
79 | 79 | |
|
|
80 | 80 | /** |
|
|
81 | 81 | * registers services local to the the activation context |
|
|
82 | 82 | * |
|
|
83 | 83 | * @name{string} the name of the service |
|
|
84 | 84 | * @service{string} the service descriptor to register |
|
|
85 | 85 | */ |
|
|
86 | 86 | register<K extends ConfigurableKeys<S>>(name: K, service: RegistrationMap<S>[K]) { |
|
|
87 | 87 | argumentNotNull(name, "name"); |
|
|
88 | 88 | |
|
|
89 | 89 | this._services[name] = service; |
|
|
90 | 90 | } |
|
|
91 | 91 | |
|
|
92 | 92 | createLifetime<T>(): ILifetime<T> { |
|
|
93 | 93 | const id = nextId++; |
|
|
94 | 94 | return { |
|
|
95 | 95 | initialize() {}, |
|
|
96 | 96 | has: () => id in this._cache, |
|
|
97 | 97 | get: () => this._cache[id] as T, |
|
|
98 | 98 | store: item => { |
|
|
99 | 99 | this._cache[id] = item; |
|
|
100 | 100 | } |
|
|
101 | 101 | }; |
|
|
102 | 102 | } |
|
|
103 | 103 | |
|
|
104 | 104 | activate<T>(d: Descriptor<S, T>, name: string) { |
|
|
105 | 105 | // TODO: add logging |
|
|
106 | 106 | // if (trace.isLogEnabled()) |
|
|
107 | 107 | // trace.log("enter {0} {1}", name, d); |
|
|
108 | 108 | |
|
|
109 | 109 | const ctx = this.enter(d, name); |
|
|
110 | 110 | const v = d.activate(ctx); |
|
|
111 | 111 | |
|
|
112 | 112 | // if (trace.isLogEnabled()) |
|
|
113 | 113 | // trace.log(`leave ${name}`); |
|
|
114 | 114 | |
|
|
115 | 115 | return v; |
|
|
116 | 116 | } |
|
|
117 | 117 | |
|
|
118 | 118 | getStack(): ActivationContextInfo[] { |
|
|
119 | 119 | const stack = [{ |
|
|
120 | 120 | name: this._name, |
|
|
121 | 121 | service: this._service.toString() |
|
|
122 | 122 | }]; |
|
|
123 | 123 | |
|
|
124 | 124 | return this._parent ? |
|
|
125 | 125 | stack.concat(this._parent.getStack()) : |
|
|
126 | 126 | stack; |
|
|
127 | 127 | } |
|
|
128 | 128 | |
|
|
129 |
private enter(service: Descriptor<S, unknown>, name: string) |
|
|
|
130 | const clone = Object.create(this) as this; | |
|
|
131 | clone._name = name; | |
|
|
132 |
|
|
|
|
133 | clone._parent = this; | |
|
|
134 |
|
|
|
|
135 | return clone; | |
|
|
136 | } | |
|
|
137 | ||
|
|
138 | /** Creates a clone for the current context, used to protect it from modifications */ | |
|
|
139 | clone(): this { | |
|
|
140 | const clone = Object.create(this) as this; | |
|
|
141 | clone._services = Object.create(this._services) as typeof this._services; | |
|
|
142 | return clone; | |
|
|
129 | private enter(service: Descriptor<S, unknown>, name: string) { | |
|
|
130 | return new ActivationContext( | |
|
|
131 | this._containerLifetimeManager, | |
|
|
132 | Object.create(this._services) as typeof this._services, | |
|
|
133 | name, | |
|
|
134 | service, | |
|
|
135 | this._cache | |
|
|
136 | ); | |
|
|
143 | 137 | } |
|
|
144 | 138 | } |
General Comments 0
You need to be logged in to leave comments.
Login now
