##// END OF EJS Templates
Added CancelledError, fixed lint warnings
cin -
r172:3969a8fb8049 release v1.4.6 default
parent child
Show More
@@ -0,0 +1,12
1 import { Cancellation } from "./Cancellation";
2 import { ICancellation } from "./interfaces";
3
4 export class CancelledError extends Error {
5 readonly cancellationToken: ICancellation;
6
7 constructor(message = "The operation is cancelled", ct = Cancellation.none) {
8 super(message);
9 this.cancellationToken = ct;
10 this.name = "CancelledError";
11 }
12 }
@@ -1,106 +1,107
1 import { CancellationAggregate } from "./CancellationAggregate";
1 import { CancellationAggregate } from "./CancellationAggregate";
2 import { CancelledError } from "./CancelledError";
2 import { ICancellation, IDestroyable } from "./interfaces";
3 import { ICancellation, IDestroyable } from "./interfaces";
3 import { argumentNotNull, destroyed } from "./safe";
4 import { argumentNotNull, destroyed } from "./safe";
4
5
5 export class Cancellation implements ICancellation {
6 export class Cancellation implements ICancellation {
6 private _reason: any;
7 private _reason: any;
7 private _cbs: Array<(e: any) => void> | undefined;
8 private _cbs: Array<(e: any) => void> | undefined;
8
9
9 constructor(action: (cancel: (e?: any) => void) => void) {
10 constructor(action: (cancel: (e?: any) => void) => void) {
10 argumentNotNull(action, "action");
11 argumentNotNull(action, "action");
11
12
12 action(this._cancel.bind(this));
13 action(this._cancel.bind(this));
13 }
14 }
14
15
15 isSupported(): boolean {
16 isSupported(): boolean {
16 return true;
17 return true;
17 }
18 }
18 throwIfRequested(): void {
19 throwIfRequested(): void {
19 if (this._reason)
20 if (this._reason)
20 throw this._reason;
21 throw this._reason;
21 }
22 }
22
23
23 isRequested(): boolean {
24 isRequested(): boolean {
24 return !!this._reason;
25 return !!this._reason;
25 }
26 }
26
27
27 register(cb: (e: any) => void): IDestroyable {
28 register(cb: (e: any) => void): IDestroyable {
28 argumentNotNull(cb, "cb");
29 argumentNotNull(cb, "cb");
29
30
30 if (this._reason) {
31 if (this._reason) {
31 cb(this._reason);
32 cb(this._reason);
32 return destroyed;
33 return destroyed;
33 } else {
34 } else {
34 if (!this._cbs)
35 if (!this._cbs)
35 this._cbs = [cb];
36 this._cbs = [cb];
36 else
37 else
37 this._cbs.push(cb);
38 this._cbs.push(cb);
38
39
39 const me = this;
40 const me = this;
40 return {
41 return {
41 destroy() {
42 destroy() {
42 me._unregister(cb);
43 me._unregister(cb);
43 }
44 }
44 };
45 };
45 }
46 }
46 }
47 }
47
48
48 private _unregister(cb: any) {
49 private _unregister(cb: any) {
49 if (this._cbs) {
50 if (this._cbs) {
50 const i = this._cbs.indexOf(cb);
51 const i = this._cbs.indexOf(cb);
51 if (i >= 0)
52 if (i >= 0)
52 this._cbs.splice(i, 1);
53 this._cbs.splice(i, 1);
53 }
54 }
54 }
55 }
55
56
56 private _cancel(reason: any) {
57 private _cancel(reason: any) {
57 if (this._reason)
58 if (this._reason)
58 return;
59 return;
59
60
60 this._reason = (reason = reason || new Error("Operation cancelled"));
61 this._reason = (reason = reason || new CancelledError(undefined, this));
61
62
62 if (this._cbs) {
63 if (this._cbs) {
63 this._cbs.forEach(cb => cb(reason));
64 this._cbs.forEach(cb => cb(reason));
64 this._cbs = undefined;
65 this._cbs = undefined;
65 }
66 }
66 }
67 }
67
68
68 static readonly none: ICancellation = {
69 static readonly none: ICancellation = {
69 isSupported(): boolean {
70 isSupported(): boolean {
70 return false;
71 return false;
71 },
72 },
72
73
73 throwIfRequested(): void {
74 throwIfRequested(): void {
74 },
75 },
75
76
76 isRequested(): boolean {
77 isRequested(): boolean {
77 return false;
78 return false;
78 },
79 },
79
80
80 register(_cb: (e: any) => void): IDestroyable {
81 register(_cb: (e: any) => void): IDestroyable {
81 return destroyed;
82 return destroyed;
82 }
83 }
83 };
84 };
84
85
85 /**
86 /**
86 * Combines multiple cancellation tokens to the single aggregated token.
87 * Combines multiple cancellation tokens to the single aggregated token.
87 *
88 *
88 * Aggregated token will be considered as signalled when some tokens are
89 * Aggregated token will be considered as signalled when some tokens are
89 * signalled. The cancellation callback can be registered with the `register`
90 * signalled. The cancellation callback can be registered with the `register`
90 * method, it will be fired once with the first signalled token, all other
91 * method, it will be fired once with the first signalled token, all other
91 * tokens will be ignored.
92 * tokens will be ignored.
92 *
93 *
93 * The tokens which don't support cancellation are filtered out, if there are
94 * The tokens which don't support cancellation are filtered out, if there are
94 * no tokens left in the list the method returns `Cancellation.none`.
95 * no tokens left in the list the method returns `Cancellation.none`.
95 *
96 *
96 * @param args The list of cancellation tokens to combine
97 * @param args The list of cancellation tokens to combine
97 * @returns
98 * @returns Aggregated cancellation token
98 */
99 */
99 static combine(...args: ICancellation[]) {
100 static combine(...args: ICancellation[]) {
100 const tokens = args.filter(ct => ct.isSupported());
101 const tokens = args.filter(ct => ct.isSupported());
101 return tokens.length > 1 ?
102 return tokens.length > 1 ?
102 new CancellationAggregate(tokens) :
103 new CancellationAggregate(tokens) :
103 tokens.length == 1 ? tokens[0] :
104 tokens.length === 1 ? tokens[0] :
104 this.none;
105 this.none;
105 }
106 }
106 }
107 }
@@ -1,41 +1,41
1 import { ICancellation, IDestroyable } from "./interfaces";
1 import { ICancellation, IDestroyable } from "./interfaces";
2
2
3 export class CancellationAggregate implements ICancellation {
3 export class CancellationAggregate implements ICancellation {
4 private readonly _tokens: ICancellation[];
4 private readonly _tokens: ICancellation[];
5
5
6 constructor(tokens: ICancellation[]) {
6 constructor(tokens: ICancellation[]) {
7 this._tokens = tokens || [];
7 this._tokens = tokens || [];
8 }
8 }
9
9
10 throwIfRequested() {
10 throwIfRequested() {
11 this._tokens.forEach(ct => ct.throwIfRequested());
11 this._tokens.forEach(ct => ct.throwIfRequested());
12 }
12 }
13
13
14 isRequested() {
14 isRequested() {
15 return this._tokens.some(ct => ct.isRequested());
15 return this._tokens.some(ct => ct.isRequested());
16 }
16 }
17 isSupported() {
17 isSupported() {
18 return !!this._tokens.length;
18 return !!this._tokens.length;
19 }
19 }
20 register(cb: (e: any) => void): IDestroyable {
20 register(cb: (e: any) => void): IDestroyable {
21 let fired = false;
21 let fired = false;
22
22
23 const once = (e: any) => {
23 const once = (e: any) => {
24 if (!fired) {
24 if (!fired) {
25 fired = true;
25 fired = true;
26 destroy();
26 destroy();
27 cb(e);
27 cb(e);
28 }
28 }
29 }
29 };
30
30
31 const destroy = () => subscriptions
31 const destroy = () => subscriptions
32 .splice(0,subscriptions.length) // empty array
32 .splice(0, subscriptions.length) // empty array
33 .forEach(subscription => subscription.destroy()); // cleanup
33 .forEach(subscription => subscription.destroy()); // cleanup
34
34
35 const subscriptions = this._tokens.map(ct => ct.register(once))
35 const subscriptions = this._tokens.map(ct => ct.register(once));
36
36
37 return {
37 return {
38 destroy
38 destroy
39 };
39 };
40 }
40 }
41 }
41 }
@@ -1,35 +1,38
1 import { Cancellation } from "../Cancellation";
1 import { Cancellation } from "../Cancellation";
2 import { IAsyncComponent, ICancellation, ICancellable } from "../interfaces";
2 import { IAsyncComponent, ICancellation, ICancellable } from "../interfaces";
3
3
4 const noop = () => void (0);
4 const noop = () => void (0);
5
5
6 export class AsyncComponent implements IAsyncComponent, ICancellable {
6 export class AsyncComponent implements IAsyncComponent, ICancellable {
7 _cancel: ((e: any) => void) = noop;
7 _cancel: ((e: any) => void) = noop;
8
8
9 _completion: Promise<void> = Promise.resolve();
9 _completion: Promise<void> = Promise.resolve();
10
10
11 getCompletion() { return this._completion; }
11 getCompletion() { return this._completion; }
12
12
13 runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) {
13 runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) {
14 // create inner cancellation bound to the passed cancellation token
14 // create inner cancellation bound to the passed cancellation token
15 const inner = new Cancellation(cancel => {
15 const inner = new Cancellation(cancel => {
16 this._cancel = cancel;
16 this._cancel = cancel;
17 });
17 });
18
18
19 const guard = async () => {
19 const guard = async () => {
20 try {
20 try {
21 return op(Cancellation.combine(ct, inner));
21 const combined = Cancellation.combine(ct, inner);
22 const result = await op(combined);
23 combined.throwIfRequested();
24 return result;
22 } finally {
25 } finally {
23 // after the operation is complete we need to cleanup the
26 // after the operation is complete we need to cleanup the
24 // resources
27 // resources
25 this._cancel = noop;
28 this._cancel = noop;
26 }
29 }
27 };
30 };
28
31
29 return this._completion = guard();
32 return this._completion = guard();
30 }
33 }
31
34
32 cancel(reason: any) {
35 cancel(reason: any) {
33 this._cancel(reason);
36 this._cancel(reason);
34 }
37 }
35 }
38 }
@@ -1,216 +1,216
1 import { IDestroyable, MapOf } from "../interfaces";
1 import { IDestroyable, MapOf } from "../interfaces";
2 import { argumentNotNull, isDestroyable, argumentNotEmptyString, isRemovable } from "../safe";
2 import { argumentNotNull, isDestroyable, argumentNotEmptyString, isRemovable } from "../safe";
3 import { ILifetime, ServiceContainer } from "./interfaces";
3 import { ILifetime, ServiceContainer } from "./interfaces";
4 import { ActivationContext } from "./ActivationContext";
4 import { ActivationContext } from "./ActivationContext";
5
5
6 function safeCall(item: () => void) {
6 function safeCall(item: () => void) {
7 try {
7 try {
8 item();
8 item();
9 } catch {
9 } catch {
10 // silence!
10 // silence!
11 }
11 }
12 }
12 }
13
13
14 const emptyLifetime: ILifetime = Object.freeze({
14 const emptyLifetime: ILifetime = Object.freeze({
15 has() {
15 has() {
16 return false;
16 return false;
17 },
17 },
18
18
19 initialize() {
19 initialize() {
20
20
21 },
21 },
22
22
23 get() {
23 get() {
24 throw new Error("The specified item isn't registered with this lifetime manager");
24 throw new Error("The specified item isn't registered with this lifetime manager");
25 },
25 },
26
26
27 store() {
27 store() {
28 // does nothing
28 // does nothing
29 },
29 },
30
30
31 toString() {
31 toString() {
32 return `[object EmptyLifetime]`;
32 return `[object EmptyLifetime]`;
33 }
33 }
34
34
35 });
35 });
36
36
37 const unknownLifetime: ILifetime = Object.freeze({
37 const unknownLifetime: ILifetime = Object.freeze({
38 has() {
38 has() {
39 return false;
39 return false;
40 },
40 },
41 initialize() {
41 initialize() {
42 throw new Error("Can't call initialize on the unknown lifetime object");
42 throw new Error("Can't call initialize on the unknown lifetime object");
43 },
43 },
44 get() {
44 get() {
45 throw new Error("The lifetime object isn't initialized");
45 throw new Error("The lifetime object isn't initialized");
46 },
46 },
47 store() {
47 store() {
48 throw new Error("Can't store a value in the unknown lifetime object");
48 throw new Error("Can't store a value in the unknown lifetime object");
49 },
49 },
50 toString() {
50 toString() {
51 return `[object UnknownLifetime]`;
51 return `[object UnknownLifetime]`;
52 }
52 }
53 });
53 });
54
54
55 let nextId = 0;
55 let nextId = 0;
56
56
57 const singletons: any = {};
57 const singletons: any = {};
58
58
59 export class LifetimeManager implements IDestroyable {
59 export class LifetimeManager implements IDestroyable {
60 private _cleanup: (() => void)[] = [];
60 private _cleanup: (() => void)[] = [];
61 private _cache: MapOf<any> = {};
61 private _cache: MapOf<any> = {};
62 private _destroyed = false;
62 private _destroyed = false;
63
63
64 private _pending: MapOf<boolean> = {};
64 private _pending: MapOf<boolean> = {};
65
65
66 create(): ILifetime {
66 create(): ILifetime {
67 const self = this;
67 const self = this;
68 const id = ++nextId;
68 const id = ++nextId;
69 return {
69 return {
70 has() {
70 has() {
71 return (id in self._cache);
71 return (id in self._cache);
72 },
72 },
73
73
74 get() {
74 get() {
75 const t = self._cache[id];
75 const t = self._cache[id];
76 if (t === undefined)
76 if (t === undefined)
77 throw new Error(`The item with with the key ${id} isn't found`);
77 throw new Error(`The item with with the key ${id} isn't found`);
78 return t;
78 return t;
79 },
79 },
80
80
81 initialize() {
81 initialize() {
82 if (self._pending[id])
82 if (self._pending[id])
83 throw Error(`Cyclic reference detected: the item with the key ${id} is already activating.`);
83 throw Error(`Cyclic reference detected: the item with the key ${id} is already activating.`);
84 self._pending[id] = true;
84 self._pending[id] = true;
85 },
85 },
86
86
87 store(item: any, cleanup?: (item: any) => void) {
87 store(item: any, cleanup?: (item: any) => void) {
88 argumentNotNull(id, "id");
88 argumentNotNull(id, "id");
89 argumentNotNull(item, "item");
89 argumentNotNull(item, "item");
90
90
91 if (this.has())
91 if (this.has())
92 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
92 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
93 delete self._pending[id];
93 delete self._pending[id];
94
94
95 self._cache[id] = item;
95 self._cache[id] = item;
96
96
97 if (self._destroyed)
97 if (self._destroyed)
98 throw new Error("Lifetime manager is destroyed");
98 throw new Error("Lifetime manager is destroyed");
99 if (cleanup) {
99 if (cleanup) {
100 self._cleanup.push(() => cleanup(item));
100 self._cleanup.push(() => cleanup(item));
101 } else if (isDestroyable(item)) {
101 } else if (isDestroyable(item)) {
102 self._cleanup.push(() => item.destroy());
102 self._cleanup.push(() => item.destroy());
103 }
103 }
104 }
104 }
105 };
105 };
106 }
106 }
107
107
108 destroy() {
108 destroy() {
109 if (!this._destroyed) {
109 if (!this._destroyed) {
110 this._destroyed = true;
110 this._destroyed = true;
111 this._cleanup.forEach(safeCall);
111 this._cleanup.forEach(safeCall);
112 this._cleanup.length = 0;
112 this._cleanup.length = 0;
113 }
113 }
114 }
114 }
115
115
116 static empty(): ILifetime {
116 static empty(): ILifetime {
117 return emptyLifetime;
117 return emptyLifetime;
118 }
118 }
119
119
120 static hierarchyLifetime() {
120 static hierarchyLifetime() {
121 let _lifetime = unknownLifetime;
121 let _lifetime = unknownLifetime;
122 return {
122 return {
123 initialize(context: ActivationContext<any>) {
123 initialize(context: ActivationContext<any>) {
124 if (_lifetime !== unknownLifetime)
124 if (_lifetime !== unknownLifetime)
125 throw new Error("Cyclic reference activation detected");
125 throw new Error("Cyclic reference activation detected");
126
126
127 _lifetime = context.getContainer().getLifetimeManager().create();
127 _lifetime = context.getContainer().getLifetimeManager().create();
128 },
128 },
129 get() {
129 get() {
130 return _lifetime.get();
130 return _lifetime.get();
131 },
131 },
132 has() {
132 has() {
133 return _lifetime.has();
133 return _lifetime.has();
134 },
134 },
135 store(item: any, cleanup?: (item: any) => void) {
135 store(item: any, cleanup?: (item: any) => void) {
136 return _lifetime.store(item, cleanup);
136 return _lifetime.store(item, cleanup);
137 },
137 },
138 toString() {
138 toString() {
139 return `[object HierarchyLifetime, has=${this.has()}]`;
139 return `[object HierarchyLifetime, has=${this.has()}]`;
140 }
140 }
141 };
141 };
142 }
142 }
143
143
144 static contextLifetime() {
144 static contextLifetime() {
145 let _lifetime = unknownLifetime;
145 let _lifetime = unknownLifetime;
146 return {
146 return {
147 initialize(context: ActivationContext<any>) {
147 initialize(context: ActivationContext<any>) {
148 if (_lifetime !== unknownLifetime)
148 if (_lifetime !== unknownLifetime)
149 throw new Error("Cyclic reference detected");
149 throw new Error("Cyclic reference detected");
150 _lifetime = context.createLifetime();
150 _lifetime = context.createLifetime();
151 },
151 },
152 get() {
152 get() {
153 return _lifetime.get();
153 return _lifetime.get();
154 },
154 },
155 has() {
155 has() {
156 return _lifetime.has();
156 return _lifetime.has();
157 },
157 },
158 store(item: any) {
158 store(item: any) {
159 _lifetime.store(item);
159 _lifetime.store(item);
160 },
160 },
161 toString() {
161 toString() {
162 return `[object ContextLifetime, has=${this.has()}]`;
162 return `[object ContextLifetime, has=${this.has()}]`;
163 }
163 }
164 };
164 };
165 }
165 }
166
166
167 static singletonLifetime(typeId: string) {
167 static singletonLifetime(typeId: string) {
168 argumentNotEmptyString(typeId, "typeId");
168 argumentNotEmptyString(typeId, "typeId");
169 let pending = false;
169 let pending = false;
170 return {
170 return {
171 has() {
171 has() {
172 return typeId in singletons;
172 return typeId in singletons;
173 },
173 },
174 get() {
174 get() {
175 if (!this.has())
175 if (!this.has())
176 throw new Error(`The instance ${typeId} doesn't exists`);
176 throw new Error(`The instance ${typeId} doesn't exists`);
177 return singletons[typeId];
177 return singletons[typeId];
178 },
178 },
179 initialize() {
179 initialize() {
180 if (pending)
180 if (pending)
181 throw new Error("Cyclic reference detected");
181 throw new Error("Cyclic reference detected");
182 pending = true;
182 pending = true;
183 },
183 },
184 store(item: any) {
184 store(item: any) {
185 singletons[typeId] = item;
185 singletons[typeId] = item;
186 pending = false;
186 pending = false;
187 },
187 },
188 toString() {
188 toString() {
189 return `[object SingletonLifetime, has=${this.has()}, typeId=${typeId}]`;
189 return `[object SingletonLifetime, has=${this.has()}, typeId=${typeId}]`;
190 }
190 }
191 };
191 };
192 }
192 }
193
193
194 static containerLifetime(container: ServiceContainer<any>) {
194 static containerLifetime(container: ServiceContainer<any>) {
195 let _lifetime = unknownLifetime;
195 let _lifetime = unknownLifetime;
196 return {
196 return {
197 initialize(context: ActivationContext<any>) {
197 initialize(context: ActivationContext<any>) {
198 if (_lifetime !== unknownLifetime)
198 if (_lifetime !== unknownLifetime)
199 throw new Error("Cyclic reference detected");
199 throw new Error("Cyclic reference detected");
200 _lifetime = container.getLifetimeManager().create();
200 _lifetime = container.getLifetimeManager().create();
201 },
201 },
202 get() {
202 get() {
203 return _lifetime.get();
203 return _lifetime.get();
204 },
204 },
205 has() {
205 has() {
206 return _lifetime.has();
206 return _lifetime.has();
207 },
207 },
208 store(item: any) {
208 store(item: any) {
209 _lifetime.store(item);
209 _lifetime.store(item);
210 },
210 },
211 toString() {
211 toString() {
212 return `[object ContainerLifetime, has=${_lifetime.has()}]`
212 return `[object ContainerLifetime, has=${_lifetime.has()}]`;
213 }
213 }
214 };
214 };
215 }
215 }
216 }
216 }
@@ -1,126 +1,164
1 export interface Constructor<T = {}> {
1 export interface Constructor<T = {}> {
2 new(...args: any[]): T;
2 new(...args: any[]): T;
3 prototype: T;
3 prototype: T;
4 }
4 }
5
5
6 export type PromiseOrValue<T> = T | PromiseLike<T>;
6 export type PromiseOrValue<T> = T | PromiseLike<T>;
7
7
8 export type Factory<T = {}> = (...args: any[]) => T;
8 export type Factory<T = {}> = (...args: any[]) => T;
9
9
10 export type Predicate<T = any> = (x: T) => boolean;
10 export type Predicate<T = any> = (x: T) => boolean;
11
11
12 export type MatchingMemberKeys<T, U> = { [K in keyof T]: T[K] extends U ? K : never}[keyof T];
12 export type MatchingMemberKeys<T, U> = { [K in keyof T]: T[K] extends U ? K : never}[keyof T];
13
13
14 export type NotMatchingMemberKeys<T, U> = { [K in keyof T]: T[K] extends U ? never : K}[keyof T];
14 export type NotMatchingMemberKeys<T, U> = { [K in keyof T]: T[K] extends U ? never : K}[keyof T];
15
15
16 export type ExtractMembers<T, U> = Pick<T, MatchingMemberKeys<T, U>>;
16 export type ExtractMembers<T, U> = Pick<T, MatchingMemberKeys<T, U>>;
17
17
18 export type ExcludeMembers<T, U> = Pick<T, NotMatchingMemberKeys<T, U>>;
18 export type ExcludeMembers<T, U> = Pick<T, NotMatchingMemberKeys<T, U>>;
19
19
20 export interface MapOf<T> {
20 export interface MapOf<T> {
21 [key: string]: T;
21 [key: string]: T;
22 }
22 }
23
23
24 export interface IDestroyable {
24 export interface IDestroyable {
25 destroy(): void;
25 destroy(): void;
26 }
26 }
27
27
28 export interface IRemovable {
28 export interface IRemovable {
29 remove(): void;
29 remove(): void;
30 }
30 }
31
31
32 /**
33 * Interface for the cancellation token. Cancellation token is
34 * a marker indicating that the cancellation was requested, it
35 * is up to the operation to decide whether to interrupt or
36 * to complete its execution.
37 *
38 * This interface defines several methods of interaction with
39 * the cancellation i.e. either to poll its status or to react
40 * through the callback.
41 */
32 export interface ICancellation {
42 export interface ICancellation {
43 /**
44 * Throws an exception if the cancellation has been requested,
45 * otherwise does nothing.
46 */
33 throwIfRequested(): void;
47 throwIfRequested(): void;
48
49 /**
50 * Checks whether the cancellation is requested.
51 * @returns true is the cancellation has been requested,
52 * otherwise returns false.
53 */
34 isRequested(): boolean;
54 isRequested(): boolean;
55
56 /**
57 * Checks the ability of the token to be signaled.
58 *
59 * @returns true if the token is able to request
60 * the cancellation, false otherwise.
61 */
35 isSupported(): boolean;
62 isSupported(): boolean;
63
64 /**
65 * Registers the callback to be called when the cancellation
66 * is requested.
67 *
68 * @param cb The callback which receives the reason of the
69 * cancellation.
70 * @returns The subscription, after the operation is completed
71 * it should unregister the callback to free resources by
72 * calling the `destroy()` method of the subscription.
73 */
36 register(cb: (e: any) => void): IDestroyable;
74 register(cb: (e: any) => void): IDestroyable;
37 }
75 }
38
76
39 /**
77 /**
40 * Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉ Π°ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½Π½ΡƒΡŽ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΡŽ
78 * Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉ Π°ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½Π½ΡƒΡŽ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΡŽ
41 */
79 */
42 export interface IActivatable {
80 export interface IActivatable {
43 /**
81 /**
44 * @returns Boolean indicates the current state
82 * @returns Boolean indicates the current state
45 */
83 */
46 isActive(): boolean;
84 isActive(): boolean;
47
85
48 /**
86 /**
49 * Starts the component activation
87 * Starts the component activation
50 * @param ct cancellation token for this operation
88 * @param ct cancellation token for this operation
51 */
89 */
52 activate(ct?: ICancellation): Promise<void>;
90 activate(ct?: ICancellation): Promise<void>;
53
91
54 /**
92 /**
55 * Starts the component deactivation
93 * Starts the component deactivation
56 * @param ct cancellation token for this operation
94 * @param ct cancellation token for this operation
57 */
95 */
58 deactivate(ct?: ICancellation): Promise<void>;
96 deactivate(ct?: ICancellation): Promise<void>;
59
97
60 /**
98 /**
61 * Sets the activation controller for this component
99 * Sets the activation controller for this component
62 * @param controller The activation controller
100 * @param controller The activation controller
63 *
101 *
64 * Activation controller checks whether this component
102 * Activation controller checks whether this component
65 * can be activated and manages the active state of the
103 * can be activated and manages the active state of the
66 * component
104 * component
67 */
105 */
68 setActivationController(controller: IActivationController): void;
106 setActivationController(controller: IActivationController): void;
69
107
70 /** Indicates whether this component has an activation controller */
108 /** Indicates whether this component has an activation controller */
71 hasActivationController(): boolean;
109 hasActivationController(): boolean;
72
110
73 /**
111 /**
74 * Gets the current activation controller for this component
112 * Gets the current activation controller for this component
75 */
113 */
76 getActivationController(): IActivationController;
114 getActivationController(): IActivationController;
77 }
115 }
78
116
79 export interface IActivationController {
117 export interface IActivationController {
80 activating(component: IActivatable, ct?: ICancellation): Promise<void>;
118 activating(component: IActivatable, ct?: ICancellation): Promise<void>;
81
119
82 activated(component: IActivatable, ct?: ICancellation): Promise<void>;
120 activated(component: IActivatable, ct?: ICancellation): Promise<void>;
83
121
84 deactivating(component: IActivatable, ct?: ICancellation): Promise<void>;
122 deactivating(component: IActivatable, ct?: ICancellation): Promise<void>;
85
123
86 deactivated(component: IActivatable, ct?: ICancellation): Promise<void>;
124 deactivated(component: IActivatable, ct?: ICancellation): Promise<void>;
87
125
88 deactivate(ct?: ICancellation): Promise<void>;
126 deactivate(ct?: ICancellation): Promise<void>;
89
127
90 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
128 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
91
129
92 hasActive(): boolean;
130 hasActive(): boolean;
93
131
94 getActive(): IActivatable;
132 getActive(): IActivatable;
95 }
133 }
96
134
97 export interface IAsyncComponent {
135 export interface IAsyncComponent {
98 getCompletion(): Promise<void>;
136 getCompletion(): Promise<void>;
99 }
137 }
100
138
101 export interface ICancellable {
139 export interface ICancellable {
102 cancel(reason?: any): void;
140 cancel(reason?: any): void;
103 }
141 }
104
142
105 export interface IObservable<T> {
143 export interface IObservable<T> {
106 on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable;
144 on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable;
107 next(ct?: ICancellation): Promise<T>;
145 next(ct?: ICancellation): Promise<T>;
108 }
146 }
109
147
110 export interface IObserver<T> {
148 export interface IObserver<T> {
111 next(event: T): void;
149 next(event: T): void;
112
150
113 error(e: any): void;
151 error(e: any): void;
114
152
115 complete(): void;
153 complete(): void;
116 }
154 }
117
155
118 export interface TextWriter {
156 export interface TextWriter {
119 write(obj: any): void;
157 write(obj: any): void;
120 write(format: string, ...args: any[]): void;
158 write(format: string, ...args: any[]): void;
121
159
122 writeLine(obj?: any): void;
160 writeLine(obj?: any): void;
123 writeLine(format: string, ...args: any[]): void;
161 writeLine(format: string, ...args: any[]): void;
124
162
125 writeValue(value: any, spec?: string): void;
163 writeValue(value: any, spec?: string): void;
126 }
164 }
@@ -1,80 +1,80
1 import { test } from "./TestTraits";
1 import { test } from "./TestTraits";
2 import { fluent } from "../di/traits";
2 import { fluent } from "../di/traits";
3 import { Bar } from "../mock/Bar";
3 import { Bar } from "../mock/Bar";
4 import { Container } from "../di/Container";
4 import { Container } from "../di/Container";
5 import { Foo } from "../mock/Foo";
5 import { Foo } from "../mock/Foo";
6 import { Box } from "../mock/Box";
6 import { Box } from "../mock/Box";
7 import { delay } from "../safe";
7 import { delay } from "../safe";
8 import { FooServices, Services } from "../mock/services";
8 import { FooServices, Services } from "../mock/services";
9 import { ContainerConfiguration } from "../di/fluent/interfaces";
9 import { ContainerConfiguration } from "../di/fluent/interfaces";
10
10
11 test("Simple fluent config", async t => {
11 test("Simple fluent config", async t => {
12 const config = fluent<{ host: string; bar: Bar; foo: Foo }>()
12 const config = fluent<{ host: string; bar: Bar; foo: Foo }>()
13 .register({
13 .register({
14 host: it => it.value("example.com"),
14 host: it => it.value("example.com"),
15 bar: it => it.factory(resolve => new Bar({ host: resolve("host") }, "s-bar")),
15 bar: it => it.factory(resolve => new Bar({ host: resolve("host") }, "s-bar")),
16 foo: it => import("../mock/Foo").then(m => it.lifetime("container").factory(() => new m.Foo()))
16 foo: it => import("../mock/Foo").then(m => it.lifetime("container").factory(() => new m.Foo()))
17 });
17 });
18
18
19 const c1 = new Container<{}>();
19 const c1 = new Container<{}>();
20 const container = await config.apply(c1);
20 const container = await config.apply(c1);
21
21
22 t.equal(container.resolve("host"), "example.com", "The value should be resolved");
22 t.equal(container.resolve("host"), "example.com", "The value should be resolved");
23 t.assert(container.resolve("bar"), "The service should de activated");
23 t.assert(container.resolve("bar"), "The service should de activated");
24 t.equal(container.resolve("foo"), container.resolve("foo"), "The service should be activated once");
24 t.equal(container.resolve("foo"), container.resolve("foo"), "The service should be activated once");
25 });
25 });
26
26
27 test("Nested async configuration", async t => {
27 test("Nested async configuration", async t => {
28 const container = await new Container<{
28 const container = await new Container<{
29 foo: Foo;
29 foo: Foo;
30 box: Box<Foo>
30 box: Box<Foo>
31 }>().fluent({
31 }>().fluent({
32 foo: it => delay(0).then(() => it.factory(() => new Foo())),
32 foo: it => delay(0).then(() => it.factory(() => new Foo())),
33 box: it => it.lifetime("context").factory($dependency => new Box($dependency("foo")))
33 box: it => it.lifetime("context").factory($dependency => new Box($dependency("foo")))
34 });
34 });
35
35
36 t.assert(container.resolve("box").getValue(), "The dependency should be set");
36 t.assert(container.resolve("box").getValue(), "The dependency should be set");
37 t.equals(container.resolve("box").getValue(), container.resolve("box").getValue(), "The service should be activated once")
37 t.equals(container.resolve("box").getValue(), container.resolve("box").getValue(), "The service should be activated once");
38 });
38 });
39
39
40 test("Bad fluent config", async t => {
40 test("Bad fluent config", async t => {
41 try {
41 try {
42 await new Container<{
42 await new Container<{
43 foo: Foo;
43 foo: Foo;
44 box: Box<Foo>
44 box: Box<Foo>
45 }>().fluent({
45 }>().fluent({
46 foo: it => delay(0).then(() => it.factory(() => new Foo())),
46 foo: it => delay(0).then(() => it.factory(() => new Foo())),
47 box: it => it.lifetime("context")
47 box: it => it.lifetime("context")
48 .override("foo", () => { throw new Error("bad override"); })
48 .override("foo", () => { throw new Error("bad override"); })
49 .factory($dependency => new Box($dependency("foo")))
49 .factory($dependency => new Box($dependency("foo")))
50 });
50 });
51 t.fail("Should throw");
51 t.fail("Should throw");
52 } catch (e) {
52 } catch (e) {
53 t.pass("The configuration should fail");
53 t.pass("The configuration should fail");
54 t.equal(e.message, "bad override", "the error should pass");
54 t.equal(e.message, "bad override", "the error should pass");
55 }
55 }
56 });
56 });
57
57
58 test("Load fluent config", async t => {
58 test("Load fluent config", async t => {
59 const container = new Container<Services>();
59 const container = new Container<Services>();
60
60
61 await container.configure("../mock/config", { contextRequire: require });
61 await container.configure("../mock/config", { contextRequire: require });
62
62
63 t.assert(container.resolve("host"), "Should resolve simple value");
63 t.assert(container.resolve("host"), "Should resolve simple value");
64 });
64 });
65
65
66 test("Container applyConfig", async t => {
66 test("Container applyConfig", async t => {
67 const container = await new Container<{}>().applyConfig(import("../mock/config"));
67 const container = await new Container<{}>().applyConfig(import("../mock/config"));
68
68
69 t.assert(container.resolve("host"), "Should resolve simple value");
69 t.assert(container.resolve("host"), "Should resolve simple value");
70 });
70 });
71
71
72 test("Child container config", async t => {
72 test("Child container config", async t => {
73 const container = await new Container<{}>().applyConfig(import("../mock/config"));
73 const container = await new Container<{}>().applyConfig(import("../mock/config"));
74
74
75 const fooServices: ContainerConfiguration<FooServices> = (await import("../mock/config2")).default;
75 const fooServices: ContainerConfiguration<FooServices> = (await import("../mock/config2")).default;
76
76
77 const child = await fooServices.apply(container.createChildContainer());
77 const child = await fooServices.apply(container.createChildContainer());
78
78
79 t.assert(child.resolve("foo"), "foo should be resolved");
79 t.assert(child.resolve("foo"), "foo should be resolved");
80 });
80 });
General Comments 0
You need to be logged in to leave comments. Login now