##// END OF EJS Templates
WIP service descriptors
cin -
r13:dc3d64c43573 default
parent child
Show More
@@ -29,7 +29,7 export class ActivationContext<S> implem
29 29
30 30 private readonly _service: Descriptor<S, unknown>;
31 31
32 private readonly _containerLifetimeManager: ILifetimeManager;
32 private readonly _lifetimeManagers: ILifetimeManager[];
33 33
34 34 private readonly _parent: ActivationContext<S> | undefined;
35 35
@@ -41,12 +41,12 export class ActivationContext<S> implem
41 41 * @param service the service to activate, this parameter is used for the
42 42 * debug purpose.
43 43 */
44 constructor(containerLifetimeManager: ILifetimeManager, services: DescriptorMap<S>, name: string, service: Descriptor<S, unknown>, cache = {}) {
44 constructor(lifetimeManagers: ILifetimeManager[], services: DescriptorMap<S>, name: string, service: Descriptor<S, unknown>, cache = {}) {
45 45 this._name = name;
46 46 this._service = service;
47 47 this._cache = cache;
48 48 this._services = services;
49 this._containerLifetimeManager = containerLifetimeManager;
49 this._lifetimeManagers = lifetimeManagers;
50 50 }
51 51
52 52 /** the name of the current resolving dependency */
@@ -54,10 +54,6 export class ActivationContext<S> implem
54 54 return this._name;
55 55 }
56 56
57 createContainerLifetime<T>() {
58 return this._containerLifetimeManager.create<T>();
59 }
60
61 57 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
62 58 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
63 59 resolve<K extends keyof S, T>(name: K, def?: T): NonNullable<S[K]> | T | undefined {
@@ -1,32 +1,22
1 1 import { ActivationContext } from "./ActivationContext";
2 2 import { ActivationError } from "./ActivationError";
3 3 import { ContainerBuilder } from "./ContainerBuilder";
4 import { LifetimeManager } from "./LifetimeManager";
4 5 import { DescriptorMap, IContainerBuilder, IDestroyable, ILifetimeManager, ServiceLocator } from "./interfaces";
5 6
6 7 export class Container<S> implements ServiceLocator<S>, IDestroyable {
7 8 private readonly _services: DescriptorMap<S>;
8 9
9 private readonly _lifetimeManager: ILifetimeManager;
10 private readonly _lifetimeManagers: ILifetimeManager[];
10 11
11 12 private _disposed: boolean;
12 13
13 14 private readonly _onDestroyed: () => void;
14 15
15 constructor(services: DescriptorMap<S>, lifetimeManager: ILifetimeManager, destroyed: () => void) {
16 this._services = {
17 ...services,
18 container: {
19 configurable: false,
20 activate: () => this
21 },
22 childContainer: {
23 configurable: false,
24 activate: () => this.createChildBuilder(),
25 }
26 };
27
16 constructor(services: DescriptorMap<S>, lifetimeManagers: ILifetimeManager[], destroyed: () => void) {
17 this._services = services;
28 18 this._disposed = false;
29 this._lifetimeManager = lifetimeManager;
19 this._lifetimeManagers = lifetimeManagers.concat(new LifetimeManager());
30 20 this._onDestroyed = destroyed;
31 21
32 22 }
@@ -56,7 +46,7 export class Container<S> implements Ser
56 46 else
57 47 throw new Error(`Service '${String(name)}' isn't found`);
58 48 } else {
59 const context = new ActivationContext(this._lifetimeManager, this._services, String(name), d);
49 const context = new ActivationContext(this._lifetimeManagers, this._services, String(name), d);
60 50 try {
61 51 return d.activate(context);
62 52 } catch (error) {
@@ -1,25 +1,30
1 1 import { Container } from "./Container";
2 2 import { DescriptorBuilder } from "./DescriptorBuilder";
3 import { Descriptor, IContainerBuilder, IDescriptorBuilder, DescriptorMap, ServiceLocator, ILifetime, IDestroyable } from "./interfaces";
3 import { containerSelfDescriptor } from "./DescriptorImpl";
4 import { Descriptor, IContainerBuilder, IDescriptorBuilder, DescriptorMap, ServiceLocator, ILifetime, IDestroyable, ContainerServices, ContainerServicesConstraint } from "./interfaces";
4 5 import { emptyLifetime, LifetimeManager } from "./LifetimeManager";
5 6 import { isDestroyable, prototype } from "./traits";
6 7
7 8 /**
8 9 * Container builder used to prepare service descriptors and create a IoC container
9 10 */
10 export class ContainerBuilder<S, U extends keyof S> implements
11 export class ContainerBuilder<S extends ContainerServicesConstraint<S>, U extends keyof S> implements
11 12 IContainerBuilder<S, U> {
12 13
13 14 private _pending = 1;
14 15
15 private readonly _services: DescriptorMap<S>;
16 private readonly _services: DescriptorMap<ContainerServices<S>>;
16 17
17 18 private readonly _lifetimeManager = new LifetimeManager();
18 19
19 20 private readonly _lifetime: ILifetime<IDestroyable>;
20 21
21 22 constructor(parentServices: DescriptorMap<S> | null = null, lifetime?: ILifetime<IDestroyable>) {
22 this._services = prototype(parentServices);
23 this._services = {
24 ...parentServices,
25 container: containerSelfDescriptor as any,
26 childContainer: containerSelfDescriptor as any
27 };
23 28 this._lifetimeManager = new LifetimeManager();
24 29 this._lifetime = lifetime ?? emptyLifetime();
25 30 }
@@ -13,6 +13,13 export interface DescriptorImplArgs<S, T
13 13 dependencies?: DepsMap<S>;
14 14 }
15 15
16 export const containerSelfDescriptor = <S>() => Object.freeze({
17 level: 0,
18 activate(context: IActivationContext<S>) {
19 return context.createChildContainer();
20 }
21 });
22
16 23
17 24 export class DescriptorImpl<S, T> implements Descriptor<S, T> {
18 25
@@ -29,12 +29,10 const _emptySlot = Object.freeze({
29 29 cleanup: noop,
30 30 });
31 31
32 const _destroy = (item: IDestroyable) => item.destroy();
32 const _destroy = (item: unknown) => () => isDestroyable(item) && item.destroy() ;
33 33
34 34 const _makeCleanup = <T>(value: T, cleanup?: (item: T) => void) =>
35 cleanup ? () => cleanup(value) :
36 isDestroyable(value) ? () => _destroy(value) :
37 noop;
35 cleanup ? () => cleanup(value) : _destroy(value);
38 36
39 37 const newSlot = <T>(put: (item: ILifetimeSlot<T>) => void, remove: () => void): ILifetimeSlot<T> => ({
40 38 has: () => false,
@@ -119,6 +117,12 export const emptyLifetime = <T>() => ()
119 117
120 118 let nextId = 1;
121 119
120 export const containerLifetime = <T>() => {
121 const slotId = nextId ++;
122 return (context: ILifetimeContext) =>
123 context.ownerSlot<T>(slotId);
124 };
125
122 126 export const hierarchyLifetime = <T>() => {
123 127 const slotId = nextId++;
124 128 return (context: ILifetimeContext) =>
@@ -1,3 +1,4
1 import { ContainerBuilder } from "./ContainerBuilder";
1 2 import { key } from "./traits";
2 3
3 4 export interface IDestroyable {
@@ -157,6 +158,10 export type ContainerServicesConstraint<
157 158
158 159 export interface Descriptor<S, T> {
159 160
161 /** The level of the service in the containers chain.
162 */
163 readonly level: number;
164
160 165 /** This flags indicates that this registration can be replaced or overridden. */
161 166 readonly configurable?: boolean;
162 167
@@ -170,6 +175,9 export interface Descriptor<S, T> {
170 175
171 176 /** The context used to initialize lifetime instance {@linkcode ILifetime} */
172 177 export interface ILifetimeContext {
178
179 ownerSlot<T>(slotId: string | number): ILifetimeSlot<T>;
180
173 181 contextSlot<T>(slotId: string | number): ILifetimeSlot<T>;
174 182
175 183 containerSlot<T>(slotId: string | number): ILifetimeSlot<T>;
@@ -181,6 +189,10 export interface IActivationContext<S> e
181 189 register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]): void;
182 190
183 191 fail(error: unknown): never;
192
193 selfContainer(): ServiceLocator<S>;
194
195 createChildContainer(): IContainerBuilder<S, Exclude<keyof S, ContainerKeys>>;
184 196 }
185 197
186 198 /**
@@ -196,10 +208,10 export type DescriptorMap<S> = {
196 208
197 209 type ContainerKeys = keyof ContainerProvided<object>;
198 210
199 export type ContainerProvided<S extends ContainerServicesConstraint<S>> = {
200 container: ServiceLocator<ContainerServices<S>>;
211 export type ContainerProvided<S, U extends keyof S = keyof S> = {
212 container: ServiceLocator<ContainerProvided<S>>;
201 213
202 childContainer: IContainerBuilder<ContainerServices<S>, Exclude<keyof S, ContainerKeys>>;
214 childContainer: IContainerBuilder<S, U>;
203 215 };
204 216
205 217
@@ -208,8 +220,8 export type ContainerProvided<S extends
208 220 *
209 221 * Сервисы, предоставляемые контейнером не могут быть null или undefined.
210 222 */
211 export type ContainerServices<S extends ContainerServicesConstraint<S>> = {
212 [k in keyof S | ContainerKeys]:
223 export type ContainerServices<S> = {
224 [k in (keyof S) | ContainerKeys]:
213 225 k extends ContainerKeys ? ContainerProvided<S>[k] :
214 226 k extends keyof S ? S[k] : never
215 227 };
@@ -2,7 +2,7
2 2 import { describe, it } from "mocha";
3 3 import { Container } from "../Container";
4 4 import { ContainerBuilder } from "../ContainerBuilder";
5 import { ContainerServices, DepsMap, IContainerBuilder, Refs, Resolver } from "../interfaces";
5 import { ContainerServices, DepsMap, IContainerBuilder, Refs, Resolver, ServiceLocator } from "../interfaces";
6 6 import { fluent } from "../traits";
7 7
8 8 class Foo {
@@ -48,9 +48,9 if (refs && refs.name === "foo") {
48 48 refs.default;
49 49 }
50 50
51 declare const x: ContainerServices<Services>;
51 declare const x: ServiceLocator<ContainerServices<Services>>;
52 52
53 x.container.resolve("container");
53 x.resolve("container").resolve("container");
54 54
55 55
56 56 mmap({
General Comments 0
You need to be logged in to leave comments. Login now