##// END OF EJS Templates
Added safe.delay...
cin -
r76:a193ba786ffc default
parent child
Show More
@@ -0,0 +1,86
1 import tape = require("tape");
2 import { delay } from "./TestTraits";
3 import { Cancellation } from "@implab/core/Cancellation";
4 import { first, isPromise } from "@implab/core/safe";
5
6 tape("await delay test", async t => {
7 // schedule delay
8 let resolved = false;
9 let res = delay(0).then(() => resolved = true);
10
11 t.false(resolved, "the delay should be async");
12
13 await res;
14 t.pass("await delay");
15
16 // create cancellation token
17 let cancel: (e?: any) => void;
18 const ct = new Cancellation(c => cancel = c);
19
20 // schedule delay
21 resolved = false;
22 res = delay(0, ct).then(() => resolved = true);
23
24 t.false(resolved, "created delay with ct");
25
26 // cancel
27 cancel();
28
29 try {
30 await res;
31 t.fail("the delay should fail when it is cancelled");
32 } catch {
33 t.pass("the delay is cancelled");
34 }
35
36 let died = false;
37
38 // try schedule delay after the cancellation is requested
39 res = delay(0, ct).then(x => true, () => died = true);
40
41 t.false(died, "The delay should be scheduled even if the cancellation is requested");
42
43 await res;
44 t.true(died, "the delay should fail when cancelled");
45
46 t.end();
47 });
48
49 tape("sequemce test", async t => {
50 const sequence = ["a", "b", "c"];
51 const empty = [];
52
53 // synchronous tests
54 t.equals(first(sequence), "a", "Should return the first element");
55
56 let v: string;
57 let e: Error;
58 first(sequence, x => v = x);
59 t.equal(v, "a", "The callback should be called for the first element");
60
61 t.throws(() => {
62 first(empty);
63 }, "Should throw when the sequence is empty");
64
65 t.throws(() => {
66 first(empty, x => v = x);
67 }, "Should throw when the sequence is empty");
68
69 first(empty, null, x => e = x);
70 t.true(e, "The errorback should be called for the empty sequence");
71
72 // async tests
73 const asyncSequence = Promise.resolve(sequence);
74 const asyncEmptySequence = Promise.resolve(empty);
75
76 const promise = first(asyncSequence);
77 t.true(isPromise(promise), "Should return promise");
78
79 v = await promise;
80 t.equal(v, "a", "Should return the first element");
81
82 v = await new Promise(resolve => first(asyncSequence, resolve));
83 t.equal(v, "a", "The callback should be called for the first element");
84
85 t.end();
86 });
@@ -1,68 +1,68
1 # BUILD
1 # BUILD
2
2
3 Для сборки необходимо иметь
3 Для сборки необходимо иметь
4
4
5 * nodejs >= 8
5 * nodejs >= 8
6 * npm
6 * npm
7 * eslint
7 * eslint
8 * mercurial (для автоматического определения версии)
8 * mercurial (для автоматического определения версии)
9
9
10 В пцессе сборки будут установлены зависимости из package.json
10 В пцессе сборки будут установлены зависимости из package.json
11
11
12 ## Properties
12 ## Properties
13
13
14 Свойства, испольуземые для управления сборкой, через них можно указать имя,
14 Свойства, испольуземые для управления сборкой, через них можно указать имя,
15 версию пакета, тип модулей, версию стандарта js для которого осуществляется
15 версию пакета, тип модулей, версию стандарта js для которого осуществляется
16 сборка. Значения по-умолчанию заданы в `gradle.properties`
16 сборка. Значения по-умолчанию заданы в `gradle.properties`
17
17
18 ### npmName
18 ### npmName
19
19
20 `default: core`
20 `default: core`
21
21
22 Имя пакета в терминологии npm.
22 Имя пакета в терминологии npm.
23
23
24 ### npmScope
24 ### npmScope
25
25
26 `default: @implab`
26 `default: @implab`
27
27
28 Пространство в терминологии npm для пакета.
28 Пространство в терминологии npm для пакета.
29
29
30 ### target
30 ### target
31
31
32 `default: es5`
32 `default: es5`
33
33
34 Версии стандарта js в среде выполнения, возможные значения:
34 Версии стандарта js в среде выполнения, возможные значения:
35
35
36 * es3 - требует полифилы для es5, promise
36 * es3 - требует полифилы для es5, promise
37 * es5 - требует полифил для promise
37 * es5 - требует полифил для promise
38 * es6
38 * es6
39 * es2015
39 * es2015
40 * es2016
40 * es2016
41 * es2017
41 * es2017
42
42
43 ### jsmodule
43 ### jsmodule
44
44
45 `default: amd`
45 `default: amd`
46
46
47 * amd - requirejs модули, хорошо подходит для использования в браузерах.
47 * amd - requirejs модули, хорошо подходит для использования в браузерах.
48 * commonjs - формат модулей для nodejs
48 * commonjs - формат модулей для nodejs
49
49
50 ## Tasks
50 ## Tasks
51
51
52 ### build
52 ### build
53
53
54 ### test
54 ### test
55
55
56 ### pack
56 ### pack
57
57
58 ### publish
58 ### publish
59
59
60 ## Examples
60 ## Examples
61
61
62 ```shell
62 ```shell
63 ./gradlew test pack -PnpmName=core-amd
63 ./gradlew test pack -PnpmName=core-amd
64 ```
64 ```
65
65
66 ```shell
66 ```shell
67 ./gradlew test pack -Pjsmodule=commonjs -Ptarget=es2017
67 ./gradlew test pack -Pjsmodule=commonjs -Ptarget=es2017
68 ``` No newline at end of file
68 ```
@@ -1,88 +1,83
1 import { ICancellation, IDestroyable } from "./interfaces";
1 import { ICancellation, IDestroyable } from "./interfaces";
2 import { argumentNotNull } from "./safe";
2 import { argumentNotNull, destroyed } from "./safe";
3
4 const destroyed = {
5 destroy() {
6 }
7 };
8
3
9 export class Cancellation implements ICancellation {
4 export class Cancellation implements ICancellation {
10 private _reason: any;
5 private _reason: any;
11 private _cbs: Array<(e) => void>;
6 private _cbs: Array<(e: any) => void>;
12
7
13 constructor(action: (cancel: (e) => void) => void) {
8 constructor(action: (cancel: (e?: any) => void) => void) {
14 argumentNotNull(action, "action");
9 argumentNotNull(action, "action");
15
10
16 action(this._cancel.bind(this));
11 action(this._cancel.bind(this));
17 }
12 }
18
13
19 isSupported(): boolean {
14 isSupported(): boolean {
20 return true;
15 return true;
21 }
16 }
22 throwIfRequested(): void {
17 throwIfRequested(): void {
23 if (this._reason)
18 if (this._reason)
24 throw this._reason;
19 throw this._reason;
25 }
20 }
26
21
27 isRequested(): boolean {
22 isRequested(): boolean {
28 return !!this._reason;
23 return !!this._reason;
29 }
24 }
30
25
31 register(cb: (e: any) => void): IDestroyable {
26 register(cb: (e: any) => void): IDestroyable {
32 argumentNotNull(cb, "cb");
27 argumentNotNull(cb, "cb");
33
28
34 if (this._reason) {
29 if (this._reason) {
35 cb(this._reason);
30 cb(this._reason);
36 return destroyed;
31 return destroyed;
37 } else {
32 } else {
38 if (!this._cbs)
33 if (!this._cbs)
39 this._cbs = [cb];
34 this._cbs = [cb];
40 else
35 else
41 this._cbs.push(cb);
36 this._cbs.push(cb);
42
37
43 const me = this;
38 const me = this;
44 return {
39 return {
45 destroy() {
40 destroy() {
46 me._unregister(cb);
41 me._unregister(cb);
47 }
42 }
48 };
43 };
49 }
44 }
50 }
45 }
51
46
52 private _unregister(cb) {
47 private _unregister(cb) {
53 if (this._cbs) {
48 if (this._cbs) {
54 const i = this._cbs.indexOf(cb);
49 const i = this._cbs.indexOf(cb);
55 if (i >= 0)
50 if (i >= 0)
56 this._cbs.splice(i, 1);
51 this._cbs.splice(i, 1);
57 }
52 }
58 }
53 }
59
54
60 private _cancel(reason) {
55 private _cancel(reason) {
61 if (this._reason)
56 if (this._reason)
62 return;
57 return;
63
58
64 this._reason = (reason = reason || new Error("Operation cancelled"));
59 this._reason = (reason = reason || new Error("Operation cancelled"));
65
60
66 if (this._cbs) {
61 if (this._cbs) {
67 this._cbs.forEach(cb => cb(reason));
62 this._cbs.forEach(cb => cb(reason));
68 this._cbs = null;
63 this._cbs = null;
69 }
64 }
70 }
65 }
71
66
72 static readonly none: ICancellation = {
67 static readonly none: ICancellation = {
73 isSupported(): boolean {
68 isSupported(): boolean {
74 return false;
69 return false;
75 },
70 },
76
71
77 throwIfRequested(): void {
72 throwIfRequested(): void {
78 },
73 },
79
74
80 isRequested(): boolean {
75 isRequested(): boolean {
81 return false;
76 return false;
82 },
77 },
83
78
84 register(_cb: (e: any) => void): IDestroyable {
79 register(_cb: (e: any) => void): IDestroyable {
85 return destroyed;
80 return destroyed;
86 }
81 }
87 };
82 };
88 }
83 }
@@ -1,184 +1,208
1 import { IObservable, IDestroyable, ICancellation } from "./interfaces";
1 import { IObservable, IDestroyable, ICancellation, IObserver } from "./interfaces";
2 import { Cancellation } from "./Cancellation";
2 import { Cancellation } from "./Cancellation";
3 import { argumentNotNull } from "./safe";
3 import { argumentNotNull, destroyed } from "./safe";
4
4
5 type Handler<T> = (x: T) => void;
5 type Handler<T> = (x: T) => void;
6
6
7 type Initializer<T> = (notify: Handler<T>, error?: (e: any) => void, complete?: () => void) => void;
7 type Initializer<T> = (notify: Handler<T>, error?: (e: any) => void, complete?: () => void) => void;
8
8
9 // TODO: think about to move this interfaces.ts and make it public
9 const noop = () => { };
10 interface IObserver<T> {
11 next(event: T): void;
12
10
13 error(e: any): void;
11 function isObserver(val: any): val is IObserver<any> {
14
12 return val && (typeof val.next === "function");
15 complete(): void;
16 }
13 }
17
14
18 const noop = () => { };
19
20 export class Observable<T> implements IObservable<T> {
15 export class Observable<T> implements IObservable<T> {
21 private _once = new Array<IObserver<T>>();
16 private _once = new Array<IObserver<T>>();
22
17
23 private _observers = new Array<IObserver<T>>();
18 private _observers = new Array<IObserver<T>>();
24
19
25 private _complete: boolean;
20 private _complete: boolean;
26
21
27 private _error: any;
22 private _error: any;
28
23
29 constructor(func?: Initializer<T>) {
24 constructor(func?: Initializer<T>) {
30 if (func)
25 if (func)
31 func(
26 func(
32 this._notifyNext.bind(this),
27 this._notifyNext.bind(this),
33 this._notifyError.bind(this),
28 this._notifyError.bind(this),
34 this._notifyCompleted.bind(this)
29 this._notifyCompleted.bind(this)
35 );
30 );
36 }
31 }
37
32
38 /**
33 /**
39 * Registers handlers for the current observable object.
34 * Registers handlers for the current observable object.
40 *
35 *
41 * @param next the handler for events
36 * @param next the handler for events
42 * @param error the handler for a error
37 * @param error the handler for a error
43 * @param complete the handler for a completion
38 * @param complete the handler for a completion
44 * @returns {IDestroyable} the handler for the current subscription, this
39 * @returns {IDestroyable} the handler for the current subscription, this
45 * handler can be used to unsubscribe from events.
40 * handler can be used to unsubscribe from events.
46 *
41 *
47 */
42 */
48 on(next: Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
43 on(next: Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
49 argumentNotNull(next, "next");
44 argumentNotNull(next, "next");
50
45
51 const me = this;
46 const me = this;
52
47
53 const observer: IObserver<T> & IDestroyable = {
48 const observer: IObserver<T> & IDestroyable = {
54 next,
49 next,
55 error: error ? error.bind(null) : noop,
50 error: error ? error.bind(null) : noop,
56 complete: complete ? complete.bind(null) : noop,
51 complete: complete ? complete.bind(null) : noop,
57
52
58 destroy() {
53 destroy() {
59 me._removeObserver(this);
54 me._removeObserver(this);
60 }
55 }
61 };
56 };
62
57
63 this._addObserver(observer);
58 this._addObserver(observer);
64
59
65 return observer;
60 return observer;
66 }
61 }
67
62
63 subscribe(next: IObserver<T> | Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
64 if (isObserver(next)) {
65 const me = this;
66 const subscription = {
67 destroy() {
68 me._removeObserver(next);
69 }
70 };
71 this._addObserver(next);
72 return subscription;
73 } else if (next) {
74 const observer = {
75 next,
76 error,
77 complete
78 };
79 const me = this;
80 const subscription = {
81 destroy() {
82 me._removeObserver(observer);
83 }
84 };
85 this._addObserver(observer);
86 return subscription;
87 } else {
88 return destroyed;
89 }
90 }
91
68 private _addObserver(observer: IObserver<T>) {
92 private _addObserver(observer: IObserver<T>) {
69 if (this._complete) {
93 if (this._complete) {
70 try {
94 try {
71 if (this._error)
95 if (this._error)
72 observer.error(this._error);
96 observer.error(this._error);
73 else
97 else
74 observer.complete();
98 observer.complete();
75 } catch (e) {
99 } catch (e) {
76 this.onObserverException(e);
100 this.onObserverException(e);
77 }
101 }
78 } else {
102 } else {
79 this._observers.push(observer);
103 this._observers.push(observer);
80 }
104 }
81 }
105 }
82
106
83 /**
107 /**
84 * Waits for the next event. This method can't be used to read messages
108 * Waits for the next event. This method can't be used to read messages
85 * as a sequence since it can skip some messages between calls.
109 * as a sequence since it can skip some messages between calls.
86 *
110 *
87 * @param ct a cancellation token
111 * @param ct a cancellation token
88 */
112 */
89 next(ct: ICancellation = Cancellation.none): Promise<T> {
113 next(ct: ICancellation = Cancellation.none) {
90 return new Promise<T>((resolve, reject) => {
114 return new Promise<T>((resolve, reject) => {
91 const observer: IObserver<T> = {
115 const observer: IObserver<T> = {
92 next: resolve,
116 next: resolve,
93 error: reject,
117 error: reject,
94 complete: () => reject("No more events are available")
118 complete: () => reject("No more events are available")
95 };
119 };
96
120
97 if (this._addOnce(observer) && ct.isSupported()) {
121 if (this._addOnce(observer) && ct.isSupported()) {
98 ct.register(e => {
122 ct.register(e => {
99 this._removeOnce(observer);
123 this._removeOnce(observer);
100 reject(e);
124 reject(e);
101 });
125 });
102 }
126 }
103 });
127 });
104 }
128 }
105
129
106 private _addOnce(observer: IObserver<T>) {
130 private _addOnce(observer: IObserver<T>) {
107 if (this._complete) {
131 if (this._complete) {
108 try {
132 try {
109 if (this._error)
133 if (this._error)
110 observer.error(this._error);
134 observer.error(this._error);
111 else
135 else
112 observer.complete();
136 observer.complete();
113 } catch (e) {
137 } catch (e) {
114 this.onObserverException(e);
138 this.onObserverException(e);
115 }
139 }
116 return false;
140 return false;
117 }
141 }
118
142
119 this._once.push(observer);
143 this._once.push(observer);
120 return true;
144 return true;
121 }
145 }
122
146
123 protected onObserverException(e: any) {
147 protected onObserverException(e: any) {
124 }
148 }
125
149
126 private _removeOnce(d: IObserver<T>) {
150 private _removeOnce(d: IObserver<T>) {
127 const i = this._once.indexOf(d);
151 const i = this._once.indexOf(d);
128 if (i >= 0)
152 if (i >= 0)
129 this._once.splice(i, 1);
153 this._once.splice(i, 1);
130 }
154 }
131
155
132 private _removeObserver(d: IObserver<T>) {
156 private _removeObserver(d: IObserver<T>) {
133 const i = this._observers.indexOf(d);
157 const i = this._observers.indexOf(d);
134 if (i >= 0)
158 if (i >= 0)
135 this._observers.splice(i, 1);
159 this._observers.splice(i, 1);
136 }
160 }
137
161
138 private _notify(guard: (observer: IObserver<T>) => void) {
162 private _notify(guard: (observer: IObserver<T>) => void) {
139 this._once.forEach(guard);
163 this._once.forEach(guard);
140 this._once = [];
164 this._once = [];
141
165
142 this._observers.forEach(guard);
166 this._observers.forEach(guard);
143 }
167 }
144
168
145 protected _notifyNext(evt: T) {
169 protected _notifyNext(evt: T) {
146 const guard = (observer: IObserver<T>) => {
170 const guard = (observer: IObserver<T>) => {
147 try {
171 try {
148 observer.next(evt);
172 observer.next(evt);
149 } catch (e) {
173 } catch (e) {
150 this.onObserverException(e);
174 this.onObserverException(e);
151 }
175 }
152 };
176 };
153
177
154 this._notify(guard);
178 this._notify(guard);
155 }
179 }
156
180
157 protected _notifyError(e: any) {
181 protected _notifyError(e: any) {
158 const guard = (observer: IObserver<T>) => {
182 const guard = (observer: IObserver<T>) => {
159 try {
183 try {
160 observer.error(e);
184 observer.error(e);
161 } catch (e) {
185 } catch (e) {
162 this.onObserverException(e);
186 this.onObserverException(e);
163 }
187 }
164 };
188 };
165
189
166 this._notify(guard);
190 this._notify(guard);
167 this._observers = [];
191 this._observers = [];
168 this._complete = true;
192 this._complete = true;
169 }
193 }
170
194
171 protected _notifyCompleted() {
195 protected _notifyCompleted() {
172 const guard = (observer: IObserver<T>) => {
196 const guard = (observer: IObserver<T>) => {
173 try {
197 try {
174 observer.complete();
198 observer.complete();
175 } catch (e) {
199 } catch (e) {
176 this.onObserverException(e);
200 this.onObserverException(e);
177 }
201 }
178 };
202 };
179
203
180 this._notify(guard);
204 this._notify(guard);
181 this._observers = [];
205 this._observers = [];
182 this._complete = true;
206 this._complete = true;
183 }
207 }
184 }
208 }
@@ -1,348 +1,348
1 import {
1 import {
2 ServiceRegistration,
2 ServiceRegistration,
3 TypeRegistration,
3 TypeRegistration,
4 FactoryRegistration,
4 FactoryRegistration,
5 ServiceMap,
5 ServiceMap,
6 isDescriptor,
6 isDescriptor,
7 isDependencyRegistration,
7 isDependencyRegistration,
8 DependencyRegistration,
8 DependencyRegistration,
9 ValueRegistration,
9 ValueRegistration,
10 ActivationType,
10 ActivationType,
11 isValueRegistration,
11 isValueRegistration,
12 isTypeRegistration,
12 isTypeRegistration,
13 isFactoryRegistration
13 isFactoryRegistration
14 } from "./interfaces";
14 } from "./interfaces";
15
15
16 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get } from "../safe";
16 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get } from "../safe";
17 import { AggregateDescriptor } from "./AggregateDescriptor";
17 import { AggregateDescriptor } from "./AggregateDescriptor";
18 import { ValueDescriptor } from "./ValueDescriptor";
18 import { ValueDescriptor } from "./ValueDescriptor";
19 import { Container } from "./Container";
19 import { Container } from "./Container";
20 import { ReferenceDescriptor } from "./ReferenceDescriptor";
20 import { ReferenceDescriptor } from "./ReferenceDescriptor";
21 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
21 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
22 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
22 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
23 import { TraceSource } from "../log/TraceSource";
23 import { TraceSource } from "../log/TraceSource";
24 import { ConfigError } from "./ConfigError";
24 import { ConfigError } from "./ConfigError";
25 import { Cancellation } from "../Cancellation";
25 import { Cancellation } from "../Cancellation";
26 import { makeResolver } from "./ResolverHelper";
26 import { makeResolver } from "./ResolverHelper";
27 import { ICancellation } from "../interfaces";
27 import { ICancellation } from "../interfaces";
28
28
29 const trace = TraceSource.get("@implab/core/di/Configuration");
29 const trace = TraceSource.get("@implab/core/di/Configuration");
30
30
31 async function mapAll(data: object | any[], map?: (v, k) => any): Promise<any> {
31 async function mapAll(data: object | any[], map?: (v, k) => any): Promise<any> {
32 if (data instanceof Array) {
32 if (data instanceof Array) {
33 return Promise.all(map ? data.map(map) : data);
33 return Promise.all(map ? data.map(map) : data);
34 } else {
34 } else {
35 const keys = Object.keys(data);
35 const keys = Object.keys(data);
36
36
37 const o: any = {};
37 const o: any = {};
38
38
39 await Promise.all(keys.map(async k => {
39 await Promise.all(keys.map(async k => {
40 const v = map ? map(data[k], k) : data[k];
40 const v = map ? map(data[k], k) : data[k];
41 o[k] = isPromise(v) ? await v : v;
41 o[k] = isPromise(v) ? await v : v;
42 }));
42 }));
43
43
44 return o;
44 return o;
45 }
45 }
46 }
46 }
47
47
48 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
48 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
49
49
50 type _key = string | number;
50 type _key = string | number;
51
51
52 export class Configuration {
52 export class Configuration {
53
53
54 _hasInnerDescriptors = false;
54 _hasInnerDescriptors = false;
55
55
56 _container: Container;
56 _container: Container;
57
57
58 _path: Array<_key>;
58 _path: Array<_key>;
59
59
60 _configName: string;
60 _configName: string;
61
61
62 _require: ModuleResolver;
62 _require: ModuleResolver;
63
63
64 constructor(container: Container) {
64 constructor(container: Container) {
65 argumentNotNull(container, container);
65 argumentNotNull(container, "container");
66 this._container = container;
66 this._container = container;
67 this._path = [];
67 this._path = [];
68 }
68 }
69
69
70 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
70 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
71 argumentNotEmptyString(moduleName, "moduleName");
71 argumentNotEmptyString(moduleName, "moduleName");
72
72
73 trace.log(
73 trace.log(
74 "loadConfiguration moduleName={0}, contextRequire={1}",
74 "loadConfiguration moduleName={0}, contextRequire={1}",
75 moduleName,
75 moduleName,
76 contextRequire ? typeof (contextRequire) : "<nil>"
76 contextRequire ? typeof (contextRequire) : "<nil>"
77 );
77 );
78
78
79 this._configName = moduleName;
79 this._configName = moduleName;
80
80
81 const r = await makeResolver(null, contextRequire);
81 const r = await makeResolver(null, contextRequire);
82
82
83 const config = await r(moduleName, ct);
83 const config = await r(moduleName, ct);
84
84
85 await this._applyConfiguration(
85 await this._applyConfiguration(
86 config,
86 config,
87 await makeResolver(moduleName, contextRequire),
87 await makeResolver(moduleName, contextRequire),
88 ct
88 ct
89 );
89 );
90 }
90 }
91
91
92 async applyConfiguration(data: object, contextRequire?: any, ct = Cancellation.none) {
92 async applyConfiguration(data: object, contextRequire?: any, ct = Cancellation.none) {
93 argumentNotNull(data, "data");
93 argumentNotNull(data, "data");
94
94
95 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
95 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
96 }
96 }
97
97
98 async _applyConfiguration(data: object, resolver?: ModuleResolver, ct = Cancellation.none) {
98 async _applyConfiguration(data: object, resolver?: ModuleResolver, ct = Cancellation.none) {
99 trace.log("applyConfiguration");
99 trace.log("applyConfiguration");
100
100
101 this._configName = "$";
101 this._configName = "$";
102
102
103 if (resolver)
103 if (resolver)
104 this._require = resolver;
104 this._require = resolver;
105
105
106 let services: ServiceMap;
106 let services: ServiceMap;
107
107
108 try {
108 try {
109 services = await this._visitRegistrations(data, "$");
109 services = await this._visitRegistrations(data, "$");
110 } catch (e) {
110 } catch (e) {
111 throw this._makeError(e);
111 throw this._makeError(e);
112 }
112 }
113
113
114 this._container.register(services);
114 this._container.register(services);
115 }
115 }
116
116
117 _makeError(inner) {
117 _makeError(inner) {
118 const e = new ConfigError("Failed to load configuration", inner);
118 const e = new ConfigError("Failed to load configuration", inner);
119 e.configName = this._configName;
119 e.configName = this._configName;
120 e.path = this._makePath();
120 e.path = this._makePath();
121 return e;
121 return e;
122 }
122 }
123
123
124 _makePath() {
124 _makePath() {
125 return this._path
125 return this._path
126 .reduce(
126 .reduce(
127 (prev, cur) => typeof cur === "number" ?
127 (prev, cur) => typeof cur === "number" ?
128 `${prev}[${cur}]` :
128 `${prev}[${cur}]` :
129 `${prev}.${cur}`
129 `${prev}.${cur}`
130 )
130 )
131 .toString();
131 .toString();
132 }
132 }
133
133
134 async _resolveType(moduleName: string, localName: string) {
134 async _resolveType(moduleName: string, localName: string) {
135 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
135 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
136 try {
136 try {
137 const m = await this._loadModule(moduleName);
137 const m = await this._loadModule(moduleName);
138 return localName ? get(localName, m) : m;
138 return localName ? get(localName, m) : m;
139 } catch (e) {
139 } catch (e) {
140 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
140 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
141 throw e;
141 throw e;
142 }
142 }
143 }
143 }
144
144
145 _loadModule(moduleName: string) {
145 _loadModule(moduleName: string) {
146 trace.debug("loadModule {0}", moduleName);
146 trace.debug("loadModule {0}", moduleName);
147
147
148 return this._require(moduleName);
148 return this._require(moduleName);
149 }
149 }
150
150
151 async _visitRegistrations(data, name: _key) {
151 async _visitRegistrations(data, name: _key) {
152 this._enter(name);
152 this._enter(name);
153
153
154 if (data.constructor &&
154 if (data.constructor &&
155 data.constructor.prototype !== Object.prototype)
155 data.constructor.prototype !== Object.prototype)
156 throw new Error("Configuration must be a simple object");
156 throw new Error("Configuration must be a simple object");
157
157
158 const o: ServiceMap = {};
158 const o: ServiceMap = {};
159 const keys = Object.keys(data);
159 const keys = Object.keys(data);
160
160
161 const services = await mapAll(data, async (v, k) => {
161 const services = await mapAll(data, async (v, k) => {
162 const d = await this._visit(v, k);
162 const d = await this._visit(v, k);
163 return isDescriptor(d) ? d : new AggregateDescriptor(d);
163 return isDescriptor(d) ? d : new AggregateDescriptor(d);
164 }) as ServiceMap;
164 }) as ServiceMap;
165
165
166 this._leave();
166 this._leave();
167
167
168 return services;
168 return services;
169 }
169 }
170
170
171 _enter(name: _key) {
171 _enter(name: _key) {
172 this._path.push(name);
172 this._path.push(name);
173 trace.debug(">{0}", name);
173 trace.debug(">{0}", name);
174 }
174 }
175
175
176 _leave() {
176 _leave() {
177 const name = this._path.pop();
177 const name = this._path.pop();
178 trace.debug("<{0}", name);
178 trace.debug("<{0}", name);
179 }
179 }
180
180
181 async _visit(data, name: string) {
181 async _visit(data, name: string) {
182 if (isPrimitive(data) || isDescriptor(data))
182 if (isPrimitive(data) || isDescriptor(data))
183 return data;
183 return data;
184
184
185 if (isDependencyRegistration(data)) {
185 if (isDependencyRegistration(data)) {
186 return this._visitDependencyRegistration(data, name);
186 return this._visitDependencyRegistration(data, name);
187 } else if (isValueRegistration(data)) {
187 } else if (isValueRegistration(data)) {
188 return this._visitValueRegistration(data, name);
188 return this._visitValueRegistration(data, name);
189 } else if (isTypeRegistration(data)) {
189 } else if (isTypeRegistration(data)) {
190 return this._visitTypeRegistration(data, name);
190 return this._visitTypeRegistration(data, name);
191 } else if (isFactoryRegistration(data)) {
191 } else if (isFactoryRegistration(data)) {
192 return this._visitFactoryRegistration(data, name);
192 return this._visitFactoryRegistration(data, name);
193 } else if (data instanceof Array) {
193 } else if (data instanceof Array) {
194 return this._visitArray(data, name);
194 return this._visitArray(data, name);
195 }
195 }
196
196
197 return this._visitObject(data, name);
197 return this._visitObject(data, name);
198 }
198 }
199
199
200 async _visitObject(data: object, name: _key) {
200 async _visitObject(data: object, name: _key) {
201 if (data.constructor &&
201 if (data.constructor &&
202 data.constructor.prototype !== Object.prototype)
202 data.constructor.prototype !== Object.prototype)
203 return new ValueDescriptor(data);
203 return new ValueDescriptor(data);
204
204
205 this._enter(name);
205 this._enter(name);
206
206
207 const v = await mapAll(data, delegate(this, "_visit"));
207 const v = await mapAll(data, delegate(this, "_visit"));
208
208
209 // TODO: handle inline descriptors properly
209 // TODO: handle inline descriptors properly
210 // const ex = {
210 // const ex = {
211 // activate(ctx) {
211 // activate(ctx) {
212 // const value = ctx.activate(this.prop, "prop");
212 // const value = ctx.activate(this.prop, "prop");
213 // // some code
213 // // some code
214 // },
214 // },
215 // // will be turned to ReferenceDescriptor
215 // // will be turned to ReferenceDescriptor
216 // prop: { $dependency: "depName" }
216 // prop: { $dependency: "depName" }
217 // };
217 // };
218
218
219 this._leave();
219 this._leave();
220 return v;
220 return v;
221 }
221 }
222
222
223 async _visitArray(data: any[], name: _key) {
223 async _visitArray(data: any[], name: _key) {
224 if (data.constructor &&
224 if (data.constructor &&
225 data.constructor.prototype !== Array.prototype)
225 data.constructor.prototype !== Array.prototype)
226 return new ValueDescriptor(data);
226 return new ValueDescriptor(data);
227
227
228 this._enter(name);
228 this._enter(name);
229
229
230 const v = await mapAll(data, delegate(this, "_visit"));
230 const v = await mapAll(data, delegate(this, "_visit"));
231 this._leave();
231 this._leave();
232
232
233 return v;
233 return v;
234 }
234 }
235
235
236 _makeServiceParams(data: ServiceRegistration) {
236 _makeServiceParams(data: ServiceRegistration) {
237 const opts: any = {
237 const opts: any = {
238 owner: this._container
238 owner: this._container
239 };
239 };
240 if (data.services)
240 if (data.services)
241 opts.services = this._visitRegistrations(data.services, "services");
241 opts.services = this._visitRegistrations(data.services, "services");
242
242
243 if (data.inject) {
243 if (data.inject) {
244 this._enter("inject");
244 this._enter("inject");
245 opts.inject = mapAll(
245 opts.inject = mapAll(
246 data.inject instanceof Array ?
246 data.inject instanceof Array ?
247 data.inject :
247 data.inject :
248 [data.inject],
248 [data.inject],
249 delegate(this, "_visitObject")
249 delegate(this, "_visitObject")
250 );
250 );
251 this._leave();
251 this._leave();
252 }
252 }
253
253
254 if ("params" in data)
254 if ("params" in data)
255 opts.params = data.params instanceof Array ?
255 opts.params = data.params instanceof Array ?
256 this._visitArray(data.params, "params") :
256 this._visitArray(data.params, "params") :
257 this._visit(data.params, "params");
257 this._visit(data.params, "params");
258
258
259 if (data.activation) {
259 if (data.activation) {
260 if (typeof (data.activation) === "string") {
260 if (typeof (data.activation) === "string") {
261 switch (data.activation.toLowerCase()) {
261 switch (data.activation.toLowerCase()) {
262 case "singleton":
262 case "singleton":
263 opts.activation = ActivationType.Singleton;
263 opts.activation = ActivationType.Singleton;
264 break;
264 break;
265 case "container":
265 case "container":
266 opts.activation = ActivationType.Container;
266 opts.activation = ActivationType.Container;
267 break;
267 break;
268 case "hierarchy":
268 case "hierarchy":
269 opts.activation = ActivationType.Hierarchy;
269 opts.activation = ActivationType.Hierarchy;
270 break;
270 break;
271 case "context":
271 case "context":
272 opts.activation = ActivationType.Context;
272 opts.activation = ActivationType.Context;
273 break;
273 break;
274 case "call":
274 case "call":
275 opts.activation = ActivationType.Call;
275 opts.activation = ActivationType.Call;
276 break;
276 break;
277 default:
277 default:
278 throw new Error("Unknown activation type: " +
278 throw new Error("Unknown activation type: " +
279 data.activation);
279 data.activation);
280 }
280 }
281 } else {
281 } else {
282 opts.activation = Number(data.activation);
282 opts.activation = Number(data.activation);
283 }
283 }
284 }
284 }
285
285
286 if (data.cleanup)
286 if (data.cleanup)
287 opts.cleanup = data.cleanup;
287 opts.cleanup = data.cleanup;
288
288
289 return opts;
289 return opts;
290 }
290 }
291
291
292 async _visitValueRegistration(data: ValueRegistration, name: _key) {
292 async _visitValueRegistration(data: ValueRegistration, name: _key) {
293 this._enter(name);
293 this._enter(name);
294 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
294 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
295 this._leave();
295 this._leave();
296 return d;
296 return d;
297 }
297 }
298
298
299 async _visitDependencyRegistration(data: DependencyRegistration, name: _key) {
299 async _visitDependencyRegistration(data: DependencyRegistration, name: _key) {
300 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
300 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
301 this._enter(name);
301 this._enter(name);
302 const d = new ReferenceDescriptor({
302 const d = new ReferenceDescriptor({
303 name: data.$dependency,
303 name: data.$dependency,
304 lazy: data.lazy,
304 lazy: data.lazy,
305 optional: data.optional,
305 optional: data.optional,
306 default: data.default,
306 default: data.default,
307 services: data.services && await this._visitRegistrations(data.services, "services")
307 services: data.services && await this._visitRegistrations(data.services, "services")
308 });
308 });
309 this._leave();
309 this._leave();
310 return d;
310 return d;
311 }
311 }
312
312
313 async _visitTypeRegistration(data: TypeRegistration, name: _key) {
313 async _visitTypeRegistration(data: TypeRegistration, name: _key) {
314 argumentNotNull(data.$type, "data.$type");
314 argumentNotNull(data.$type, "data.$type");
315 this._enter(name);
315 this._enter(name);
316
316
317 const opts = this._makeServiceParams(data);
317 const opts = this._makeServiceParams(data);
318 if (data.$type instanceof Function) {
318 if (data.$type instanceof Function) {
319 opts.type = data.$type;
319 opts.type = data.$type;
320 } else {
320 } else {
321 const [moduleName, typeName] = data.$type.split(":", 2);
321 const [moduleName, typeName] = data.$type.split(":", 2);
322 opts.type = this._resolveType(moduleName, typeName);
322 opts.type = this._resolveType(moduleName, typeName);
323 }
323 }
324
324
325 const d = new TypeServiceDescriptor(
325 const d = new TypeServiceDescriptor(
326 await mapAll(opts)
326 await mapAll(opts)
327 );
327 );
328
328
329 this._leave();
329 this._leave();
330
330
331 return d;
331 return d;
332 }
332 }
333
333
334 async _visitFactoryRegistration(data: FactoryRegistration, name: _key) {
334 async _visitFactoryRegistration(data: FactoryRegistration, name: _key) {
335 argumentOfType(data.$factory, Function, "data.$factory");
335 argumentOfType(data.$factory, Function, "data.$factory");
336 this._enter(name);
336 this._enter(name);
337
337
338 const opts = this._makeServiceParams(data);
338 const opts = this._makeServiceParams(data);
339 opts.factory = data.$factory;
339 opts.factory = data.$factory;
340
340
341 const d = new FactoryServiceDescriptor(
341 const d = new FactoryServiceDescriptor(
342 await mapAll(opts)
342 await mapAll(opts)
343 );
343 );
344
344
345 this._leave();
345 this._leave();
346 return d;
346 return d;
347 }
347 }
348 }
348 }
@@ -1,93 +1,101
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 Factory<T = {}> = (...args: any[]) => T;
6 export type Factory<T = {}> = (...args: any[]) => T;
7
7
8 export type Predicate<T = any> = (x: T) => boolean;
8 export type Predicate<T = any> = (x: T) => boolean;
9
9
10 export interface MapOf<T> {
10 export interface MapOf<T> {
11 [key: string]: T;
11 [key: string]: T;
12 }
12 }
13
13
14 export interface IDestroyable {
14 export interface IDestroyable {
15 destroy(): void;
15 destroy(): void;
16 }
16 }
17
17
18 export interface IRemovable {
18 export interface IRemovable {
19 remove(): void;
19 remove(): void;
20 }
20 }
21
21
22 export interface ICancellation {
22 export interface ICancellation {
23 throwIfRequested(): void;
23 throwIfRequested(): void;
24 isRequested(): boolean;
24 isRequested(): boolean;
25 isSupported(): boolean;
25 isSupported(): boolean;
26 register(cb: (e: any) => void): IDestroyable;
26 register(cb: (e: any) => void): IDestroyable;
27 }
27 }
28
28
29 /**
29 /**
30 * Интерфейс поддерживающий асинхронную активацию
30 * Интерфейс поддерживающий асинхронную активацию
31 */
31 */
32 export interface IActivatable {
32 export interface IActivatable {
33 /**
33 /**
34 * @returns Boolean indicates the current state
34 * @returns Boolean indicates the current state
35 */
35 */
36 isActive(): boolean;
36 isActive(): boolean;
37
37
38 /**
38 /**
39 * Starts the component activation
39 * Starts the component activation
40 * @param ct cancellation token for this operation
40 * @param ct cancellation token for this operation
41 */
41 */
42 activate(ct?: ICancellation): Promise<void>;
42 activate(ct?: ICancellation): Promise<void>;
43
43
44 /**
44 /**
45 * Starts the component deactivation
45 * Starts the component deactivation
46 * @param ct cancellation token for this operation
46 * @param ct cancellation token for this operation
47 */
47 */
48 deactivate(ct?: ICancellation): Promise<void>;
48 deactivate(ct?: ICancellation): Promise<void>;
49
49
50 /**
50 /**
51 * Sets the activation controller for this component
51 * Sets the activation controller for this component
52 * @param controller The activation controller
52 * @param controller The activation controller
53 *
53 *
54 * Activation controller checks whether this component
54 * Activation controller checks whether this component
55 * can be activated and manages the active state of the
55 * can be activated and manages the active state of the
56 * component
56 * component
57 */
57 */
58 setActivationController(controller: IActivationController);
58 setActivationController(controller: IActivationController);
59
59
60 /**
60 /**
61 * Gets the current activation controller for this component
61 * Gets the current activation controller for this component
62 */
62 */
63 getActivationController(): IActivationController;
63 getActivationController(): IActivationController;
64 }
64 }
65
65
66 export interface IActivationController {
66 export interface IActivationController {
67 activating(component: IActivatable, ct?: ICancellation): Promise<void>;
67 activating(component: IActivatable, ct?: ICancellation): Promise<void>;
68
68
69 activated(component: IActivatable, ct?: ICancellation): Promise<void>;
69 activated(component: IActivatable, ct?: ICancellation): Promise<void>;
70
70
71 deactivating(component: IActivatable, ct?: ICancellation): Promise<void>;
71 deactivating(component: IActivatable, ct?: ICancellation): Promise<void>;
72
72
73 deactivated(component: IActivatable, ct?: ICancellation): Promise<void>;
73 deactivated(component: IActivatable, ct?: ICancellation): Promise<void>;
74
74
75 deactivate(ct?: ICancellation): Promise<void>;
75 deactivate(ct?: ICancellation): Promise<void>;
76
76
77 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
77 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
78
78
79 getActive(): IActivatable;
79 getActive(): IActivatable;
80 }
80 }
81
81
82 export interface IAsyncComponent {
82 export interface IAsyncComponent {
83 getCompletion(): Promise<void>;
83 getCompletion(): Promise<void>;
84 }
84 }
85
85
86 export interface ICancellable {
86 export interface ICancellable {
87 cancel(reason?: any): void;
87 cancel(reason?: any): void;
88 }
88 }
89
89
90 export interface IObservable<T> {
90 export interface IObservable<T> {
91 on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable;
91 on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable;
92 next(ct?: ICancellation): Promise<T>;
92 next(ct?: ICancellation): Promise<T>;
93 }
93 }
94
95 export interface IObserver<T> {
96 next(event: T): void;
97
98 error(e: any): void;
99
100 complete(): void;
101 }
@@ -1,428 +1,456
1 import { ICancellable, Constructor } from "./interfaces";
1 import { ICancellable, Constructor } from "./interfaces";
2 import { Cancellation } from "./Cancellation";
2
3
3 let _nextOid = 0;
4 let _nextOid = 0;
4 const _oid = typeof Symbol === "function" ?
5 const _oid = typeof Symbol === "function" ?
5 Symbol("__implab__oid__") :
6 Symbol("__implab__oid__") :
6 "__implab__oid__";
7 "__implab__oid__";
7
8
8 export function oid(instance: object): string {
9 export function oid(instance: object): string {
9 if (isNull(instance))
10 if (isNull(instance))
10 return null;
11 return null;
11
12
12 if (_oid in instance)
13 if (_oid in instance)
13 return instance[_oid];
14 return instance[_oid];
14 else
15 else
15 return (instance[_oid] = "oid_" + (++_nextOid));
16 return (instance[_oid] = "oid_" + (++_nextOid));
16 }
17 }
17
18
18 export function argumentNotNull(arg: any, name: string) {
19 export function argumentNotNull(arg: any, name: string) {
19 if (arg === null || arg === undefined)
20 if (arg === null || arg === undefined)
20 throw new Error("The argument " + name + " can't be null or undefined");
21 throw new Error("The argument " + name + " can't be null or undefined");
21 }
22 }
22
23
23 export function argumentNotEmptyString(arg: any, name: string) {
24 export function argumentNotEmptyString(arg: any, name: string) {
24 if (typeof (arg) !== "string" || !arg.length)
25 if (typeof (arg) !== "string" || !arg.length)
25 throw new Error("The argument '" + name + "' must be a not empty string");
26 throw new Error("The argument '" + name + "' must be a not empty string");
26 }
27 }
27
28
28 export function argumentNotEmptyArray(arg: any, name: string) {
29 export function argumentNotEmptyArray(arg: any, name: string) {
29 if (!(arg instanceof Array) || !arg.length)
30 if (!(arg instanceof Array) || !arg.length)
30 throw new Error("The argument '" + name + "' must be a not empty array");
31 throw new Error("The argument '" + name + "' must be a not empty array");
31 }
32 }
32
33
33 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
34 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
34 if (!(arg instanceof type))
35 if (!(arg instanceof type))
35 throw new Error("The argument '" + name + "' type doesn't match");
36 throw new Error("The argument '" + name + "' type doesn't match");
36 }
37 }
37
38
38 export function isNull(val: any) {
39 export function isNull(val: any) {
39 return (val === null || val === undefined);
40 return (val === null || val === undefined);
40 }
41 }
41
42
42 export function isPrimitive(val: any): val is string | number | boolean | undefined | null {
43 export function isPrimitive(val: any): val is string | number | boolean | undefined | null {
43 return (val === null || val === undefined || typeof (val) === "string" ||
44 return (val === null || val === undefined || typeof (val) === "string" ||
44 typeof (val) === "number" || typeof (val) === "boolean");
45 typeof (val) === "number" || typeof (val) === "boolean");
45 }
46 }
46
47
47 export function isInteger(val: any): val is number {
48 export function isInteger(val: any): val is number {
48 return parseInt(val, 10) === val;
49 return parseInt(val, 10) === val;
49 }
50 }
50
51
51 export function isNumber(val: any): val is number {
52 export function isNumber(val: any): val is number {
52 return parseFloat(val) === val;
53 return parseFloat(val) === val;
53 }
54 }
54
55
55 export function isString(val: any): val is string {
56 export function isString(val: any): val is string {
56 return typeof (val) === "string" || val instanceof String;
57 return typeof (val) === "string" || val instanceof String;
57 }
58 }
58
59
59 export function isPromise(val: any): val is PromiseLike<any> {
60 export function isPromise(val: any): val is PromiseLike<any> {
60 return val && typeof val.then === "function";
61 return val && typeof val.then === "function";
61 }
62 }
62
63
63 export function isCancellable(val: any): val is ICancellable {
64 export function isCancellable(val: any): val is ICancellable {
64 return val && typeof val.cancel === "function";
65 return val && typeof val.cancel === "function";
65 }
66 }
66
67
67 export function isNullOrEmptyString(val: any): val is string | null | undefined {
68 export function isNullOrEmptyString(val: any): val is string | null | undefined {
68 if (val === null || val === undefined ||
69 if (val === null || val === undefined ||
69 ((typeof (val) === "string" || val instanceof String) && val.length === 0))
70 ((typeof (val) === "string" || val instanceof String) && val.length === 0))
70 return true;
71 return true;
71 }
72 }
72
73
73 export function isNotEmptyArray(arg: any): arg is Array<any> {
74 export function isNotEmptyArray(arg: any): arg is Array<any> {
74 return (arg instanceof Array && arg.length > 0);
75 return (arg instanceof Array && arg.length > 0);
75 }
76 }
76
77
77 function _isStrictMode() {
78 function _isStrictMode() {
78 return !this;
79 return !this;
79 }
80 }
80
81
81 function _getNonStrictGlobal() {
82 function _getNonStrictGlobal() {
82 return this;
83 return this;
83 }
84 }
84
85
85 export function getGlobal() {
86 export function getGlobal() {
86 // in es3 we can't use indirect call to eval, since it will
87 // in es3 we can't use indirect call to eval, since it will
87 // be executed in the current call context.
88 // be executed in the current call context.
88 if (!_isStrictMode()) {
89 if (!_isStrictMode()) {
89 return _getNonStrictGlobal();
90 return _getNonStrictGlobal();
90 } else {
91 } else {
91 // tslint:disable-next-line:no-eval
92 // tslint:disable-next-line:no-eval
92 return eval.call(null, "this");
93 return eval.call(null, "this");
93 }
94 }
94 }
95 }
95
96
96 export function get(member: string, context?: object) {
97 export function get(member: string, context?: object) {
97 argumentNotEmptyString(member, "member");
98 argumentNotEmptyString(member, "member");
98 let that = context || getGlobal();
99 let that = context || getGlobal();
99 const parts = member.split(".");
100 const parts = member.split(".");
100 for (const m of parts) {
101 for (const m of parts) {
101 if (!m)
102 if (!m)
102 continue;
103 continue;
103 if (isNull(that = that[m]))
104 if (isNull(that = that[m]))
104 break;
105 break;
105 }
106 }
106 return that;
107 return that;
107 }
108 }
108
109
109 /**
110 /**
110 * Выполняет метод для каждого элемента массива, останавливается, когда
111 * Выполняет метод для каждого элемента массива, останавливается, когда
111 * либо достигнут конец массива, либо функция <c>cb</c> вернула
112 * либо достигнут конец массива, либо функция <c>cb</c> вернула
112 * значение.
113 * значение.
113 *
114 *
114 * @param {Array | Object} obj массив элементов для просмотра
115 * @param {Array | Object} obj массив элементов для просмотра
115 * @param {Function} cb функция, вызываемая для каждого элемента
116 * @param {Function} cb функция, вызываемая для каждого элемента
116 * @param {Object} thisArg значение, которое будет передано в качестве
117 * @param {Object} thisArg значение, которое будет передано в качестве
117 * <c>this</c> в <c>cb</c>.
118 * <c>this</c> в <c>cb</c>.
118 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
119 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
119 * если достигнут конец массива.
120 * если достигнут конец массива.
120 */
121 */
121 export function each(obj, cb, thisArg?) {
122 export function each(obj, cb, thisArg?) {
122 argumentNotNull(cb, "cb");
123 argumentNotNull(cb, "cb");
123 if (obj instanceof Array) {
124 if (obj instanceof Array) {
124 for (let i = 0; i < obj.length; i++) {
125 for (let i = 0; i < obj.length; i++) {
125 const x = cb.call(thisArg, obj[i], i);
126 const x = cb.call(thisArg, obj[i], i);
126 if (x !== undefined)
127 if (x !== undefined)
127 return x;
128 return x;
128 }
129 }
129 } else {
130 } else {
130 const keys = Object.keys(obj);
131 const keys = Object.keys(obj);
131 for (const k of keys) {
132 for (const k of keys) {
132 const x = cb.call(thisArg, obj[k], k);
133 const x = cb.call(thisArg, obj[k], k);
133 if (x !== undefined)
134 if (x !== undefined)
134 return x;
135 return x;
135 }
136 }
136 }
137 }
137 }
138 }
138
139
139 /** Copies property values from a source object to the destination and returns
140 /** Copies property values from a source object to the destination and returns
140 * the destination onject.
141 * the destination onject.
141 *
142 *
142 * @param dest The destination object into which properties from the source
143 * @param dest The destination object into which properties from the source
143 * object will be copied.
144 * object will be copied.
144 * @param source The source of values which will be copied to the destination
145 * @param source The source of values which will be copied to the destination
145 * object.
146 * object.
146 * @param template An optional parameter specifies which properties should be
147 * @param template An optional parameter specifies which properties should be
147 * copied from the source and how to map them to the destination. If the
148 * copied from the source and how to map them to the destination. If the
148 * template is an array it contains the list of property names to copy from the
149 * template is an array it contains the list of property names to copy from the
149 * source to the destination. In case of object the templates contains the map
150 * source to the destination. In case of object the templates contains the map
150 * where keys are property names in the source and the values are property
151 * where keys are property names in the source and the values are property
151 * names in the destination object. If the template isn't specified then the
152 * names in the destination object. If the template isn't specified then the
152 * own properties of the source are entirely copied to the destination.
153 * own properties of the source are entirely copied to the destination.
153 *
154 *
154 */
155 */
155 export function mixin<T, S>(dest: T, source: S, template?: string[] | object): T & S {
156 export function mixin<T, S>(dest: T, source: S, template?: string[] | object): T & S {
156 argumentNotNull(dest, "to");
157 argumentNotNull(dest, "to");
157 const _res = dest as T & S;
158 const _res = dest as T & S;
158
159
159 if (isPrimitive(source))
160 if (isPrimitive(source))
160 return _res;
161 return _res;
161
162
162 if (template instanceof Array) {
163 if (template instanceof Array) {
163 for (const p of template) {
164 for (const p of template) {
164 if (p in source)
165 if (p in source)
165 _res[p] = source[p];
166 _res[p] = source[p];
166 }
167 }
167 } else if (template) {
168 } else if (template) {
168 const keys = Object.keys(source);
169 const keys = Object.keys(source);
169 for (const p of keys) {
170 for (const p of keys) {
170 if (p in template)
171 if (p in template)
171 _res[template[p]] = source[p];
172 _res[template[p]] = source[p];
172 }
173 }
173 } else {
174 } else {
174 const keys = Object.keys(source);
175 const keys = Object.keys(source);
175 for (const p of keys)
176 for (const p of keys)
176 _res[p] = source[p];
177 _res[p] = source[p];
177 }
178 }
178
179
179 return _res;
180 return _res;
180 }
181 }
181
182
182 /** Wraps the specified function to emulate an asynchronous execution.
183 /** Wraps the specified function to emulate an asynchronous execution.
183 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
184 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
184 * @param{Function|String} fn [Required] Function wich will be wrapped.
185 * @param{Function|String} fn [Required] Function wich will be wrapped.
185 */
186 */
186 export function async(_fn: (...args: any[]) => any, thisArg): (...args: any[]) => PromiseLike<any> {
187 export function async(_fn: (...args: any[]) => any, thisArg): (...args: any[]) => PromiseLike<any> {
187 let fn = _fn;
188 let fn = _fn;
188
189
189 if (arguments.length === 2 && !(fn instanceof Function))
190 if (arguments.length === 2 && !(fn instanceof Function))
190 fn = thisArg[fn];
191 fn = thisArg[fn];
191
192
192 if (fn == null)
193 if (fn == null)
193 throw new Error("The function must be specified");
194 throw new Error("The function must be specified");
194
195
195 function wrapresult(x, e?): PromiseLike<any> {
196 function wrapresult(x, e?): PromiseLike<any> {
196 if (e) {
197 if (e) {
197 return {
198 return {
198 then(cb, eb) {
199 then(cb, eb) {
199 try {
200 try {
200 return eb ? wrapresult(eb(e)) : this;
201 return eb ? wrapresult(eb(e)) : this;
201 } catch (e2) {
202 } catch (e2) {
202 return wrapresult(null, e2);
203 return wrapresult(null, e2);
203 }
204 }
204 }
205 }
205 };
206 };
206 } else {
207 } else {
207 if (x && x.then)
208 if (x && x.then)
208 return x;
209 return x;
209 return {
210 return {
210 then(cb) {
211 then(cb) {
211 try {
212 try {
212 return cb ? wrapresult(cb(x)) : this;
213 return cb ? wrapresult(cb(x)) : this;
213 } catch (e2) {
214 } catch (e2) {
214 return wrapresult(e2);
215 return wrapresult(e2);
215 }
216 }
216 }
217 }
217 };
218 };
218 }
219 }
219 }
220 }
220
221
221 return (...args) => {
222 return (...args) => {
222 try {
223 try {
223 return wrapresult(fn.apply(thisArg, args));
224 return wrapresult(fn.apply(thisArg, args));
224 } catch (e) {
225 } catch (e) {
225 return wrapresult(null, e);
226 return wrapresult(null, e);
226 }
227 }
227 };
228 };
228 }
229 }
229
230
230 type _AnyFn = (...args) => any;
231 type _AnyFn = (...args) => any;
231
232
232 export function delegate<T, K extends keyof T>(target: T, _method: (K | _AnyFn)) {
233 export function delegate<T, K extends keyof T>(target: T, _method: (K | _AnyFn)) {
233 let method;
234 let method;
234
235
235 if (!(_method instanceof Function)) {
236 if (!(_method instanceof Function)) {
236 argumentNotNull(target, "target");
237 argumentNotNull(target, "target");
237 method = target[_method];
238 method = target[_method];
238 if (!(method instanceof Function))
239 if (!(method instanceof Function))
239 throw new Error("'method' argument must be a Function or a method name");
240 throw new Error("'method' argument must be a Function or a method name");
240 } else {
241 } else {
241 method = _method;
242 method = _method;
242 }
243 }
243
244
244 return (...args) => {
245 return (...args) => {
245 return method.apply(target, args);
246 return method.apply(target, args);
246 };
247 };
247 }
248 }
248
249
250 export function delay(timeMs: number, ct = Cancellation.none) {
251 return new Promise((resolve, reject) => {
252 if (ct.isRequested()) {
253 ct.register(reject);
254 } else {
255 const h = ct.register(e => {
256 clearTimeout(id);
257 reject(e);
258 // we don't nedd to unregister h, since ct is already disposed
259 });
260 const id = setTimeout(() => {
261 h.destroy();
262 resolve();
263 }, timeMs);
264 }
265 });
266 }
267
249 /**
268 /**
250 * Для каждого элемента массива вызывает указанную функцию и сохраняет
269 * Для каждого элемента массива вызывает указанную функцию и сохраняет
251 * возвращенное значение в массиве результатов.
270 * возвращенное значение в массиве результатов.
252 *
271 *
253 * @remarks cb может выполняться асинхронно, при этом одновременно будет
272 * @remarks cb может выполняться асинхронно, при этом одновременно будет
254 * только одна операция.
273 * только одна операция.
255 *
274 *
256 * @async
275 * @async
257 */
276 */
258 export function pmap(items, cb) {
277 export function pmap(items, cb) {
259 argumentNotNull(cb, "cb");
278 argumentNotNull(cb, "cb");
260
279
261 if (isPromise(items))
280 if (isPromise(items))
262 return items.then(data => pmap(data, cb));
281 return items.then(data => pmap(data, cb));
263
282
264 if (isNull(items) || !items.length)
283 if (isNull(items) || !items.length)
265 return items;
284 return items;
266
285
267 let i = 0;
286 let i = 0;
268 const result = [];
287 const result = [];
269
288
270 function next() {
289 function next() {
271 let r;
290 let r;
272 let ri;
291 let ri;
273
292
274 function chain(x) {
293 function chain(x) {
275 result[ri] = x;
294 result[ri] = x;
276 return next();
295 return next();
277 }
296 }
278
297
279 while (i < items.length) {
298 while (i < items.length) {
280 r = cb(items[i], i);
299 r = cb(items[i], i);
281 ri = i;
300 ri = i;
282 i++;
301 i++;
283 if (isPromise(r)) {
302 if (isPromise(r)) {
284 return r.then(chain);
303 return r.then(chain);
285 } else {
304 } else {
286 result[ri] = r;
305 result[ri] = r;
287 }
306 }
288 }
307 }
289 return result;
308 return result;
290 }
309 }
291
310
292 return next();
311 return next();
293 }
312 }
294
313
295 export function pfor(items, cb) {
314 export function pfor(items, cb) {
296 argumentNotNull(cb, "cb");
315 argumentNotNull(cb, "cb");
297
316
298 if (isPromise(items))
317 if (isPromise(items))
299 return items.then(data => {
318 return items.then(data => {
300 return pmap(data, cb);
319 return pmap(data, cb);
301 });
320 });
302
321
303 if (isNull(items) || !items.length)
322 if (isNull(items) || !items.length)
304 return items;
323 return items;
305
324
306 let i = 0;
325 let i = 0;
307
326
308 function next() {
327 function next() {
309 while (i < items.length) {
328 while (i < items.length) {
310 const r = cb(items[i], i);
329 const r = cb(items[i], i);
311 i++;
330 i++;
312 if (isPromise(r))
331 if (isPromise(r))
313 return r.then(next);
332 return r.then(next);
314 }
333 }
315 }
334 }
316
335
317 return next();
336 return next();
318 }
337 }
319
338
320 export function first<T>(sequence: ArrayLike<T>): T;
339 export function first<T>(sequence: ArrayLike<T>): T;
321 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
340 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
322 export function first<T>(
341 export function first<T>(
323 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
342 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
324 cb: (x: T) => void,
343 cb: (x: T) => void,
325 err?: (x: Error) => void
344 err?: (x: Error) => void
326 ): void;
345 ): void;
327 /**
346 /**
328 * Выбирает первый элемент из последовательности, или обещания, если в
347 * Выбирает первый элемент из последовательности, или обещания, если в
329 * качестве параметра используется обещание, оно должно вернуть массив.
348 * качестве параметра используется обещание, оно должно вернуть массив.
330 *
349 *
331 * @param {Function} cb обработчик результата, ему будет передан первый
350 * @param {Function} cb обработчик результата, ему будет передан первый
332 * элемент последовательности в случае успеха
351 * элемент последовательности в случае успеха
333 * @param {Function} err обработчик исключения, если массив пустой, либо
352 * @param {Function} err обработчик исключения, если массив пустой, либо
334 * не массив
353 * не массив
335 *
354 *
336 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
355 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
337 * обещание, либо первый элемент.
356 * обещание, либо первый элемент.
338 * @async
357 * @async
339 */
358 */
340 export function first<T>(
359 export function first<T>(
341 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
360 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
342 cb?: (x: T) => void,
361 cb?: (x: T) => void,
343 err?: (x: Error) => void
362 err?: (x: Error) => void
344 ) {
363 ) {
345 if (isPromise(sequence)) {
364 if (isPromise(sequence)) {
346 return sequence.then(res => first(res, cb, err));
365 return sequence.then(res => first(res, cb, err));
347 } else if (sequence && "length" in sequence) {
366 } else if (sequence && "length" in sequence) {
348 if (sequence.length === 0) {
367 if (sequence.length === 0) {
349 if (err)
368 if (err)
350 return err(new Error("The sequence is empty"));
369 return err(new Error("The sequence is empty"));
351 else
370 else
352 throw new Error("The sequence is empty");
371 throw new Error("The sequence is empty");
353 } else if (cb) {
372 } else if (cb) {
354 cb(sequence[0]);
373 cb(sequence[0]);
355 } else {
374 } else {
356 return sequence[0];
375 return sequence[0];
357 }
376 }
358 } else {
377 } else {
359 if (err)
378 if (err)
360 err(new Error("The sequence is required"));
379 err(new Error("The sequence is required"));
361 else
380 else
362 throw new Error("The sequence is required");
381 throw new Error("The sequence is required");
363 }
382 }
364 }
383 }
365
384
366 export function firstWhere<T>(
385 export function firstWhere<T>(
367 sequence: ArrayLike<T>,
386 sequence: ArrayLike<T>,
368 predicate: (x: T) => boolean
387 predicate: (x: T) => boolean
369 ): T;
388 ): T;
370 export function firstWhere<T>(
389 export function firstWhere<T>(
371 sequence: PromiseLike<ArrayLike<T>>,
390 sequence: PromiseLike<ArrayLike<T>>,
372 predicate: (x: T) => boolean
391 predicate: (x: T) => boolean
373 ): PromiseLike<T>;
392 ): PromiseLike<T>;
374 export function firstWhere<T>(
393 export function firstWhere<T>(
375 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
394 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
376 predicate: (x: T) => boolean,
395 predicate: (x: T) => boolean,
377 cb: (x: T) => void,
396 cb: (x: T) => void,
378 err?: (x: Error) => void
397 err?: (x: Error) => void
379 ): void;
398 ): void;
380
399
381 export function firstWhere<T>(
400 export function firstWhere<T>(
382 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
401 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
383 predicate?: (x: T) => boolean,
402 predicate?: (x: T) => boolean,
384 cb?: (x: T) => void,
403 cb?: (x: T) => any,
385 err?: (x: Error) => void
404 err?: (x: Error) => any
386 ) {
405 ) {
387 if (isPromise(sequence)) {
406 if (isPromise(sequence)) {
388 return sequence.then(res => firstWhere(res, predicate, cb, err));
407 return sequence.then(res => firstWhere(res, predicate, cb, err));
389 } else if (sequence && "length" in sequence) {
408 } else if (sequence && "length" in sequence) {
390 if (sequence.length === 0) {
409 if (sequence.length === 0) {
391 if (err)
410 if (err)
392 err(new Error("The sequence is empty"));
411 err(new Error("The sequence is empty"));
393 else
412 else
394 throw new Error("The sequence is empty");
413 throw new Error("The sequence is empty");
395 } else {
414 } else {
396 if (!predicate) {
415 if (!predicate) {
397 return cb ? cb(sequence[0]) : sequence[0];
416 return cb ? cb(sequence[0]) && void (0) : sequence[0];
398 } else {
417 } else {
399 for (let i = 0; i < sequence.length; i++) {
418 for (let i = 0; i < sequence.length; i++) {
400 const v = sequence[i];
419 const v = sequence[i];
401 if (predicate(v))
420 if (predicate(v))
402 return cb ? cb(v) : v;
421 return cb ? cb(v) : v;
403 }
422 }
404 if (err)
423 if (err)
405 err(new Error("The sequence doesn't contain matching items"));
424 err(new Error("The sequence doesn't contain matching items"));
406 else
425 else
407 throw new Error("The sequence doesn't contain matching items");
426 throw new Error("The sequence doesn't contain matching items");
408 }
427 }
409 }
428 }
410 } else {
429 } else {
411 if (err)
430 if (err)
412 err(new Error("The sequence is required"));
431 err(new Error("The sequence is required"));
413 else
432 else
414 throw new Error("The sequence is required");
433 throw new Error("The sequence is required");
415 }
434 }
416 }
435 }
417
436
418 export function destroy(d: any) {
437 export function destroy(d: any) {
419 if (d && "destroy" in d)
438 if (d && "destroy" in d)
420 d.destroy();
439 d.destroy();
421 }
440 }
422
441
423 /**
442 /**
424 * Used to mark that the async operation isn't awaited intentionally.
443 * Used to mark that the async operation isn't awaited intentionally.
425 * @param p The promise which represents the async operation.
444 * @param p The promise which represents the async operation.
426 */
445 */
427 export function nowait(p: Promise<any>) {
446 export function nowait(p: Promise<any>) {
428 }
447 }
448
449 /** represents already destroyed object.
450 */
451 export const destroyed = {
452 /** Calling to this method doesn't affect anything, noop.
453 */
454 destroy() {
455 }
456 };
@@ -1,8 +1,9
1 define([
1 define([
2 "./ActivatableTests",
2 "./ActivatableTests",
3 "./trace-test",
3 "./trace-test",
4 "./TraceSourceTests",
4 "./TraceSourceTests",
5 "./CancellationTests",
5 "./CancellationTests",
6 "./ObservableTests",
6 "./ObservableTests",
7 "./ContainerTests"
7 "./ContainerTests",
8 "./SafeTests"
8 ]); No newline at end of file
9 ]);
General Comments 0
You need to be logged in to leave comments. Login now