##// 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 import { Descriptor, ILifetime, IActivationContext, DescriptorMap, ILifetimeManager } from "./interfaces";
2 import { Descriptor, ILifetime, IActivationContext, DescriptorMap, ILifetimeManager } from "./interfaces";
2 import { argumentNotNull } from "./traits";
3 import { argumentNotNull, prototype } from "./traits";
3
4
4 export interface ActivationContextInfo {
5 export interface ActivationContextInfo {
5 name: string;
6 name: string;
@@ -110,7 +111,14 export class ActivationContext<S> implem
110 // if (trace.isLogEnabled())
111 // if (trace.isLogEnabled())
111 // trace.log("enter {0} {1}", name, d);
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 const v = d.activate(ctx);
122 const v = d.activate(ctx);
115
123
116 // if (trace.isLogEnabled())
124 // if (trace.isLogEnabled())
@@ -130,15 +138,8 export class ActivationContext<S> implem
130 stack;
138 stack;
131 }
139 }
132
140
133 private enter(service: Descriptor<S, unknown>, name: string) {
141 fail(innerException: unknown): never {
134 return new ActivationContext(
142 throw new ActivationError(this._name, this.getStack(), innerException);
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 );
143 }
143 }
144
144 }
145 }
@@ -2,7 +2,7 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 { Descriptor, IContainerBuilder, IDescriptorBuilder, DescriptorMap, ServiceLocator, ILifetime, IDestroyable } from "./interfaces";
4 import { emptyLifetime, LifetimeManager } from "./LifetimeManager";
4 import { emptyLifetime, LifetimeManager } from "./LifetimeManager";
5 import { isDestroyable } from "./traits";
5 import { isDestroyable, prototype } from "./traits";
6
6
7 /**
7 /**
8 * Container builder used to prepare service descriptors and create a IoC container
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 private readonly _lifetime: ILifetime<IDestroyable>;
19 private readonly _lifetime: ILifetime<IDestroyable>;
20
20
21 constructor(parentServices?: DescriptorMap<S>, lifetime?: ILifetime<IDestroyable>) {
21 constructor(parentServices: DescriptorMap<S> | null = null, lifetime?: ILifetime<IDestroyable>) {
22 this._services = Object.create(parentServices ? parentServices : null) as DescriptorMap<S>;
22 this._services = prototype(parentServices);
23 this._lifetimeManager = new LifetimeManager();
23 this._lifetimeManager = new LifetimeManager();
24 this._lifetime = lifetime ?? emptyLifetime();
24 this._lifetime = lifetime ?? emptyLifetime();
25 }
25 }
@@ -1,3 +1,4
1 import { ActivationError } from "./ActivationError";
1 import { Descriptor, ILifetime, DepsMap, IActivationContext, DescriptorMap } from "./interfaces";
2 import { Descriptor, ILifetime, DepsMap, IActivationContext, DescriptorMap } from "./interfaces";
2 import { each, key } from "./traits";
3 import { each, key } from "./traits";
3
4
@@ -53,13 +54,19 export class DescriptorImpl<S, T> implem
53
54
54 const resolve = <K extends keyof S>({ name, lazy, ...opts }: { name: K; lazy?: boolean; default?: S[K] | null; }) => {
55 const resolve = <K extends keyof S>({ name, lazy, ...opts }: { name: K; lazy?: boolean; default?: S[K] | null; }) => {
55 if (lazy) {
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 } else {
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 Object.keys(deps)
70 Object.keys(deps)
64 .map(k => {
71 .map(k => {
65 const ref = deps[k];
72 const ref = deps[k];
@@ -70,11 +77,13 export class DescriptorImpl<S, T> implem
70 .reduce((a, p) => ({ ...a, ...p }), {} ):
77 .reduce((a, p) => ({ ...a, ...p }), {}) :
71 {};
78 {};
72
79
73 const instance = this._factory.call(undefined, makeRefs(this._deps));
80 try {
74
81 const instance = (0,this._factory)(refs);
75 this._lifetime.store(instance, this._cleanup);
82 this._lifetime.store(instance, this._cleanup);
76
77 return instance;
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 const _emptySlot = Object.freeze({
19 const _emptySlot = Object.freeze({
14 has: () => false,
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: () => {
26 store: noop,
19 throw new Error("The specified item isn't registered with this lifetime manager");
20 },
21
27
22 store: () => void (0)
28 remove: noop
23 });
29 });
24
30
25 const _unknonwSlot = Object.freeze({
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 const pendingSlot = <T>(store: (item: T) => void) => ({
46 const pendingSlot = <T>(store: (item: T) => void) => ({
45 has: () => false,
47 has: () => false,
@@ -55,8 +57,6 const pendingSlot = <T>(store: (item: T)
55 store
57 store
56 });
58 });
57
59
58 const noop = () => void(0);
59
60 const valueSlot = <T>(value: T, cleanup: (item: T) => void) => ({
60 const valueSlot = <T>(value: T, cleanup: (item: T) => void) => ({
61 has: () => true,
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 export class LifetimeManager implements ILifetimeManager {
76 export class LifetimeManager implements ILifetimeManager {
75 private _destroyed = false;
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 private _assertNotDestroyed() {
110 private _assertNotDestroyed() {
105 if (this._destroyed)
111 if (this._destroyed)
106 throw new Error("The lifetime manager is destroyed");
112 throw new Error("The lifetime manager is destroyed");
@@ -110,8 +116,7 export class LifetimeManager implements
110 destroy() {
116 destroy() {
111 if (!this._destroyed) {
117 if (!this._destroyed) {
112 this._destroyed = true;
118 this._destroyed = true;
113 this._cleanup.forEach(safeCall);
119 Object.values(this._slots).forEach(({clean}) => )
114 this._cleanup.length = 0;
115 }
120 }
116 }
121 }
117
122
@@ -122,6 +127,10 export const emptyLifetime = <T>(): ILif
122 };
127 };
123
128
124 export const hierarchyLifetime = <T>(): ILifetime<T> => {
129 export const hierarchyLifetime = <T>(): ILifetime<T> => {
130 // TODO: вот здесь ошибка, при первой активации сервиса будет получен и
131 // привязан lifetime из дочернего контейнера, при активации через второй
132 // дочерний контейнера это приведет к ошибке, точнее будет взят экземпляр
133 // из первого контейнера.
125 let _lifetime: ILifetime<T> = _unknownLifetime;
134 let _lifetime: ILifetime<T> = _unknownLifetime;
126 return {
135 return {
127 initialize(context: ILifetimeContext) {
136 initialize(context: ILifetimeContext) {
@@ -170,15 +170,17 export interface Descriptor<S, T> {
170
170
171 /** The context used to initialize lifetime instance {@linkcode ILifetime} */
171 /** The context used to initialize lifetime instance {@linkcode ILifetime} */
172 export interface ILifetimeContext {
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 export interface IActivationContext<S> extends ILifetimeContext, ServiceLocator<S> {
179 export interface IActivationContext<S> extends ILifetimeContext, ServiceLocator<S> {
180
180
181 register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]): void;
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 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
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 export interface IContainerBuilder<S, U extends keyof S> {
229 export interface IContainerBuilder<S, U extends keyof S> {
231 createServiceBuilder<K extends U>(name: K):
230 createServiceBuilder<K extends U>(name: K):
@@ -46,4 +46,7 export const oid = <T extends object>(in
46 const val = (instance as OidSlot)[_oid];
46 const val = (instance as OidSlot)[_oid];
47
47
48 return val ? val : ((instance as OidSlot)[_oid] = `oid_${++_nextOid}`);
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 "listFiles": true,
6 "listFiles": true,
7 "strict": true,
7 "strict": true,
8 "types": [],
8 "types": [],
9 "target": "ES5",
9 "target": "ES2018",
10 "lib": ["es5", "es2015.promise", "es2015.symbol", "es2015.iterable", "dom", "scripthost"]
10 "lib": ["ES2018", "DOM", "ScriptHost"]
11 }
11 }
12 } No newline at end of file
12 }
General Comments 0
You need to be logged in to leave comments. Login now