##// END OF EJS Templates
WIP lifetime services, change target to ES2018
cin -
r11:dd37d4287c45 default
parent child
Show More
@@ -1,5 +1,6
1 import { ActivationError } from "./ActivationError";
1 2 import { Descriptor, ILifetime, IActivationContext, DescriptorMap, ILifetimeManager } from "./interfaces";
2 import { argumentNotNull } from "./traits";
3 import { argumentNotNull, prototype } from "./traits";
3 4
4 5 export interface ActivationContextInfo {
5 6 name: string;
@@ -110,7 +111,14 export class ActivationContext<S> implem
110 111 // if (trace.isLogEnabled())
111 112 // trace.log("enter {0} {1}", name, d);
112 113
113 const ctx = this.enter(d, name);
114 const ctx = new ActivationContext(
115 this._containerLifetimeManager,
116 d.hasOverrides ? prototype(this._services) : this._services,
117 name,
118 d,
119 this._cache
120 );
121
114 122 const v = d.activate(ctx);
115 123
116 124 // if (trace.isLogEnabled())
@@ -130,15 +138,8 export class ActivationContext<S> implem
130 138 stack;
131 139 }
132 140
133 private enter(service: Descriptor<S, unknown>, name: string) {
134 return new ActivationContext(
135 this._containerLifetimeManager,
136 service.hasOverrides ?
137 Object.create(this._services) as typeof this._services :
138 this._services,
139 name,
140 service,
141 this._cache
142 );
141 fail(innerException: unknown): never {
142 throw new ActivationError(this._name, this.getStack(), innerException);
143 143 }
144
144 145 }
@@ -2,7 +2,7 import { Container } from "./Container";
2 2 import { DescriptorBuilder } from "./DescriptorBuilder";
3 3 import { Descriptor, IContainerBuilder, IDescriptorBuilder, DescriptorMap, ServiceLocator, ILifetime, IDestroyable } from "./interfaces";
4 4 import { emptyLifetime, LifetimeManager } from "./LifetimeManager";
5 import { isDestroyable } from "./traits";
5 import { isDestroyable, prototype } from "./traits";
6 6
7 7 /**
8 8 * Container builder used to prepare service descriptors and create a IoC container
@@ -18,8 +18,8 export class ContainerBuilder<S, U exten
18 18
19 19 private readonly _lifetime: ILifetime<IDestroyable>;
20 20
21 constructor(parentServices?: DescriptorMap<S>, lifetime?: ILifetime<IDestroyable>) {
22 this._services = Object.create(parentServices ? parentServices : null) as DescriptorMap<S>;
21 constructor(parentServices: DescriptorMap<S> | null = null, lifetime?: ILifetime<IDestroyable>) {
22 this._services = prototype(parentServices);
23 23 this._lifetimeManager = new LifetimeManager();
24 24 this._lifetime = lifetime ?? emptyLifetime();
25 25 }
@@ -1,3 +1,4
1 import { ActivationError } from "./ActivationError";
1 2 import { Descriptor, ILifetime, DepsMap, IActivationContext, DescriptorMap } from "./interfaces";
2 3 import { each, key } from "./traits";
3 4
@@ -53,13 +54,19 export class DescriptorImpl<S, T> implem
53 54
54 55 const resolve = <K extends keyof S>({ name, lazy, ...opts }: { name: K; lazy?: boolean; default?: S[K] | null; }) => {
55 56 if (lazy) {
56 return () => "default" in opts ? context.resolve(name, opts.default) : context.resolve(name);
57 return "default" in opts ?
58 () => context.resolve(name, opts.default) :
59 () => context.resolve(name);
57 60 } else {
58 return "default" in opts ? context.resolve(name, opts.default) : context.resolve(name);
61 return "default" in opts ?
62 context.resolve(name, opts.default) :
63 context.resolve(name);
59 64 }
60 65 };
61 66
62 const makeRefs = (deps: typeof this._deps) => deps ?
67 const deps = this._deps;
68
69 const refs = deps ?
63 70 Object.keys(deps)
64 71 .map(k => {
65 72 const ref = deps[k];
@@ -70,11 +77,13 export class DescriptorImpl<S, T> implem
70 77 .reduce((a, p) => ({ ...a, ...p }), {} ):
71 78 {};
72 79
73 const instance = this._factory.call(undefined, makeRefs(this._deps));
74
80 try {
81 const instance = (0,this._factory)(refs);
75 82 this._lifetime.store(instance, this._cleanup);
76
77 83 return instance;
84 } catch(err) {
85 context.fail(err);
86 }
78 87 }
79 88
80 89
@@ -10,16 +10,22 const safeCall = (item: () => void) => {
10 10 }
11 11 };
12 12
13 const noop = () => {};
14
15 const fail = (message: string) => {
16 throw new Error(message);
17 };
18
13 19 const _emptySlot = Object.freeze({
14 20 has: () => false,
15 21
16 initialize: () => void (0),
22 initialize: noop,
23
24 get: fail("The specified item isn't registered with a lifetime manager"),
17 25
18 get: () => {
19 throw new Error("The specified item isn't registered with this lifetime manager");
20 },
26 store: noop,
21 27
22 store: () => void (0)
28 remove: noop
23 29 });
24 30
25 31 const _unknonwSlot = Object.freeze({
@@ -36,10 +42,6 const _unknonwSlot = Object.freeze({
36 42 }
37 43 });
38 44
39 let nextId = 0;
40
41 const singletons: { [K: string]: unknown } = {};
42
43 45
44 46 const pendingSlot = <T>(store: (item: T) => void) => ({
45 47 has: () => false,
@@ -55,8 +57,6 const pendingSlot = <T>(store: (item: T)
55 57 store
56 58 });
57 59
58 const noop = () => void(0);
59
60 60 const valueSlot = <T>(value: T, cleanup: (item: T) => void) => ({
61 61 has: () => true,
62 62
@@ -71,6 +71,8 const valueSlot = <T>(value: T, cleanup:
71 71 }
72 72 });
73 73
74 const singletons: { [K: string]: unknown } = {};
75
74 76 export class LifetimeManager implements ILifetimeManager {
75 77 private _destroyed = false;
76 78
@@ -101,6 +103,10 export class LifetimeManager implements
101 103 };
102 104 }
103 105
106 remove(cookie: string) {
107 delete this._slots[cookie];
108 }
109
104 110 private _assertNotDestroyed() {
105 111 if (this._destroyed)
106 112 throw new Error("The lifetime manager is destroyed");
@@ -110,8 +116,7 export class LifetimeManager implements
110 116 destroy() {
111 117 if (!this._destroyed) {
112 118 this._destroyed = true;
113 this._cleanup.forEach(safeCall);
114 this._cleanup.length = 0;
119 Object.values(this._slots).forEach(({clean}) => )
115 120 }
116 121 }
117 122
@@ -122,6 +127,10 export const emptyLifetime = <T>(): ILif
122 127 };
123 128
124 129 export const hierarchyLifetime = <T>(): ILifetime<T> => {
130 // TODO: вот здесь ошибка, при первой активации сервиса будет получен и
131 // привязан lifetime из дочернего контейнера, при активации через второй
132 // дочерний контейнера это приведет к ошибке, точнее будет взят экземпляр
133 // из первого контейнера.
125 134 let _lifetime: ILifetime<T> = _unknownLifetime;
126 135 return {
127 136 initialize(context: ILifetimeContext) {
@@ -170,15 +170,17 export interface Descriptor<S, T> {
170 170
171 171 /** The context used to initialize lifetime instance {@linkcode ILifetime} */
172 172 export interface ILifetimeContext {
173 createLifetime<T>(): ILifetime<T>;
173 contextSlot<T>(slotId: string): ILifetimeSlot<T>;
174 174
175 createContainerLifetime<T>(): ILifetime<T>;
175 containerSlot<T>(slotId: string): ILifetimeSlot<T>;
176 176
177 177 }
178 178
179 179 export interface IActivationContext<S> extends ILifetimeContext, ServiceLocator<S> {
180 180
181 181 register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]): void;
182
183 fail(error: unknown): never;
182 184 }
183 185
184 186 /**
@@ -223,9 +225,6 export interface ServiceLocator<S> {
223 225 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
224 226 }
225 227
226 export interface LifetimeContainer {
227 createLifetime<T>(): ILifetime<T>;
228 }
229 228
230 229 export interface IContainerBuilder<S, U extends keyof S> {
231 230 createServiceBuilder<K extends U>(name: K):
@@ -46,4 +46,7 export const oid = <T extends object>(in
46 46 const val = (instance as OidSlot)[_oid];
47 47
48 48 return val ? val : ((instance as OidSlot)[_oid] = `oid_${++_nextOid}`);
49 }; No newline at end of file
49 };
50
51 export const prototype = <T extends object>(instance: T | null): T =>
52 Object.create(instance) as T; No newline at end of file
@@ -6,7 +6,7
6 6 "listFiles": true,
7 7 "strict": true,
8 8 "types": [],
9 "target": "ES5",
10 "lib": ["es5", "es2015.promise", "es2015.symbol", "es2015.iterable", "dom", "scripthost"]
9 "target": "ES2018",
10 "lib": ["ES2018", "DOM", "ScriptHost"]
11 11 }
12 12 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now