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