##// END OF EJS Templates
ts code cleanup, linting
cin -
r39:ed82314aa5c8 di-typescript
parent child
Show More
@@ -1,89 +1,88
1 1 import { ICancellation, IDestroyable } from "./interfaces";
2 2 import { argumentNotNull } from "./safe";
3 3
4 4 const destroyed = {
5 5 destroy() {
6 6 }
7 7 };
8 8
9 9 export class Cancellation implements ICancellation {
10 10 private _reason: any;
11 11 private _cbs: Array<(e) => void>;
12 12
13 13 constructor(action: (cancel: (e) => void) => void) {
14 14 argumentNotNull(action, "action");
15 15
16 16 action(this._cancel.bind(this));
17 17 }
18 18
19 19 isSupported(): boolean {
20 20 return true;
21 21 }
22 22 throwIfRequested(): void {
23 23 if (this._reason)
24 24 throw this._reason;
25 25 }
26 26
27 27 isRequested(): boolean {
28 28 return !!this._reason;
29 29 }
30 30
31 31 register(cb: (e: any) => void): IDestroyable {
32 32 argumentNotNull(cb, "cb");
33 33
34 34 if (this._reason) {
35 35 cb(this._reason);
36 36 return destroyed;
37 37 } else {
38 38 if (!this._cbs)
39 39 this._cbs = [cb];
40 40 else
41 41 this._cbs.push(cb);
42 42
43 let me = this;
43 const me = this;
44 44 return {
45 45 destroy() {
46 46 me._unregister(cb);
47 47 }
48 48 };
49 49 }
50 50 }
51 51
52 52 private _unregister(cb) {
53 53 if(this._cbs) {
54 let i = this._cbs.indexOf(cb);
54 const i = this._cbs.indexOf(cb);
55 55 if ( i>=0 )
56 56 this._cbs.splice(i,1);
57 57 }
58 58 }
59 59
60 60 private _cancel(reason) {
61 61 if (this._reason)
62 62 return;
63 63
64 64 this._reason = (reason = reason || new Error("Operation cancelled"));
65 65
66
67 66 if (this._cbs) {
68 67 this._cbs.forEach(cb => cb(reason));
69 68 this._cbs = null;
70 69 }
71 70 }
72 71
73 72 static readonly none: ICancellation = {
74 73 isSupported(): boolean {
75 74 return false;
76 75 },
77 76
78 77 throwIfRequested(): void {
79 78 },
80 79
81 80 isRequested(): boolean {
82 81 return false;
83 82 },
84 83
85 84 register(_cb: (e: any) => void): IDestroyable {
86 85 return destroyed;
87 86 }
88 87 };
89 } No newline at end of file
88 }
@@ -1,195 +1,184
1 import { IObservable, IDestroyable, ICancellation } from './interfaces';
2 import { Cancellation } from './Cancellation'
3 import { argumentNotNull } from './safe';
4
1 import { IObservable, IDestroyable, ICancellation } from "./interfaces";
2 import { Cancellation } from "./Cancellation";
3 import { argumentNotNull } from "./safe";
5 4
6 interface Handler<T> {
7 (x: T): void
8 }
5 type Handler<T> = (x: T) => void;
9 6
10 interface Initializer<T> {
11 (notify: Handler<T>, error?: (e: any) => void, complete?: () => void): void;
12 }
7 type Initializer<T> = (notify: Handler<T>, error?: (e: any) => void, complete?: () => void) => void;
13 8
14 9 // TODO: think about to move this interfaces.ts and make it public
15 10 interface IObserver<T> {
16 next(event: T): void
11 next(event: T): void;
17 12
18 error(e: any): void
13 error(e: any): void;
19 14
20 complete(): void
15 complete(): void;
21 16 }
22 17
23 18 const noop = () => {};
24 19
25 20 export class Observable<T> implements IObservable<T> {
26 21 private _once = new Array<IObserver<T>>();
27 22
28 23 private _observers = new Array<IObserver<T>>();
29 24
25 private _complete: boolean;
30 26
31 private _complete: boolean
32
33 private _error: any
27 private _error: any;
34 28
35 29 constructor(func?: Initializer<T>) {
36 30 if (func)
37 31 func(
38 32 this._notifyNext.bind(this),
39 33 this._notifyError.bind(this),
40 34 this._notifyCompleted.bind(this)
41 35 );
42 36 }
43 37
44 38 /**
45 39 * Registers handlers for the current observable object.
46 40 *
47 41 * @param next the handler for events
48 42 * @param error the handler for a error
49 43 * @param complete the handler for a completion
50 44 * @returns {IDestroyable} the handler for the current subscription, this
51 45 * handler can be used to unsubscribe from events.
52 46 *
53 47 */
54 48 on(next: Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
55 49 argumentNotNull(next, "next");
56 50
57 let me = this;
51 const me = this;
58 52
59 let observer: IObserver<T> & IDestroyable = {
60 next: next,
53 const observer: IObserver<T> & IDestroyable = {
54 next,
61 55 error: error ? error.bind(null) : noop,
62 56 complete: complete ? complete.bind(null) : noop,
63 57
64 58 destroy() {
65 59 me._removeObserver(this);
66 60 }
67 61 };
68 62
69 63 this._addObserver(observer);
70 64
71
72 65 return observer;
73 66 }
74 67
75 68 private _addObserver(observer: IObserver<T>) {
76 69 if (this._complete) {
77 70 try {
78 71 if (this._error)
79 72 observer.error(this._error);
80 73 else
81 74 observer.complete();
82 75 } catch (e) {
83 76 this.onObserverException(e);
84 77 }
85 78 } else {
86 79 this._observers.push(observer);
87 80 }
88 81 }
89 82
90 83 /**
91 84 * Waits for the next event. This method can't be used to read messages
92 85 * as a sequence since it can skip some messages between calls.
93 86 *
94 87 * @param ct a cancellation token
95 88 */
96 89 next(ct: ICancellation = Cancellation.none): Promise<T> {
97 90 return new Promise<T>((resolve, reject) => {
98 let observer: IObserver<T> = {
91 const observer: IObserver<T> = {
99 92 next: resolve,
100 93 error: reject,
101 94 complete: () => reject("No more events are available")
102 95 };
103 96
104 97 if (this._addOnce(observer) && ct.isSupported()) {
105 ct.register((e) => {
98 ct.register(e => {
106 99 this._removeOnce(observer);
107 100 reject(e);
108 101 });
109 102 }
110 103 });
111 104 }
112 105
113 106 private _addOnce(observer: IObserver<T>) {
114 107 if (this._complete) {
115 108 try {
116 109 if (this._error)
117 110 observer.error(this._error);
118 111 else
119 112 observer.complete();
120 113 } catch (e) {
121 114 this.onObserverException(e);
122 115 }
123 116 return false;
124 117 }
125 118
126 119 this._once.push(observer);
127 120 return true;
128 121 }
129 122
130 123 protected onObserverException(e: any) {
131 124 }
132 125
133 126 private _removeOnce(d: IObserver<T>) {
134 let i = this._once.indexOf(d);
127 const i = this._once.indexOf(d);
135 128 if (i >= 0)
136 129 this._once.splice(i, 1);
137 130 }
138 131
139 132 private _removeObserver(d: IObserver<T>) {
140 let i = this._observers.indexOf(d);
133 const i = this._observers.indexOf(d);
141 134 if (i >= 0)
142 135 this._observers.splice(i, 1);
143 136 }
144 137
145 138 private _notify(guard: (observer: IObserver<T>) => void) {
146 if (this._once.length) {
147 for (let i = 0; i < this._once.length; i++)
148 guard(this._once[i]);
139 this._once.forEach(guard);
149 140 this._once = [];
150 }
151 141
152 for (let i = 0; i < this._observers.length; i++)
153 guard(this._observers[i]);
142 this._observers.forEach(guard);
154 143 }
155 144
156 145 protected _notifyNext(evt: T) {
157 let guard = (observer: IObserver<T>) => {
146 const guard = (observer: IObserver<T>) => {
158 147 try {
159 148 observer.next(evt);
160 149 } catch (e) {
161 150 this.onObserverException(e);
162 151 }
163 }
152 };
164 153
165 154 this._notify(guard);
166 155 }
167 156
168 157 protected _notifyError(e: any) {
169 let guard = (observer: IObserver<T>) => {
158 const guard = (observer: IObserver<T>) => {
170 159 try {
171 160 observer.error(e);
172 161 } catch (e) {
173 162 this.onObserverException(e);
174 163 }
175 }
164 };
176 165
177 166 this._notify(guard);
178 167 this._observers = [];
179 168 this._complete = true;
180 169 }
181 170
182 171 protected _notifyCompleted() {
183 let guard = (observer: IObserver<T>) => {
172 const guard = (observer: IObserver<T>) => {
184 173 try {
185 174 observer.complete();
186 175 } catch (e) {
187 176 this.onObserverException(e);
188 177 }
189 }
178 };
190 179
191 180 this._notify(guard);
192 181 this._observers = [];
193 182 this._complete = true;
194 183 }
195 } No newline at end of file
184 }
@@ -1,270 +1,267
1 1 // Typescript port of the uuid.js
2 2 // Copyright (c) 2018 Sergey Smirnov
3 3 // BSD-2-Clause License https://opensource.org/licenses/BSD-2-Clause
4 4 //
5 5 // uuid.js
6 6 // Copyright (c) 2010-2012 Robert Kieffer
7 7 // MIT License - http://opensource.org/licenses/mit-license.php
8 8
9 9 declare var window: any;
10 10
11 let _window: any = 'undefined' !== typeof window ? window : null;
11 const _window: any = "undefined" !== typeof window ? window : null;
12 12
13 13 // Unique ID creation requires a high quality random # generator. We
14 14 // feature
15 15 // detect to determine the best RNG source, normalizing to a function
16 16 // that
17 17 // returns 128-bits of randomness, since that's what's usually required
18 18 let _rng;
19 19
20 20 function setupBrowser() {
21 21 // Allow for MSIE11 msCrypto
22 let _crypto = _window.crypto || _window.msCrypto;
22 const _crypto = _window.crypto || _window.msCrypto;
23 23
24 24 if (!_rng && _crypto && _crypto.getRandomValues) {
25 25 // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
26 26 //
27 27 // Moderately fast, high quality
28 28 try {
29 let _rnds8 = new Uint8Array(16);
29 const _rnds8 = new Uint8Array(16);
30 30 _rng = function whatwgRNG() {
31 31 _crypto.getRandomValues(_rnds8);
32 32 return _rnds8;
33 33 };
34 34 _rng();
35 35 } catch (e) { /**/ }
36 36 }
37 37
38 38 if (!_rng) {
39 39 // Math.random()-based (RNG)
40 40 //
41 41 // If all else fails, use Math.random(). It's fast, but is of
42 42 // unspecified
43 43 // quality.
44 let _rnds = new Array(16);
45 _rng = function () {
46 for (var i = 0, r; i < 16; i++) {
44 const _rnds = new Array(16);
45 _rng = () => {
46 for (let i = 0, r; i < 16; i++) {
47 47 if ((i & 0x03) === 0) {
48 48 r = Math.random() * 0x100000000;
49 49 }
50 50 _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
51 51 }
52 52
53 53 return _rnds;
54 54 };
55 if ('undefined' !== typeof console && console.warn) {
55 if ("undefined" !== typeof console && console.warn) {
56 56 console.warn("[SECURITY] node-uuid: crypto not usable, falling back to insecure Math.random()");
57 57 }
58 58 }
59 59 }
60 60
61 61 function setupNode() {
62 62 // Node.js crypto-based RNG -
63 63 // http://nodejs.org/docs/v0.6.2/api/crypto.html
64 64 //
65 65 // Moderately fast, high quality
66 if ('function' === typeof require) {
66 if ("function" === typeof require) {
67 67 try {
68 let _rb = require('crypto').randomBytes;
69 _rng = _rb && function () {
70 return _rb(16);
71 };
68 const _rb = require("crypto").randomBytes;
69 _rng = _rb && (() => _rb(16));
72 70 _rng();
73 71 } catch (e) { /**/ }
74 72 }
75 73 }
76 74
77 75 if (_window) {
78 76 setupBrowser();
79 77 } else {
80 78 setupNode();
81 79 }
82 80
83 81 // Buffer class to use
84 let BufferClass = ('function' === typeof Buffer) ? Buffer : Array;
82 const BufferClass = ("function" === typeof Buffer) ? Buffer : Array;
85 83
86 84 // Maps for number <-> hex string conversion
87 let _byteToHex = [];
88 let _hexToByte = {};
85 const _byteToHex = [];
86 const _hexToByte = {};
89 87 for (let i = 0; i < 256; i++) {
90 88 _byteToHex[i] = (i + 0x100).toString(16).substr(1);
91 89 _hexToByte[_byteToHex[i]] = i;
92 90 }
93 91
94 92 // **`parse()` - Parse a UUID into it's component bytes**
95 function _parse(s, buf?, offset?): Array<string> {
96 let i = (buf && offset) || 0, ii = 0;
93 export function _parse(s, buf?, offset?): Array<string> {
94 const i = (buf && offset) || 0; let ii = 0;
97 95
98 96 buf = buf || [];
99 s.toLowerCase().replace(/[0-9a-f]{2}/g, function (oct) {
97 s.toLowerCase().replace(/[0-9a-f]{2}/g, oct => {
100 98 if (ii < 16) { // Don't overflow!
101 99 buf[i + ii++] = _hexToByte[oct];
102 100 }
103 101 });
104 102
105 103 // Zero out remaining bytes if string was short
106 104 while (ii < 16) {
107 105 buf[i + ii++] = 0;
108 106 }
109 107
110 108 return buf;
111 109 }
112 110
113 111 // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
114 112 function _unparse(buf, offset?): string {
115 let i = offset || 0, bth = _byteToHex;
113 let i = offset || 0; const bth = _byteToHex;
116 114 return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] +
117 bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' +
118 bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] +
119 bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] +
115 bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] + "-" +
116 bth[buf[i++]] + bth[buf[i++]] + "-" + bth[buf[i++]] +
117 bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] +
120 118 bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]];
121 119 }
122 120
123 121 // **`v1()` - Generate time-based UUID**
124 122 //
125 123 // Inspired by https://github.com/LiosK/UUID.js
126 124 // and http://docs.python.org/library/uuid.html
127 125
128 126 // random #'s we need to init node and clockseq
129 let _seedBytes = _rng();
127 const _seedBytes = _rng();
130 128
131 129 // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit =
132 130 // 1)
133 let _nodeId = [
131 const _nodeId = [
134 132 _seedBytes[0] | 0x01,
135 133 _seedBytes[1],
136 134 _seedBytes[2],
137 135 _seedBytes[3],
138 136 _seedBytes[4],
139 137 _seedBytes[5]
140 138 ];
141 139
142 140 // Per 4.2.2, randomize (14 bit) clockseq
143 141 let _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
144 142
145 143 // Previous uuid creation time
146 let _lastMSecs = 0, _lastNSecs = 0;
144 let _lastMSecs = 0; let _lastNSecs = 0;
147 145
148 146 // See https://github.com/broofa/node-uuid for API details
149 function _v1(options?, buf?, offset?): string {
147 export function _v1(options?, buf?, offset?): string {
150 148 let i = buf && offset || 0;
151 let b = buf || [];
149 const b = buf || [];
152 150
153 151 options = options || {};
154 152
155 153 let clockseq = (options.clockseq != null) ? options.clockseq : _clockseq;
156 154
157 155 // UUID timestamps are 100 nano-second units since the Gregorian
158 156 // epoch,
159 157 // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
160 158 // time is handled internally as 'msecs' (integer milliseconds) and
161 159 // 'nsecs'
162 160 // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01
163 161 // 00:00.
164 162 let msecs = (options.msecs != null) ? options.msecs : new Date()
165 163 .getTime();
166 164
167 165 // Per 4.2.1.2, use count of uuid's generated during the current
168 166 // clock
169 167 // cycle to simulate higher resolution clock
170 168 let nsecs = (options.nsecs != null) ? options.nsecs : _lastNSecs + 1;
171 169
172 170 // Time since last uuid creation (in msecs)
173 let dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000;
171 const dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000;
174 172
175 173 // Per 4.2.1.2, Bump clockseq on clock regression
176 174 if (dt < 0 && options.clockseq == null) {
177 175 clockseq = clockseq + 1 & 0x3fff;
178 176 }
179 177
180 178 // Reset nsecs if clock regresses (new clockseq) or we've moved onto
181 179 // a new
182 180 // time interval
183 181 if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {
184 182 nsecs = 0;
185 183 }
186 184
187 185 // Per 4.2.1.2 Throw error if too many uuids are requested
188 186 if (nsecs >= 10000) {
189 187 throw new Error(
190 'uuid.v1(): Can\'t create more than 10M uuids/sec');
188 "uuid.v1(): Can't create more than 10M uuids/sec");
191 189 }
192 190
193 191 _lastMSecs = msecs;
194 192 _lastNSecs = nsecs;
195 193 _clockseq = clockseq;
196 194
197 195 // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
198 196 msecs += 12219292800000;
199 197
200 198 // `time_low`
201 let tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
199 const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
202 200 b[i++] = tl >>> 24 & 0xff;
203 201 b[i++] = tl >>> 16 & 0xff;
204 202 b[i++] = tl >>> 8 & 0xff;
205 203 b[i++] = tl & 0xff;
206 204
207 205 // `time_mid`
208 let tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
206 const tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
209 207 b[i++] = tmh >>> 8 & 0xff;
210 208 b[i++] = tmh & 0xff;
211 209
212 210 // `time_high_and_version`
213 211 b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
214 212 b[i++] = tmh >>> 16 & 0xff;
215 213
216 214 // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
217 215 b[i++] = clockseq >>> 8 | 0x80;
218 216
219 217 // `clock_seq_low`
220 218 b[i++] = clockseq & 0xff;
221 219
222 220 // `node`
223 let node = options.node || _nodeId;
221 const node = options.node || _nodeId;
224 222 for (let n = 0; n < 6; n++) {
225 223 b[i + n] = node[n];
226 224 }
227 225
228 226 return buf ? buf : _unparse(b);
229 227 }
230 228
231 229 // **`v4()` - Generate random UUID**
232 230
233 231 // See https://github.com/broofa/node-uuid for API details
234 function _v4(options?, buf?, offset?): string {
232 export function _v4(options?, buf?, offset?): string {
235 233 // Deprecated - 'format' argument, as supported in v1.2
236 let i = buf && offset || 0;
234 const i = buf && offset || 0;
237 235
238 if (typeof (options) === 'string') {
239 buf = (options === 'binary') ? new BufferClass(16) : null;
236 if (typeof (options) === "string") {
237 buf = (options === "binary") ? new BufferClass(16) : null;
240 238 options = null;
241 239 }
242 240 options = options || {};
243 241
244 let rnds = options.random || (options.rng || _rng)();
242 const rnds = options.random || (options.rng || _rng)();
245 243
246 244 // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
247 245 rnds[6] = (rnds[6] & 0x0f) | 0x40;
248 246 rnds[8] = (rnds[8] & 0x3f) | 0x80;
249 247
250 248 // Copy bytes to buffer, if provided
251 249 if (buf) {
252 250 for (let ii = 0; ii < 16; ii++) {
253 251 buf[i + ii] = rnds[ii];
254 252 }
255 253 }
256 254
257 255 return buf || _unparse(rnds);
258 256 }
259 257
260 258 export function Uuid() {
261 259 return _v4();
262 260 }
263 261
264 262 export namespace Uuid {
265 263 export const v4 = _v4;
266 264 export const v1 = _v1;
267 265 export const empty = "00000000-0000-0000-0000-000000000000";
268 266 export const parse = _parse;
269 export const unparse = _unparse;
270 } No newline at end of file
267 }
@@ -1,140 +1,134
1 1 import { TraceSource } from "../log/TraceSource";
2 2 import { argumentNotNull, argumentNotEmptyString, isPrimitive, each, isNull } from "../safe";
3 3 import { Descriptor, ServiceMap, isDescriptor } from "./interfaces";
4 4 import { Container } from "./Container";
5 5
6 let trace = TraceSource.get("di");
6 const trace = TraceSource.get("@implab/core/di/ActivationContext");
7 7
8 export class ActivationContextInfo {
9 name: string
8 export interface ActivationContextInfo {
9 name: string;
10 10
11 service: string
11 service: Descriptor;
12 12
13 scope: ServiceMap
13 scope: ServiceMap;
14 14 }
15 15
16 export class ActivationContext {
17 _cache: object;
16 18
17 export class ActivationContext {
18 _cache: object
19
20 _services: ServiceMap
19 _services: ServiceMap;
21 20
22 _stack: ActivationContextInfo[]
21 _stack: ActivationContextInfo[];
23 22
24 _visited: any
23 _visited: object;
25 24
26 container: any
27
25 container: Container;
28 26
29 27 constructor(container: Container, services: ServiceMap, cache?: object, visited?) {
30 28 argumentNotNull(container, "container");
31 29 argumentNotNull(services, "services");
32 30
33 31 this._visited = visited || {};
34 32 this._stack = [];
35 33 this._cache = cache || {};
36 34 this._services = services;
37 35 this.container = container;
38 36 }
39 37
40 38 getService(name, def?): any {
41 let d = this._services[name];
39 const d = this._services[name];
42 40
43 41 if (!d)
44 42 if (arguments.length > 1)
45 43 return def;
46 44 else
47 throw new Error("Service '" + name + "' not found");
45 throw new Error(`Service ${name} not found`);
48 46
49 47 return d.activate(this, name);
50 48 }
51 49
52 50 /**
53 51 * registers services local to the the activation context
54 52 *
55 53 * @name{string} the name of the service
56 54 * @service{string} the service descriptor to register
57 55 */
58 56 register(name: string, service: Descriptor) {
59 57 argumentNotEmptyString(name, "name");
60 58
61 59 this._services[name] = service;
62 60 }
63 61
64 62 clone() {
65 63 return new ActivationContext(
66 64 this.container,
67 65 Object.create(this._services),
68 66 this._cache,
69 67 this._visited
70 68 );
71
72 69 }
73 70
74 has(id) {
71 has(id: string) {
75 72 return id in this._cache;
76 73 }
77 74
78 get(id) {
75 get(id: string) {
79 76 return this._cache[id];
80 77 }
81 78
82 store(id, value) {
79 store(id: string, value) {
83 80 return (this._cache[id] = value);
84 81 }
85 82
86 parse(data: any, name) {
87 var me = this;
83 parse(data: object, name: string) {
88 84 if (isPrimitive(data))
89 85 return data;
90 86
91 87 if (isDescriptor(data)) {
92 88 return data.activate(this, name);
93 89 } else if (data instanceof Array) {
94 me.enter(name);
95 var v = data.map(function (x, i) {
96 return me.parse(x, "." + i);
97 });
98 me.leave();
90 this.enter(name);
91 const v = data.map( (x, i) => this.parse(x, `[${i}]`));
92 this.leave();
99 93 return v;
100 94 } else {
101 me.enter(name);
102 var result = {};
103 for (var p in data)
104 result[p] = me.parse(data[p], "." + p);
105 me.leave();
95 this.enter(name);
96 const result = {};
97 for (const p in data)
98 result[p] = this.parse(data[p], "." + p);
99 this.leave();
106 100 return result;
107 101 }
108 102 }
109 103
110 visit(id) {
111 var count = this._visited[id] || 0;
104 visit(id: string) {
105 const count = this._visited[id] || 0;
112 106 this._visited[id] = count + 1;
113 107 return count;
114 108 }
115 109
116 110 getStack() {
117 111 return this._stack.slice().reverse();
118 112 }
119 113
120 enter(name, d?, localize?) {
114 enter(name: string, d?: Descriptor, localize?: boolean) {
121 115 if (trace.isLogEnabled())
122 116 trace.log("enter " + name + " " + (d || "") +
123 117 (localize ? " localize" : ""));
124 118 this._stack.push({
125 name: name,
119 name,
126 120 service: d,
127 121 scope: this._services
128 122 });
129 123 if (localize)
130 124 this._services = Object.create(this._services);
131 125 }
132 126
133 127 leave() {
134 var ctx = this._stack.pop();
128 const ctx = this._stack.pop();
135 129 this._services = ctx.scope;
136 130
137 131 if (trace.isLogEnabled())
138 132 trace.log("leave " + ctx.name + " " + (ctx.service || ""));
139 133 }
140 } No newline at end of file
134 }
@@ -1,37 +1,36
1 1 import { ActivationContextInfo } from "./ActivationContext";
2 2
3 3 export class ActivationError {
4 activationStack: ActivationContextInfo[]
4 activationStack: ActivationContextInfo[];
5 5
6 service: string
6 service: string;
7 7
8 innerException: any
8 innerException: any;
9 9
10 message: string
10 message: string;
11 11
12 12 constructor(service: string, activationStack: ActivationContextInfo[], innerException) {
13 13 this.message = "Failed to activate the service";
14 14 this.activationStack = activationStack;
15 15 this.service = service;
16 16 this.innerException = innerException;
17 17 }
18 18
19 19 toString() {
20 var parts = [this.message];
20 const parts = [this.message];
21 21 if (this.service)
22 22 parts.push("when activating: " + this.service.toString());
23 23
24 24 if (this.innerException)
25 25 parts.push("caused by: " + this.innerException.toString());
26 26
27 27 if (this.activationStack) {
28 28 parts.push("at");
29 this.activationStack.forEach(function (x) {
30 parts.push(" " + x.name + " " +
31 (x.service ? x.service.toString() : ""));
32 });
29 this.activationStack
30 .forEach(x => parts.push(` ${x.name} ${x.service ? x.service.toString() : ""}`));
31
33 32 }
34 33
35 34 return parts.join("\n");
36 35 }
37 } No newline at end of file
36 }
@@ -1,24 +1,24
1 1 import { Descriptor } from "./interfaces";
2 2 import { ActivationContext } from "./ActivationContext";
3 3
4 export class AggregateDescriptor<T> implements Descriptor {
5 _value: T;
4 export class AggregateDescriptor implements Descriptor {
5 _value: object;
6 6
7 constructor(value: T) {
8
7 constructor(value: object) {
8 this._value = value;
9 9 }
10 10
11 11 activate(context: ActivationContext, name: string) {
12 12 context.enter(name);
13 13 const v = context.parse(this._value, ".params");
14 14 context.leave();
15 15 return v;
16 16 }
17 17
18 18 isInstanceCreated(): boolean {
19 19 return false;
20 20 }
21 getInstance(): T {
22 throw new Error("Not supported exception");
21 getInstance(): any {
22 throw new Error("Not supported");
23 23 }
24 24 }
@@ -1,263 +1,259
1 1 import { ActivationContext } from "./ActivationContext";
2 2 import { ValueDescriptor } from "./ValueDescriptor";
3 3 import { ActivationError } from "./ActivationError";
4 4 import { isDescriptor, ActivationType, ServiceMap, isDependencyRegistration, isValueRegistration, ServiceRegistration } from "./interfaces";
5 5 import { AggregateDescriptor } from "./AggregateDescriptor";
6 6 import { isPrimitive } from "../safe";
7 7 import { ReferenceDescriptor } from "./ReferenceDescriptor";
8 8 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
9 9 import { ModuleResolverBase } from "./ModuleResolverBase";
10 10 import format = require("../text/format");
11 11
12 12 export class Container {
13 13 _services: ServiceMap;
14 14
15 15 _cache: object;
16 16
17 17 _cleanup: (() => void)[];
18 18
19 19 _root: Container;
20 20
21 21 _parent: Container;
22 22
23 23 _resolver: ModuleResolverBase;
24 24
25 25 constructor(parent?: Container) {
26 26 this._parent = parent;
27 27 this._services = parent ? Object.create(parent._services) : {};
28 28 this._cache = {};
29 29 this._cleanup = [];
30 30 this._root = parent ? parent.getRootContainer() : this;
31 31 this._services.container = new ValueDescriptor(this);
32 32 }
33 33
34 34 getRootContainer() {
35 35 return this._root;
36 36 }
37 37
38 38 getParent() {
39 39 return this._parent;
40 40 }
41 41
42 42 getService<T = any>(name: string, def?: T) {
43 43 const d = this._services[name];
44 44 if (!d)
45 45 if (arguments.length > 1)
46 46 return def;
47 47 else
48 48 throw new Error("Service '" + name + "' isn't found");
49 49
50 50 if (d.isInstanceCreated())
51 51 return d.getInstance() as T;
52 52
53 53 const context = new ActivationContext(this, this._services);
54 54
55 55 try {
56 56 return d.activate(context, name) as T;
57 57 } catch (error) {
58 58 throw new ActivationError(name, context.getStack(), error);
59 59 }
60 60 }
61 61
62 62 register(nameOrCollection, service?) {
63 63 if (arguments.length === 1) {
64 64 const data = nameOrCollection;
65 65 for (const name in data)
66 66 this.register(name, data[name]);
67 67 } else {
68 68 if (!(isDescriptor(service)))
69 69 service = new ValueDescriptor(service);
70 70 this._services[nameOrCollection] = service;
71 71 }
72 72 return this;
73 73 }
74 74
75 75 onDispose(callback) {
76 76 if (!(callback instanceof Function))
77 77 throw new Error("The callback must be a function");
78 78 this._cleanup.push(callback);
79 79 }
80 80
81 81 dispose() {
82 82 if (this._cleanup) {
83 83 for (const f of this._cleanup)
84 84 f();
85 85 this._cleanup = null;
86 86 }
87 87 }
88 88
89 89 /**
90 90 * @param{String|Object} config
91 91 * The configuration of the contaier. Can be either a string or an object,
92 92 * if the configuration is an object it's treated as a collection of
93 93 * services which will be registed in the contaier.
94 94 *
95 95 * @param{Function} opts.contextRequire
96 96 * The function which will be used to load a configuration or types for services.
97 97 *
98 98 */
99 99 async configure(config: string | object, opts?: object) {
100 100 if (typeof (config) === "string") {
101 101 const resolver = await this._resolver.createResolver(config, opts);
102 102 const data = await this._resolver.loadModule(config);
103 103 return this._configure(data, { resolver });
104 104 } else {
105 105 return this._configure(config);
106 106 }
107 107 }
108 108
109 109 createChildContainer() {
110 110 return new Container(this);
111 111 }
112 112
113 113 has(id) {
114 114 return id in this._cache;
115 115 }
116 116
117 117 get(id) {
118 118 return this._cache[id];
119 119 }
120 120
121 121 store(id, value) {
122 122 return (this._cache[id] = value);
123 123 }
124 124
125 125 async _configure(data: object, opts?: { resolver: ModuleResolverBase }) {
126 126 const resolver = (opts && opts.resolver) || this._resolver;
127 127
128 128 const services: ServiceMap = {};
129 129
130 130 resolver.beginBatch();
131 131
132 132 async function parse(k) {
133 133 services[k] = await this._parse(data[k], resolver);
134 134 }
135 135
136 136 const batch = Object.keys(data).map(parse);
137 137
138 138 resolver.completeBatch();
139 139
140 140 await Promise.all(batch);
141 141
142 142 this.register(services);
143 143 }
144 144
145 145 async _parse(registration: any, resolver: ModuleResolverBase) {
146 146 if (isPrimitive(registration) || isDescriptor(registration))
147 147 return registration;
148 148
149 149 if (isDependencyRegistration(registration)) {
150 150
151 151 return new ReferenceDescriptor(
152 152 registration.$dependency,
153 153 registration.lazy,
154 154 registration.optional,
155 155 registration["default"],
156 156 registration.services && this._parseObject(registration.services, resolver)
157 157 );
158 158
159 159 } else if (isValueRegistration(registration)) {
160 160
161 161 return !registration.parse ?
162 162 new ValueDescriptor(registration.$value) :
163 163 new AggregateDescriptor(this._parse(registration.$value, resolver));
164 164
165 165 } else if (registration.$type || registration.$factory) {
166 166 return this._parseService(registration, resolver);
167 167 } else if (registration instanceof Array) {
168 168 return this._parseArray(registration, resolver);
169 169 }
170 170
171 171 return this._parseObject(registration, resolver);
172 172 }
173 173
174 174 async _parseService(data: ServiceRegistration, resolver: ModuleResolverBase) {
175 175 const opts: ServiceDescriptorParams = {
176 176 owner: this
177 177 };
178 178
179 function guard<T>(fn: () => PromiseLike<T>) {
180 return fn();
181 }
182
183 179 if (data.$type) {
184 180 if (data.$type instanceof Function)
185 181 opts.type = data.$type;
186 182 else if (typeof data.$type === "string")
187 183 opts.type = await resolver.resolve(data.$type);
188 184 else
189 185 throw new Error(format("Unsupported type specification: {0:json}", data.$type));
190 186 } else {
191 187 if (data.$factory instanceof Function)
192 188 opts.factory = data.$factory;
193 189 else if (typeof data.$factory === "string")
194 190 opts.factory = await resolver.resolve(data.$factory);
195 191 else
196 192 throw new Error(format("Unsupported factory specification: {0:json}", data.$factory));
197 193 }
198 194
199 195 if (data.services)
200 196 opts.services = await this._parseObject(data.services, resolver);
201 197
202 198 if (data.inject instanceof Array)
203 199 opts.inject = await Promise.all(data.inject.map(x => this._parseObject(x, resolver)));
204 200 else
205 opts.inject = this._parseObject(data.inject, resolver);
201 opts.inject = [await this._parseObject(data.inject, resolver)];
206 202
207 203 if (data.params)
208 204 opts.params = this._parse(data.params, resolver);
209 205
210 206 if (data.activation) {
211 207 if (typeof (data.activation) === "string") {
212 208 switch (data.activation.toLowerCase()) {
213 209 case "singleton":
214 210 opts.activation = ActivationType.Singleton;
215 211 break;
216 212 case "container":
217 213 opts.activation = ActivationType.Container;
218 214 break;
219 215 case "hierarchy":
220 216 opts.activation = ActivationType.Hierarchy;
221 217 break;
222 218 case "context":
223 219 opts.activation = ActivationType.Context;
224 220 break;
225 221 case "call":
226 222 opts.activation = ActivationType.Call;
227 223 break;
228 224 default:
229 225 throw new Error("Unknown activation type: " +
230 226 data.activation);
231 227 }
232 228 } else {
233 229 opts.activation = Number(data.activation);
234 230 }
235 231 }
236 232
237 233 if (data.cleanup)
238 234 opts.cleanup = data.cleanup;
239 235
240 236 return new ServiceDescriptor(opts);
241 237 }
242 238
243 _parseObject(data: any, typemap) {
239 _parseObject(data: object, resolver: ModuleResolverBase) {
244 240 if (data.constructor &&
245 241 data.constructor.prototype !== Object.prototype)
246 242 return new ValueDescriptor(data);
247 243
248 244 const o = {};
249 245
250 246 for (const p in data)
251 o[p] = this._parse(data[p], typemap);
247 o[p] = this._parse(data[p], resolver);
252 248
253 249 return o;
254 250 }
255 251
256 _parseArray(data, typemap) {
252 _parseArray(data: Array<any>, resolver: ModuleResolverBase) {
257 253 if (data.constructor &&
258 254 data.constructor.prototype !== Array.prototype)
259 255 return new ValueDescriptor(data);
260 256
261 return data.map(x => this._parse(x, typemap));
257 return data.map(x => this._parse(x, resolver));
262 258 }
263 259 }
@@ -1,100 +1,106
1 1 import { isNull, argumentNotEmptyString, each } from "../safe";
2 2 import { ActivationContext } from "./ActivationContext";
3 3 import { ServiceMap, Descriptor } from "./interfaces";
4 4 import { ActivationError } from "./ActivationError";
5 5
6 6 export class ReferenceDescriptor implements Descriptor {
7 _name: string
7 _name: string;
8 8
9 _lazy = false
9 _lazy = false;
10 10
11 _optional = false
11 _optional = false;
12 12
13 _default: any
13 _default: any;
14 14
15 _services: ServiceMap
15 _services: ServiceMap;
16 16
17 17 constructor(name: string, lazy: boolean, optional: boolean, def, services: ServiceMap) {
18 18 argumentNotEmptyString(name, "name");
19 19 this._name = name;
20 20 this._lazy = Boolean(lazy);
21 21 this._optional = Boolean(optional);
22 22 this._default = def;
23 23 this._services = services;
24 24 }
25 25
26 26 activate(context: ActivationContext, name: string) {
27 var me = this;
28 27
29 context.enter(name, this, true);
28 if (this._lazy) {
29 // сохраняем контекст активации
30 context = context.clone();
30 31
31 32 // добавляем сервисы
32 if (me._services) {
33 for (var p in me._services) {
34 var sv = me._services[p];
35 context.register(p, sv);
36 }
33 if (this._services) {
34 for (const p of Object.keys(this._services))
35 context.register(p, this._services[p]);
37 36 }
38 37
39 if (me._lazy) {
40 // сохраняем контекст активации
41 context = context.clone();
42 return function (cfg: ServiceMap) {
38 return (cfg: ServiceMap) => {
43 39 // защищаем контекст на случай исключения в процессе
44 40 // активации
45 var ct = context.clone();
41 const ct = context.clone();
46 42 try {
47 if (cfg)
48 for(let k in cfg)
49 ct.register(k, cfg[v]);
43 if (cfg) {
44 for (const k in cfg)
45 ct.register(k, cfg[k]);
46 }
50 47
51 return me._optional ? ct.getService(me._name, me._default) : ct
52 .getService(me._name);
48 return this._optional ? ct.getService(this._name, this._default) : ct
49 .getService(this._name);
53 50 } catch (error) {
54 throw new ActivationError(me._name, ct.getStack(), error);
51 throw new ActivationError(this._name, ct.getStack(), error);
55 52 }
56 53 };
54 } else {
55 context.enter(name, this, !!this._services);
56
57 // добавляем сервисы
58 if (this._services) {
59 for (const p of Object.keys(this._services))
60 context.register(p, this._services[p]);
57 61 }
58 62
59 var v = me._optional ?
60 context.getService(me._name, me._default) :
61 context.getService(me._name);
63 const v = this._optional ?
64 context.getService(this._name, this._default) :
65 context.getService(this._name);
62 66
63 67 context.leave();
68
64 69 return v;
65 70 }
71 }
66 72
67 73 isInstanceCreated() {
68 74 return false;
69 75 }
70 76
71 77 getInstance() {
72 78 throw new Error("The reference descriptor doesn't allowed to hold an instance");
73 79 }
74 80
75 81 toString() {
76 var opts = [];
82 const opts = [];
77 83 if (this._optional)
78 84 opts.push("optional");
79 85 if (this._lazy)
80 86 opts.push("lazy");
81 87
82 var parts = [
88 const parts = [
83 89 "@ref "
84 90 ];
85 91 if (opts.length) {
86 92 parts.push("{");
87 93 parts.push(opts.join());
88 94 parts.push("} ");
89 95 }
90 96
91 97 parts.push(this._name);
92 98
93 99 if (!isNull(this._default)) {
94 100 parts.push(" = ");
95 101 parts.push(this._default);
96 102 }
97 103
98 104 return parts.join("");
99 105 }
100 } No newline at end of file
106 }
@@ -1,265 +1,273
1 1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ActivationType, ServiceMap, Constructor, Factory } from "./interfaces";
2 import { Descriptor, ActivationType, ServiceMap } from "./interfaces";
3 3 import { Container } from "./Container";
4 import { argumentNotNull, isPrimitive, oid } from "../safe";
5 import { ClientResponse } from "http";
4 import { argumentNotNull, isPrimitive, oid, isPromise } from "../safe";
5 import { Constructor, Factory } from "../interfaces";
6 6
7 7 let cacheId = 0;
8 8
9 9 function injectMethod(target, method, context, args) {
10 10 const m = target[method];
11 11 if (!m)
12 12 throw new Error("Method '" + method + "' not found");
13 13
14 14 if (args instanceof Array)
15 m.apply(target, context.parse(args, "." + method));
15 return m.apply(target, context.parse(args, "." + method));
16 16 else
17 m.call(target, context.parse(args, "." + method));
17 return m.call(target, context.parse(args, "." + method));
18 18 }
19 19
20 function makeClenupCallback(target, method: (instance) => void | string) {
20 function makeClenupCallback(target, method: ((instance) => void) | string) {
21 21 if (typeof (method) === "string") {
22 22 return () => {
23 23 target[method]();
24 24 };
25 25 } else {
26 26 return () => {
27 27 method(target);
28 28 };
29 29 }
30 30 }
31 31
32 export interface ServiceDescriptorParams<T = {}> {
32 export interface ServiceDescriptorParams {
33 33 activation?: ActivationType;
34 34
35 35 owner: Container;
36 36
37 type?: Constructor<T>;
37 type?: Constructor;
38 38
39 factory?: Factory<T>;
39 factory?: Factory;
40 40
41 41 params?;
42 42
43 inject?;
43 inject?: object[];
44 44
45 45 services?: ServiceMap;
46 46
47 cleanup?: (instance: T) => void | string;
47 cleanup?: ((x) => void) | string;
48 48 }
49 49
50 export class ServiceDescriptor<T = {}> implements Descriptor {
51 _instance: T = null;
50 export class ServiceDescriptor implements Descriptor {
51 _instance;
52 52
53 53 _hasInstance = false;
54 54
55 55 _activationType = ActivationType.Call;
56 56
57 57 _services: ServiceMap;
58 58
59 _type: Constructor<T> = null;
59 _type: Constructor = null;
60 60
61 _factory: Factory<T> = null;
61 _factory: Factory = null;
62 62
63 63 _params;
64 64
65 _inject: Array<object>;
65 _inject: object[];
66 66
67 _cleanup: (instance: T) => void;
67 _cleanup: ((x) => void) | string;
68 68
69 69 _cacheId: any;
70 70
71 71 _owner: Container;
72 72
73 constructor(opts: ServiceDescriptorParams<T>) {
73 constructor(opts: ServiceDescriptorParams) {
74 74 argumentNotNull(opts, "opts");
75 75 argumentNotNull(opts.owner, "owner");
76 76
77 77 this._owner = opts.owner;
78 78
79 79 if (!(opts.type || opts.factory))
80 80 throw new Error(
81 81 "Either a type or a factory must be specified");
82 82
83 83 if (opts.activation)
84 84 this._activationType = opts.activation;
85 85
86 86 if (opts.type)
87 87 this._type = opts.type;
88 88
89 89 if (opts.params)
90 90 this._params = opts.params;
91 91
92 92 if (opts.inject)
93 this._inject = opts.inject instanceof Array ? opts.inject : [opts.inject];
93 this._inject = opts.inject;
94 94
95 95 if (opts.services)
96 96 this._services = opts.services;
97 97
98 98 if (opts.factory)
99 99 this._factory = opts.factory;
100 100
101 101 if (opts.cleanup) {
102 102 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
103 103 throw new Error(
104 104 "The cleanup parameter must be either a function or a function name");
105 105
106 106 this._cleanup = opts.cleanup;
107 107 }
108 108
109 109 if (this._activationType === ActivationType.Singleton) {
110 110 const tof = this._type || this._factory;
111 111
112 112 // create the persistent cache identifier for the type
113 113 if (isPrimitive(tof))
114 114 this._cacheId = tof;
115 115 else
116 116 this._cacheId = oid(tof);
117 117 } else {
118 118 this._cacheId = ++cacheId;
119 119 }
120 120 }
121 121
122 122 activate(context: ActivationContext, name: string) {
123 123 // if we have a local service records, register them first
124 124 let instance;
125 125
126 126 switch (this._activationType) {
127 127 case ActivationType.Singleton: // SINGLETON
128 128 // if the value is cached return it
129 129 if (this._hasInstance)
130 130 return this._instance;
131 131
132 132 // singletons are bound to the root container
133 133 const container = context.container.getRootContainer();
134 134
135 135 if (container.has(this._cacheId)) {
136 136 instance = container.get(this._cacheId);
137 137 } else {
138 138 instance = this._create(context, name);
139 139 container.store(this._cacheId, instance);
140 140 if (this._cleanup)
141 141 container.onDispose(
142 142 makeClenupCallback(instance, this._cleanup));
143 143 }
144 144
145 145 this._hasInstance = true;
146 146 return (this._instance = instance);
147 147
148 148 case ActivationType.Container: // CONTAINER
149 149 // return a cached value
150 150
151 151 if (this._hasInstance)
152 152 return this._instance;
153 153
154 154 // create an instance
155 155 instance = this._create(context, name);
156 156
157 157 // the instance is bound to the container
158 158 if (this._cleanup)
159 159 this._owner.onDispose(
160 160 makeClenupCallback(instance, this._cleanup));
161 161
162 162 // cache and return the instance
163 163 this._hasInstance = true;
164 164 return (this._instance = instance);
165 165 case ActivationType.Context: // CONTEXT
166 166 // return a cached value if one exists
167 167
168 168 if (context.has(this._cacheId))
169 169 return context.get(this._cacheId);
170 170 // context context activated instances are controlled by callers
171 171 return context.store(this._cacheId, this._create(
172 172 context,
173 173 name));
174 174 case ActivationType.Call: // CALL
175 175 // per-call created instances are controlled by callers
176 176 return this._create(context, name);
177 177 case ActivationType.Hierarchy: // HIERARCHY
178 178 // hierarchy activated instances are behave much like container activated
179 179 // except they are created and bound to the child container
180 180
181 181 // return a cached value
182 182 if (context.container.has(this._cacheId))
183 183 return context.container.get(this._cacheId);
184 184
185 185 instance = this._create(context, name);
186 186
187 187 if (this._cleanup)
188 188 context.container.onDispose(makeClenupCallback(
189 189 instance,
190 190 this._cleanup));
191 191
192 192 return context.container.store(this._cacheId, instance);
193 193 default:
194 194 throw new Error("Invalid activation type: " + this._activationType);
195 195 }
196 196 }
197 197
198 198 isInstanceCreated() {
199 199 return this._hasInstance;
200 200 }
201 201
202 202 getInstance() {
203 203 return this._instance;
204 204 }
205 205
206 206 _create(context, name) {
207 207 context.enter(name, this, Boolean(this._services));
208 208
209 209 if (this._activationType !== ActivationType.Call &&
210 210 context.visit(this._cacheId) > 0)
211 211 throw new Error("Recursion detected");
212 212
213 213 if (this._services) {
214 214 for (const p in this._services)
215 215 context.register(p, this._services[p]);
216 216 }
217 217
218 218 let instance;
219 219
220 220 if (!this._factory) {
221 221 const ctor = this._type;
222 if (this._params && this._params.length) {
222 223 this._factory = (...args) => {
223 return new ctor(...args);
224 const t = Object.create(ctor.prototype);
225 const inst = ctor.apply(t, args);
226 return isPrimitive(inst) ? t : inst;
224 227 };
228 } else {
229 this._factory = () => {
230 return new ctor();
231 };
232 }
225 233 }
226 234
227 235 if (this._params === undefined) {
228 236 instance = this._factory();
229 237 } else if (this._params instanceof Array) {
230 238 instance = this._factory.apply(this, context.parse(
231 239 this._params,
232 240 ".params"));
233 241 } else {
234 242 instance = this._factory(context.parse(
235 243 this._params,
236 244 ".params"));
237 245 }
238 246
239 247 if (this._inject) {
240 248 this._inject.forEach(spec => {
241 249 for (const m in spec)
242 250 injectMethod(instance, m, context, spec[m]);
243 251 });
244 252 }
245 253
246 254 context.leave();
247 255
248 256 return instance;
249 257 }
250 258
251 259 // @constructor {singleton} foo/bar/Baz
252 260 // @factory {singleton}
253 261 toString() {
254 262 const parts = [];
255 263
256 264 parts.push(this._type ? "@constructor" : "@factory");
257 265
258 266 parts.push(ActivationType[this._activationType]);
259 267
260 268 if (typeof (this._type) === "string")
261 269 parts.push(this._type);
262 270
263 271 return parts.join(" ");
264 272 }
265 273 }
@@ -1,23 +1,23
1 1 import { Descriptor } from "./interfaces";
2 2 import { ActivationContext } from "./ActivationContext";
3 3
4 export class ValueDescriptor<T> implements Descriptor {
5 _value: T
4 export class ValueDescriptor implements Descriptor {
5 _value;
6 6
7 constructor(value: T) {
7 constructor(value) {
8 8 this._value = value;
9 9 }
10 10
11 11 activate(context: ActivationContext, name: string) {
12 12 context.enter(name);
13 let v = this._value;
13 const v = this._value;
14 14 context.leave();
15 15 return v;
16 16 }
17 17 isInstanceCreated(): boolean {
18 18 return true;
19 19 }
20 getInstance(): T {
20 getInstance() {
21 21 return this._value;
22 22 }
23 } No newline at end of file
23 }
@@ -1,71 +1,68
1 1 import { isNull } from "../safe";
2 2 import { ActivationContext } from "./ActivationContext";
3 import { Constructor, Factory } from "../interfaces";
3 4
4 5 export interface Descriptor {
5 6 activate(context: ActivationContext, name?: string);
6 7 isInstanceCreated(): boolean;
7 8 getInstance();
8 9 }
9 10
10 export type Constructor<T = {}> = new (...args: any[]) => T;
11
12 export type Factory<T = {}> = (...args: any[]) => T;
13
14 11 export function isDescriptor(instance): instance is Descriptor {
15 12 return (!isNull(instance)) &&
16 13 ("activate" in instance);
17 14 }
18 15
19 16 export interface ServiceMap {
20 17 [s: string]: Descriptor;
21 18 }
22 19
23 20 export enum ActivationType {
24 21 Singleton,
25 22 Container,
26 23 Hierarchy,
27 24 Context,
28 25 Call
29 26 }
30 27
31 28 export interface RegistrationWithServices {
32 29 services?: object;
33 30 }
34 31
35 32 export interface ServiceRegistration extends RegistrationWithServices {
36 33 $type?: string | Constructor;
37 34
38 35 $factory?: string | Factory;
39 36
40 37 activation?: "singleton" | "container" | "hierarchy" | "context" | "call";
41 38
42 39 params?;
43 40
44 41 inject?: object | object[];
45 42
46 43 cleanup: (instance) => void | string;
47 44 }
48 45
49 46 export interface ValueRegistration {
50 47 $value;
51 48 parse?: boolean;
52 49 }
53 50
54 51 export interface DependencyRegistration extends RegistrationWithServices {
55 52 $dependency: string;
56 53 lazy?: boolean;
57 54 optional?: boolean;
58 55 default?;
59 56 }
60 57
61 58 export function isServiceRegistration(x): x is ServiceRegistration {
62 59 return x && ("$type" in x || "$factory" in x);
63 60 }
64 61
65 62 export function isValueRegistration(x): x is ValueRegistration {
66 63 return x && "$value" in x;
67 64 }
68 65
69 66 export function isDependencyRegistration(x): x is DependencyRegistration {
70 67 return x && "$depdendency" in x;
71 68 }
@@ -1,76 +1,80
1 export type Constructor<T = {}> = new (...args: any[]) => T;
2
3 export type Factory<T = {}> = (...args: any[]) => T;
4
1 5 export interface IDestroyable {
2 6 destroy();
3 7 }
4 8
5 9 export interface ICancellation {
6 10 throwIfRequested(): void;
7 11 isRequested(): boolean;
8 12 isSupported(): boolean;
9 13 register(cb: (e: any) => void): IDestroyable;
10 14 }
11 15
12 16 /**
13 17 * Интерфейс поддерживающий асинхронную активацию
14 18 */
15 19 export interface IActivatable {
16 20 /**
17 21 * @returns Boolean indicates the current state
18 22 */
19 23 isActive(): boolean;
20 24
21 25 /**
22 26 * Starts the component activation
23 27 * @param ct cancellation token for this operation
24 28 */
25 29 activate(ct?: ICancellation) : Promise<void>;
26 30
27 31 /**
28 32 * Starts the component deactivation
29 33 * @param ct cancellation token for this operation
30 34 */
31 35 deactivate(ct?: ICancellation) : Promise<void>;
32 36
33 37 /**
34 38 * Sets the activation controller for this component
35 39 * @param controller The activation controller
36 40 *
37 41 * Activation controller checks whether this component
38 42 * can be activated and manages the active state of the
39 43 * component
40 44 */
41 45 setActivationController(controller: IActivationController);
42 46
43 47 /**
44 48 * Gets the current activation controller for this component
45 49 */
46 50 getActivationController(): IActivationController;
47 51 }
48 52
49 53 export interface IActivationController {
50 54 activating(component: IActivatable, ct?: ICancellation): Promise<void>;
51 55
52 56 activated(component: IActivatable, ct?: ICancellation): Promise<void>;
53 57
54 58 deactivating(component: IActivatable, ct?: ICancellation): Promise<void>;
55 59
56 60 deactivated(component: IActivatable, ct?: ICancellation): Promise<void>;
57 61
58 62 deactivate(ct?: ICancellation): Promise<void>;
59 63
60 64 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
61 65
62 66 getActive(): IActivatable;
63 67 }
64 68
65 69 export interface IAsyncComponent {
66 70 getCompletion(): Promise<void>;
67 71 }
68 72
69 73 export interface ICancellable {
70 74 cancel(reason?: any): void;
71 75 }
72 76
73 77 export interface IObservable<T> {
74 78 on(next: (x:T) => void, error?: (e:any) => void, complete?:() => void): IDestroyable;
75 79 next(ct?: ICancellation) : Promise<T>;
76 } No newline at end of file
80 }
@@ -1,305 +1,306
1 1 let _nextOid = 0;
2 2 const _oid = Symbol("__oid");
3 3
4 export function oid(instance: object) {
4 export function oid(instance: object): string {
5 5 if (isNull(instance))
6 6 return null;
7 7
8 8 if (_oid in instance)
9 9 return instance[_oid];
10 10 else
11 11 return (instance[_oid] = "oid_" + (++_nextOid));
12 12 }
13 13
14 14 export function argumentNotNull(arg, name) {
15 15 if (arg === null || arg === undefined)
16 16 throw new Error("The argument " + name + " can't be null or undefined");
17 17 }
18 18
19 19 export function argumentNotEmptyString(arg, name) {
20 20 if (typeof (arg) !== "string" || !arg.length)
21 21 throw new Error("The argument '" + name + "' must be a not empty string");
22 22 }
23 23
24 24 export function argumentNotEmptyArray(arg, name) {
25 25 if (!(arg instanceof Array) || !arg.length)
26 26 throw new Error("The argument '" + name + "' must be a not empty array");
27 27 }
28 28
29 29 export function argumentOfType(arg, type, name) {
30 30 if (!(arg instanceof type))
31 31 throw new Error("The argument '" + name + "' type doesn't match");
32 32 }
33 33
34 34 export function isNull(arg) {
35 35 return (arg === null || arg === undefined);
36 36 }
37 37
38 38 export function isPrimitive(arg) {
39 39 return (arg === null || arg === undefined || typeof (arg) === "string" ||
40 40 typeof (arg) === "number" || typeof (arg) === "boolean");
41 41 }
42 42
43 43 export function isInteger(arg) {
44 44 return parseInt(arg, 10) === arg;
45 45 }
46 46
47 47 export function isNumber(arg) {
48 48 return parseFloat(arg) === arg;
49 49 }
50 50
51 51 export function isString(val) {
52 52 return typeof (val) === "string" || val instanceof String;
53 53 }
54 54
55 export function isPromise(val): val is PromiseLike<any> {
56 return "then" in val && val.then instanceof Function;
57 }
58
55 59 export function isNullOrEmptyString(str) {
56 60 if (str === null || str === undefined ||
57 61 ((typeof (str) === "string" || str instanceof String) && str.length === 0))
58 62 return true;
59 63 }
60 64
61 65 export function isNotEmptyArray(arg): arg is Array<any> {
62 66 return (arg instanceof Array && arg.length > 0);
63 67 }
64 68
65 69 export function getGlobal() {
66 70 return this;
67 71 }
68 72
69 73 export function get(member: string, context?: object) {
70 74 argumentNotEmptyString(member, "member");
71 75 let that = context || getGlobal();
72 76 const parts = member.split(".");
73 77 for (const m of parts) {
74 78 if (!m)
75 79 continue;
76 80 if (isNull(that = that[m]))
77 81 break;
78 82 }
79 83 return that;
80 84 }
81 85
82 86 /**
83 87 * Выполняет метод для каждого элемента массива, останавливается, когда
84 88 * либо достигнут конец массива, либо функция <c>cb</c> вернула
85 89 * значение.
86 90 *
87 91 * @param {Array | Object} obj массив элементов для просмотра
88 92 * @param {Function} cb функция, вызываемая для каждого элемента
89 93 * @param {Object} thisArg значение, которое будет передано в качестве
90 94 * <c>this</c> в <c>cb</c>.
91 95 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
92 96 * если достигнут конец массива.
93 97 */
94 98 export function each(obj, cb, thisArg?) {
95 99 argumentNotNull(cb, "cb");
96 100 if (obj instanceof Array) {
97 101 for (let i = 0; i < obj.length; i++) {
98 102 const x = cb.call(thisArg, obj[i], i);
99 103 if (x !== undefined)
100 104 return x;
101 105 }
102 106 } else {
103 107 const keys = Object.keys(obj);
104 108 for (const k of keys) {
105 109 const x = cb.call(thisArg, obj[k], k);
106 110 if (x !== undefined)
107 111 return x;
108 112 }
109 113 }
110 114 }
111 115
112 116 /** Copies property values from a source object to the destination and returns
113 117 * the destination onject.
114 118 *
115 119 * @param dest The destination object into which properties from the source
116 120 * object will be copied.
117 121 * @param source The source of values which will be copied to the destination
118 122 * object.
119 123 * @param template An optional parameter specifies which properties should be
120 124 * copied from the source and how to map them to the destination. If the
121 125 * template is an array it contains the list of property names to copy from the
122 126 * source to the destination. In case of object the templates contains the map
123 127 * where keys are property names in the source and the values are property
124 128 * names in the destination object. If the template isn't specified then the
125 129 * own properties of the source are entirely copied to the destination.
126 130 *
127 131 */
128 132 export function mixin<T, S>(dest: T, source: S, template?: string[] | object): T & S {
129 133 argumentNotNull(dest, "to");
130 134 const _res = dest as T & S;
131 135
132 136 if (template instanceof Array) {
133 137 for (const p of template) {
134 138 if (p in source)
135 139 _res[p] = source[p];
136 140 }
137 141 } else if (template) {
138 142 const keys = Object.keys(source);
139 143 for (const p of keys) {
140 144 if (p in template)
141 145 _res[template[p]] = source[p];
142 146 }
143 147 } else {
144 148 const keys = Object.keys(source);
145 149 for (const p of keys)
146 150 _res[p] = source[p];
147 151 }
148 152
149 153 return _res;
150 154 }
151 155
152 156 /** Wraps the specified function to emulate an asynchronous execution.
153 157 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
154 158 * @param{Function|String} fn [Required] Function wich will be wrapped.
155 159 */
156 160 export function async(_fn: (...args: any[]) => any, thisArg): (...args: any[]) => PromiseLike<any> {
157 161 let fn = _fn;
158 162
159 163 if (arguments.length === 2 && !(fn instanceof Function))
160 164 fn = thisArg[fn];
161 165
162 166 if (fn == null)
163 167 throw new Error("The function must be specified");
164 168
165 169 function wrapresult(x, e?): PromiseLike<any> {
166 170 if (e) {
167 171 return {
168 172 then(cb, eb) {
169 173 try {
170 174 return eb ? wrapresult(eb(e)) : this;
171 175 } catch (e2) {
172 176 return wrapresult(null, e2);
173 177 }
174 178 }
175 179 };
176 180 } else {
177 181 if (x && x.then)
178 182 return x;
179 183 return {
180 184 then(cb) {
181 185 try {
182 186 return cb ? wrapresult(cb(x)) : this;
183 187 } catch (e2) {
184 188 return wrapresult(e2);
185 189 }
186 190 }
187 191 };
188 192 }
189 193 }
190 194
191 195 return (...args) => {
192 196 try {
193 197 return wrapresult(fn.apply(thisArg, args));
194 198 } catch (e) {
195 199 return wrapresult(null, e);
196 200 }
197 201 };
198 202 }
199 203
200 204 type _AnyFn = (...args) => any;
201 205
202 206 export function delegate<T, K extends keyof T>(target: T, _method: (K | _AnyFn)) {
203 207 let method;
204 208
205 209 if (!(_method instanceof Function)) {
206 210 argumentNotNull(target, "target");
207 211 method = target[_method];
208 212 if (!(method instanceof Function))
209 213 throw new Error("'method' argument must be a Function or a method name");
210 214 } else {
211 215 method = _method;
212 216 }
213 217
214 218 return (...args) => {
215 219 return method.apply(target, args);
216 220 };
217 221 }
218 222
219 223 /**
220 224 * Для каждого элемента массива вызывает указанную функцию и сохраняет
221 225 * возвращенное значение в массиве результатов.
222 226 *
223 227 * @remarks cb может выполняться асинхронно, при этом одновременно будет
224 228 * только одна операция.
225 229 *
226 230 * @async
227 231 */
228 232 export function pmap(items, cb) {
229 233 argumentNotNull(cb, "cb");
230 234
231 235 if (items && items.then instanceof Function)
232 return items.then(function (data) {
233 return pmap(data, cb);
234 });
236 return items.then(data => pmap(data, cb));
235 237
236 238 if (isNull(items) || !items.length)
237 239 return items;
238 240
239 var i = 0,
240 result = [];
241 let i = 0;
242 const result = [];
241 243
242 244 function next() {
243 var r, ri;
245 let r;
246 let ri;
244 247
245 248 function chain(x) {
246 249 result[ri] = x;
247 250 return next();
248 251 }
249 252
250 253 while (i < items.length) {
251 254 r = cb(items[i], i);
252 255 ri = i;
253 256 i++;
254 if (r && r.then) {
257 if (isPromise(r)) {
255 258 return r.then(chain);
256 259 } else {
257 260 result[ri] = r;
258 261 }
259 262 }
260 263 return result;
261 264 }
262 265
263 266 return next();
264 267 }
265 268
266 269 /**
267 270 * Выбирает первый элемент из последовательности, или обещания, если в
268 271 * качестве параметра используется обещание, оно должно вернуть массив.
269 272 *
270 273 * @param {Function} cb обработчик результата, ему будет передан первый
271 274 * элемент последовательности в случае успеха
272 275 * @param {Function} err обработчик исключения, если массив пустой, либо
273 276 * не массив
274 277 *
275 278 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
276 279 * обещание, либо первый элемент.
277 280 * @async
278 281 */
279 export function first(sequence: any, cb: Function, err: Function) {
282 export function first(sequence, cb: (x) => any, err: (x) => any) {
280 283 if (sequence) {
281 if (sequence.then instanceof Function) {
282 return sequence.then(function (res) {
283 return first(res, cb, err);
284 }, err);
284 if (isPromise(sequence)) {
285 return sequence.then(res => first(res, cb, err));
285 286 } else if (sequence && "length" in sequence) {
286 287 if (sequence.length === 0) {
287 288 if (err)
288 289 return err(new Error("The sequence is empty"));
289 290 else
290 291 throw new Error("The sequence is empty");
291 292 }
292 293 return cb ? cb(sequence[0]) : sequence[0];
293 294 }
294 295 }
295 296
296 297 if (err)
297 298 return err(new Error("The sequence is required"));
298 299 else
299 300 throw new Error("The sequence is required");
300 301 }
301 302
302 export function destroy(d: any) {
303 if (d && 'destroy' in d)
303 export function destroy(d) {
304 if (d && "destroy" in d)
304 305 d.destroy();
305 } No newline at end of file
306 }
@@ -1,3 +1,3
1 //define(["./ActivatableTests", "./trace-test", "./TraceSourceTests", "./CancellationTests"]);
1 define(["./ActivatableTests", "./trace-test", "./TraceSourceTests", "./CancellationTests"]);
2 2 //define(["./CancellationTests"]);
3 define(["./ObservableTests"]); No newline at end of file
3 //define(["./ObservableTests"]); No newline at end of file
@@ -1,13 +1,13
1 import * as tape from 'tape';
2 import * as uuid from '@implab/core/Uuid';
1 import * as tape from "tape";
2 import { Uuid } from "@implab/core/Uuid";
3 3
4 tape('simple', function(t){
4 tape("simple", t => {
5 5 t.pass("sync assert");
6 6 setTimeout(() => {
7 7 t.pass("async assert");
8 t.comment(uuid());
9 t.ok(uuid() != uuid());
8 t.comment(Uuid());
9 t.ok(Uuid() !== Uuid());
10 10 // end should be called after the last assertion
11 11 t.end();
12 12 }, 100);
13 }); No newline at end of file
13 });
@@ -1,15 +1,15
1 1 {
2 2 "compilerOptions": {
3 "target": "es5",
3 "target": "es3",
4 4 "module": "amd",
5 5 "sourceMap": true,
6 6 "outDir" : "build/dist",
7 7 "declaration": true,
8 8 "lib": [
9 9 "es2015"
10 10 ]
11 11 },
12 12 "include" : [
13 13 "src/ts/**/*.ts"
14 14 ]
15 15 } No newline at end of file
@@ -1,15 +1,15
1 1 {
2 2 "compilerOptions": {
3 "target": "es5",
3 "target": "es3",
4 4 "module": "amd",
5 5 "sourceMap": true,
6 6 "outDir" : "build/test",
7 7 "moduleResolution": "node",
8 8 "lib": [
9 "ES2015"
9 "es2015"
10 10 ]
11 11 },
12 12 "include" : [
13 13 "test/ts/**/*.ts"
14 14 ]
15 15 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now