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