##// END OF EJS Templates
Async operation cancellation proposal...
cin -
r9:c1c00bfb5487 propose cancellat...
parent child
Show More
@@ -0,0 +1,25
1 @startuml
2
3 participant Component as a
4 participant Other as b
5
6 [-> a : activate(ct)
7 activate a
8 <-- a : promise
9 a -> a : onActivating(ct)
10 activate a
11 a -> b : doAsyncWork(ct)
12 deactivate a
13 deactivate a
14 activate b
15
16 [-> b : ct.cancel
17 b --> a : reject(Cancelled)
18 deactivate b
19 activate a
20
21 a -> a : setFailState()
22
23 [<-- a : reject(Cancelled)
24
25 @enduml No newline at end of file
@@ -0,0 +1,20
1 import { ICancellation } from "./ICancellation";
2
3 export class EmptyCancellation implements ICancellation {
4 isSupported(): boolean {
5 return false;
6 }
7 throwIfRequested(): void {
8 }
9
10 isRequested(): boolean {
11 return false;
12 }
13
14 register(_cb: () => void): void {
15
16 }
17
18 static readonly default : EmptyCancellation = new EmptyCancellation();
19
20 } No newline at end of file
@@ -0,0 +1,6
1 export interface ICancellation {
2 throwIfRequested(): void;
3 isRequested(): boolean;
4 isSupported(): boolean;
5 register(cb: () => void): void;
6 } No newline at end of file
@@ -0,0 +1,92
1 import { IActivationController } from './IActivationController';
2 import { IActivatable } from './IActivatable';
3 import { AsyncComponent } from './AsyncComponent';
4 import { ICancellation } from '../ICancellation';
5 import { EmptyCancellation } from '../EmptyCancellation';
6
7 type Constructor<T = {}> = new (...args: any[]) => T;
8
9 function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
10 return class extends Base implements IActivatable {
11 _controller: IActivationController;
12
13 _active: boolean;
14
15 isActive() {
16 return this._active;
17 }
18
19 getActivationController() {
20 return this._controller;
21 }
22
23 setActivationController(controller: IActivationController) {
24 this._controller = controller;
25 }
26
27 async onActivating(ct: ICancellation) {
28 if (this._controller)
29 await this._controller.activating(this, ct);
30 }
31
32 async onActivated(ct: ICancellation) {
33 if (this._controller)
34 await this._controller.activated(this, ct);
35 }
36
37 async activate(ct: ICancellation = EmptyCancellation.default) {
38 if (this.isActive())
39 return;
40 ct = this.startOperation(ct);
41 try {
42 await this.onActivating(ct);
43 this._active = true;
44 try {
45 await this.onActivated(ct);
46 } catch {
47 // TODO log error
48 }
49 this.completeSuccess();
50 } catch (e) {
51 this.completeFail(e);
52 }
53 return this.getCompletion();
54 }
55
56 async onDeactivating(ct: ICancellation) {
57 if (this._controller)
58 await this._controller.deactivating(this, ct);
59 }
60
61 async onDeactivated(ct: ICancellation) {
62 if (this._controller)
63 await this._controller.deactivated(this, ct);
64 }
65
66 async deactivate(ct: ICancellation = EmptyCancellation.default) {
67 if (!this.isActive())
68 return;
69 ct = this.startOperation(ct);
70 try {
71 await this.onDeactivating(ct);
72 this._active = false;
73 try {
74 await this.onDeactivated(ct);
75 } catch {
76 // TODO log error
77 }
78 this.completeSuccess();
79 } catch (e) {
80 this.completeFail(e);
81 }
82 return this.getCompletion();
83 }
84
85 }
86 }
87
88 namespace ActivatableMixin {
89
90 }
91
92 export = ActivatableMixin; No newline at end of file
@@ -0,0 +1,47
1 import { ICancellation } from "../ICancellation";
2 import { EmptyCancellation } from "../EmptyCancellation";
3
4 export class AsyncComponent {
5 _completion: Promise<void>;
6
7 _deferred: {
8 resolve(): void
9 reject(reason: any): void
10 };
11
12 getCompletion() { return this._completion };
13
14 startOperation(ct: ICancellation = EmptyCancellation.default) {
15 if (this._deferred)
16 throw new Error("The async operation is already pending");
17
18 this._completion = new Promise<void>((resolve, reject) => {
19 this._deferred = {
20 resolve: resolve,
21 reject: reject
22 }
23 });
24 return ct;
25 }
26
27 completeSuccess() {
28 this._deferred.resolve();
29 this._deferred = null;
30 }
31
32 completeFail(reason: any) {
33 this._deferred.reject(reason);
34 this._deferred = null;
35 }
36
37 async runOperation(cb: (ct: ICancellation) => Promise<void>, ct: ICancellation = EmptyCancellation.default) {
38 //safe.argumentNotNull(cb, "cb")
39 ct = this.startOperation(ct);
40 try {
41 await cb(ct);
42 this.completeSuccess();
43 } catch(e) {
44 this.completeFail(e);
45 }
46 }
47 } No newline at end of file
@@ -0,0 +1,39
1 import { IActivationController } from "./IActivationController";
2 import { ICancellation } from "../ICancellation";
3
4 /**
5 * Интерфейс поддерживающий асинхронную активацию
6 */
7 export interface IActivatable {
8 /**
9 * @returns Boolean indicates the current state
10 */
11 isActive(): boolean;
12
13 /**
14 * Starts the component activation
15 * @param ct cancellation token for this operation
16 */
17 activate(ct?: ICancellation): Promise<void>;
18
19 /**
20 * Starts the component deactivation
21 * @param ct cancellation token for this operation
22 */
23 deactivate(ct?: ICancellation): Promise<void>;
24
25 /**
26 * Sets the activation controller for this component
27 * @param controller The activation controller
28 *
29 * Activation controller checks whether this component
30 * can be activated and manages the active state of the
31 * component
32 */
33 setActivationController(controller: IActivationController);
34
35 /**
36 * Gets the current activation controller for this component
37 */
38 getActivationController(): IActivationController;
39 } No newline at end of file
@@ -0,0 +1,19
1 import { IActivatable } from './IActivatable';
2 import { ICancellation } from '../ICancellation';
3 import { EmptyCancellation } from '../EmptyCancellation';
4
5 export interface IActivationController {
6 activating(component: IActivatable, ct?: ICancellation): Promise<void>;
7
8 activated(component: IActivatable, ct?: ICancellation): Promise<void>;
9
10 deactivating(component: IActivatable, ct?: ICancellation): Promise<void>;
11
12 deactivated(component: IActivatable, ct?: ICancellation): Promise<void>;
13
14 deactivate(ct?: ICancellation): Promise<void>;
15
16 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
17
18 getActive(): IActivatable;
19 } No newline at end of file
@@ -0,0 +1,20
1 import * as TraceSource from './TraceSource'
2
3 class TraceEventArgs {
4 source : TraceSource
5
6 message : string
7
8 level : number
9
10 constructor(source: TraceSource, message: string) {
11 this.source = source;
12 this.message = message;
13 }
14 }
15
16 namespace TraceEventArgs {
17
18 }
19
20 export = TraceEventArgs No newline at end of file
@@ -0,0 +1,116
1 import * as TraceEventArgs from './TraceEventArgs'
2 import * as format from '../text/format'
3
4 interface Handler {
5 (arg: TraceEventArgs): void;
6 }
7
8 interface Destroyable {
9 destroy();
10 }
11
12 class HandlerDescriptor implements Destroyable {
13 private _target: TraceSource
14
15 readonly handler: Handler;
16
17 constructor(target: TraceSource, handler: Handler) {
18 this._target = target;
19 this.handler = handler;
20 }
21
22 destroy() {
23 this._target.remove(this);
24 }
25 }
26
27
28 class TraceSource {
29 readonly id: any
30
31 private _handlers: Array<HandlerDescriptor>
32
33 level: number
34
35 constructor(id: any) {
36 this.id = id || new Object();
37 this._handlers = new Array<HandlerDescriptor>();
38 }
39
40 on(handler: Handler): Destroyable {
41 if (!handler)
42 throw new Error("A handler must be specified");
43
44 let d = new HandlerDescriptor(this, handler)
45
46 this._handlers.push(d);
47
48 return d;
49 }
50
51 remove(cookie: any): void {
52 let i = this._handlers.indexOf(cookie);
53 if (i >= 0)
54 this._handlers.splice(i, 1);
55 }
56
57 protected emit(level: number, msg: string, ...args: any[]) {
58 if (level <= this.level) {
59 let event = new TraceEventArgs(this, format(msg, args));
60
61 this._handlers.forEach(d => {
62 try {
63 d.handler.call(null, event);
64 } catch {
65 // suppress error in log handlers
66 }
67 });
68 }
69 }
70
71 isDebugEnabled() {
72 return this.level >= TraceSource.DebugLevel;
73 }
74
75 debug(msg: string, ...args: any[]): void {
76 this.emit(TraceSource.DebugLevel, msg, args);
77 }
78
79 isLogEnabled() {
80 return this.level >= TraceSource.LogLevel;
81 }
82
83 log(msg: string, ...args: any[]): void {
84 this.emit(TraceSource.LogLevel, msg, args);
85 }
86
87 isWarnEnabled() {
88 return this.level >= TraceSource.WarnLevel;
89 }
90
91 warn(msg: string, ...args: any[]): void {
92 this.emit(TraceSource.WarnLevel, msg, args);
93 }
94
95 isErrorEnabled() {
96 return this.level >= TraceSource.ErrorLevel;
97 }
98
99 error(msg: string, ...args: any[]): void {
100 this.emit(TraceSource.ErrorLevel, msg, args);
101 }
102 }
103
104 namespace TraceSource {
105 export const DebugLevel = 400;
106
107 export const LogLevel = 300;
108
109 export const WarnLevel = 200;
110
111 export const ErrorLevel = 100;
112
113 export const SilentLevel = 0;
114 }
115
116 export = TraceSource; No newline at end of file
@@ -0,0 +1,7
1 declare function format(format: string, ...args: any[]): string;
2
3 declare namespace format {
4
5 }
6
7 export = format; No newline at end of file
@@ -0,0 +1,110
1 import * as tape from 'tape';
2 import * as ActivatableMixin from '@implab/core/components/ActivatableMixin';
3 import { AsyncComponent } from '@implab/core/components/AsyncComponent';
4 import { IActivationController } from '@implab/core/components/IActivationController';
5 import { IActivatable } from '@implab/core/components/IActivatable';
6 import { ICancellation } from '@implab/core/ICancellation';
7 import { EmptyCancellation } from '@implab/core/EmptyCancellation';
8
9 class SimpleActivatable extends ActivatableMixin(AsyncComponent) {
10
11 }
12
13 class MockActivationController implements IActivationController {
14
15 _active: IActivatable = null;
16
17
18 getActive() : IActivatable {
19 return this._active;
20 }
21
22 async deactivate() {
23 if (this._active)
24 await this._active.deactivate();
25 this._active = null;
26 }
27
28 async activate(component: IActivatable) {
29 if (!component || component.isActive())
30 return;
31 component.setActivationController(this);
32
33 await component.activate();
34 }
35
36 async activating(component: IActivatable, ct: ICancellation = EmptyCancellation.default) {
37 if (component != this._active)
38 await this.deactivate();
39 }
40
41 async activated(component: IActivatable, ct: ICancellation = EmptyCancellation.default) {
42 this._active = component;
43 }
44
45 async deactivating(component: IActivatable, ct: ICancellation = EmptyCancellation.default) {
46
47 }
48
49 async deactivated(component: IActivatable, ct: ICancellation = EmptyCancellation.default) {
50 if (this._active == component)
51 this._active = null;
52 }
53 }
54
55 tape('simple activation',async function(t){
56
57 let a = new SimpleActivatable();
58 t.false(a.isActive());
59
60 await a.activate();
61 t.true(a.isActive());
62
63 await a.deactivate();
64 t.false(a.isActive());
65
66 t.end();
67 });
68
69 tape('controller activation', async function(t) {
70
71 let a = new SimpleActivatable();
72 let c = new MockActivationController();
73
74 t.false(a.isActive(), "the component is not active by default");
75 t.assert(c.getActive() == null, "the activation controller doesn't have an active component by default");
76 t.assert(a.getActivationController() == null, "the component doesn't have an activation controller by default");
77
78 t.comment("Active the component through the controller");
79 await c.activate(a);
80 t.true(a.isActive(), "The component should successfully activate");
81 t.assert(c.getActive() == a, "The controller should point to the activated component");
82 t.assert(a.getActivationController() == c, "The component should point to the controller");
83
84 t.comment("Deactive the component throug the controller");
85 await c.deactivate();
86
87 t.false(a.isActive(), "The component should successfully deactivate");
88 t.assert(c.getActive() == null, "The controller shouldn't point to any component");
89 t.assert(a.getActivationController() == c, "The componet should point to it's controller");
90
91 t.end();
92 });
93
94 tape('handle error in onActivating', async function(t) {
95 let a = new SimpleActivatable();
96
97 a.onActivating = async function() {
98 throw "Should fail";
99 };
100
101 try {
102 await a.activate();
103 t.fail("activation should fail");
104 } catch {
105 }
106
107 t.false(a.isActive(), "the component should remain inactive");
108
109 t.end();
110 }); No newline at end of file
@@ -1,90 +1,91
1 1
2 2 println "version: $version"
3 3
4 4 def distDir = "$buildDir/dist"
5 5 def testDir = "$buildDir/test"
6 6
7 7 task clean {
8 8 doLast {
9 9 delete buildDir
10 delete 'node_modules/@implab'
10 11 }
11 12 }
12 13
13 14 task cleanNpm {
14 15 doLast {
15 16 delete 'node_modules'
16 17 }
17 18 }
18 19
19 20 task _npmInstall() {
20 21 inputs.file("package.json")
21 22 outputs.dir("node_modules")
22 23 doLast {
23 24 exec {
24 25 commandLine 'npm', 'install'
25 26 }
26 27 }
27 28 }
28 29
29 30 task _legacyJs(type:Copy) {
30 31 from 'src/js/'
31 32 into distDir
32 33 }
33 34
34 35 task _buildTs(dependsOn: _npmInstall, type:Exec) {
35 36 inputs.dir('src/ts')
36 37 inputs.file('tsc.json')
37 38 outputs.dir(distDir)
38 39
39 40 commandLine 'node_modules/.bin/tsc', '-p', 'tsc.json'
40 41 }
41 42
42 43 task _packageMeta(type: Copy) {
43 44 inputs.property("version", version)
44 45 from('.') {
45 46 include 'package.json', 'readme.md', 'license', 'history.md'
46 47 }
47 48 into distDir
48 49 doLast {
49 50 exec {
50 51 workingDir distDir
51 52 commandLine 'npm', 'version', version
52 53 }
53 54 }
54 55 }
55 56
56 57 task build(dependsOn: [_npmInstall, _buildTs, _legacyJs, _packageMeta]) {
57 58
58 59 }
59 60
60 61 task _localInstall(dependsOn: build, type: Exec) {
61 62 inputs.file("$distDir/package.json")
62 63 outputs.upToDateWhen {
63 64 new File("$projectDir/node_modules/@implab/core").exists()
64 65 }
65 66
66 67 commandLine 'npm', 'install', '--no-save', '--force', distDir
67 68 }
68 69
69 70 task copyJsTests(type: Copy) {
70 71 from 'test/js'
71 72 into testDir
72 73 }
73 74
74 75 task buildTests(dependsOn: _localInstall, type: Exec) {
75 76 inputs.dir('test/ts')
76 77 inputs.file('tsc.test.json')
77 78 outputs.dir(testDir)
78 79
79 80 commandLine 'node_modules/.bin/tsc', '-p', 'tsc.test.json'
80 81 }
81 82
82 83 task test(dependsOn: [copyJsTests, buildTests], type: Exec) {
83 84 commandLine 'node', 'run-amd-tests.js'
84 85 }
85 86
86 87 task pack(dependsOn: build, type: Exec) {
87 88 workingDir = distDir
88 89
89 90 commandLine 'npm', 'pack'
90 91 } No newline at end of file
@@ -1,299 +1,299
1 1 define([
2 2 "../declare",
3 3 "../safe",
4 4 "../Uuid",
5 5 "../Deferred",
6 6 "./ActivationContext",
7 7 "./Descriptor",
8 8 "./ValueDescriptor",
9 9 "./ReferenceDescriptor",
10 10 "./ServiceDescriptor",
11 11 "./ActivationError"
12 12 ], function (
13 13 declare,
14 14 safe,
15 15 Uuid,
16 16 Deferred,
17 17 ActivationContext,
18 18 Descriptor,
19 19 Value,
20 20 Reference,
21 21 Service,
22 22 ActivationError) {
23 23 var Container = declare(null, {
24 24 _services: null,
25 25 _cache: null,
26 26 _cleanup: null,
27 27 _root: null,
28 28 _parent: null,
29 29
30 30 constructor: function (parent) {
31 31 this._parent = parent;
32 32 this._services = parent ? Object.create(parent._services) : {};
33 33 this._cache = {};
34 34 this._cleanup = [];
35 35 this._root = parent ? parent.getRootContainer() : this;
36 36 this._services.container = new Value(this, true);
37 37 },
38 38
39 39 getRootContainer: function () {
40 40 return this._root;
41 41 },
42 42
43 43 getParent: function () {
44 44 return this._parent;
45 45 },
46 46
47 47 /**
48 48 *
49 49 */
50 50 getService: function (name, def) {
51 51 var d = this._services[name];
52 52 if (!d)
53 53 if (arguments.length > 1)
54 54 return def;
55 55 else
56 56 throw new Error("Service '" + name + "' isn't found");
57 57 if (d.isInstanceCreated())
58 58 return d.getInstance();
59 59
60 60 var context = new ActivationContext(this, this._services);
61 61
62 62 try {
63 63 return d.activate(context, name);
64 64 } catch (error) {
65 65 throw new ActivationError(name, context.getStack(), error);
66 66 }
67 67 },
68 68
69 69 register: function (name, service) {
70 70 if (arguments.length == 1) {
71 71 var data = name;
72 72 for (name in data)
73 73 this.register(name, data[name]);
74 74 } else {
75 75 if (!(service instanceof Descriptor))
76 76 service = new Value(service, true);
77 77 this._services[name] = service;
78 78 }
79 79 return this;
80 80 },
81 81
82 82 onDispose: function (callback) {
83 83 if (!(callback instanceof Function))
84 84 throw new Error("The callback must be a function");
85 85 this._cleanup.push(callback);
86 86 },
87 87
88 88 dispose: function () {
89 89 if (this._cleanup) {
90 90 for (var i = 0; i < this._cleanup.length; i++)
91 91 this._cleanup[i].call(null);
92 92 this._cleanup = null;
93 93 }
94 94 },
95 95
96 96 /**
97 97 * @param{String|Object} config
98 98 * The configuration of the contaier. Can be either a string or an object,
99 99 * if the configuration is an object it's treated as a collection of
100 100 * services which will be registed in the contaier.
101 101 *
102 102 * @param{Function} opts.contextRequire
103 103 * The function which will be used to load a configuration or types for services.
104 104 *
105 105 */
106 106 configure: function (config, opts) {
107 107 var p, me = this,
108 108 contextRequire = (opts && opts.contextRequire);
109 109
110 110 if (typeof (config) === "string") {
111 111 p = new Deferred();
112 112 if (!contextRequire) {
113 var shim = [config, new Uuid()].join(config.indexOf("/") != -1 ? "-" : "/");
113 var shim = [config, Uuid()].join(config.indexOf("/") != -1 ? "-" : "/");
114 114 define(shim, ["require", config], function (ctx, data) {
115 115 p.resolve([data, {
116 116 contextRequire: ctx
117 117 }]);
118 118 });
119 119 require([shim]);
120 120 } else {
121 121 // TODO how to get correct contextRequire for the relative config module?
122 122 contextRequire([config], function (data) {
123 123 p.resolve([data, {
124 124 contextRequire: contextRequire
125 125 }]);
126 126 });
127 127 }
128 128
129 129 return p.then(function (args) {
130 130 return me._configure.apply(me, args);
131 131 });
132 132 } else {
133 133 return me._configure(config, opts);
134 134 }
135 135 },
136 136
137 137 createChildContainer: function () {
138 138 return new Container(this);
139 139 },
140 140
141 141 has: function (id) {
142 142 return id in this._cache;
143 143 },
144 144
145 145 get: function (id) {
146 146 return this._cache[id];
147 147 },
148 148
149 149 store: function (id, value) {
150 150 return (this._cache[id] = value);
151 151 },
152 152
153 153 _configure: function (data, opts) {
154 154 var typemap = {},
155 155 d = new Deferred(),
156 156 me = this,
157 157 p,
158 158 contextRequire = (opts && opts.contextRequire) || require;
159 159
160 160 var services = {};
161 161
162 162 for (p in data) {
163 163 var service = me._parse(data[p], typemap);
164 164 if (!(service instanceof Descriptor))
165 165 service = new Value(service, false);
166 166 services[p] = service;
167 167 }
168 168
169 169 me.register(services);
170 170
171 171 var names = [];
172 172
173 173 for (p in typemap)
174 174 names.push(p);
175 175
176 176 if (names.length) {
177 177 contextRequire(names, function () {
178 178 for (var i = 0; i < names.length; i++)
179 179 typemap[names[i]] = arguments[i];
180 180 d.resolve(me);
181 181 });
182 182 } else {
183 183 d.resolve(me);
184 184 }
185 185 return d.promise;
186 186 },
187 187
188 188 _parse: function (data, typemap) {
189 189 if (safe.isPrimitive(data) || data instanceof Descriptor)
190 190 return data;
191 191 if (data.$dependency)
192 192 return new Reference(
193 193 data.$dependency,
194 194 data.lazy,
195 195 data.optional,
196 196 data["default"],
197 197 data.services && this._parseObject(data.services, typemap));
198 198 if (data.$value) {
199 199 var raw = !data.parse;
200 200 return new Value(raw ? data.$value : this._parse(
201 201 data.$value,
202 202 typemap), raw);
203 203 }
204 204 if (data.$type || data.$factory)
205 205 return this._parseService(data, typemap);
206 206 if (data instanceof Array)
207 207 return this._parseArray(data, typemap);
208 208
209 209 return this._parseObject(data, typemap);
210 210 },
211 211
212 212 _parseService: function (data, typemap) {
213 213 var me = this,
214 214 opts = {
215 215 owner: this
216 216 };
217 217 if (data.$type) {
218 218
219 219 opts.type = data.$type;
220 220
221 221 if (typeof (data.$type) === "string") {
222 222 typemap[data.$type] = null;
223 223 opts.typeMap = typemap;
224 224 }
225 225 }
226 226
227 227 if (data.$factory)
228 228 opts.factory = data.$factory;
229 229
230 230 if (data.services)
231 231 opts.services = me._parseObject(data.services, typemap);
232 232 if (data.inject)
233 233 opts.inject = data.inject instanceof Array ? data.inject.map(function (x) {
234 234 return me._parseObject(x, typemap);
235 235 }) : me._parseObject(data.inject, typemap);
236 236 if (data.params)
237 237 opts.params = me._parse(data.params, typemap);
238 238
239 239 if (data.activation) {
240 240 if (typeof (data.activation) === "string") {
241 241 switch (data.activation.toLowerCase()) {
242 242 case "singleton":
243 243 opts.activation = Service.SINGLETON;
244 244 break;
245 245 case "container":
246 246 opts.activation = Service.CONTAINER;
247 247 break;
248 248 case "hierarchy":
249 249 opts.activation = Service.HIERARCHY;
250 250 break;
251 251 case "context":
252 252 opts.activation = Service.CONTEXT;
253 253 break;
254 254 case "call":
255 255 opts.activation = Service.CALL;
256 256 break;
257 257 default:
258 258 throw new Error("Unknown activation type: " +
259 259 data.activation);
260 260 }
261 261 } else {
262 262 opts.activation = Number(data.activation);
263 263 }
264 264 }
265 265
266 266 if (data.cleanup)
267 267 opts.cleanup = data.cleanup;
268 268
269 269 return new Service(opts);
270 270 },
271 271
272 272 _parseObject: function (data, typemap) {
273 273 if (data.constructor &&
274 274 data.constructor.prototype !== Object.prototype)
275 275 return new Value(data, true);
276 276
277 277 var o = {};
278 278
279 279 for (var p in data)
280 280 o[p] = this._parse(data[p], typemap);
281 281
282 282 return o;
283 283 },
284 284
285 285 _parseArray: function (data, typemap) {
286 286 if (data.constructor &&
287 287 data.constructor.prototype !== Array.prototype)
288 288 return new Value(data, true);
289 289
290 290 var me = this;
291 291 return data.map(function (x) {
292 292 return me._parse(x, typemap);
293 293 });
294 294 }
295 295
296 296 });
297 297
298 298 return Container;
299 299 }); No newline at end of file
@@ -1,323 +1,325
1 1 define([],
2 2
3 3 function () {
4 4 var _create = Object.create,
5 5 _keys = Object.keys;
6 6
7 7 var safe = null;
8 8 safe = {
9 9 argumentNotNull: function (arg, name) {
10 10 if (arg === null || arg === undefined)
11 11 throw new Error("The argument " + name + " can't be null or undefined");
12 12 },
13 13
14 14 argumentNotEmptyString: function (arg, name) {
15 15 if (typeof (arg) !== "string" || !arg.length)
16 16 throw new Error("The argument '" + name + "' must be a not empty string");
17 17 },
18 18
19 19 argumentNotEmptyArray: function (arg, name) {
20 20 if (!(arg instanceof Array) || !arg.length)
21 21 throw new Error("The argument '" + name + "' must be a not empty array");
22 22 },
23 23
24 24 argumentOfType: function (arg, type, name) {
25 25 if (!(arg instanceof type))
26 26 throw new Error("The argument '" + name + "' type doesn't match");
27 27 },
28 28
29 29 isNull: function (arg) {
30 30 return (arg === null || arg === undefined);
31 31 },
32 32
33 33 isPrimitive: function (arg) {
34 34 return (arg === null || arg === undefined || typeof (arg) === "string" ||
35 35 typeof (arg) === "number" || typeof (arg) === "boolean");
36 36 },
37 37
38 38 isInteger: function (arg) {
39 39 return parseInt(arg) == arg;
40 40 },
41 41
42 42 isNumber: function (arg) {
43 43 return parseFloat(arg) == arg;
44 44 },
45 45
46 46 isString: function (val) {
47 47 return typeof (val) == "string" || val instanceof String;
48 48 },
49 49
50 50 isNullOrEmptyString: function (str) {
51 51 if (str === null || str === undefined ||
52 52 ((typeof (str) == "string" || str instanceof String) && str.length === 0))
53 53 return true;
54 54 },
55 55
56 56 isNotEmptyArray: function (arg) {
57 57 return (arg instanceof Array && arg.length > 0);
58 58 },
59 59
60 60 /**
61 61 * Выполняет метод для каждого элемента массива, останавливается, когда
62 62 * либо достигнут конец массива, либо функция <c>cb</c> вернула
63 63 * значение.
64 64 *
65 65 * @param{Array | Object} obj массив элементов для просмотра
66 66 * @param{Function} cb функция, вызываемая для каждого элемента
67 67 * @param{Object} thisArg значение, которое будет передано в качестве
68 68 * <c>this</c> в <c>cb</c>.
69 69 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
70 70 * если достигнут конец массива.
71 71 */
72 72 each: function (obj, cb, thisArg) {
73 73 safe.argumentNotNull(cb, "cb");
74 74 var i, x;
75 75 if (obj instanceof Array) {
76 76 for (i = 0; i < obj.length; i++) {
77 77 x = cb.call(thisArg, obj[i], i);
78 78 if (x !== undefined)
79 79 return x;
80 80 }
81 81 } else {
82 82 var keys = _keys(obj);
83 83 for (i = 0; i < keys.length; i++) {
84 84 var k = keys[i];
85 85 x = cb.call(thisArg, obj[k], k);
86 86 if (x !== undefined)
87 87 return x;
88 88 }
89 89 }
90 90 },
91 91
92 92 /**
93 93 * Копирует свойства одного объекта в другой.
94 94 *
95 95 * @param{Any} dest объект в который нужно скопировать значения
96 96 * @param{Any} src источник из которого будут копироваться значения
97 97 * @tmpl{Object|Array} tmpl шаблон по которому будет происходить
98 98 * копирование. Если шаблон является массивом
99 99 * (список свойств), тогда значения этого массива
100 100 * являются именами свойсвт которые будут
101 101 * скопированы. Если шаблон является объектом (карта
102 102 * преобразования имен свойств src->dst), тогда
103 103 * копирование будет осуществляться только
104 104 * собственных свойств источника, присутсвующих в
105 105 * шаблоне, при этом значение свойства шаблона
106 106 * является именем свойства в которое будет
107 107 * произведено коприрование
108 108 */
109 109 mixin: function (dest, src, tmpl) {
110 110 safe.argumentNotNull(dest, "dest");
111 111 if (!src)
112 112 return dest;
113 113
114 114 var keys, i, p;
115 115 if (arguments.length < 3) {
116 116 keys = _keys(src);
117 117 for (i = 0; i < keys.length; i++) {
118 118 p = keys[i];
119 119 dest[p] = src[p];
120 120 }
121 121 } else {
122 122 if (tmpl instanceof Array) {
123 123 for (i = 0; i < tmpl.length; i++) {
124 124 p = tmpl[i];
125 125 if (p in src)
126 126 dest[p] = src[p];
127 127 }
128 128
129 129 } else {
130 130 keys = _keys(src);
131 131 for (i = 0; i < keys.length; i++) {
132 132 p = keys[i];
133 133 if (p in tmpl)
134 134 dest[tmpl[p]] = src[p];
135 135 }
136 136 }
137 137 }
138 138 return dest;
139 139 },
140 140
141 141 /** Wraps the specified function to emulate an asynchronous execution.
142 142 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
143 143 * @param{Function|String} fn [Required] Function wich will be wrapped.
144 144 */
145 145 async: function (fn, thisArg) {
146 146 if (arguments.length == 2 && !(fn instanceof Function))
147 147 fn = thisArg[fn];
148 148
149 149 if (fn == null)
150 150 throw new Error("The function must be specified");
151 151
152 152 function wrapresult(x, e) {
153 153 if (e) {
154 154 return {
155 155 then: function (cb, eb) {
156 156 try {
157 157 return eb ? wrapresult(eb(e)) : this;
158 158 } catch (e2) {
159 159 return wrapresult(null, e2);
160 160 }
161 161 }
162 162 };
163 163 } else {
164 164 if (x && x.then)
165 165 return x;
166 166 return {
167 167 then : function(cb) {
168 168 try {
169 169 return cb ? wrapresult(cb(x)) : this;
170 170 } catch(e2) {
171 171 return wrapresult(e2);
172 172 }
173 173 }
174 174 };
175 175 }
176 176 }
177 177
178 try {
179 return wrapresult(fn.apply(thisArg, arguments));
180 } catch (e) {
181 return wrapresult(null, e);
182 }
178 return function() {
179 try {
180 return wrapresult(fn.apply(thisArg, arguments));
181 } catch (e) {
182 return wrapresult(null, e);
183 }
184 };
183 185 },
184 186
185 187 create: function () {
186 188 if (console && console.warn)
187 189 console.warn("implab/safe::create is deprecated use Object.create instead");
188 190 _create.apply(this, arguments);
189 191 },
190 192
191 193 delegate: function (target, method) {
192 194 if (!(method instanceof Function)) {
193 195 this.argumentNotNull(target, "target");
194 196 method = target[method];
195 197 }
196 198
197 199 if (!(method instanceof Function))
198 200 throw new Error("'method' argument must be a Function or a method name");
199 201
200 202 return function () {
201 203 return method.apply(target, arguments);
202 204 };
203 205 },
204 206
205 207 /**
206 208 * Для каждого элемента массива вызывает указанную функцию и сохраняет
207 209 * возвращенное значение в массиве результатов.
208 210 *
209 211 * @remarks cb может выполняться асинхронно, при этом одновременно будет
210 212 * только одна операция.
211 213 *
212 214 * @async
213 215 */
214 216 pmap: function (items, cb) {
215 217 safe.argumentNotNull(cb, "cb");
216 218
217 219 if (items && items.then instanceof Function)
218 220 return items.then(function (data) {
219 221 return safe.pmap(data, cb);
220 222 });
221 223
222 224 if (safe.isNull(items) || !items.length)
223 225 return items;
224 226
225 227 var i = 0,
226 228 result = [];
227 229
228 230 function next() {
229 231 var r, ri;
230 232
231 233 function chain(x) {
232 234 result[ri] = x;
233 235 return next();
234 236 }
235 237
236 238 while (i < items.length) {
237 239 r = cb(items[i], i);
238 240 ri = i;
239 241 i++;
240 242 if (r && r.then) {
241 243 return r.then(chain);
242 244 } else {
243 245 result[ri] = r;
244 246 }
245 247 }
246 248 return result;
247 249 }
248 250
249 251 return next();
250 252 },
251 253
252 254 /**
253 255 * Для каждого элемента массива вызывает указанную функцию, результаты
254 256 * не сохраняются
255 257 *
256 258 * @remarks cb может выполняться асинхронно, при этом одновременно будет
257 259 * только одна операция.
258 260 * @async
259 261 */
260 262 pfor: function (items, cb) {
261 263 safe.argumentNotNull(cb, "cb");
262 264
263 265 if (items && items.then instanceof Function)
264 266 return items.then(function (data) {
265 267 return safe.pmap(data, cb);
266 268 });
267 269
268 270 if (safe.isNull(items) || !items.length)
269 271 return items;
270 272
271 273 var i = 0;
272 274
273 275 function next() {
274 276 while (i < items.length) {
275 277 var r = cb(items[i], i);
276 278 i++;
277 279 if (r && r.then)
278 280 return r.then(next);
279 281 }
280 282 }
281 283
282 284 return next();
283 285 },
284 286
285 287 /**
286 288 * Выбирает первый элемент из последовательности, или обещания, если в
287 289 * качестве параметра используется обещание, оно должно вернуть массив.
288 290 *
289 291 * @param{Function} cb обработчик результата, ему будет передан первый
290 292 * элемент последовательности в случае успеха
291 293 * @param{Fucntion} err обработчик исключения, если массив пустой, либо
292 294 * не массив
293 295 *
294 296 * @remarks Если не указаны ни cb ни err, тогда функция вернет либо
295 297 * обещание, либо первый элемент.
296 298 * @async
297 299 */
298 300 first: function (sequence, cb, err) {
299 301 if (sequence) {
300 302 if (sequence.then instanceof Function) {
301 303 return sequence.then(function (res) {
302 304 return safe.first(res, cb, err);
303 305 }, err);
304 306 } else if (sequence && "length" in sequence) {
305 307 if (sequence.length === 0) {
306 308 if (err)
307 309 return err(new Error("The sequence is empty"));
308 310 else
309 311 throw new Error("The sequence is empty");
310 312 }
311 313 return cb ? cb(sequence[0]) : sequence[0];
312 314 }
313 315 }
314 316
315 317 if (err)
316 318 return err(new Error("The sequence is required"));
317 319 else
318 320 throw new Error("The sequence is required");
319 321 }
320 322 };
321 323
322 324 return safe;
323 325 }); No newline at end of file
@@ -1,280 +1,282
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 declare var window: any;
10
9 11 let _window : any = 'undefined' !== typeof window ? window : null;
10 12
11 13 // Unique ID creation requires a high quality random # generator. We
12 14 // feature
13 15 // detect to determine the best RNG source, normalizing to a function
14 16 // that
15 17 // returns 128-bits of randomness, since that's what's usually required
16 18 let _rng;
17 19
18 20 function setupBrowser() {
19 21 // Allow for MSIE11 msCrypto
20 22 let _crypto = _window.crypto || _window.msCrypto;
21 23
22 24 if (!_rng && _crypto && _crypto.getRandomValues) {
23 25 // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
24 26 //
25 27 // Moderately fast, high quality
26 28 try {
27 29 let _rnds8 = new Uint8Array(16);
28 30 _rng = function whatwgRNG() {
29 31 _crypto.getRandomValues(_rnds8);
30 32 return _rnds8;
31 33 };
32 34 _rng();
33 35 } catch (e) { /**/ }
34 36 }
35 37
36 38 if (!_rng) {
37 39 // Math.random()-based (RNG)
38 40 //
39 41 // If all else fails, use Math.random(). It's fast, but is of
40 42 // unspecified
41 43 // quality.
42 44 let _rnds = new Array(16);
43 45 _rng = function () {
44 46 for (var i = 0, r; i < 16; i++) {
45 47 if ((i & 0x03) === 0) {
46 48 r = Math.random() * 0x100000000;
47 49 }
48 50 _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
49 51 }
50 52
51 53 return _rnds;
52 54 };
53 55 if ('undefined' !== typeof console && console.warn) {
54 56 console.warn("[SECURITY] node-uuid: crypto not usable, falling back to insecure Math.random()");
55 57 }
56 58 }
57 59 }
58 60
59 61 function setupNode() {
60 62 // Node.js crypto-based RNG -
61 63 // http://nodejs.org/docs/v0.6.2/api/crypto.html
62 64 //
63 65 // Moderately fast, high quality
64 66 if ('function' === typeof require) {
65 67 try {
66 68 let _rb = require('crypto').randomBytes;
67 69 _rng = _rb && function () {
68 70 return _rb(16);
69 71 };
70 72 _rng();
71 73 } catch (e) { /**/ }
72 74 }
73 75 }
74 76
75 77 if (_window) {
76 78 setupBrowser();
77 79 } else {
78 80 setupNode();
79 81 }
80 82
81 83 // Buffer class to use
82 84 let BufferClass = ('function' === typeof Buffer) ? Buffer : Array;
83 85
84 86 // Maps for number <-> hex string conversion
85 87 let _byteToHex = [];
86 88 let _hexToByte = {};
87 89 for (let i = 0; i < 256; i++) {
88 90 _byteToHex[i] = (i + 0x100).toString(16).substr(1);
89 91 _hexToByte[_byteToHex[i]] = i;
90 92 }
91 93
92 94 // **`parse()` - Parse a UUID into it's component bytes**
93 95 function parse(s, buf?, offset?) : Array<string> {
94 96 let i = (buf && offset) || 0, ii = 0;
95 97
96 98 buf = buf || [];
97 99 s.toLowerCase().replace(/[0-9a-f]{2}/g, function (oct) {
98 100 if (ii < 16) { // Don't overflow!
99 101 buf[i + ii++] = _hexToByte[oct];
100 102 }
101 103 });
102 104
103 105 // Zero out remaining bytes if string was short
104 106 while (ii < 16) {
105 107 buf[i + ii++] = 0;
106 108 }
107 109
108 110 return buf;
109 111 }
110 112
111 113 // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
112 114 function unparse(buf, offset?) : string {
113 115 let i = offset || 0, bth = _byteToHex;
114 116 return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] +
115 117 bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] + '-' +
116 118 bth[buf[i++]] + bth[buf[i++]] + '-' + bth[buf[i++]] +
117 119 bth[buf[i++]] + '-' + bth[buf[i++]] + bth[buf[i++]] +
118 120 bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]];
119 121 }
120 122
121 123 // **`v1()` - Generate time-based UUID**
122 124 //
123 125 // Inspired by https://github.com/LiosK/UUID.js
124 126 // and http://docs.python.org/library/uuid.html
125 127
126 128 // random #'s we need to init node and clockseq
127 129 let _seedBytes = _rng();
128 130
129 131 // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit =
130 132 // 1)
131 133 let _nodeId = [
132 134 _seedBytes[0] | 0x01,
133 135 _seedBytes[1],
134 136 _seedBytes[2],
135 137 _seedBytes[3],
136 138 _seedBytes[4],
137 139 _seedBytes[5]
138 140 ];
139 141
140 142 // Per 4.2.2, randomize (14 bit) clockseq
141 143 let _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
142 144
143 145 // Previous uuid creation time
144 146 let _lastMSecs = 0, _lastNSecs = 0;
145 147
146 148 // See https://github.com/broofa/node-uuid for API details
147 149 function v1(options?, buf?, offset?) : string {
148 150 let i = buf && offset || 0;
149 151 let b = buf || [];
150 152
151 153 options = options || {};
152 154
153 155 let clockseq = (options.clockseq != null) ? options.clockseq : _clockseq;
154 156
155 157 // UUID timestamps are 100 nano-second units since the Gregorian
156 158 // epoch,
157 159 // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
158 160 // time is handled internally as 'msecs' (integer milliseconds) and
159 161 // 'nsecs'
160 162 // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01
161 163 // 00:00.
162 164 let msecs = (options.msecs != null) ? options.msecs : new Date()
163 165 .getTime();
164 166
165 167 // Per 4.2.1.2, use count of uuid's generated during the current
166 168 // clock
167 169 // cycle to simulate higher resolution clock
168 170 let nsecs = (options.nsecs != null) ? options.nsecs : _lastNSecs + 1;
169 171
170 172 // Time since last uuid creation (in msecs)
171 173 let dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000;
172 174
173 175 // Per 4.2.1.2, Bump clockseq on clock regression
174 176 if (dt < 0 && options.clockseq == null) {
175 177 clockseq = clockseq + 1 & 0x3fff;
176 178 }
177 179
178 180 // Reset nsecs if clock regresses (new clockseq) or we've moved onto
179 181 // a new
180 182 // time interval
181 183 if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {
182 184 nsecs = 0;
183 185 }
184 186
185 187 // Per 4.2.1.2 Throw error if too many uuids are requested
186 188 if (nsecs >= 10000) {
187 189 throw new Error(
188 190 'uuid.v1(): Can\'t create more than 10M uuids/sec');
189 191 }
190 192
191 193 _lastMSecs = msecs;
192 194 _lastNSecs = nsecs;
193 195 _clockseq = clockseq;
194 196
195 197 // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
196 198 msecs += 12219292800000;
197 199
198 200 // `time_low`
199 201 let tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
200 202 b[i++] = tl >>> 24 & 0xff;
201 203 b[i++] = tl >>> 16 & 0xff;
202 204 b[i++] = tl >>> 8 & 0xff;
203 205 b[i++] = tl & 0xff;
204 206
205 207 // `time_mid`
206 208 let tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
207 209 b[i++] = tmh >>> 8 & 0xff;
208 210 b[i++] = tmh & 0xff;
209 211
210 212 // `time_high_and_version`
211 213 b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
212 214 b[i++] = tmh >>> 16 & 0xff;
213 215
214 216 // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
215 217 b[i++] = clockseq >>> 8 | 0x80;
216 218
217 219 // `clock_seq_low`
218 220 b[i++] = clockseq & 0xff;
219 221
220 222 // `node`
221 223 let node = options.node || _nodeId;
222 224 for (let n = 0; n < 6; n++) {
223 225 b[i + n] = node[n];
224 226 }
225 227
226 228 return buf ? buf : unparse(b);
227 229 }
228 230
229 231 // **`v4()` - Generate random UUID**
230 232
231 233 // See https://github.com/broofa/node-uuid for API details
232 234 function v4(options?, buf?, offset?) : string {
233 235 // Deprecated - 'format' argument, as supported in v1.2
234 236 let i = buf && offset || 0;
235 237
236 238 if (typeof (options) === 'string') {
237 239 buf = (options === 'binary') ? new BufferClass(16) : null;
238 240 options = null;
239 241 }
240 242 options = options || {};
241 243
242 244 let rnds = options.random || (options.rng || _rng)();
243 245
244 246 // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
245 247 rnds[6] = (rnds[6] & 0x0f) | 0x40;
246 248 rnds[8] = (rnds[8] & 0x3f) | 0x80;
247 249
248 250 // Copy bytes to buffer, if provided
249 251 if (buf) {
250 252 for (let ii = 0; ii < 16; ii++) {
251 253 buf[i + ii] = rnds[ii];
252 254 }
253 255 }
254 256
255 257 return buf || unparse(rnds);
256 258 }
257 259
258 260 // Export public API
259 261 const empty = "00000000-0000-0000-0000-000000000000";
260 262
261 263 interface uuid {
262 264 (options?, buf?, offset?) : string;
263 265 v1(options?, buf?, offset?) : string;
264 266 v4(options?, buf?, offset?) : string;
265 267 readonly empty: string;
266 268 parse(s, buf?, offset?) : Array<string>;
267 269 unparse(buf, offset?) : string;
268 270 }
269 271
270 272 export = <uuid>(() =>{
271 273 var f : any = function(options?, buf?, offset?) : string {
272 274 return v4(options, buf, offset);
273 275 };
274 276 f.v1 = v1;
275 277 f.v4 = v4;
276 278 f.empty = empty;
277 279 f.parse = parse;
278 280 f.unparse = unparse;
279 281 return f;
280 282 })(); No newline at end of file
@@ -1,1 +1,1
1 define(["./dummy", "./example"]); No newline at end of file
1 define(["./dummy", "./example", "./ActivatableTests"]); No newline at end of file
@@ -1,12 +1,15
1 1 {
2 2 "compilerOptions": {
3 3 "target": "es5",
4 4 "module": "amd",
5 5 "sourceMap": true,
6 6 "outDir" : "build/dist",
7 "declaration": true
7 "declaration": true,
8 "lib": [
9 "ES2015"
10 ]
8 11 },
9 12 "include" : [
10 13 "src/ts/**/*.ts"
11 14 ]
12 15 } No newline at end of file
@@ -1,12 +1,15
1 1 {
2 2 "compilerOptions": {
3 3 "target": "es5",
4 4 "module": "amd",
5 5 "sourceMap": true,
6 6 "outDir" : "build/test",
7 "moduleResolution": "node"
7 "moduleResolution": "node",
8 "lib": [
9 "ES2015"
10 ]
8 11 },
9 12 "include" : [
10 13 "test/ts/**/*.ts"
11 14 ]
12 15 } No newline at end of file
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