##// END OF EJS Templates
ported IoC container to typescript...
cin -
r34:bf1098a8d031 di-typescript
parent child
Show More
@@ -0,0 +1,37
1 import { ActivationContextInfo } from "./ActivationContext";
2
3 export class ActivationError {
4 activationStack: ActivationContextInfo[]
5
6 service: string
7
8 innerException: any
9
10 message: string
11
12 constructor(service: string, activationStack: ActivationContextInfo[], innerException) {
13 this.message = "Failed to activate the service";
14 this.activationStack = activationStack;
15 this.service = service;
16 this.innerException = innerException;
17 }
18
19 toString() {
20 var parts = [this.message];
21 if (this.service)
22 parts.push("when activating: " + this.service.toString());
23
24 if (this.innerException)
25 parts.push("caused by: " + this.innerException.toString());
26
27 if (this.activationStack) {
28 parts.push("at");
29 this.activationStack.forEach(function (x) {
30 parts.push(" " + x.name + " " +
31 (x.service ? x.service.toString() : ""));
32 });
33 }
34
35 return parts.join("\n");
36 }
37 } No newline at end of file
@@ -0,0 +1,24
1 import { Descriptor } from "./interfaces";
2 import { ActivationContext } from "./ActivationContext";
3
4 export class AggregateDescriptor<T> implements Descriptor {
5 _value: T
6
7 constructor(value: T) {
8
9 }
10
11 activate(context: ActivationContext, name: string) {
12 context.enter(name);
13 let v = context.parse(this._value, ".params");
14 context.leave();
15 return v;
16 }
17
18 isInstanceCreated(): boolean {
19 return false;
20 }
21 getInstance(): T {
22 throw new Error("Not supported exception");
23 }
24 }
@@ -0,0 +1,17
1 import { argumentNotEmptyString, get } from "../safe";
2
3 export abstract class ModuleResolverBase {
4
5
6 async resolve(typeName: string) {
7 argumentNotEmptyString(typeName, "typeName");
8 let [moduleName, localName] = typeName.split("#", 2);
9
10 let moduleObject = await this.loadModule(moduleName);
11 return localName ? get(localName, moduleObject) : moduleObject;
12 }
13
14 abstract loadModule(moduleName: string): PromiseLike<Object>
15
16 abstract createResolver(moduleName: string): PromiseLike<ModuleResolverBase>
17 } No newline at end of file
@@ -0,0 +1,75
1 import { ModuleResolverBase } from "./ModuleResolverBase";
2 import { Uuid } from "../Uuid";
3 import { argumentNotEmptyString } from "../safe";
4 import { TraceSource } from "../log/TraceSource";
5
6 declare function require(modules: string[], cb?: (...args: any[]) => any): void;
7
8 declare function define(name: string, modules: string[], cb?: (...args: any[]) => any): void;
9
10 class RequireJsResolverParams {
11 contextRequire: (modules: string[], cb?: (...args: any[]) => any) => void
12
13 base: string
14 }
15
16 TraceSource.get("RequireJsResolver");
17
18 export class RequireJsResolver extends ModuleResolverBase {
19 _contextRequire = require
20
21 _base: string
22
23 constructor(opts) {
24 super();
25
26 if (opts) {
27
28 if (opts.contextRequire)
29 this._contextRequire = opts.contextRequire;
30
31 if (opts.base) {
32 if (opts.base.indexOf("./") == 0)
33 throw new Error(`A module id should be an absolute: '${opts.base}'`);
34 this._base = opts.base;
35 }
36 }
37
38 }
39
40 async createResolver(moduleName: string): Promise<ModuleResolverBase> {
41 argumentNotEmptyString(moduleName, "moduleName");
42
43 let parts = moduleName.split("/");
44 if (parts[0] == ".") {
45 if (this._base)
46 parts[0] = this._base;
47 else
48 throw new Error(`Can't resolve a relative module '${moduleName}'`);
49 }
50
51 if(parts.length > 1)
52 parts.splice(-1,1,Uuid());
53 else
54 parts.push(Uuid());
55
56 var shim = parts.join('/');
57
58 let contextRequire = await new Promise((resolve, reject) => {
59 define(shim, ["require"], function (ctx) {
60 resolve(ctx);
61 })
62 });
63
64 return new RequireJsResolver({
65 base: parts.slice(0,-1).join('/'),
66 contextRequire: contextRequire
67 });
68 }
69
70 async loadModule(moduleName: string): Promise<Object> {
71 return new Promise<Object>((resolve) => this._contextRequire.call(null, [moduleName], resolve)
72 );
73 }
74
75 } No newline at end of file
@@ -0,0 +1,275
1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ActivationType, ServiceMap, Constructor, Factory } from "./interfaces";
3 import { Container } from "./Container";
4 import { argumentNotNull, isPrimitive, oid } from "../safe";
5
6 let cacheId = 0;
7
8 function injectMethod(target, method, context, args) {
9 var m = target[method];
10 if (!m)
11 throw new Error("Method '" + method + "' not found");
12
13 if (args instanceof Array)
14 m.apply(target, context.parse(args, "." + method));
15 else
16 m.call(target, context.parse(args, "." + method));
17 };
18
19 function makeClenupCallback(target, method) {
20 if (typeof (method) === "string") {
21 return function () {
22 target[method]();
23 };
24 } else {
25 return function () {
26 method(target);
27 };
28 }
29 };
30
31 export interface ServiceDescriptorParams<T> {
32 activation: ActivationType
33
34 owner: Container
35
36 type: Constructor<T>
37
38 factory: Factory<T>
39
40 params
41
42 inject
43
44 services: ServiceMap
45
46 cleanup: (instance: T) => void
47 }
48
49 export class ServiceDescriptor<T> implements Descriptor {
50 _instance: T = null
51
52 _hasInstance = false
53
54 _activationType = ActivationType.CALL
55
56 _services: ServiceMap
57
58 _type: Constructor<T> = null
59
60 _factory: Factory<T> = null
61
62 _params
63
64 _inject: Array<Object>
65
66 _cleanup: (instance: T) => void
67
68 _cacheId: any
69
70 _owner: Container
71
72 constructor(opts: ServiceDescriptorParams<T>) {
73 argumentNotNull(opts, "opts");
74 argumentNotNull(opts.owner, "owner");
75
76 this._owner = opts.owner;
77
78 if (!(opts.type || opts.factory))
79 throw new Error(
80 "Either a type or a factory must be specified");
81
82 if (opts.activation)
83 this._activationType = opts.activation;
84
85 if (opts.type)
86 this._type = opts.type;
87
88 if (opts.params)
89 this._params = opts.params;
90
91 if (opts.inject)
92 this._inject = opts.inject instanceof Array ? opts.inject : [opts.inject];
93
94 if (opts.services)
95 this._services = opts.services;
96
97 if (opts.factory)
98 this._factory = opts.factory;
99
100 if (opts.cleanup) {
101 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
102 throw new Error(
103 "The cleanup parameter must be either a function or a function name");
104
105 this._cleanup = opts.cleanup;
106 }
107
108 if (this._activationType == ActivationType.SINGLETON) {
109 let tof = this._type || this._factory;
110
111 // create the persistent cache identifier for the type
112 if (isPrimitive(tof))
113 this._cacheId = tof;
114 else
115 this._cacheId = oid(tof);
116 } else {
117 this._cacheId = ++cacheId;
118 }
119 }
120
121 activate(context: ActivationContext, name: string) {
122 // if we have a local service records, register them first
123 let instance;
124
125 switch (this._activationType) {
126 case ActivationType.SINGLETON: // SINGLETON
127 // if the value is cached return it
128 if (this._hasInstance)
129 return this._instance;
130
131 // singletons are bound to the root container
132 let container = context.container.getRootContainer();
133
134 if (container.has(this._cacheId)) {
135 instance = container.get(this._cacheId);
136 } else {
137 instance = this._create(context, name);
138 container.store(this._cacheId, instance);
139 if (this._cleanup)
140 container.onDispose(
141 makeClenupCallback(instance, this._cleanup));
142 }
143
144 this._hasInstance = true;
145 return (this._instance = instance);
146
147 case ActivationType.CONTAINER: // CONTAINER
148 //return a cached value
149 if (this._hasInstance)
150 return this._instance;
151
152 // create an instance
153 instance = this._create(context, name);
154
155 // the instance is bound to the container
156 if (this._cleanup)
157 this._owner.onDispose(
158 makeClenupCallback(instance, this._cleanup));
159
160 // cache and return the instance
161 this._hasInstance = true;
162 return (this._instance = instance);
163 case ActivationType.CONTEXT: // CONTEXT
164 //return a cached value if one exists
165 if (context.has(this._cacheId))
166 return context.get(this._cacheId);
167 // context context activated instances are controlled by callers
168 return context.store(this._cacheId, this._create(
169 context,
170 name));
171 case ActivationType.CALL: // CALL
172 // per-call created instances are controlled by callers
173 return this._create(context, name);
174 case ActivationType.HIERARCHY: // HIERARCHY
175 // hierarchy activated instances are behave much like container activated
176 // except they are created and bound to the child container
177
178 // return a cached value
179 if (context.container.has(this._cacheId))
180 return context.container.get(this._cacheId);
181
182 instance = this._create(context, name);
183
184 if (this._cleanup)
185 context.container.onDispose(makeClenupCallback(
186 instance,
187 this._cleanup));
188
189 return context.container.store(this._cacheId, instance);
190 default:
191 throw "Invalid activation type: " + this._activationType;
192 }
193 }
194
195 isInstanceCreated() {
196 return this._hasInstance;
197 }
198
199 getInstance() {
200 return this._instance;
201 }
202
203 _create(context, name) {
204 context.enter(name, this, Boolean(this._services));
205
206 if (this._activationType != ActivationType.CALL &&
207 context.visit(this._cacheId) > 0)
208 throw new Error("Recursion detected");
209
210 if (this._services) {
211 for (var p in this._services)
212 context.register(p, this._services[p]);
213 }
214
215 var instance;
216
217 if (!this._factory) {
218 var ctor = this._type;
219
220 if (this._params === undefined) {
221 this._factory = function () {
222 return new ctor();
223 };
224 } else if (this._params instanceof Array) {
225 this._factory = function () {
226 var inst = Object.create(ctor.prototype);
227 var ret = ctor.apply(inst, arguments);
228 return typeof (ret) === "object" ? ret : inst;
229 };
230 } else {
231 this._factory = function (param) {
232 return new ctor(param);
233 };
234 }
235 }
236
237 if (this._params === undefined) {
238 instance = this._factory();
239 } else if (this._params instanceof Array) {
240 instance = this._factory.apply(this, context.parse(
241 this._params,
242 ".params"));
243 } else {
244 instance = this._factory(context.parse(
245 this._params,
246 ".params"));
247 }
248
249 if (this._inject) {
250 this._inject.forEach(function (spec) {
251 for (var m in spec)
252 injectMethod(instance, m, context, spec[m]);
253 });
254 }
255
256 context.leave();
257
258 return instance;
259 }
260
261 // @constructor {singleton} foo/bar/Baz
262 // @factory {singleton}
263 toString() {
264 var parts = [];
265
266 parts.push(this._type ? "@constructor" : "@factory");
267
268 parts.push(ActivationType[this._activationType]);
269
270 if (typeof (this._type) === "string")
271 parts.push(this._type);
272
273 return parts.join(" ");
274 }
275 } No newline at end of file
@@ -0,0 +1,23
1 import { Descriptor } from "./interfaces";
2 import { ActivationContext } from "./ActivationContext";
3
4 export class ValueDescriptor<T> implements Descriptor {
5 _value: T
6
7 constructor(value: T) {
8 this._value = value;
9 }
10
11 activate(context: ActivationContext, name: string) {
12 context.enter(name);
13 let v = this._value;
14 context.leave();
15 return v;
16 }
17 isInstanceCreated(): boolean {
18 return true;
19 }
20 getInstance(): T {
21 return this._value;
22 }
23 } No newline at end of file
@@ -0,0 +1,28
1 import { isNull } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
3
4 export interface Descriptor {
5 activate(context: ActivationContext, name?: string);
6 }
7
8 export type Constructor<T = {}> = new (...args: any[]) => T;
9
10
11 export type Factory<T = {}> = (...args: any[]) => T;
12
13 export function isDescriptor(instance): instance is Descriptor {
14 return (!isNull(instance)) &&
15 ('activate' in instance);
16 }
17
18 export interface ServiceMap {
19 [s: string] : Descriptor
20 }
21
22 export enum ActivationType {
23 SINGLETON,
24 CONTAINER,
25 HIERARCHY,
26 CONTEXT,
27 CALL
28 } No newline at end of file
@@ -0,0 +1,40
1 {
2 "extends": "tslint:recommended",
3 "rules": {
4 "align": [
5 true,
6 "parameters",
7 "statements"
8 ],
9 "interface-name": [false],
10 "max-line-length": [ true, 185 ],
11 "member-access": false,
12 "member-ordering": [
13 false,
14 "variables-before-functions"
15 ],
16 "no-bitwise": false,
17 "no-empty": false,
18 "no-namespace": false,
19 "no-string-literal": false,
20 "ordered-imports": false,
21 "one-line": [
22 true,
23 "check-open-brace",
24 "check-catch",
25 "check-whitespace"
26 ],
27 "object-literal-sort-keys": false,
28 "trailing-comma": [
29 true,
30 {
31 "singleline": "never",
32 "multiline": "never"
33 }
34 ],
35 "variable-name": false,
36 "curly": false,
37 "array-type": false,
38 "arrow-parens": [true, "ban-single-arg-parens"]
39 }
40 } No newline at end of file
@@ -1,94 +1,94
1 1 if (release != 'rtm') {
2 2 version += "-$release"
3 3 }
4 4
5 5 println "version: $version"
6 6
7 7 def distDir = "$buildDir/dist"
8 8 def testDir = "$buildDir/test"
9 9
10 10 task clean {
11 11 doLast {
12 12 delete buildDir
13 13 delete 'node_modules/@implab'
14 14 }
15 15 }
16 16
17 17 task cleanNpm {
18 18 doLast {
19 19 delete 'node_modules'
20 20 }
21 21 }
22 22
23 23 task _npmInstall() {
24 24 inputs.file("package.json")
25 25 outputs.dir("node_modules")
26 26 doLast {
27 27 exec {
28 28 commandLine 'npm', 'install'
29 29 }
30 30 }
31 31 }
32 32
33 33 task _legacyJs(type:Copy) {
34 34 from 'src/js/'
35 35 into distDir
36 36 }
37 37
38 38 task _buildTs(dependsOn: _npmInstall, type:Exec) {
39 39 inputs.dir('src/ts')
40 40 inputs.file('tsc.json')
41 41 outputs.dir(distDir)
42 42
43 43 commandLine 'node_modules/.bin/tsc', '-p', 'tsconfig.json'
44 44 }
45 45
46 46 task _packageMeta(type: Copy) {
47 47 inputs.property("version", version)
48 48 from('.') {
49 49 include 'package.json', '.npmignore', 'readme.md', 'license', 'history.md'
50 50 }
51 51 into distDir
52 52 doLast {
53 53 exec {
54 54 workingDir distDir
55 55 commandLine 'npm', 'version', version
56 56 }
57 57 }
58 58 }
59 59
60 task build(dependsOn: [_npmInstall, _buildTs, _legacyJs, _packageMeta]) {
60 task build(dependsOn: [_legacyJs, _npmInstall, _buildTs, _packageMeta]) {
61 61
62 62 }
63 63
64 64 task _localInstall(dependsOn: build, type: Exec) {
65 65 inputs.file("$distDir/package.json")
66 66 outputs.upToDateWhen {
67 67 new File("$projectDir/node_modules/@implab/core").exists()
68 68 }
69 69
70 70 commandLine 'npm', 'install', '--no-save', '--force', distDir
71 71 }
72 72
73 73 task copyJsTests(type: Copy) {
74 74 from 'test/js'
75 75 into testDir
76 76 }
77 77
78 78 task buildTests(dependsOn: _localInstall, type: Exec) {
79 79 inputs.dir('test/ts')
80 80 inputs.file('tsc.test.json')
81 81 outputs.dir(testDir)
82 82
83 83 commandLine 'node_modules/.bin/tsc', '-p', 'tsconfig.test.json'
84 84 }
85 85
86 86 task test(dependsOn: [copyJsTests, buildTests], type: Exec) {
87 87 commandLine 'node', 'run-amd-tests.js'
88 88 }
89 89
90 90 task pack(dependsOn: build, type: Exec) {
91 91 workingDir = distDir
92 92
93 93 commandLine 'npm', 'pack'
94 94 } No newline at end of file
@@ -1,270 +1,270
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 11 let _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 22 let _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 29 let _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 44 let _rnds = new Array(16);
45 45 _rng = function () {
46 46 for (var 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 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 66 if ('function' === typeof require) {
67 67 try {
68 68 let _rb = require('crypto').randomBytes;
69 69 _rng = _rb && function () {
70 70 return _rb(16);
71 71 };
72 72 _rng();
73 73 } catch (e) { /**/ }
74 74 }
75 75 }
76 76
77 77 if (_window) {
78 78 setupBrowser();
79 79 } else {
80 80 setupNode();
81 81 }
82 82
83 83 // Buffer class to use
84 84 let BufferClass = ('function' === typeof Buffer) ? Buffer : Array;
85 85
86 86 // Maps for number <-> hex string conversion
87 87 let _byteToHex = [];
88 88 let _hexToByte = {};
89 89 for (let i = 0; i < 256; i++) {
90 90 _byteToHex[i] = (i + 0x100).toString(16).substr(1);
91 91 _hexToByte[_byteToHex[i]] = i;
92 92 }
93 93
94 94 // **`parse()` - Parse a UUID into it's component bytes**
95 95 function _parse(s, buf?, offset?): Array<string> {
96 96 let i = (buf && offset) || 0, ii = 0;
97 97
98 98 buf = buf || [];
99 99 s.toLowerCase().replace(/[0-9a-f]{2}/g, function (oct) {
100 100 if (ii < 16) { // Don't overflow!
101 101 buf[i + ii++] = _hexToByte[oct];
102 102 }
103 103 });
104 104
105 105 // Zero out remaining bytes if string was short
106 106 while (ii < 16) {
107 107 buf[i + ii++] = 0;
108 108 }
109 109
110 110 return buf;
111 111 }
112 112
113 113 // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
114 114 function _unparse(buf, offset?): string {
115 115 let i = offset || 0, bth = _byteToHex;
116 116 return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] +
117 117 bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' +
118 118 bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] +
119 119 bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] +
120 120 bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]];
121 121 }
122 122
123 123 // **`v1()` - Generate time-based UUID**
124 124 //
125 125 // Inspired by https://github.com/LiosK/UUID.js
126 126 // and http://docs.python.org/library/uuid.html
127 127
128 128 // random #'s we need to init node and clockseq
129 129 let _seedBytes = _rng();
130 130
131 131 // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit =
132 132 // 1)
133 133 let _nodeId = [
134 134 _seedBytes[0] | 0x01,
135 135 _seedBytes[1],
136 136 _seedBytes[2],
137 137 _seedBytes[3],
138 138 _seedBytes[4],
139 139 _seedBytes[5]
140 140 ];
141 141
142 142 // Per 4.2.2, randomize (14 bit) clockseq
143 143 let _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
144 144
145 145 // Previous uuid creation time
146 146 let _lastMSecs = 0, _lastNSecs = 0;
147 147
148 148 // See https://github.com/broofa/node-uuid for API details
149 149 function _v1(options?, buf?, offset?): string {
150 150 let i = buf && offset || 0;
151 151 let b = buf || [];
152 152
153 153 options = options || {};
154 154
155 155 let clockseq = (options.clockseq != null) ? options.clockseq : _clockseq;
156 156
157 157 // UUID timestamps are 100 nano-second units since the Gregorian
158 158 // epoch,
159 159 // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
160 160 // time is handled internally as 'msecs' (integer milliseconds) and
161 161 // 'nsecs'
162 162 // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01
163 163 // 00:00.
164 164 let msecs = (options.msecs != null) ? options.msecs : new Date()
165 165 .getTime();
166 166
167 167 // Per 4.2.1.2, use count of uuid's generated during the current
168 168 // clock
169 169 // cycle to simulate higher resolution clock
170 170 let nsecs = (options.nsecs != null) ? options.nsecs : _lastNSecs + 1;
171 171
172 172 // Time since last uuid creation (in msecs)
173 173 let dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000;
174 174
175 175 // Per 4.2.1.2, Bump clockseq on clock regression
176 176 if (dt < 0 && options.clockseq == null) {
177 177 clockseq = clockseq + 1 & 0x3fff;
178 178 }
179 179
180 180 // Reset nsecs if clock regresses (new clockseq) or we've moved onto
181 181 // a new
182 182 // time interval
183 183 if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {
184 184 nsecs = 0;
185 185 }
186 186
187 187 // Per 4.2.1.2 Throw error if too many uuids are requested
188 188 if (nsecs >= 10000) {
189 189 throw new Error(
190 190 'uuid.v1(): Can\'t create more than 10M uuids/sec');
191 191 }
192 192
193 193 _lastMSecs = msecs;
194 194 _lastNSecs = nsecs;
195 195 _clockseq = clockseq;
196 196
197 197 // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
198 198 msecs += 12219292800000;
199 199
200 200 // `time_low`
201 201 let tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
202 202 b[i++] = tl >>> 24 & 0xff;
203 203 b[i++] = tl >>> 16 & 0xff;
204 204 b[i++] = tl >>> 8 & 0xff;
205 205 b[i++] = tl & 0xff;
206 206
207 207 // `time_mid`
208 208 let tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
209 209 b[i++] = tmh >>> 8 & 0xff;
210 210 b[i++] = tmh & 0xff;
211 211
212 212 // `time_high_and_version`
213 213 b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
214 214 b[i++] = tmh >>> 16 & 0xff;
215 215
216 216 // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
217 217 b[i++] = clockseq >>> 8 | 0x80;
218 218
219 219 // `clock_seq_low`
220 220 b[i++] = clockseq & 0xff;
221 221
222 222 // `node`
223 223 let node = options.node || _nodeId;
224 224 for (let n = 0; n < 6; n++) {
225 225 b[i + n] = node[n];
226 226 }
227 227
228 228 return buf ? buf : _unparse(b);
229 229 }
230 230
231 231 // **`v4()` - Generate random UUID**
232 232
233 233 // See https://github.com/broofa/node-uuid for API details
234 234 function _v4(options?, buf?, offset?): string {
235 235 // Deprecated - 'format' argument, as supported in v1.2
236 236 let i = buf && offset || 0;
237 237
238 238 if (typeof (options) === 'string') {
239 239 buf = (options === 'binary') ? new BufferClass(16) : null;
240 240 options = null;
241 241 }
242 242 options = options || {};
243 243
244 244 let rnds = options.random || (options.rng || _rng)();
245 245
246 246 // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
247 247 rnds[6] = (rnds[6] & 0x0f) | 0x40;
248 248 rnds[8] = (rnds[8] & 0x3f) | 0x80;
249 249
250 250 // Copy bytes to buffer, if provided
251 251 if (buf) {
252 252 for (let ii = 0; ii < 16; ii++) {
253 253 buf[i + ii] = rnds[ii];
254 254 }
255 255 }
256 256
257 257 return buf || _unparse(rnds);
258 258 }
259 259
260 260 export function Uuid() {
261
261 return _v4();
262 262 }
263 263
264 264 export namespace Uuid {
265 265 export const v4 = _v4;
266 266 export const v1 = _v1;
267 267 export const empty = "00000000-0000-0000-0000-000000000000";
268 268 export const parse = _parse;
269 269 export const unparse = _unparse;
270 270 } No newline at end of file
@@ -1,126 +0,0
1 import { TraceSource } from "./log/TraceSource";
2 import { argumentNotNull, argumentNotEmptyString, isPrimitive, each, isNull } from "./safe";
3 import { Uuid } from './Uuid';
4 import {ActivationContext} from "./di/ActivationContext";
5
6 let trace = TraceSource.get("di");
7
8 export interface Descriptor {
9 activate(context: ActivationContext, name: string): any
10 }
11
12 export function isDescriptor(value: any): value is Descriptor {
13 return ("activate" in value);
14 }
15
16 export interface ServiceMap {
17 [s: string] : Descriptor
18 }
19
20 export class ActivationContextInfo {
21 name: string
22
23 service: string
24
25 scope: ServiceMap
26 }
27
28 export class ActivationError {
29 activationStack: ActivationContextInfo[]
30
31 service: string
32
33 innerException: any
34
35 message: string
36
37 constructor(service: string, activationStack: ActivationContextInfo[], innerException) {
38 this.message = "Failed to activate the service";
39 this.activationStack = activationStack;
40 this.service = service;
41 this.innerException = innerException;
42 }
43
44 toString() {
45 var parts = [this.message];
46 if (this.service)
47 parts.push("when activating: " + this.service.toString());
48
49 if (this.innerException)
50 parts.push("caused by: " + this.innerException.toString());
51
52 if (this.activationStack) {
53 parts.push("at");
54 this.activationStack.forEach(function (x) {
55 parts.push(" " + x.name + " " +
56 (x.service ? x.service.toString() : ""));
57 });
58 }
59
60 return parts.join("\n");
61 }
62 }
63
64
65 export enum ActivationType {
66 SINGLETON,
67 CONTAINER,
68 HIERARCHY,
69 CONTEXT,
70 CALL
71 }
72
73
74
75 interface ServiceDescriptorParams {
76
77 }
78
79 class ServiceDescriptor extends Descriptor {
80 constructor(opts: ServiceDescriptorParams) {
81 super();
82 }
83
84 activate(context: ActivationContext, name: string) {
85 throw new Error("Method not implemented.");
86 }
87 isInstanceCreated(): boolean {
88 throw new Error("Method not implemented.");
89 }
90 getInstance() {
91 throw new Error("Method not implemented.");
92 }
93 }
94
95
96
97 class AggregateDescriptor<T> extends Descriptor {
98 constructor(value: T) {
99 super();
100 }
101
102 activate(context: ActivationContext, name: string) {
103 throw new Error("Method not implemented.");
104 }
105 isInstanceCreated(): boolean {
106 throw new Error("Method not implemented.");
107 }
108 getInstance(): T {
109 throw new Error("Method not implemented.");
110 }
111 }
112
113 class ValueDescriptor<T> implements Descriptor {
114 activate(context: ActivationContext, name: string) {
115 throw new Error("Method not implemented.");
116 }
117 isInstanceCreated(): boolean {
118 throw new Error("Method not implemented.");
119 }
120 getInstance(): T {
121 throw new Error("Method not implemented.");
122 }
123 constructor(value: T) {
124
125 }
126 } No newline at end of file
@@ -1,132 +1,140
1 1 import { TraceSource } from "../log/TraceSource";
2 2 import { argumentNotNull, argumentNotEmptyString, isPrimitive, each, isNull } from "../safe";
3 import { Uuid } from '../Uuid';
4 import { Container, ActivationContextInfo, ServiceMap, Descriptor, isDescriptor } from "../di";
3 import { Descriptor, ServiceMap, isDescriptor } from "./interfaces";
4 import { Container } from "./Container";
5 5
6 6 let trace = TraceSource.get("di");
7 7
8 export class ActivationContextInfo {
9 name: string
10
11 service: string
12
13 scope: ServiceMap
14 }
15
8 16
9 17 export class ActivationContext {
10 18 _cache: object
11 19
12 20 _services: ServiceMap
13 21
14 22 _stack: ActivationContextInfo[]
15 23
16 24 _visited: any
17 25
18 26 container: any
19 27
20 28
21 29 constructor(container: Container, services: ServiceMap, cache?: object, visited?) {
22 30 argumentNotNull(container, "container");
23 31 argumentNotNull(services, "services");
24 32
25 33 this._visited = visited || {};
26 34 this._stack = [];
27 35 this._cache = cache || {};
28 36 this._services = services;
29 37 this.container = container;
30 38 }
31 39
32 40 getService(name, def?): any {
33 41 let d = this._services[name];
34 42
35 43 if (!d)
36 44 if (arguments.length > 1)
37 45 return def;
38 46 else
39 47 throw new Error("Service '" + name + "' not found");
40 48
41 49 return d.activate(this, name);
42 50 }
43 51
44 52 /**
45 53 * registers services local to the the activation context
46 54 *
47 55 * @name{string} the name of the service
48 56 * @service{string} the service descriptor to register
49 57 */
50 58 register(name: string, service: Descriptor) {
51 59 argumentNotEmptyString(name, "name");
52 60
53 61 this._services[name] = service;
54 62 }
55 63
56 64 clone() {
57 65 return new ActivationContext(
58 66 this.container,
59 67 Object.create(this._services),
60 68 this._cache,
61 69 this._visited
62 70 );
63 71
64 72 }
65 73
66 74 has(id) {
67 75 return id in this._cache;
68 76 }
69 77
70 78 get(id) {
71 79 return this._cache[id];
72 80 }
73 81
74 82 store(id, value) {
75 83 return (this._cache[id] = value);
76 84 }
77 85
78 86 parse(data: any, name) {
79 87 var me = this;
80 88 if (isPrimitive(data))
81 89 return data;
82 90
83 91 if (isDescriptor(data)) {
84 92 return data.activate(this, name);
85 93 } else if (data instanceof Array) {
86 94 me.enter(name);
87 95 var v = data.map(function (x, i) {
88 96 return me.parse(x, "." + i);
89 97 });
90 98 me.leave();
91 99 return v;
92 100 } else {
93 101 me.enter(name);
94 102 var result = {};
95 103 for (var p in data)
96 104 result[p] = me.parse(data[p], "." + p);
97 105 me.leave();
98 106 return result;
99 107 }
100 108 }
101 109
102 110 visit(id) {
103 111 var count = this._visited[id] || 0;
104 112 this._visited[id] = count + 1;
105 113 return count;
106 114 }
107 115
108 116 getStack() {
109 117 return this._stack.slice().reverse();
110 118 }
111 119
112 120 enter(name, d?, localize?) {
113 121 if (trace.isLogEnabled())
114 122 trace.log("enter " + name + " " + (d || "") +
115 123 (localize ? " localize" : ""));
116 124 this._stack.push({
117 125 name: name,
118 126 service: d,
119 127 scope: this._services
120 128 });
121 129 if (localize)
122 130 this._services = Object.create(this._services);
123 131 }
124 132
125 133 leave() {
126 134 var ctx = this._stack.pop();
127 135 this._services = ctx.scope;
128 136
129 137 if (trace.isLogEnabled())
130 138 trace.log("leave " + ctx.name + " " + (ctx.service || ""));
131 139 }
132 140 } No newline at end of file
@@ -1,282 +1,292
1 declare function require(modules: string[], cb?: (...args: any[]) => any) : void;
2
3 declare function define(name:string, modules: string[], cb?: (...args: any[]) => any) : void;
4
1 5 import { Uuid } from "../Uuid";
2 6 import { ActivationContext } from "./ActivationContext";
3 import { ActivationError } from "../di";
7 import { ValueDescriptor } from "./ValueDescriptor";
8 import { ActivationError } from "./ActivationError";
9 import { isDescriptor, ActivationType } from "./interfaces";
10 import { AggregateDescriptor } from "./AggregateDescriptor";
11 import { isPrimitive } from "../safe";
12 import { ReferenceDescriptor } from "./ReferenceDescriptor";
13 import { ServiceDescriptor } from "./ServiceDescriptor";
4 14
5 15
6 16 export class Container {
7 17 _services
8 18
9 19 _cache
10 20
11 21 _cleanup: any[]
12 22
13 23 _root: Container
14 24
15 25 _parent: Container
16 26
17 27 constructor(parent?: Container) {
18 28 this._parent = parent;
19 29 this._services = parent ? Object.create(parent._services) : {};
20 30 this._cache = {};
21 31 this._cleanup = [];
22 32 this._root = parent ? parent.getRootContainer() : this;
23 33 this._services.container = new ValueDescriptor(this);
24 34 }
25 35
26 36 getRootContainer() {
27 37 return this._root;
28 38 }
29 39
30 40 getParent() {
31 41 return this._parent;
32 42 }
33 43
34 44 getService<T = any>(name: string, def?: T) {
35 45 let d = this._services[name];
36 46 if (!d)
37 47 if (arguments.length > 1)
38 48 return def;
39 49 else
40 50 throw new Error("Service '" + name + "' isn't found");
41 51
42 52 if (d.isInstanceCreated())
43 53 return d.getInstance();
44 54
45 55 var context = new ActivationContext(this, this._services);
46 56
47 57 try {
48 58 return d.activate(context, name);
49 59 } catch (error) {
50 60 throw new ActivationError(name, context.getStack(), error);
51 61 }
52 62 }
53 63
54 64 register(nameOrCollection, service?) {
55 65 if (arguments.length == 1) {
56 66 var data = nameOrCollection;
57 67 for (let name in data)
58 68 this.register(name, data[name]);
59 69 } else {
60 if (!(service instanceof Descriptor))
70 if (!(isDescriptor(service)))
61 71 service = new ValueDescriptor(service);
62 72 this._services[nameOrCollection] = service;
63 73 }
64 74 return this;
65 75 }
66 76
67 77 onDispose(callback) {
68 78 if (!(callback instanceof Function))
69 79 throw new Error("The callback must be a function");
70 80 this._cleanup.push(callback);
71 81 }
72 82
73 83 dispose() {
74 84 if (this._cleanup) {
75 85 for (var i = 0; i < this._cleanup.length; i++)
76 86 this._cleanup[i].call(null);
77 87 this._cleanup = null;
78 88 }
79 89 }
80 90
81 91 /**
82 92 * @param{String|Object} config
83 93 * The configuration of the contaier. Can be either a string or an object,
84 94 * if the configuration is an object it's treated as a collection of
85 95 * services which will be registed in the contaier.
86 96 *
87 97 * @param{Function} opts.contextRequire
88 98 * The function which will be used to load a configuration or types for services.
89 99 *
90 100 */
91 101 async configure(config, opts) {
92 102 var me = this,
93 103 contextRequire = (opts && opts.contextRequire);
94 104
95 105 if (typeof (config) === "string") {
96 106 let args;
97 107 if (!contextRequire) {
98 108 var shim = [config, Uuid()].join(config.indexOf("/") != -1 ? "-" : "/");
99 109 args = await new Promise((resolve, reject) => {
100 110 define(shim, ["require", config], function (ctx, data) {
101 111 resolve([data, {
102 112 contextRequire: ctx
103 113 }]);
104 114 })
105 115 });
106 116 require([shim]);
107 117 } else {
108 118 // TODO how to get correct contextRequire for the relative config module?
109 119 args = await new Promise((resolve, reject) => {
110 120 contextRequire([config], function (data) {
111 121 resolve([data, {
112 122 contextRequire: contextRequire
113 123 }]);
114 124 });
115 125 });
116 126 }
117 127
118 128 return me._configure.apply(me, args);
119 129 } else {
120 130 return me._configure(config, opts);
121 131 }
122 132 }
123 133
124 134 createChildContainer() {
125 135 return new Container(this);
126 136 }
127 137
128 138 has(id) {
129 139 return id in this._cache;
130 140 }
131 141
132 142 get(id) {
133 143 return this._cache[id];
134 144 }
135 145
136 146 store(id, value) {
137 147 return (this._cache[id] = value);
138 148 }
139 149
140 150 async _configure(data, opts) {
141 151 var typemap = {},
142 152 me = this,
143 153 p,
144 154 contextRequire = (opts && opts.contextRequire) || require;
145 155
146 156 var services = {};
147 157
148 158 for (p in data) {
149 159 var service = me._parse(data[p], typemap);
150 if (!(service instanceof Descriptor))
160 if (!(isDescriptor(service)))
151 161 service = new AggregateDescriptor(service);
152 162 services[p] = service;
153 163 }
154 164
155 165 me.register(services);
156 166
157 167 var names = [];
158 168
159 169 for (p in typemap)
160 170 names.push(p);
161 171
162 172 return new Promise((resolve, reject) => {
163 173 if (names.length) {
164 174 contextRequire(names, function () {
165 175 for (var i = 0; i < names.length; i++)
166 176 typemap[names[i]] = arguments[i];
167 177 resolve(me);
168 178 });
169 179 } else {
170 180 resolve(me);
171 181 }
172 182 });
173 183
174 184 }
175 185
176 186 _parse(data, typemap) {
177 if (isPrimitive(data) || data instanceof Descriptor)
187 if (isPrimitive(data) || isDescriptor(data))
178 188 return data;
179 189 if (data.$dependency) {
180 190 return new ReferenceDescriptor(
181 191 data.$dependency,
182 192 data.lazy,
183 193 data.optional,
184 194 data["default"],
185 195 data.services && this._parseObject(data.services, typemap));
186 196 } else if (data.$value) {
187 197 return !data.parse ?
188 198 new ValueDescriptor(data.$value) :
189 199 new AggregateDescriptor(this._parse(data.$value, typemap));
190 200 } else if (data.$type || data.$factory) {
191 201 return this._parseService(data, typemap);
192 202 } else if (data instanceof Array) {
193 203 return this._parseArray(data, typemap);
194 204 }
195 205
196 206 return this._parseObject(data, typemap);
197 207 }
198 208
199 209 _parseService(data, typemap) {
200 210 var me = this,
201 211 opts: any = {
202 212 owner: this
203 213 };
204 214 if (data.$type) {
205 215
206 216 opts.type = data.$type;
207 217
208 218 if (typeof (data.$type) === "string") {
209 219 typemap[data.$type] = null;
210 220 opts.typeMap = typemap;
211 221 }
212 222 }
213 223
214 224 if (data.$factory)
215 225 opts.factory = data.$factory;
216 226
217 227 if (data.services)
218 228 opts.services = me._parseObject(data.services, typemap);
219 229 if (data.inject)
220 230 opts.inject = data.inject instanceof Array ? data.inject.map(function (x) {
221 231 return me._parseObject(x, typemap);
222 232 }) : me._parseObject(data.inject, typemap);
223 233 if (data.params)
224 234 opts.params = me._parse(data.params, typemap);
225 235
226 236 if (data.activation) {
227 237 if (typeof (data.activation) === "string") {
228 238 switch (data.activation.toLowerCase()) {
229 239 case "singleton":
230 240 opts.activation = ActivationType.SINGLETON;
231 241 break;
232 242 case "container":
233 243 opts.activation = ActivationType.CONTAINER;
234 244 break;
235 245 case "hierarchy":
236 246 opts.activation = ActivationType.HIERARCHY;
237 247 break;
238 248 case "context":
239 249 opts.activation = ActivationType.CONTEXT;
240 250 break;
241 251 case "call":
242 252 opts.activation = ActivationType.CALL;
243 253 break;
244 254 default:
245 255 throw new Error("Unknown activation type: " +
246 256 data.activation);
247 257 }
248 258 } else {
249 259 opts.activation = Number(data.activation);
250 260 }
251 261 }
252 262
253 263 if (data.cleanup)
254 264 opts.cleanup = data.cleanup;
255 265
256 266 return new ServiceDescriptor(opts);
257 267 }
258 268
259 269 _parseObject(data, typemap) {
260 270 if (data.constructor &&
261 271 data.constructor.prototype !== Object.prototype)
262 272 return new ValueDescriptor(data);
263 273
264 274 var o = {};
265 275
266 276 for (var p in data)
267 277 o[p] = this._parse(data[p], typemap);
268 278
269 279 return o;
270 280 }
271 281
272 282 _parseArray(data, typemap) {
273 283 if (data.constructor &&
274 284 data.constructor.prototype !== Array.prototype)
275 285 return new ValueDescriptor(data);
276 286
277 287 var me = this;
278 288 return data.map(function (x) {
279 289 return me._parse(x, typemap);
280 290 });
281 291 }
282 292 } No newline at end of file
@@ -1,98 +1,100
1 1 import { isNull, argumentNotEmptyString, each } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
3 import { ServiceMap, Descriptor } from "./interfaces";
4 import { ActivationError } from "./ActivationError";
2 5
3 class ReferenceDescriptor extends Descriptor {
6 export class ReferenceDescriptor implements Descriptor {
4 7 _name: string
5 8
6 9 _lazy = false
7 10
8 11 _optional = false
9 12
10 13 _default: any
11 14
12 _services: object
15 _services: ServiceMap
13 16
14 constructor(name: string, lazy: boolean, optional: boolean, def, services: object) {
15 super();
17 constructor(name: string, lazy: boolean, optional: boolean, def, services: ServiceMap) {
16 18 argumentNotEmptyString(name, "name");
17 19 this._name = name;
18 20 this._lazy = Boolean(lazy);
19 21 this._optional = Boolean(optional);
20 22 this._default = def;
21 23 this._services = services;
22 24 }
23 25
24 26 activate(context: ActivationContext, name: string) {
25 27 var me = this;
26 28
27 29 context.enter(name, this, true);
28 30
29 31 // добавляем сервисы
30 32 if (me._services) {
31 33 for (var p in me._services) {
32 34 var sv = me._services[p];
33 context.register(p, sv instanceof Descriptor ? sv : new AggregateDescriptor(sv));
35 context.register(p, sv);
34 36 }
35 37 }
36 38
37 39 if (me._lazy) {
38 40 // сохраняем контекст активации
39 41 context = context.clone();
40 return function (cfg) {
42 return function (cfg: ServiceMap) {
41 43 // защищаем контекст на случай исключения в процессе
42 44 // активации
43 45 var ct = context.clone();
44 46 try {
45 47 if (cfg)
46 each(cfg, function (v, k) {
47 ct.register(k, v instanceof Descriptor ? v : new AggregateDescriptor(v));
48 });
48 for(let k in cfg)
49 ct.register(k, cfg[v]);
50
49 51 return me._optional ? ct.getService(me._name, me._default) : ct
50 52 .getService(me._name);
51 53 } catch (error) {
52 54 throw new ActivationError(me._name, ct.getStack(), error);
53 55 }
54 56 };
55 57 }
56 58
57 59 var v = me._optional ?
58 60 context.getService(me._name, me._default) :
59 61 context.getService(me._name);
60 62
61 63 context.leave();
62 64 return v;
63 65 }
64 66
65 67 isInstanceCreated() {
66 68 return false;
67 69 }
68 70
69 71 getInstance() {
70 72 throw new Error("The reference descriptor doesn't allowed to hold an instance");
71 73 }
72 74
73 75 toString() {
74 76 var opts = [];
75 77 if (this._optional)
76 78 opts.push("optional");
77 79 if (this._lazy)
78 80 opts.push("lazy");
79 81
80 82 var parts = [
81 83 "@ref "
82 84 ];
83 85 if (opts.length) {
84 86 parts.push("{");
85 87 parts.push(opts.join());
86 88 parts.push("} ");
87 89 }
88 90
89 91 parts.push(this._name);
90 92
91 93 if (!isNull(this._default)) {
92 94 parts.push(" = ");
93 95 parts.push(this._default);
94 96 }
95 97
96 98 return parts.join("");
97 99 }
98 100 } No newline at end of file
@@ -1,279 +1,305
1 let _nextOid = 0;
2 const _oid = Symbol("__oid");
3
4 export function oid(instance: object) {
5 if (isNull(instance))
6 return null;
7
8 if (_oid in instance)
9 return instance[_oid];
10 else
11 return (instance[_oid] = "oid_" + (++_nextOid));
12 }
13
1 14 export function argumentNotNull(arg, name) {
2 15 if (arg === null || arg === undefined)
3 16 throw new Error("The argument " + name + " can't be null or undefined");
4 17 }
5 18
6 19 export function argumentNotEmptyString(arg, name) {
7 20 if (typeof (arg) !== "string" || !arg.length)
8 21 throw new Error("The argument '" + name + "' must be a not empty string");
9 22 }
10 23
11 24 export function argumentNotEmptyArray(arg, name) {
12 25 if (!(arg instanceof Array) || !arg.length)
13 26 throw new Error("The argument '" + name + "' must be a not empty array");
14 27 }
15 28
16 29 export function argumentOfType(arg, type, name) {
17 30 if (!(arg instanceof type))
18 31 throw new Error("The argument '" + name + "' type doesn't match");
19 32 }
20 33
21 34 export function isNull(arg) {
22 35 return (arg === null || arg === undefined);
23 36 }
24 37
25 38 export function isPrimitive(arg) {
26 39 return (arg === null || arg === undefined || typeof (arg) === "string" ||
27 40 typeof (arg) === "number" || typeof (arg) === "boolean");
28 41 }
29 42
30 43 export function isInteger(arg) {
31 return parseInt(arg) == arg;
44 return parseInt(arg, 10) === arg;
32 45 }
33 46
34 47 export function isNumber(arg) {
35 return parseFloat(arg) == arg;
48 return parseFloat(arg) === arg;
36 49 }
37 50
38 51 export function isString(val) {
39 return typeof (val) == "string" || val instanceof String;
52 return typeof (val) === "string" || val instanceof String;
40 53 }
41 54
42 55 export function isNullOrEmptyString(str) {
43 56 if (str === null || str === undefined ||
44 ((typeof (str) == "string" || str instanceof String) && str.length === 0))
57 ((typeof (str) === "string" || str instanceof String) && str.length === 0))
45 58 return true;
46 59 }
47 60
48 61 export function isNotEmptyArray(arg): arg is Array<any> {
49 62 return (arg instanceof Array && arg.length > 0);
50 63 }
51 64
65 export function getGlobal() {
66 return this;
67 }
68
69 export function get(member: string, context?: object) {
70 argumentNotEmptyString(member, "member");
71 let that = context || getGlobal();
72 const parts = member.split(".");
73 for (const m of parts) {
74 if (!m)
75 continue;
76 if (isNull(that = that[m]))
77 break;
78 }
79 return that;
80 }
81
52 82 /**
53 83 * Выполняет метод для каждого элемента массива, останавливается, когда
54 84 * либо достигнут конец массива, либо функция <c>cb</c> вернула
55 85 * значение.
56 *
86 *
57 87 * @param {Array | Object} obj массив элементов для просмотра
58 88 * @param {Function} cb функция, вызываемая для каждого элемента
59 89 * @param {Object} thisArg значение, которое будет передано в качестве
60 90 * <c>this</c> в <c>cb</c>.
61 91 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
62 92 * если достигнут конец массива.
63 93 */
64 94 export function each(obj, cb, thisArg?) {
65 95 argumentNotNull(cb, "cb");
66 var i, x;
67 96 if (obj instanceof Array) {
68 for (i = 0; i < obj.length; i++) {
69 x = cb.call(thisArg, obj[i], i);
97 for (let i = 0; i < obj.length; i++) {
98 const x = cb.call(thisArg, obj[i], i);
70 99 if (x !== undefined)
71 100 return x;
72 101 }
73 102 } else {
74 var keys = Object.keys(obj);
75 for (i = 0; i < keys.length; i++) {
76 var k = keys[i];
77 x = cb.call(thisArg, obj[k], k);
103 const keys = Object.keys(obj);
104 for (const k of keys) {
105 const x = cb.call(thisArg, obj[k], k);
78 106 if (x !== undefined)
79 107 return x;
80 108 }
81 109 }
82 110 }
83 111
84 112 /** Copies property values from a source object to the destination and returns
85 113 * the destination onject.
86 *
114 *
87 115 * @param dest The destination object into which properties from the source
88 116 * object will be copied.
89 117 * @param source The source of values which will be copied to the destination
90 118 * object.
91 119 * @param template An optional parameter specifies which properties should be
92 120 * copied from the source and how to map them to the destination. If the
93 121 * template is an array it contains the list of property names to copy from the
94 122 * source to the destination. In case of object the templates contains the map
95 123 * where keys are property names in the source and the values are property
96 124 * names in the destination object. If the template isn't specified then the
97 125 * own properties of the source are entirely copied to the destination.
98 *
126 *
99 127 */
100 export function mixin<T,S>(dest: T, source: S, template?: string[] | object) : T & S {
128 export function mixin<T, S>(dest: T, source: S, template?: string[] | object): T & S {
101 129 argumentNotNull(dest, "to");
102 let _res = <T & S>dest;
130 const _res = dest as T & S;
103 131
104 132 if (template instanceof Array) {
105 for(let i = 0; i < template.length; i++) {
106 let p = template[i];
133 for (const p of template) {
107 134 if (p in source)
108 135 _res[p] = source[p];
109 136 }
110 137 } else if (template) {
111 let keys = Object.keys(source);
112 for(let i = 0; i < keys.length; i++) {
113 let p = keys[i];
138 const keys = Object.keys(source);
139 for (const p of keys) {
114 140 if (p in template)
115 141 _res[template[p]] = source[p];
116 142 }
117 143 } else {
118 let keys = Object.keys(source);
119 for(let i = 0; i < keys.length; i++) {
120 let p = keys[i];
144 const keys = Object.keys(source);
145 for (const p of keys)
121 146 _res[p] = source[p];
122 }
123 147 }
124 148
125 149 return _res;
126 150 }
127 151
128 152 /** Wraps the specified function to emulate an asynchronous execution.
129 153 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
130 154 * @param{Function|String} fn [Required] Function wich will be wrapped.
131 155 */
132 156 export function async(_fn: (...args: any[]) => any, thisArg): (...args: any[]) => PromiseLike<any> {
133 157 let fn = _fn;
134 158
135 if (arguments.length == 2 && !(fn instanceof Function))
159 if (arguments.length === 2 && !(fn instanceof Function))
136 160 fn = thisArg[fn];
137 161
138 162 if (fn == null)
139 163 throw new Error("The function must be specified");
140 164
141 165 function wrapresult(x, e?): PromiseLike<any> {
142 166 if (e) {
143 167 return {
144 then: function (cb, eb) {
168 then(cb, eb) {
145 169 try {
146 170 return eb ? wrapresult(eb(e)) : this;
147 171 } catch (e2) {
148 172 return wrapresult(null, e2);
149 173 }
150 174 }
151 175 };
152 176 } else {
153 177 if (x && x.then)
154 178 return x;
155 179 return {
156 then: function (cb) {
180 then(cb) {
157 181 try {
158 182 return cb ? wrapresult(cb(x)) : this;
159 183 } catch (e2) {
160 184 return wrapresult(e2);
161 185 }
162 186 }
163 187 };
164 188 }
165 189 }
166 190
167 return function () {
191 return (...args) => {
168 192 try {
169 return wrapresult(fn.apply(thisArg, arguments));
193 return wrapresult(fn.apply(thisArg, args));
170 194 } catch (e) {
171 195 return wrapresult(null, e);
172 196 }
173 197 };
174 198 }
175 199
176 export function delegate<T, K extends keyof T>(target: T, _method: (K | Function)) {
200 type _AnyFn = (...args) => any;
201
202 export function delegate<T, K extends keyof T>(target: T, _method: (K | _AnyFn)) {
177 203 let method;
178 204
179 205 if (!(_method instanceof Function)) {
180 206 argumentNotNull(target, "target");
181 207 method = target[_method];
182 208 if (!(method instanceof Function))
183 209 throw new Error("'method' argument must be a Function or a method name");
184 210 } else {
185 211 method = _method;
186 212 }
187 213
188 return function () {
189 return method.apply(target, arguments);
214 return (...args) => {
215 return method.apply(target, args);
190 216 };
191 217 }
192 218
193 219 /**
194 220 * Для каждого элемента массива вызывает указанную функцию и сохраняет
195 221 * возвращенное значение в массиве результатов.
196 *
222 *
197 223 * @remarks cb может выполняться асинхронно, при этом одновременно будет
198 224 * только одна операция.
199 *
225 *
200 226 * @async
201 227 */
202 228 export function pmap(items, cb) {
203 229 argumentNotNull(cb, "cb");
204 230
205 231 if (items && items.then instanceof Function)
206 232 return items.then(function (data) {
207 233 return pmap(data, cb);
208 234 });
209 235
210 236 if (isNull(items) || !items.length)
211 237 return items;
212 238
213 239 var i = 0,
214 240 result = [];
215 241
216 242 function next() {
217 243 var r, ri;
218 244
219 245 function chain(x) {
220 246 result[ri] = x;
221 247 return next();
222 248 }
223 249
224 250 while (i < items.length) {
225 251 r = cb(items[i], i);
226 252 ri = i;
227 253 i++;
228 254 if (r && r.then) {
229 255 return r.then(chain);
230 256 } else {
231 257 result[ri] = r;
232 258 }
233 259 }
234 260 return result;
235 261 }
236 262
237 263 return next();
238 264 }
239 265
240 266 /**
241 267 * Выбирает первый элемент из последовательности, или обещания, если в
242 268 * качестве параметра используется обещание, оно должно вернуть массив.
243 269 *
244 270 * @param {Function} cb обработчик результата, ему будет передан первый
245 271 * элемент последовательности в случае успеха
246 272 * @param {Function} err обработчик исключения, если массив пустой, либо
247 273 * не массив
248 274 *
249 275 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
250 276 * обещание, либо первый элемент.
251 277 * @async
252 278 */
253 279 export function first(sequence: any, cb: Function, err: Function) {
254 280 if (sequence) {
255 281 if (sequence.then instanceof Function) {
256 282 return sequence.then(function (res) {
257 283 return first(res, cb, err);
258 284 }, err);
259 285 } else if (sequence && "length" in sequence) {
260 286 if (sequence.length === 0) {
261 287 if (err)
262 288 return err(new Error("The sequence is empty"));
263 289 else
264 290 throw new Error("The sequence is empty");
265 291 }
266 292 return cb ? cb(sequence[0]) : sequence[0];
267 293 }
268 294 }
269 295
270 296 if (err)
271 297 return err(new Error("The sequence is required"));
272 298 else
273 299 throw new Error("The sequence is required");
274 300 }
275 301
276 302 export function destroy(d: any) {
277 303 if (d && 'destroy' in d)
278 304 d.destroy();
279 305 } No newline at end of file
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now