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