##// END OF EJS Templates
working on support commonjs modules format
cin -
r59:ba3ff79c2832 default
parent child
Show More
@@ -0,0 +1,13
1 # Сборка проекта
2
3 Проект представляет собой сложную структуру, которая делится на несколько наборов, которые используются при условной сборке.
4 Каждый набор сожержит в себе множество артефактов, которые класифицируются по способу сборки, например, исходные тексты js и ts и набор файлов, которые будут просто скопированы без изменений.
5
6 Такая структура позволяет сочитать в проекте несколько языков, а также делать сборки с разными параметрами для разных платформ.
7
8 ## NPM
9
10 Довольно ограниченная среда, поскольку может быть опубликован только один вариант библиотеки, можно конечно использовать разные версии, чтобы публиковать разные сборки, но это приведет к определенным сложностям, по крайней мере при использовании зависимостей, уже не говоря о том, что это будет путать разработчиков.
11 Использование суффиксов в имени пакета только частично решает задачу, поскольку не все системы могут использовать указание путей при настройке загрузчиков. RequireJS позволяет указывать месторасположение пакета, commonJs не позволяет это сделать без использования специальных средств.
12
13 npm позволяет устанавливать библиотеку используя ссылку, что дает возможность ее устанавки из произвольных мест. например, можно установить нужный вариант библиотеки по ссылке <https://implab.org/pub/js/implab/core-es2017-commonjs-1.2.0-rc1.tgz> . No newline at end of file
@@ -0,0 +1,56
1 # Обмен сообщениями
2
3 ## Session
4
5 Контекст обмена сообщениями, отвечает за создание конечных точек для получения и отправки сообщений, а также инкапсулирует в себе работу с провайдером системы обмена сообщениями.
6
7 Сессия позволяет выполнить конфигурацию компонент обработки сообщений, до начала реального обмена и после окончания конфигурации выполнить метод `start` после которого начнется реальная обработка. Такой способ позволяет избежать ошибки и потерю сообщений по причине того, что часть компонент готова к работе и уже получает и отправляет сообщения, а часть еще не настроена.
8
9 ```ts
10
11 // some provider related code
12 const connection = new StompService("ws://broker.329broker.com:15674/ws", { user: "user", pass: "secret" });
13 const session = connection.createSession();
14
15 // create and configure consumers and producers
16 const consumer = session.createConsumer("topic://notify");
17
18 // make event driven consumer
19 consumer.observe().on(msg => {
20 // do something
21
22 // mark the message as processed
23 msg.ack();
24 });
25
26 const producer = session.createProducer("queue://requests");
27
28 // signal the session to start
29 session.start();
30
31 // await the session is started
32 await session.getCompletion();
33
34 ```
35
36 ### start
37
38 Начинает сессию
39
40 ### createConsumer
41
42 ### createProducer
43
44 ## Consumer
45
46 ### Push-consumer
47
48 #### messages
49
50 ### Pull-consumer
51
52 #### read
53
54 ## Producer
55
56 ### post No newline at end of file
@@ -0,0 +1,66
1 import { Uuid } from "../Uuid";
2 import { argumentNotEmptyString, getGlobal } from "../safe";
3 import { TraceSource, DebugLevel } from "../log/TraceSource";
4 import m = require("module");
5
6 const sandboxId = Uuid();
7 define(sandboxId, ["require"], r => r);
8
9 // tslint:disable-next-line:no-var-requires
10 const globalRequire = require(sandboxId);
11
12 const trace = TraceSource.get(m.id);
13
14 export async function createContextRequire(moduleName: string): Promise<Require> {
15 argumentNotEmptyString(moduleName, "moduleName");
16
17 const parts = moduleName.split("/");
18 if (parts[0] === ".")
19 throw new Error("An absolute module path is required");
20
21 if (parts.length > 1)
22 parts.splice(-1, 1, Uuid());
23 else
24 parts.push(Uuid());
25
26 const shim = parts.join("/");
27
28 trace.debug(`define shim ${shim}`);
29
30 return new Promise<Require>(cb => {
31 define(shim, ["require"], r => {
32 trace.debug("shim resolved");
33 return r;
34 });
35 require([shim], cb);
36 });
37 }
38
39 class ModuleResolver {
40 _base: string;
41 _require: Require;
42
43 constructor(req: Require, base?: string) {
44 this._base = base;
45 this._require = req || globalRequire;
46 }
47
48 resolve(moduleName: string) {
49 argumentNotEmptyString(moduleName, "moduleName");
50 const resolvedName = moduleName[0] === "." && this._base ? [this._base, moduleName].join("/") : moduleName;
51 trace.debug(`${moduleName} -> ${resolvedName}`);
52
53 const req = this._require;
54
55 return new Promise<any>((cb, eb) => {
56 req([resolvedName], cb, eb);
57 });
58 }
59 }
60
61 export function makeResolver(moduleName: string, contextRequire: Require) {
62 const base = moduleName && moduleName.split("/").slice(0, -1).join("/");
63
64 const resolver = new ModuleResolver(contextRequire, base);
65 return (id: string) => resolver.resolve(id);
66 }
@@ -0,0 +1,44
1 import { TraceSource } from "./TraceSource";
2 import { Predicate } from "../interfaces";
3
4 export = {
5 on(filter: any , cb: any) {
6 if (arguments.length === 1) {
7 cb = filter;
8 filter = undefined;
9 }
10 let test: Predicate<string>;
11 if (filter instanceof RegExp) {
12 test = chId => filter.test(chId);
13 } else if (filter instanceof Function) {
14 test = filter;
15 } else if (filter) {
16 test = chId => chId === filter;
17 }
18
19 if (test) {
20 TraceSource.on(source => {
21 if (test(source.id))
22 source.events.on(cb);
23 });
24 } else {
25 TraceSource.on(source => {
26 source.events.on(cb);
27 });
28 }
29 },
30
31 load(id: string, require: any, cb: (trace: TraceSource) => void) {
32 if (id) {
33 cb(TraceSource.get(id));
34 } else if (require.module && require.module.mid) {
35 cb(TraceSource.get(require.module.mid));
36 } else {
37 require(["module"], (module: { id: any; }) => {
38 cb(TraceSource.get(module && module.id));
39 });
40 }
41 },
42
43 dynamic: true
44 };
@@ -0,0 +1,104
1 import { format } from "./StringFormat";
2 import { TraceSource, DebugLevel } from "../log/TraceSource";
3 import { ITemplateParser, TokenType } from "./TemplateParser";
4 import m = require("module");
5
6 const trace = TraceSource.get(m.id);
7
8 type TemplateFn = (obj: object) => string;
9
10 export class TemplateCompiler {
11
12 _data: string[];
13 _code: string[];
14 _wrapWith = true;
15
16 constructor() {
17 this._code = [];
18 this._data = [];
19 }
20
21 compile(parser: ITemplateParser): TemplateFn {
22 this.preamble();
23 this.visitTemplate(parser);
24 this.postamble();
25
26 const text = this._code.join("\n");
27
28 try {
29 // tslint:disable-next-line:function-constructor
30 const compiled = new Function("obj, format, $data", text);
31 /**
32 * Функция форматирования по шаблону
33 *
34 * @type{Function}
35 * @param{Object} obj объект с параметрами для подстановки
36 */
37 return (obj: object) => compiled(obj || {}, format, this._data);
38 } catch (e) {
39 trace.traceEvent(DebugLevel, [e, text, this._data]);
40 throw e;
41 }
42 }
43
44 preamble() {
45 this._code.push(
46 "var $p = [];",
47 "var print = function(){",
48 " $p.push(format.apply(null,arguments));",
49 "};"
50 );
51
52 if (this._wrapWith)
53 this._code.push("with(obj){");
54 }
55
56 postamble() {
57 if (this._wrapWith)
58 this._code.push("}");
59
60 this._code.push("return $p.join('');");
61 }
62
63 visitTemplate(parser: ITemplateParser) {
64 while (parser.next()) {
65 switch (parser.token()) {
66 case TokenType.OpenBlock:
67 this.visitCode(parser);
68 break;
69 case TokenType.OpenInlineBlock:
70 this.visitInline(parser);
71 break;
72 default:
73 this.visitTextFragment(parser);
74 break;
75 }
76 }
77 }
78
79 visitInline(parser: ITemplateParser) {
80 const code = ["$p.push("];
81 while (parser.next()) {
82 if (parser.token() === TokenType.CloseBlock)
83 break;
84 code.push(parser.value());
85 }
86 code.push(");");
87 this._code.push(code.join(""));
88 }
89
90 visitCode(parser: ITemplateParser) {
91 const code = [];
92 while (parser.next()) {
93 if (parser.token() === TokenType.CloseBlock)
94 break;
95 code.push(parser.value());
96 }
97 this._code.push(code.join(""));
98 }
99
100 visitTextFragment(parser: ITemplateParser) {
101 const i = this._data.push(parser.value());
102 this._code.push("$p.push($data[" + i + "]);");
103 }
104 }
@@ -0,0 +1,64
1 import { argumentNotEmptyString } from "../safe";
2 import { MapOf } from "../interfaces";
3
4 const splitRx = /(<%=|\[%=|<%|\[%|%\]|%>)/;
5
6 export enum TokenType {
7 None,
8 Text,
9 OpenInlineBlock,
10 OpenBlock,
11 CloseBlock
12 }
13
14 const tokenMap: MapOf<TokenType> = {
15 "<%": TokenType.OpenBlock,
16 "[%": TokenType.OpenBlock,
17 "<%=": TokenType.OpenInlineBlock,
18 "[%=": TokenType.OpenInlineBlock,
19 "%>": TokenType.CloseBlock,
20 "%]": TokenType.CloseBlock
21 };
22
23 export interface ITemplateParser {
24 next(): boolean;
25 token(): TokenType;
26 value(): string;
27 }
28
29 export class TemplateParser implements ITemplateParser {
30
31 _tokens: string[];
32 _pos = -1;
33 _type: TokenType;
34 _value: string;
35
36 constructor(text: string) {
37 argumentNotEmptyString(text, "text");
38
39 this._tokens = text.split(splitRx);
40 this._type = TokenType.None;
41 }
42
43 next() {
44 this._pos++;
45 if (this._pos < this._tokens.length) {
46 this._value = this._tokens[this._pos];
47 this._type = tokenMap[this._value] || TokenType.Text;
48 return true;
49 } else {
50 this._type = TokenType.None;
51 this._value = undefined;
52 return false;
53 }
54 }
55
56 token() {
57 return this._type;
58 }
59
60 value() {
61 return this._value;
62 }
63
64 }
@@ -0,0 +1,33
1 import { argumentNotEmptyString } from "../safe";
2 import { TraceSource } from "../log/TraceSource";
3
4 const trace = TraceSource.get(module.id);
5
6 const mainModule = require.main;
7 const mainRequire = (id: string) => mainModule.require(id);
8
9 class ModuleResolver {
10 _base: string;
11 _require: NodeRequireFunction;
12
13 constructor(req: NodeRequireFunction, base?: string) {
14 this._base = base;
15 this._require = (req || mainRequire).bind(null);
16 }
17
18 resolve(moduleName: string) {
19 argumentNotEmptyString(moduleName, "moduleName");
20 const resolvedName = moduleName[0] === "." && this._base ? [this._base, moduleName].join("/") : moduleName;
21
22 trace.debug(`${moduleName} -> ${resolvedName}`);
23
24 return this._require(resolvedName);
25 }
26 }
27
28 export function makeResolver(moduleName: string, contextRequire: NodeRequireFunction) {
29 const base = moduleName && moduleName.split("/").slice(0, -1).join("/");
30
31 const resolver = new ModuleResolver(contextRequire, base);
32 return (id: string) => resolver.resolve(id);
33 }
@@ -0,0 +1,3
1 import { ModuleResolver } from "./Configuration";
2
3 export declare function makeResolver(moduleName?: string, contextRequire?: any): ModuleResolver; No newline at end of file
@@ -0,0 +1,33
1 import { ICancellation } from "../interfaces";
2
3 /** interface for message consumers, used to recieve messages from a single endpoint.
4 */
5 export interface IConsumer<T> {
6 /** Reads the next message from the destination for which the consumer was created.
7 * @param options A provider specific options.
8 * @param ct The cancellation token for this operation.
9 * @returns A recieved message or a promise. If message is prefetched it will
10 * be returned immediately, otherwise a promise is returned.
11 */
12 read(options?: object, ct?: ICancellation): T | Promise<T>;
13 }
14
15 /** Interface for message produsers, used to send messages to the endpoints.
16 * The producer can be bound to the particular destination or a destination
17 * can be specified as an additional option during post if supported.
18 */
19 export interface IProducer<T> {
20 /** Sends a message
21 * @param msg The message to send.
22 * @param options A provider specific options
23 * @param ct The cancellation token for this operation
24 */
25 post(msg: T, options?: object, ct?: ICancellation): void | Promise<void>;
26 }
27
28 export interface ISession {
29 start(ct: ICancellation): void;
30
31 createConsumer<T = any>(destination: string, options?: object): IConsumer<T>;
32 createProducer<T = any>(destination: string, options?: object): IProducer<T>;
33 }
@@ -0,0 +1,24
1 {
2 "name": "${packageName}",
3 "version": "${version}",
4 "description": "${description}",
5 "main": "main.js",
6 "keywords": [
7 "di",
8 "ioc",
9 "logging",
10 "template engine",
11 "dependency injection"
12 ],
13 "author": "${author}",
14 "license": "${license}",
15 "repository": "$repository",
16 "publishConfig": {
17 "access": "public"
18 },
19 "peerDependencies": {
20 "dojo": "^1.10.0"
21 },
22 "module": "${jsmodule}",
23 "target": "${target}"
24 } No newline at end of file
@@ -0,0 +1,21
1 {
2 "name": "${packageName}",
3 "version": "${version}",
4 "description": "${description}",
5 "main": "main.js",
6 "keywords": [
7 "di",
8 "ioc",
9 "logging",
10 "template engine",
11 "dependency injection"
12 ],
13 "author": "${author}",
14 "license": "${license}",
15 "repository": "$repository",
16 "publishConfig": {
17 "access": "public"
18 },
19 "module": "${jsmodule}",
20 "target": "${target}"
21 } No newline at end of file
@@ -0,0 +1,7
1 define(["tape", "core/Uuid"], function(tape, Uuid) {
2 "use strict";
3 tape('uuid', function(t) {
4 t.notEqual(Uuid(),Uuid());
5 t.end();
6 });
7 }); No newline at end of file
@@ -0,0 +1,20
1 define({
2 foo: {
3 $type: "./Foo:Foo"
4 },
5
6 bar: {
7 $type: "./Bar:Bar",
8 params: {
9 db: {
10 provider: {
11 $dependency: "db"
12 }
13 },
14 foo: {
15 $type: "./Foo:Foo"
16 }
17 }
18 },
19 db: "db://localhost"
20 }); No newline at end of file
@@ -0,0 +1,8
1 define([
2 "./ActivatableTests",
3 "./trace-test",
4 "./TraceSourceTests",
5 "./CancellationTests",
6 "./ObservableTests",
7 "./ContainerTests"
8 ]); No newline at end of file
@@ -0,0 +1,22
1 var rjs = require('requirejs');
2
3 rjs.config({
4 baseUrl: '.',
5 packages: [{
6 name: "@implab/core",
7 location: "build/dist"
8 },
9 {
10 name: "test",
11 location: "build/test"
12 },
13 {
14 name: "dojo",
15 location: "node_modules/dojo"
16 }
17 ],
18 nodeRequire: require
19 });
20
21
22 rjs(['test/plan']); No newline at end of file
@@ -0,0 +1,30
1 define(["tape"], function(tape) {
2 "use strict";
3 var sourceId = '73a633f3-eab8-49b0-8601-07cae710f234';
4 var sourceId2 = '3ba9c7cd-ed77-437b-9a2f-1cbeb1226b5b';
5 tape('Load TraceSource for the module', function(t) {
6 require(["@implab/core/log/trace!" + sourceId, "@implab/core/log/TraceSource"], function(trace, TraceSource_1) {
7 var TraceSource = TraceSource_1.TraceSource;
8 t.equal(trace && trace.id, sourceId, "trace should be taken from the loader plugin parameter");
9
10 var count = 0;
11
12 var h = TraceSource.on(function(x) {
13 if(x.id == sourceId || x.id == sourceId2)
14 count++;
15 });
16
17 t.equal(count, 1, "should see created channel immediatelly");
18 t.equal(trace, TraceSource.get(sourceId), "should get same TraceSource from registry");
19 t.equal(count, 1);
20
21 TraceSource.get(sourceId2);
22
23 t.equal(count, 2);
24
25 h.destroy();
26
27 t.end();
28 });
29 });
30 }); No newline at end of file
@@ -0,0 +1,22
1 {
2 "extends": "../tsconfig",
3 "compilerOptions": {
4 "rootDir": "ts",
5 "baseUrl": ".",
6 "paths": {
7 "@implab/core/*": [
8 "../../build/dist/*"
9 ]
10 },
11 "types": [
12 "requirejs"
13 ],
14 "rootDirs": [
15 "ts",
16 "../typings/test"
17 ]
18 },
19 "include" : [
20 "ts/**/*.ts"
21 ]
22 } No newline at end of file
@@ -1,134 +1,198
1 1 if (release != 'rtm') {
2 2 version += "-$release"
3 3 }
4 4
5 5 if(!npmName)
6 6 npmName = name;
7 7
8 if(!["amd", "cjs"].contains(platform))
9 throw new Exception("Invalid platform specified: $platform");
8 if(!["amd", "commonjs", "system", "umd", "es6", "esnext"].contains(jsmodule))
9 throw new Exception("Invalid jsmodule specified: $jsmodule");
10 if(!["es3", "es5", "es6", "es2016", "es2017", "esnext"].contains(target))
11 throw new Exception("Invalid target specified: $target")
10 12
11 def moduleTypes = [
12 "amd": "amd",
13 "cjs": "commonjs"
14 ]
13 def targetLibs = [
14 "es3" : "es5,es2015.promise,es2015.symbol,dom,scripthost",
15 "es5" : "es5,es2015.promise,es2015.symbol,dom,scripthost"
16 ];
15 17
16 ext.packageName="$npmScope/$npmName-$platform";
18 ext.packageName="$npmScope/$npmName";
17 19
18 20 def srcDir = "$projectDir/src"
19 21 def typingsDir = "$srcDir/typings"
20 def distDir = "$buildDir/dist/$platform"
21 def testDir = "$buildDir/test/$platform"
22 def moduleType = moduleTypes[platform]
22 def distDir = "$buildDir/dist"
23 def testDir = "$buildDir/test"
24 def lib = targetLibs[target] ?: "${target},dom";
25
26 println "lib: $lib";
27
28 def sourceSets = ["main", "amd", "cjs"];
29 def testSets = ["test", "testAmd", "testCjs"];
30
31 task beforeBuild {
32 }
33
34 def createSoursetTasks = { String name, String outDir ->
35 def setName = name.capitalize();
36
37 def destDir = "$buildDir/compile/$name"
38 def declDir = "$typingsDir/$name"
39 def setDir = "$projectDir/src/$name"
40
41 def beforeBuildTask = task "beforeBuild$setName"(dependsOn: beforeBuild) {
42 }
43
44 def copyJsTask = task "copyJs$setName"(dependsOn: beforeBuildTask, type: Copy) {
45 from "$setDir/js"
46 into outDir
47 }
48
49 def compileTypingsTask = task "compileTypings$setName"(dependsOn: beforeBuildTask, type: Exec) {
50 inputs.dir("$setDir/ts")
51 inputs.file("$srcDir/tsconfig.json")
52 inputs.file("$setDir/tsconfig.json")
53 outputs.dir(declDir)
23 54
24 def sourceSets = ["main", "amd", "cjs", "test"];
55 commandLine 'node_modules/.bin/tsc',
56 '-p', "$setDir/tsconfig.json",
57 '-t', target,
58 '-m', jsmodule,
59 '-d',
60 '--emitDeclarationOnly',
61 '--declarationDir', declDir
62
63 if (lib)
64 args '--lib', lib
65 }
66
67 def compileTsTask = task "compileTs$setName"(dependsOn: beforeBuildTask, type: Exec) {
68 inputs.dir("$setDir/ts")
69 inputs.file("$srcDir/tsconfig.json")
70 inputs.file("$setDir/tsconfig.json")
71 outputs.dir(destDir)
72
73 commandLine 'node_modules/.bin/tsc',
74 '-p', "$setDir/tsconfig.json",
75 '-t', target,
76 '-m', jsmodule,
77 '--outDir', destDir
78
79 if (lib)
80 args '--lib', lib
81 }
82
83 def copyTsOutputTask = task "copyTsOutput$setName"(dependsOn: compileTsTask, type: Copy) {
84 from compileTsTask
85 into outDir
86 }
87
88 def copyTypingsTask = task "copyTypings$setName"(dependsOn: compileTypingsTask, type: Copy) {
89 from compileTypingsTask
90 into outDir
91 }
92
93 task "build$setName"(dependsOn: [copyTypingsTask, copyTsOutputTask, copyJsTask]) {
94 }
95 }
25 96
26 97 task printVersion {
27 98 doLast {
28 99 println "version: $version"
29 100 println "packageName: $packageName"
30 println "platform: $platform"
31 println "module: $moduleType"
101 println "target: $target"
102 println "module: $jsmodule"
32 103 }
33 104 }
34 105
35 106 task clean {
36 107 doLast {
37 108 delete buildDir
38 delete "node_modules/$packageName"
39 109 delete typingsDir
40 110 }
41 111 }
42 112
113 task _initBuild {
114 mustRunAfter clean
115
116 def buildInfoFile = "$buildDir/platform";
117 inputs.property('target',target);
118 inputs.property('jsmodule',jsmodule);
119 outputs.file(buildInfoFile);
120
121 doLast {
122 delete buildDir
123 mkdir buildDir
124
125 def f = new File(buildInfoFile);
126 f << "$target-$jsmodule";
127 }
128 }
129
43 130 task cleanNpm {
44 131 doLast {
45 132 delete 'node_modules'
46 133 }
47 134 }
48 135
49 136 task _npmInstall() {
50 137 inputs.file("package.json")
51 138 outputs.dir("node_modules")
52 139 doLast {
53 140 exec {
54 141 commandLine 'npm', 'install'
55 142 }
56 143 }
57 144 }
58 145
59 sourceSets.each {
60 def setName = it.capitalize();
146 beforeBuild {
147 dependsOn _initBuild
148 dependsOn _npmInstall
149 }
61 150
62 def destDir = "$buildDir/compile/$it"
63 def declDir = "$typingsDir/$it"
64 def setDir = "$projectDir/src/$it"
151 sourceSets.each { createSoursetTasks(it, distDir) }
65 152
66 task "_copyJs$setName"(type:Copy) {
67 from "$setDir/js"
68 into distDir
153 testSets.each { createSoursetTasks(it, testDir) }
154
155 compileTsAmd {
156 dependsOn compileTypingsMain
69 157 }
70 158
71 task "_compileTs$setName"(dependsOn: _npmInstall, type:Exec) {
72 inputs.dir("$setDir/ts")
73 inputs.file("$srcDir/tsconfig.json")
74 inputs.file("$setDir/tsconfig.json")
75 outputs.dir(destDir)
76 outputs.dir(declDir)
159 compileTypingsAmd {
160 dependsOn compileTypingsMain
161 }
77 162
78 commandLine 'node_modules/.bin/tsc',
79 '-p', "$setDir/tsconfig.json",
80 '-m', moduleType,
81 '--outDir', destDir,
82 '--declarationDir', declDir
163 task build(dependsOn: buildMain) {
164 if (jsmodule == "amd")
165 dependsOn buildAmd
83 166 }
84 167
85 task "_buildTs$setName"(dependsOn: "_compileTs$setName", type:Copy) {
86 from tasks.getByPath("_compileTs$setName");
87 into distDir
88 }
168 compileTsTest {
169 dependsOn build
89 170 }
90 171
91 _compileTsAmd {
92 dependsOn _buildTsMain
172 compileTsTestAmd {
173 dependsOn compileTypingsTestAmd
93 174 }
94 175
95 _buildTsTest {
96 into testDir
97 }
98
99 _copyJsTest {
100 into testDir
176 task test(dependsOn: [buildTest, buildTestAmd], type: Exec) {
177 commandLine 'node', "$testDir/run-amd-tests.js"
101 178 }
102 179
103 180 task _packageMeta(type: Copy) {
181 mustRunAfter build
182
104 183 inputs.property("version", version)
105 184 from('.') {
106 185 include '.npmignore', 'readme.md', 'license', 'history.md'
107 186 }
108 from("$srcDir/package.template.json") {
187 from("$srcDir/package.${jsmodule}.tmpl.json") {
109 188 expand project.properties
110 189 rename { "package.json" }
111 190 }
112 191 into distDir
113 192 }
114 193
115 task build(dependsOn: [_copyJsMain, _copyJsAmd, _npmInstall, _buildTsMain, _buildTsAmd, _packageMeta]) {
116
117 }
118
119 _compileTsTest {
120 dependsOn build
121 }
122
123 task buildTests(dependsOn: [_copyJsTest, _buildTsTest]) {
124 }
125
126 task test(dependsOn: buildTests, type: Exec) {
127 commandLine 'node', "$testDir/run-amd-tests.js"
128 }
129
130 task pack(dependsOn: build, type: Exec) {
194 task pack(dependsOn: [build, _packageMeta], type: Exec) {
131 195 workingDir distDir
132 196
133 197 commandLine 'npm', 'pack'
134 198 } No newline at end of file
@@ -1,186 +1,182
1 1 # Observable
2 2
3 3 Универсальный способ организации потока сообщений. Данный механизм может
4 4 использоваться для оповещения об изменениях состояний объектов или для доставки
5 5 самостоятельных событий, например, связанных с действиями пользователя.
6 6
7 7 Является реализацией классического шаблона наблюдателя с возможность сообщить
8 8 о коце потока событий. Данная реализация не содержит никаких дополнительных
9 9 функций, таких как фильтрация, канал с состоянием, преобразования сообщений и
10 10 т.п. Это сделано специально, чтобы реализация оставалась максимально простой.
11 11
12 12 Пример того, как можно создать последовательность из 10 событий:
13 13
14 14 ```ts
15 15 var events = new Observable(async (notify, error, complete) => {
16 16 // цикл в котором возникает событие
17 17 for(let i = 0; i < 10; i++) {
18 18 await delay(1000);
19 19 // в качестве данных передается номер события
20 20 notify(i);
21 21 }
22 22 // по окончании последовательности информируем, что событий больше не будет
23 23 compelte();
24 24 });
25 25
26 26 // создаем окно с отображением хода событий
27 27 var progress = showProgress({ min: 0, max: 9, current: 0});
28 28
29 29 // подписываемся на события
30 30 events.on(
31 31 // обработчик очередного события
32 32 msg => {
33 33 progress.setValue(msg);
34 34 }.
35 35 // обработчик ошибки
36 36 e => {
37 37 progress.showError(e);
38 38 },
39 39 // обработчик конца потока
40 40 () => {
41 41 progress.close();
42 42 }
43 43 );
44 44
45 45 // ожидание следующего события
46 46 let firstEvent = await events.next();
47 47 ```
48 48
49 49 `Observable` можно создавать из событий другого объекта, например, виджета:
50 50
51 51 ```ts
52 52 // клсс
53 53 class Canvas {
54 54 readonly mouseMove: IObservable<[number,number]>
55 55
56 56 postCreate() {
57 57 // превращаем события виджета в Observable
58 58 this.mouseMove = new Observable<[number,number]>((notify) => {
59 59 this.mousePad.on('mousemove',(e) => notify([e.clientX, e.clientY]) );
60 60 });
61 61 }
62 62 }
63 63
64 64 ```
65 65
66 66 Если объект инкапсулирует в себе `Observable`, он также может сохранить методы
67 67 для оповещения подписчиков для дальнейшего их использования внутри класса.
68 68
69 69 ```ts
70 70 // класс, который будет генерировать события местоположения
71 71 class PositionTracker implements IDestroyable {
72 72 // _nextPosition и _complete будут связаны с position при создании
73 73 // экземпляра PositionTracker.
74 74 _nextPosition: (pos: Position) => void
75 75 _complete: () => void
76 76
77 77 readonly position: IObservable<Position>
78 78
79 79 // конструктор
80 80 constructor(...args: any[]) {
81 81 super(args);
82 82
83 83 // создаем Observable
84 84 this.position = new Observable<Position>((notify, error, complete) => {
85 85 // сохраняем методы для оповещения о новом местоположении
86 86 this._nextPosition = notify;
87 87 // метод об оповещении конца потока событий
88 88 this._complete = complete
89 89 });
90 90 }
91 91
92 92 // метод для очистки ресурсов
93 93 destroy() {
94 94 this._complete();
95 95
96 96 super();
97 97 }
98 98 }
99 99 ```
100 100
101 101 Существует также несколько варинатов получения сообщений
102 102
103 103 ```ts
104 104 // регистрация метода для получений событий
105 105 let subscription = pushEvents.on((msg) => {
106 106 displayPopup(msg);
107 107 });
108 108
109 109 // подписку можно отменить, после чего обработчики больше не будут вызываться
110 110 subcription.destroy();
111 111
112 112 // если требуется получить только одно сообщение можно использовать
113 113 // асинхронный метод next(ct?: ICancellation)
114 114
115 115 let msg = await pushEvents.next();
116 116
117 117 // пример метода для получения координат с карты, который использует
118 118 // событие нажатия мышью для определения координат.
119 119
120 120 class Map {
121 121 /**
122
123 122 Получает координаты по щелчку мыши.
124
125 123 @async
126
127 124 @returns [lon,lat]
128
129 125 */
130 126 async peekCoordinates(ct: ICancellation = Cancellation.none) {
131 127 // получаем событие клика
132 128 let evt = this.viewport.click.next(ct);
133 129
134 130 // преобразуем позицию на экране в координаты карты
135 131 return this.clientToCoodinates([evt.clientx,evt.clientY]);
136 132 }
137 133 }
138 134
139 135
140 136 let map : Map; // где-то объявлено
141 137
142 138 // пример получения координат с карты
143 139 let coords = await map.peekCoordinates();
144 140
145 141 ```
146 142
147 143 ## Observable и последовательности
148 144
149 145 Можно сичтать, что `Observable` это некоторая аналогия итератора только в
150 146 парадигме событийного (или реактивного) программировния. Следует также понимать,
151 147 что при переходе от синхронного процедурного программирования к событийному так
152 148 же меняется и направление управления (Inverse Of Control), что означает
153 149 следующее:
154 150
155 151 * при работе с итераторами клиенты сами определяют момент чтения следующего
156 152 элемента последовательности.
157 153 * при работе с `Observable` клиенты вынуждены обрабатывать эти события по мере
158 154 их поступления и не могут на это повлиять.
159 155
160 156 Последний пункт можно изменить применив, например, буффер или канал с
161 157 состоянием, т.е. очередь, но данные механизмы выходят за рамки простого шаблона
162 158 наблюдателя.
163 159
164 160 ```ts
165 161 // обработка в цикле не гарантирует получения всех сообщений
166 162 while(1) {
167 163 // ожидаем следующее событие, по сути это подписка только на одно событие
168 164 let next = await events.next();
169 165
170 166 // такой цикл может пропускать сообщения, поскольку асинхронная операция
171 167 // позволит возобновить создание новых событий, на которые мы не подписаны
172 168 await processEvent(next);
173 169
174 170 // не только асинхронные операции могут привести к пропуску события
175 171 // например вызов метода, который приводит к созданию события так же
176 172 // приведет к тому, что созданное событие не будет обработано в текущем
177 173 // цикле
178 174 doSmthAndRiseEvent();
179 175 }
180 176
181 177 // для получения всех сообщений нужно регистрировать подписчика
182 178 events.on((data) => {
183 179 // будет вызван для всех сообщений
184 180 processEvent(data);
185 181 });
186 182 ``` No newline at end of file
@@ -1,9 +1,10
1 1 version=1.2.0
2 2 release=rc
3 3 author=Implab team
4 platform=amd
4 jsmodule=amd
5 target=es5
5 6 description=Dependency injection, logging, simple and fast text template engine
6 7 license=BSD-2-Clause
7 8 repository=https://bitbucket.org/implab/implabjs
8 9 npmScope=@implab
9 10 npmName=core No newline at end of file
@@ -1,12 +1,20
1 1 HISTORY
2 2 =======
3 3
4 1.2.0
5 -----
6
7 Major rafactoring, moving to support browser (rjs) and server (cjs) environments.
8
9 - dependency injection container ported to typescript
10 - sources are split to several sets to provide the ability for the conditional build of the project.
11
4 12 1.0.1
5 13 -----
6 14
7 First release, intorduces the followinf features
15 First release, intorduces the following features
8 16
9 - `di` - dependency injection conyainer
17 - `di` - dependency injection container
10 18 - `log` - log4 style logging system
11 19 - `text` - simple and fast text templating and formatting
12 20 - `Uuid` - uuid generation traits No newline at end of file
@@ -1,462 +1,471
1 1 {
2 2 "name": "@implab/core",
3 3 "version": "0.0.1-dev",
4 4 "lockfileVersion": 1,
5 5 "requires": true,
6 6 "dependencies": {
7 7 "@types/node": {
8 "version": "10.12.15",
9 "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.15.tgz",
10 "integrity": "sha512-9kROxduaN98QghwwHmxXO2Xz3MaWf+I1sLVAA6KJDF5xix+IyXVhds0MAfdNwtcpSrzhaTsNB0/jnL86fgUhqA==",
8 "version": "10.12.18",
9 "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
10 "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==",
11 11 "dev": true
12 12 },
13 13 "@types/requirejs": {
14 14 "version": "2.1.31",
15 15 "resolved": "https://registry.npmjs.org/@types/requirejs/-/requirejs-2.1.31.tgz",
16 16 "integrity": "sha512-b2soeyuU76rMbcRJ4e0hEl0tbMhFwZeTC0VZnfuWlfGlk6BwWNsev6kFu/twKABPX29wkX84wU2o+cEJoXsiTw==",
17 17 "dev": true
18 18 },
19 19 "@types/tape": {
20 "version": "4.2.32",
21 "resolved": "http://registry.npmjs.org/@types/tape/-/tape-4.2.32.tgz",
22 "integrity": "sha512-xil0KO5wkPoixdBWGIGolPv9dekf6dVkjjJLAFYchfKcd4DICou67rgGCIO7wAh3i5Ff/6j9IDgZz+GU9cMaqQ==",
20 "version": "4.2.33",
21 "resolved": "https://registry.npmjs.org/@types/tape/-/tape-4.2.33.tgz",
22 "integrity": "sha512-ltfyuY5BIkYlGuQfwqzTDT8f0q8Z5DGppvUnWGs39oqDmMd6/UWhNpX3ZMh/VYvfxs3rFGHMrLC/eGRdLiDGuw==",
23 23 "dev": true,
24 24 "requires": {
25 25 "@types/node": "*"
26 26 }
27 27 },
28 28 "balanced-match": {
29 29 "version": "1.0.0",
30 30 "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
31 31 "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
32 32 "dev": true
33 33 },
34 34 "brace-expansion": {
35 35 "version": "1.1.11",
36 36 "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
37 37 "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
38 38 "dev": true,
39 39 "requires": {
40 40 "balanced-match": "^1.0.0",
41 41 "concat-map": "0.0.1"
42 42 }
43 43 },
44 44 "concat-map": {
45 45 "version": "0.0.1",
46 46 "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
47 47 "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
48 48 "dev": true
49 49 },
50 50 "core-util-is": {
51 51 "version": "1.0.2",
52 52 "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
53 53 "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
54 54 "dev": true
55 55 },
56 56 "deep-equal": {
57 57 "version": "0.1.2",
58 58 "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz",
59 59 "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=",
60 60 "dev": true
61 61 },
62 62 "define-properties": {
63 63 "version": "1.1.3",
64 64 "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
65 65 "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
66 66 "dev": true,
67 67 "requires": {
68 68 "object-keys": "^1.0.12"
69 69 },
70 70 "dependencies": {
71 71 "object-keys": {
72 72 "version": "1.0.12",
73 73 "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
74 74 "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
75 75 "dev": true
76 76 }
77 77 }
78 78 },
79 79 "defined": {
80 80 "version": "0.0.0",
81 81 "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz",
82 82 "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=",
83 83 "dev": true
84 84 },
85 85 "dojo": {
86 86 "version": "1.14.2",
87 87 "resolved": "https://registry.npmjs.org/dojo/-/dojo-1.14.2.tgz",
88 88 "integrity": "sha512-TI+Ytgfh/VfmHWERp45Jte6NFMdoJTPsvUP/uzJUvAXET8FP2h442LePWWJ/q/xZ4V0V8OtdJhx8It/GB+Zbxg==",
89 89 "dev": true
90 90 },
91 91 "duplexer": {
92 92 "version": "0.1.1",
93 93 "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
94 94 "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
95 95 "dev": true
96 96 },
97 97 "es-abstract": {
98 "version": "1.12.0",
99 "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
100 "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
98 "version": "1.13.0",
99 "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
100 "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
101 101 "dev": true,
102 102 "requires": {
103 "es-to-primitive": "^1.1.1",
103 "es-to-primitive": "^1.2.0",
104 104 "function-bind": "^1.1.1",
105 "has": "^1.0.1",
106 "is-callable": "^1.1.3",
107 "is-regex": "^1.0.4"
105 "has": "^1.0.3",
106 "is-callable": "^1.1.4",
107 "is-regex": "^1.0.4",
108 "object-keys": "^1.0.12"
109 },
110 "dependencies": {
111 "object-keys": {
112 "version": "1.0.12",
113 "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
114 "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
115 "dev": true
116 }
108 117 }
109 118 },
110 119 "es-to-primitive": {
111 120 "version": "1.2.0",
112 121 "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
113 122 "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
114 123 "dev": true,
115 124 "requires": {
116 125 "is-callable": "^1.1.4",
117 126 "is-date-object": "^1.0.1",
118 127 "is-symbol": "^1.0.2"
119 128 }
120 129 },
121 130 "faucet": {
122 131 "version": "0.0.1",
123 132 "resolved": "https://registry.npmjs.org/faucet/-/faucet-0.0.1.tgz",
124 133 "integrity": "sha1-WX3PHSGJosBiMhtZHo8VHtIDnZw=",
125 134 "dev": true,
126 135 "requires": {
127 136 "defined": "0.0.0",
128 137 "duplexer": "~0.1.1",
129 138 "minimist": "0.0.5",
130 139 "sprintf": "~0.1.3",
131 140 "tap-parser": "~0.4.0",
132 141 "tape": "~2.3.2",
133 142 "through2": "~0.2.3"
134 143 },
135 144 "dependencies": {
136 145 "tape": {
137 146 "version": "2.3.3",
138 147 "resolved": "http://registry.npmjs.org/tape/-/tape-2.3.3.tgz",
139 148 "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=",
140 149 "dev": true,
141 150 "requires": {
142 151 "deep-equal": "~0.1.0",
143 152 "defined": "~0.0.0",
144 153 "inherits": "~2.0.1",
145 154 "jsonify": "~0.0.0",
146 155 "resumer": "~0.0.0",
147 156 "through": "~2.3.4"
148 157 }
149 158 }
150 159 }
151 160 },
152 161 "for-each": {
153 162 "version": "0.3.3",
154 163 "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
155 164 "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
156 165 "dev": true,
157 166 "requires": {
158 167 "is-callable": "^1.1.3"
159 168 }
160 169 },
161 170 "fs.realpath": {
162 171 "version": "1.0.0",
163 172 "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
164 173 "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
165 174 "dev": true
166 175 },
167 176 "function-bind": {
168 177 "version": "1.1.1",
169 178 "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
170 179 "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
171 180 "dev": true
172 181 },
173 182 "glob": {
174 183 "version": "7.1.3",
175 184 "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
176 185 "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
177 186 "dev": true,
178 187 "requires": {
179 188 "fs.realpath": "^1.0.0",
180 189 "inflight": "^1.0.4",
181 190 "inherits": "2",
182 191 "minimatch": "^3.0.4",
183 192 "once": "^1.3.0",
184 193 "path-is-absolute": "^1.0.0"
185 194 }
186 195 },
187 196 "has": {
188 197 "version": "1.0.3",
189 198 "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
190 199 "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
191 200 "dev": true,
192 201 "requires": {
193 202 "function-bind": "^1.1.1"
194 203 }
195 204 },
196 205 "has-symbols": {
197 206 "version": "1.0.0",
198 207 "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
199 208 "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
200 209 "dev": true
201 210 },
202 211 "inflight": {
203 212 "version": "1.0.6",
204 213 "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
205 214 "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
206 215 "dev": true,
207 216 "requires": {
208 217 "once": "^1.3.0",
209 218 "wrappy": "1"
210 219 }
211 220 },
212 221 "inherits": {
213 222 "version": "2.0.3",
214 223 "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
215 224 "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
216 225 "dev": true
217 226 },
218 227 "is-callable": {
219 228 "version": "1.1.4",
220 229 "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
221 230 "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
222 231 "dev": true
223 232 },
224 233 "is-date-object": {
225 234 "version": "1.0.1",
226 235 "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
227 236 "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
228 237 "dev": true
229 238 },
230 239 "is-regex": {
231 240 "version": "1.0.4",
232 241 "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
233 242 "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
234 243 "dev": true,
235 244 "requires": {
236 245 "has": "^1.0.1"
237 246 }
238 247 },
239 248 "is-symbol": {
240 249 "version": "1.0.2",
241 250 "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
242 251 "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
243 252 "dev": true,
244 253 "requires": {
245 254 "has-symbols": "^1.0.0"
246 255 }
247 256 },
248 257 "isarray": {
249 258 "version": "0.0.1",
250 259 "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
251 260 "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
252 261 "dev": true
253 262 },
254 263 "jsonify": {
255 264 "version": "0.0.0",
256 265 "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
257 266 "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
258 267 "dev": true
259 268 },
260 269 "minimatch": {
261 270 "version": "3.0.4",
262 271 "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
263 272 "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
264 273 "dev": true,
265 274 "requires": {
266 275 "brace-expansion": "^1.1.7"
267 276 }
268 277 },
269 278 "minimist": {
270 279 "version": "0.0.5",
271 280 "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz",
272 281 "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=",
273 282 "dev": true
274 283 },
275 284 "object-inspect": {
276 285 "version": "1.6.0",
277 286 "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz",
278 287 "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==",
279 288 "dev": true
280 289 },
281 290 "object-keys": {
282 291 "version": "0.4.0",
283 292 "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz",
284 293 "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=",
285 294 "dev": true
286 295 },
287 296 "once": {
288 297 "version": "1.4.0",
289 298 "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
290 299 "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
291 300 "dev": true,
292 301 "requires": {
293 302 "wrappy": "1"
294 303 }
295 304 },
296 305 "path-is-absolute": {
297 306 "version": "1.0.1",
298 307 "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
299 308 "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
300 309 "dev": true
301 310 },
302 311 "path-parse": {
303 312 "version": "1.0.6",
304 313 "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
305 314 "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
306 315 "dev": true
307 316 },
308 317 "readable-stream": {
309 318 "version": "1.1.14",
310 319 "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
311 320 "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
312 321 "dev": true,
313 322 "requires": {
314 323 "core-util-is": "~1.0.0",
315 324 "inherits": "~2.0.1",
316 325 "isarray": "0.0.1",
317 326 "string_decoder": "~0.10.x"
318 327 }
319 328 },
320 329 "requirejs": {
321 330 "version": "2.3.6",
322 331 "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
323 332 "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
324 333 "dev": true
325 334 },
326 335 "resolve": {
327 336 "version": "1.7.1",
328 337 "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz",
329 338 "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==",
330 339 "dev": true,
331 340 "requires": {
332 341 "path-parse": "^1.0.5"
333 342 }
334 343 },
335 344 "resumer": {
336 345 "version": "0.0.0",
337 346 "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz",
338 347 "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=",
339 348 "dev": true,
340 349 "requires": {
341 350 "through": "~2.3.4"
342 351 }
343 352 },
344 353 "sprintf": {
345 354 "version": "0.1.5",
346 355 "resolved": "https://registry.npmjs.org/sprintf/-/sprintf-0.1.5.tgz",
347 356 "integrity": "sha1-j4PjmpMXwaUCy324BQ5Rxnn27c8=",
348 357 "dev": true
349 358 },
350 359 "string.prototype.trim": {
351 360 "version": "1.1.2",
352 361 "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz",
353 362 "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=",
354 363 "dev": true,
355 364 "requires": {
356 365 "define-properties": "^1.1.2",
357 366 "es-abstract": "^1.5.0",
358 367 "function-bind": "^1.0.2"
359 368 }
360 369 },
361 370 "string_decoder": {
362 371 "version": "0.10.31",
363 372 "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
364 373 "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
365 374 "dev": true
366 375 },
367 376 "tap-parser": {
368 377 "version": "0.4.3",
369 378 "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-0.4.3.tgz",
370 379 "integrity": "sha1-pOrhkMENdsehEZIf84u+TVjwnuo=",
371 380 "dev": true,
372 381 "requires": {
373 382 "inherits": "~2.0.1",
374 383 "readable-stream": "~1.1.11"
375 384 }
376 385 },
377 386 "tape": {
378 "version": "4.9.1",
379 "resolved": "https://registry.npmjs.org/tape/-/tape-4.9.1.tgz",
380 "integrity": "sha512-6fKIXknLpoe/Jp4rzHKFPpJUHDHDqn8jus99IfPnHIjyz78HYlefTGD3b5EkbQzuLfaEvmfPK3IolLgq2xT3kw==",
387 "version": "4.9.2",
388 "resolved": "https://registry.npmjs.org/tape/-/tape-4.9.2.tgz",
389 "integrity": "sha512-lPXKRKILZ1kZaUy5ynWKs8ATGSUO7HAFHCFnBam6FaGSqPdOwMWbxXHq4EXFLE8WRTleo/YOMXkaUTRmTB1Fiw==",
381 390 "dev": true,
382 391 "requires": {
383 392 "deep-equal": "~1.0.1",
384 393 "defined": "~1.0.0",
385 394 "for-each": "~0.3.3",
386 395 "function-bind": "~1.1.1",
387 396 "glob": "~7.1.2",
388 397 "has": "~1.0.3",
389 398 "inherits": "~2.0.3",
390 399 "minimist": "~1.2.0",
391 400 "object-inspect": "~1.6.0",
392 401 "resolve": "~1.7.1",
393 402 "resumer": "~0.0.0",
394 403 "string.prototype.trim": "~1.1.2",
395 404 "through": "~2.3.8"
396 405 },
397 406 "dependencies": {
398 407 "deep-equal": {
399 408 "version": "1.0.1",
400 409 "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
401 410 "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
402 411 "dev": true
403 412 },
404 413 "defined": {
405 414 "version": "1.0.0",
406 415 "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
407 416 "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
408 417 "dev": true
409 418 },
410 419 "minimist": {
411 420 "version": "1.2.0",
412 421 "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
413 422 "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
414 423 "dev": true
415 424 }
416 425 }
417 426 },
418 427 "through": {
419 428 "version": "2.3.8",
420 429 "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
421 430 "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
422 431 "dev": true
423 432 },
424 433 "through2": {
425 434 "version": "0.2.3",
426 435 "resolved": "http://registry.npmjs.org/through2/-/through2-0.2.3.tgz",
427 436 "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=",
428 437 "dev": true,
429 438 "requires": {
430 439 "readable-stream": "~1.1.9",
431 440 "xtend": "~2.1.1"
432 441 }
433 442 },
434 443 "tslib": {
435 444 "version": "1.9.3",
436 445 "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
437 446 "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
438 447 "dev": true
439 448 },
440 449 "typescript": {
441 450 "version": "3.2.2",
442 451 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz",
443 452 "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==",
444 453 "dev": true
445 454 },
446 455 "wrappy": {
447 456 "version": "1.0.2",
448 457 "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
449 458 "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
450 459 "dev": true
451 460 },
452 461 "xtend": {
453 462 "version": "2.1.2",
454 463 "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz",
455 464 "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=",
456 465 "dev": true,
457 466 "requires": {
458 467 "object-keys": "~0.4.0"
459 468 }
460 469 }
461 470 }
462 471 }
@@ -1,35 +1,35
1 1 {
2 2 "name": "@implab/core",
3 3 "version": "0.0.1-dev",
4 4 "description": "Dependency injection, logging, simple and fast text template engine",
5 5 "main": "main.js",
6 6 "keywords": [
7 7 "di",
8 8 "ioc",
9 9 "logging",
10 10 "template engine",
11 11 "dependency injection"
12 12 ],
13 13 "author": "Implab team",
14 14 "license": "BSD-2-Clause",
15 15 "repository": "https://bitbucket.org/implab/implabjs",
16 16 "publishConfig": {
17 17 "access": "public"
18 18 },
19 19 "peerDependencies": {
20 20 "dojo": "^1.10.0",
21 21 "tslib": "latest"
22 22 },
23 23 "devDependencies": {
24 "typescript": "latest",
25 "tape": "latest",
26 "@types/tape": "latest",
24 "@types/node": "latest",
27 25 "@types/requirejs": "latest",
28 "@types/node": "latest",
29 "requirejs": "latest",
26 "@types/tape": "latest",
27 "dojo": "^1.10.0",
30 28 "faucet": "latest",
31 "dojo": "^1.10.0",
32 "tslib": "latest"
29 "requirejs": "latest",
30 "tape": "^4.9.2",
31 "tslib": "latest",
32 "typescript": "latest"
33 33 },
34 34 "types": "main.d.ts"
35 35 }
@@ -1,232 +1,236
1 define(
2 [ "./declare" ],
3 function(declare) {
1 define(["./declare", "./log/trace!"], function (declare, trace) {
2 trace.warn("THIS MODULE IS DEPRECATED! use uri-js or similar alternatives.");
3
4 4 function parseURI(uri) {
5 5 var schema, host, port, path, query, hash, i;
6 6 if (typeof (uri) == "string") {
7 7 if ((i = uri.indexOf(":")) >= 0 &&
8 8 uri.substr(0, i).match(/^\w+$/)) {
9 9 schema = uri.substr(0, i);
10 10 uri = uri.substr(i + 1);
11 11 }
12 12
13 13 if (uri.indexOf("//") === 0) {
14 14 uri = uri.substr(2);
15 15 if ((i = uri.indexOf("/")) >= 0) {
16 16 host = uri.substr(0, i);
17 17 uri = uri.substr(i);
18 18 } else {
19 19 host = uri;
20 20 uri = "";
21 21 }
22 22 }
23 23
24 24 if ((i = uri.indexOf("?")) >= 0) {
25 25 path = uri.substr(0, i);
26 26 uri = uri.substr(i + 1);
27 27
28 28 } else {
29 29 path = uri;
30 30 uri = "";
31 31
32 32 if ((i = path.indexOf("#")) >= 0) {
33 33 hash = path.substr(i + 1);
34 34 path = path.substr(0, i);
35 35 }
36 36 }
37 37
38 38 if ((i = uri.indexOf("#")) >= 0) {
39 39 query = uri.substr(0, i);
40 40 hash = uri.substr(i + 1);
41 41 } else {
42 42 query = uri;
43 43 }
44 44 }
45 45
46 46 if (host && (i = host.lastIndexOf(":")) >= 0) {
47 47 port = host.substr(i + 1);
48 48 host = host.substr(0, i);
49 49 }
50 50
51 51 return {
52 52 schema : schema,
53 53 host : host,
54 54 port : port,
55 55 path : path,
56 56 query : query,
57 57 hash : hash
58 58 };
59 59 }
60 60
61 61 function makeURI(options) {
62 62 var uri = [];
63 63
64 64 if (options.schema)
65 65 uri.push(options.schema, ":");
66 66 if (options.host)
67 67 uri.push("//", options.host);
68 68 if (options.host && options.port)
69 69 uri.push(":", options.port);
70 70
71 71 if (options.path) {
72 72 if (options.host && options.path[0] != "/")
73 73 uri.push("/");
74 74 uri.push(options.path);
75 75 } else if (options.host) {
76 76 uri.push("/");
77 77 }
78 78
79 79 if (options.query)
80 80 uri.push("?", options.query);
81 81 if (options.hash)
82 82 uri.push("#", options.hash);
83 83
84 84 return uri.join("");
85 85 }
86 86
87 87 function reducePath(parts) {
88 var balance = 0, result = [], isRoot;
88 var balance = 0,
89 result = [],
90 isRoot;
89 91
90 92 for (var i = 0; i < parts.length; i++) {
91 93 var part = parts[i];
92 94 switch (part) {
93 95 case "..":
94 96 if (balance > 0) {
95 97 result.pop();
96 98 } else {
97 99 if (isRoot)
98 100 throw new Error("Unbalanced path: " + parts);
99 101
100 102 result.push(part);
101 103 }
102 104 balance--;
103 105 break;
104 106 case ".":
105 107 break;
106 108 case "":
107 109 if (i === 0) {
108 110 isRoot = true;
109 111 result.push(part);
110 112 }
111 113 break;
112 114 default:
113 115 result.push(part);
114 116 balance++;
115 117 break;
116 118 }
117 119 }
118 120
119 121 return result.join("/");
120 122 }
121 123
122 124 var meta = {
123 125 schema : null,
124 126 host : null,
125 127 port : null,
126 128 path : null,
127 129 query : null,
128 130 hash : null
129 131 };
130 132
131 133 var URI = declare(null, {
132 134 constructor : function(opts) {
135 trace.warn("This class is deprecated use uri-js or similar");
133 136 if (typeof (opts) == "string")
134 137 opts = parseURI(opts);
135 138 for ( var p in meta)
136 139 if (p in opts)
137 140 this[p] = opts[p];
138 141 },
139 142
140 143 clone : function() {
141 144 return new URI(this);
142 145 },
143 146
144 147 combine : function(rel) {
145 148 var me = this;
146 149
147 150 if (typeof (rel) === "string")
148 151 rel = new URI(rel);
149 152 else
150 153 rel = rel.clone();
151 154
152 155 // //some.host:123/path?q=a#123
153 156 if (rel.host)
154 157 return rel;
155 158
156 159 // /abs/path?q=a#123
157 160 if (rel.path && rel.path[0] == "/") {
158 161 if (me.host) {
159 162 rel.schema = me.schema;
160 163 rel.host = me.host;
161 164 rel.port = me.port;
162 165 }
163 166 return rel;
164 167 }
165 168
166 169 var base = me.clone();
167 170
168 171 // rel/path?a=b#cd
169 172 if (rel.path) {
170 173 var segments = base.getSegments();
171 174 segments.pop();
172 175 segments.push.apply(segments, rel.getSegments());
173 176
174 177 base.path = reducePath(segments);
175 178 }
176 179
177 180 // ?q=a#123
178 181 if (rel.query)
179 182 base.query = rel.query;
180 183 if (rel.hash)
181 184 base.hase = rel.hash;
182 185
183 186 return base;
184 187 },
185 188
186 189 optimize : function() {
187 190 this.path = reducePath(this.getSegments());
188 191 },
189 192
190 193 getSegments : function() {
191 194 if (typeof (this.path) === "string")
192 195 return this.path.split("/");
193 196 else
194 197 return [];
195 198 },
196 199
197 200 toString : function() {
198 var uri = [], me = this;
201 var uri = [],
202 me = this;
199 203
200 204 if (me.schema)
201 205 uri.push(me.schema, ":");
202 206 if (me.host)
203 207 uri.push("//", me.host);
204 208 if (me.host && me.port)
205 209 uri.push(":", me.port);
206 210
207 211 if (me.path) {
208 212 if (me.host && me.path[0] != "/")
209 213 uri.push("/");
210 214 uri.push(me.path);
211 215 } else if (me.host) {
212 216 uri.push("/");
213 217 }
214 218
215 219 if (me.query)
216 220 uri.push("?", me.query);
217 221 if (me.hash)
218 222 uri.push("#", me.hash);
219 223
220 224 return uri.join("");
221 225 }
222 226
223 227 });
224 228
225 229 URI.combine = function(base, rel) {
226 230 if (typeof (base) === "string")
227 231 base = new URI(base);
228 232 return base.combine(rel).toString();
229 233 };
230 234
231 235 return URI;
232 236 }); No newline at end of file
@@ -1,217 +1,215
1 define(
2 [
1 define([
3 2 "dojo/_base/declare",
4 "dojo/_base/lang",
5 3 "dojo/request",
6 4 "./Destination",
7 5 "dojo/Evented",
8 6 "dojo/Deferred",
9 "../log/_LogMixin" ],
10
11 function(declare, lang, request, Destination, Evented, Deferred, _LogMixin) {
7 "../log/_LogMixin"
8 ], function (declare, request, Destination, Evented, Deferred, _LogMixin) {
12 9
13 10 var cls = declare(
14 [ Evented, _LogMixin ],
15 {
11 [Evented, _LogMixin], {
16 12 _id : null,
17 13 _baseUrl : null,
18 14 _destinations : null,
19 15 _timeout : 100000,
20 16 _clients : null,
21 17 _started : null,
22 18 _starting : false,
23 19
24 20 constructor : function(baseUrl, options) {
25 21 if (!baseUrl)
26 22 throw new Error("baseUrl is required");
27 23 options = options || {};
28 24
29 25 this._baseUrl = baseUrl.replace(/\/*$/, "");
30 26 this._destinations = {};
31 27 this._pending = [];
32 28 this._clients = {};
33 29 if (options.timeout)
34 30 this._timeout = options.timeout;
35 31
36 32 this._started = new Deferred();
37 33 },
38 34
39 35 start : function() {
40 36 if (this._starting)
41 37 return this._started;
42 38 this._starting = true;
43 39
44 40 var me = this;
45 41 me.log("START");
46 42 request(this._baseUrl, {
47 43 method : "POST",
48 44 handleAs : "json"
49 45 }).then(function(result) {
50 46 me._id = result;
51 47 me._emitConnected();
52 48 me._poll();
53 49 me._started.resolve(me);
54 50 }, function(error) {
55 51 me._emitError(error);
56 52 me._started.reject(me);
57 53 });
58 54 return me._started.promise;
59 55 },
60 56
61 57 createClient : function(options) {
62 58 if (!options || !options.destination || !options.mode)
63 59 throw new Error("Invalid argument");
64 60
65 61 var me = this;
66 62
67 63 return me._started
68 64 .then(function() {
69 65 var url = me._makeUrl(me._id);
70 66 me.log(
71 67 "CREATE mode=${0}, destination=${1}",
72 68 options.mode,
73 69 options.destination);
74 70
75 71 return request(url, {
76 72 method : "POST",
77 73 data : {
78 74 mode : options.mode,
79 75 destination : options.destination
80 76 },
81 77 handleAs : 'json'
82 78 })
83 79 .then(
84 80 function(id) {
85 81 me
86 82 .log(
87 83 "CLIENT id=${0}, mode=${1}, destination=${2}",
88 84 id,
89 85 options.mode,
90 86 options.destination);
91 me._clients[id] = options.client
92 ? options.client
93 : function(msg) {
87 me._clients[id] = options.client ?
88 options.client :
89 function (msg) {
94 90 me
95 91 .warn(
96 92 "The client id=${0}, mode=${1}, destination=${2} isn't accepting mesages",
97 93 id,
98 94 options.mode,
99 95 options.destination);
100 96 };
101 97 return id;
102 98 });
103 99 });
104 100
105 101 },
106 102
107 103 deleteClient : function(options) {
108 104 if (!options || !options.clientId)
109 105 throw new Error("Invalid argument");
110 106
111 var me = this, id = options.clientId;
107 var me = this,
108 id = options.clientId;
112 109
113 110 return me._started.then(function() {
114 111 var url = me._makeUrl(me._id, options.clientId);
115 112
116 113 me.log("DELETE CLIENT ${0}", options.clientId);
117 114
118 115 return request(url, {
119 116 method : "DELETE",
120 117 handleAs : 'json'
121 118 }).then(function() {
122 119 me.log("CLIENT DELETED ${0}", options.clientId);
123 120 me._clients[id] = undefined;
124 121 });
125 122 });
126 123 },
127 124
128 125 _poll : function() {
129 var me = this, url = this._makeUrl(this._id);
126 var me = this,
127 url = this._makeUrl(this._id);
130 128 me.log("POLL timeout=${0}", me._timeout);
131 129 request(url, {
132 130 method : "GET",
133 131 handleAs : "json",
134 132 query : {
135 133 timeout : me._timeout
136 134 }
137 135 }).then(function(response) {
138 136 me._handlePoll(response);
139 137 me._poll();
140 138 }, function(err) {
141 139 me.error("POLL faield with ${0}", err);
142 140 me._emitError(err);
143 141 });
144 142 },
145 143
146 144 _handlePoll : function(response) {
147 145 if (!response) {
148 146 this.log("POLL response undefined, looks like a bug");
149 147 return;
150 148 }
151 149 if (!response.results || !response.results.length) {
152 150 this.log("POLL response is empty");
153 151 return;
154 152 }
155 153
156 154 var results = response.results;
157 155 this.log("POLL got ${0} results", results.length);
158 156
159 157 for (var i = 0; i < results.length; i++) {
160 158 var result = results[i];
161 159 var client = this._clients[result.clientId];
162 160 if (!client) {
163 161 // TODO this could happen due to client isn't
164 162 // registered yet
165 163 this.error("Unknown client ${0}", result.clientId);
166 164 continue;
167 165 }
168 166 client.call(this, result);
169 167 }
170 168 },
171 169
172 170 _emitError : function(err) {
173 171 this.emit("error", err);
174 172 },
175 173
176 174 _emitConnected : function() {
177 175 var me = this;
178 176 me.log("CONNECTED");
179 177 me.emit("connected");
180 178 },
181 179
182 180 _makeUrl : function() {
183 181 var parts = [ this._baseUrl ];
184 182 for (var i = 0; i < arguments.length; i++)
185 183 parts.push(arguments[i].replace(/\/*$/, ""));
186 184 return parts.join('/');
187 185 },
188 186
189 187 queue : function(name) {
190 188 return this._getDestination("queue://" + name);
191 189 },
192 190
193 191 topic : function(name) {
194 192 return this._getDestination("topic://" + name);
195 193 },
196 194
197 195 _getDestination : function(uri) {
198 196 if (uri in this._destinations)
199 197 return this._destinations[uri];
200 198
201 199 var dest = new Destination(this, uri);
202 200 this._destinations[uri] = dest;
203 201 return dest;
204 202 },
205 203
206 204 toString : function() {
207 205 return [ "[", "SESSION ", this._id, "]" ].join(" ");
208 206 }
209 207 });
210 208
211 209 cls.connect = function(url, options) {
212 210 var session = new cls(url, options);
213 211 return session.start();
214 212 };
215 213
216 214 return cls;
217 });
215 }); No newline at end of file
@@ -1,11 +1,16
1 1 {
2 2 "extends": "../tsconfig",
3 3 "compilerOptions": {
4 4 "types": [
5 "@types/node"
5 "node"
6 ],
7 "rootDir": "ts",
8 "rootDirs": [
9 "ts",
10 "../typings/main"
6 11 ]
7 12 },
8 13 "include": [
9 14 "ts/**/*.ts"
10 15 ]
11 16 } No newline at end of file
@@ -1,40 +1,40
1 1 import { Cancellation } from "../Cancellation";
2 2 import { IAsyncComponent, ICancellation, ICancellable, IDestroyable } from "../interfaces";
3 3 import { destroy } from "../safe";
4 4
5 5 export class AsyncComponent implements IAsyncComponent, ICancellable {
6 _cancel: (e) => void;
6 _cancel: (e: any) => void;
7 7
8 8 _completion: Promise<void> = Promise.resolve();
9 9
10 10 getCompletion() { return this._completion; }
11 11
12 12 runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) {
13 13 // create inner cancellation bound to the passed cancellation token
14 14 let h: IDestroyable;
15 15 const inner = new Cancellation(cancel => {
16 16
17 17 this._cancel = cancel;
18 18 h = ct.register(cancel);
19 19 });
20 20
21 21 // TODO create cancellation source here
22 22 const guard = async () => {
23 23 try {
24 24 await op(inner);
25 25 } finally {
26 26 // after the operation is complete we need to cleanup the
27 27 // resources
28 28 destroy(h);
29 29 this._cancel = null;
30 30 }
31 31 };
32 32
33 33 return this._completion = guard();
34 34 }
35 35
36 cancel(reason) {
36 cancel(reason: any) {
37 37 if (this._cancel)
38 38 this._cancel(reason);
39 39 }
40 40 }
@@ -0,0 +1,1
1 export { Container } from "./di/Container";
@@ -1,12 +1,12
1 1 export class ConfigError extends Error {
2 inner;
2 inner: any;
3 3
4 4 path: string;
5 5
6 6 configName: string;
7 7
8 constructor(message: string, inner?) {
8 constructor(message: string, inner?: any) {
9 9 super(message);
10 10 this.inner = inner;
11 11 }
12 12 }
@@ -1,348 +1,365
1 1 import {
2 2 ServiceRegistration,
3 3 TypeRegistration,
4 4 FactoryRegistration,
5 5 ServiceMap,
6 6 isDescriptor,
7 7 isDependencyRegistration,
8 8 DependencyRegistration,
9 9 ValueRegistration,
10 10 ActivationType,
11 11 isValueRegistration,
12 12 isTypeRegistration,
13 13 isFactoryRegistration
14 14 } from "./interfaces";
15 15
16 16 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get } from "../safe";
17 17 import { AggregateDescriptor } from "./AggregateDescriptor";
18 18 import { ValueDescriptor } from "./ValueDescriptor";
19 19 import { Container } from "./Container";
20 20 import { ReferenceDescriptor } from "./ReferenceDescriptor";
21 21 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
22 22 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
23 23 import { TraceSource } from "../log/TraceSource";
24 24 import { ConfigError } from "./ConfigError";
25 25 import { Cancellation } from "../Cancellation";
26 import { makeResolver } from "./ResolverHelper";
27 import { ICancellation } from "../interfaces";
26 28
27 29 const trace = TraceSource.get("@implab/core/di/Configuration");
28 30
29 31 declare const define;
30 32 declare const require;
33 declare const module;
31 34
32 35 function hasAmdLoader() {
36 try {
37 // es6 may throw the exception
33 38 return (typeof define === "function" && define.amd);
39 } catch {
40 return false;
41 }
42 }
43
44 function hasNodeJs() {
45 try {
46 return (typeof module !== "undefined" && module.exports);
47 } catch {
48 return false;
49 }
34 50 }
35 51
36 52 async function mapAll(data: object | any[], map?: (v, k) => any): Promise<any> {
37 53 if (data instanceof Array) {
38 54 return Promise.all(map ? data.map(map) : data);
39 55 } else {
40 56 const keys = Object.keys(data);
41 57
42 58 const o: any = {};
43 59
44 60 await Promise.all(keys.map(async k => {
45 61 const v = map ? map(data[k], k) : data[k];
46 62 o[k] = isPromise(v) ? await v : v;
47 63 }));
48 64
49 65 return o;
50 66 }
51 67 }
52 68
53 type Resolver = (qname: string) => any;
69 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
54 70
55 71 type _key = string | number;
56 72
57 73 export class Configuration {
58 74
59 75 _hasInnerDescriptors = false;
60 76
61 77 _container: Container;
62 78
63 79 _path: Array<_key>;
64 80
65 81 _configName: string;
66 82
67 _require: Resolver;
83 _require: ModuleResolver;
68 84
69 85 constructor(container: Container) {
70 86 argumentNotNull(container, container);
71 87 this._container = container;
72 88 this._path = [];
73 89 }
74 90
75 async loadConfiguration(moduleName: string, ct = Cancellation.none) {
91 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
76 92 argumentNotEmptyString(moduleName, "moduleName");
77 // TODO remove the code below somewehere else
78 if (hasAmdLoader()) {
79 // if we have a requirejs loader, use it directly
80 // don't rely on typescript 'import' function
81 const m = await new Promise<any>(cb => require(["./RequireJsHelper"], cb));
82 const r = m.makeResolver(require);
83 const config = await r(moduleName);
93
94 trace.log("loadConfiguration moduleName={0}", moduleName);
95
96 this._configName = moduleName;
97
98 const r = makeResolver(null, contextRequire);
84 99
85 return this.applyConfiguration(
100 const config = await r(moduleName, ct);
101
102 await this._applyConfiguration(
86 103 config,
87 m.makeResolver(await m.createContextRequire(moduleName))
104 makeResolver(moduleName, contextRequire),
105 ct
88 106 );
89 } else {
90 throw new Error("This feature is supported only with the amd loader");
91 }
92 107 }
93 108
94 async applyConfiguration(data: object, resolver?: Resolver, ct = Cancellation.none) {
109 applyConfiguration(data: object, contextRequire?: any, ct = Cancellation.none) {
95 110 argumentNotNull(data, "data");
96 111
112 return this._applyConfiguration(data, makeResolver(void(0), contextRequire), ct);
113 }
114
115 async _applyConfiguration(data: object, resolver?: ModuleResolver, ct = Cancellation.none) {
97 116 trace.log("applyConfiguration");
98 117
99 118 this._configName = "$";
100 119
101 120 if (resolver)
102 121 this._require = resolver;
103 122
104 123 let services: ServiceMap;
105 124
106 125 try {
107 126 services = await this._visitRegistrations(data, "$");
108 127 } catch (e) {
109 128 throw this._makeError(e);
110 129 }
111 130
112 131 this._container.register(services);
113 132 }
114 133
115 134 _makeError(inner) {
116 135 const e = new ConfigError("Failed to load configuration", inner);
117 136 e.configName = this._configName;
118 137 e.path = this._makePath();
119 138 return e;
120 139 }
121 140
122 141 _makePath() {
123 142 return this._path
124 143 .reduce(
125 144 (prev, cur) => typeof cur === "number" ?
126 145 `${prev}[${cur}]` :
127 146 `${prev}.${cur}`
128 147 )
129 148 .toString();
130 149 }
131 150
132 151 async _resolveType(moduleName: string, localName: string) {
133 152 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
134 153 try {
135 154 const m = await this._loadModule(moduleName);
136 155 return localName ? get(localName, m) : m;
137 156 } catch (e) {
138 157 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
139 158 throw e;
140 159 }
141 160 }
142 161
143 async _loadModule(moduleName: string) {
162 _loadModule(moduleName: string) {
144 163 trace.debug("loadModule {0}", moduleName);
145 164
146 const m = await this._require(moduleName);
147
148 return m;
165 return this._require(moduleName);
149 166 }
150 167
151 168 async _visitRegistrations(data, name: _key) {
152 169 this._enter(name);
153 170
154 171 if (data.constructor &&
155 172 data.constructor.prototype !== Object.prototype)
156 173 throw new Error("Configuration must be a simple object");
157 174
158 175 const o: ServiceMap = {};
159 176 const keys = Object.keys(data);
160 177
161 178 const services = await mapAll(data, async (v, k) => {
162 179 const d = await this._visit(v, k);
163 180 return isDescriptor(d) ? d : new AggregateDescriptor(d);
164 181 }) as ServiceMap;
165 182
166 183 this._leave();
167 184
168 185 return services;
169 186 }
170 187
171 188 _enter(name: _key) {
172 189 this._path.push(name);
173 190 trace.debug(">{0}", name);
174 191 }
175 192
176 193 _leave() {
177 194 const name = this._path.pop();
178 195 trace.debug("<{0}", name);
179 196 }
180 197
181 async _visit(data, name: string): Promise<any> {
198 _visit(data, name: string): Promise<any> {
182 199 if (isPrimitive(data) || isDescriptor(data))
183 200 return data;
184 201
185 202 if (isDependencyRegistration(data)) {
186 203 return this._visitDependencyRegistration(data, name);
187 204 } else if (isValueRegistration(data)) {
188 205 return this._visitValueRegistration(data, name);
189 206 } else if (isTypeRegistration(data)) {
190 207 return this._visitTypeRegistration(data, name);
191 208 } else if (isFactoryRegistration(data)) {
192 209 return this._visitFactoryRegistration(data, name);
193 210 } else if (data instanceof Array) {
194 211 return this._visitArray(data, name);
195 212 }
196 213
197 214 return this._visitObject(data, name);
198 215 }
199 216
200 217 async _visitObject(data: object, name: _key) {
201 218 if (data.constructor &&
202 219 data.constructor.prototype !== Object.prototype)
203 220 return new ValueDescriptor(data);
204 221
205 222 this._enter(name);
206 223
207 224 const v = await mapAll(data, delegate(this, "_visit"));
208 225
209 226 // TODO: handle inline descriptors properly
210 227 // const ex = {
211 228 // activate(ctx) {
212 229 // const value = ctx.activate(this.prop, "prop");
213 230 // // some code
214 231 // },
215 232 // // will be turned to ReferenceDescriptor
216 233 // prop: { $dependency: "depName" }
217 234 // };
218 235
219 236 this._leave();
220 237 return v;
221 238 }
222 239
223 240 async _visitArray(data: any[], name: _key) {
224 241 if (data.constructor &&
225 242 data.constructor.prototype !== Array.prototype)
226 243 return new ValueDescriptor(data);
227 244
228 245 this._enter(name);
229 246
230 247 const v = await mapAll(data, delegate(this, "_visit"));
231 248 this._leave();
232 249
233 250 return v;
234 251 }
235 252
236 253 _makeServiceParams(data: ServiceRegistration) {
237 254 const opts: any = {
238 255 owner: this._container
239 256 };
240 257 if (data.services)
241 258 opts.services = this._visitRegistrations(data.services, "services");
242 259
243 260 if (data.inject) {
244 261 this._path.push("inject");
245 262 opts.inject = mapAll(
246 263 data.inject instanceof Array ?
247 264 data.inject :
248 265 [data.inject],
249 266 delegate(this, "_visitObject")
250 267 );
251 268 this._leave();
252 269 }
253 270
254 271 if ("params" in data)
255 272 opts.params = data.params instanceof Array ?
256 273 this._visitArray(data.params, "params") :
257 274 this._visit(data.params, "params");
258 275
259 276 if (data.activation) {
260 277 if (typeof (data.activation) === "string") {
261 278 switch (data.activation.toLowerCase()) {
262 279 case "singleton":
263 280 opts.activation = ActivationType.Singleton;
264 281 break;
265 282 case "container":
266 283 opts.activation = ActivationType.Container;
267 284 break;
268 285 case "hierarchy":
269 286 opts.activation = ActivationType.Hierarchy;
270 287 break;
271 288 case "context":
272 289 opts.activation = ActivationType.Context;
273 290 break;
274 291 case "call":
275 292 opts.activation = ActivationType.Call;
276 293 break;
277 294 default:
278 295 throw new Error("Unknown activation type: " +
279 296 data.activation);
280 297 }
281 298 } else {
282 299 opts.activation = Number(data.activation);
283 300 }
284 301 }
285 302
286 303 if (data.cleanup)
287 304 opts.cleanup = data.cleanup;
288 305
289 306 return opts;
290 307 }
291 308
292 309 async _visitValueRegistration(data: ValueRegistration, name: _key) {
293 310 this._enter(name);
294 311 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
295 312 this._leave();
296 313 return d;
297 314 }
298 315
299 316 async _visitDependencyRegistration(data: DependencyRegistration, name: _key) {
300 317 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
301 318 this._enter(name);
302 319 const d = new ReferenceDescriptor({
303 320 name: data.$dependency,
304 321 lazy: data.lazy,
305 322 optional: data.optional,
306 323 default: data.default,
307 324 services: data.services && await this._visitRegistrations(data.services, "services")
308 325 });
309 326 this._leave();
310 327 return d;
311 328 }
312 329
313 330 async _visitTypeRegistration(data: TypeRegistration, name: _key) {
314 331 argumentNotNull(data.$type, "data.$type");
315 332 this._enter(name);
316 333
317 334 const opts = this._makeServiceParams(data);
318 335 if (data.$type instanceof Function) {
319 336 opts.type = data.$type;
320 337 } else {
321 338 const [moduleName, typeName] = data.$type.split(":", 2);
322 339 opts.type = this._resolveType(moduleName, typeName);
323 340 }
324 341
325 342 const d = new TypeServiceDescriptor(
326 343 await mapAll(opts)
327 344 );
328 345
329 346 this._leave();
330 347
331 348 return d;
332 349 }
333 350
334 351 async _visitFactoryRegistration(data: FactoryRegistration, name: _key) {
335 352 argumentOfType(data.$factory, Function, "data.$type");
336 353 this._enter(name);
337 354
338 355 const opts = this._makeServiceParams(data);
339 356 opts.factory = opts.$factory;
340 357
341 358 const d = new FactoryServiceDescriptor(
342 359 await mapAll(opts)
343 360 );
344 361
345 362 this._leave();
346 363 return d;
347 364 }
348 365 }
@@ -1,128 +1,128
1 1 import { ActivationContext } from "./ActivationContext";
2 2 import { ValueDescriptor } from "./ValueDescriptor";
3 3 import { ActivationError } from "./ActivationError";
4 4 import { isDescriptor, ServiceMap } from "./interfaces";
5 5 import { TraceSource } from "../log/TraceSource";
6 6 import { Configuration } from "./Configuration";
7 7 import { Cancellation } from "../Cancellation";
8 8
9 9 const trace = TraceSource.get("@implab/core/di/ActivationContext");
10 10
11 11 export class Container {
12 12 _services: ServiceMap;
13 13
14 14 _cache: object;
15 15
16 16 _cleanup: (() => void)[];
17 17
18 18 _root: Container;
19 19
20 20 _parent: Container;
21 21
22 22 constructor(parent?: Container) {
23 23 this._parent = parent;
24 24 this._services = parent ? Object.create(parent._services) : {};
25 25 this._cache = {};
26 26 this._cleanup = [];
27 27 this._root = parent ? parent.getRootContainer() : this;
28 28 this._services.container = new ValueDescriptor(this);
29 29 }
30 30
31 31 getRootContainer() {
32 32 return this._root;
33 33 }
34 34
35 35 getParent() {
36 36 return this._parent;
37 37 }
38 38
39 39 resolve(name: string, def?) {
40 40 trace.debug("resolve {0}", name);
41 41 const d = this._services[name];
42 42 if (d === undefined) {
43 43 if (arguments.length > 1)
44 44 return def;
45 45 else
46 46 throw new Error("Service '" + name + "' isn't found");
47 47 }
48 48
49 49 const context = new ActivationContext(this, this._services);
50 50 try {
51 51 return context.activate(d, name);
52 52 } catch (error) {
53 53 throw new ActivationError(name, context.getStack(), error);
54 54 }
55 55 }
56 56
57 57 /**
58 58 * @deprecated use resolve() method
59 59 */
60 60 getService() {
61 61 return this.resolve.apply(this, arguments);
62 62 }
63 63
64 64 register(nameOrCollection, service?) {
65 65 if (arguments.length === 1) {
66 66 const data = nameOrCollection;
67 67 for (const name in data)
68 68 this.register(name, data[name]);
69 69 } else {
70 70 if (!isDescriptor(service))
71 71 throw new Error("The service parameter must be a descriptor");
72 72
73 73 this._services[nameOrCollection] = service;
74 74 }
75 75 return this;
76 76 }
77 77
78 78 onDispose(callback) {
79 79 if (!(callback instanceof Function))
80 80 throw new Error("The callback must be a function");
81 81 this._cleanup.push(callback);
82 82 }
83 83
84 84 dispose() {
85 85 if (this._cleanup) {
86 86 for (const f of this._cleanup)
87 87 f();
88 88 this._cleanup = null;
89 89 }
90 90 }
91 91
92 92 /**
93 93 * @param{String|Object} config
94 94 * The configuration of the contaier. Can be either a string or an object,
95 95 * if the configuration is an object it's treated as a collection of
96 96 * services which will be registed in the contaier.
97 97 *
98 98 * @param{Function} opts.contextRequire
99 99 * The function which will be used to load a configuration or types for services.
100 100 *
101 101 */
102 async configure(config: string | object, opts?, ct = Cancellation.none) {
102 async configure(config: string | object, opts?: any, ct = Cancellation.none) {
103 103 const c = new Configuration(this);
104 104
105 105 if (typeof (config) === "string") {
106 return c.loadConfiguration(config, ct);
106 return c.loadConfiguration(config, opts && opts.contextRequire, ct);
107 107 } else {
108 108 return c.applyConfiguration(config, opts && opts.contextRequire, ct);
109 109 }
110 110 }
111 111
112 112 createChildContainer() {
113 113 return new Container(this);
114 114 }
115 115
116 116 has(id) {
117 117 return id in this._cache;
118 118 }
119 119
120 120 get(id) {
121 121 return this._cache[id];
122 122 }
123 123
124 124 store(id, value) {
125 125 return (this._cache[id] = value);
126 126 }
127 127
128 128 }
@@ -1,84 +1,86
1 1 export type Constructor<T = {}> = new (...args: any[]) => T;
2 2
3 3 export type Factory<T = {}> = (...args: any[]) => T;
4 4
5 export type Predicate<T = any> = (x: T) => boolean;
6
5 7 export interface MapOf<T> {
6 8 [key: string]: T;
7 9 }
8 10
9 11 export interface IDestroyable {
10 12 destroy();
11 13 }
12 14
13 15 export interface ICancellation {
14 16 throwIfRequested(): void;
15 17 isRequested(): boolean;
16 18 isSupported(): boolean;
17 19 register(cb: (e: any) => void): IDestroyable;
18 20 }
19 21
20 22 /**
21 23 * Интерфейс поддерживающий асинхронную активацию
22 24 */
23 25 export interface IActivatable {
24 26 /**
25 27 * @returns Boolean indicates the current state
26 28 */
27 29 isActive(): boolean;
28 30
29 31 /**
30 32 * Starts the component activation
31 33 * @param ct cancellation token for this operation
32 34 */
33 35 activate(ct?: ICancellation): Promise<void>;
34 36
35 37 /**
36 38 * Starts the component deactivation
37 39 * @param ct cancellation token for this operation
38 40 */
39 41 deactivate(ct?: ICancellation): Promise<void>;
40 42
41 43 /**
42 44 * Sets the activation controller for this component
43 45 * @param controller The activation controller
44 46 *
45 47 * Activation controller checks whether this component
46 48 * can be activated and manages the active state of the
47 49 * component
48 50 */
49 51 setActivationController(controller: IActivationController);
50 52
51 53 /**
52 54 * Gets the current activation controller for this component
53 55 */
54 56 getActivationController(): IActivationController;
55 57 }
56 58
57 59 export interface IActivationController {
58 60 activating(component: IActivatable, ct?: ICancellation): Promise<void>;
59 61
60 62 activated(component: IActivatable, ct?: ICancellation): Promise<void>;
61 63
62 64 deactivating(component: IActivatable, ct?: ICancellation): Promise<void>;
63 65
64 66 deactivated(component: IActivatable, ct?: ICancellation): Promise<void>;
65 67
66 68 deactivate(ct?: ICancellation): Promise<void>;
67 69
68 70 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
69 71
70 72 getActive(): IActivatable;
71 73 }
72 74
73 75 export interface IAsyncComponent {
74 76 getCompletion(): Promise<void>;
75 77 }
76 78
77 79 export interface ICancellable {
78 80 cancel(reason?: any): void;
79 81 }
80 82
81 83 export interface IObservable<T> {
82 84 on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable;
83 85 next(ct?: ICancellation): Promise<T>;
84 86 }
@@ -1,128 +1,128
1 1 import { Observable } from "../Observable";
2 2 import { Registry } from "./Registry";
3 3 import { format } from "../text/StringFormat";
4 4
5 5 export const DebugLevel = 400;
6 6
7 7 export const LogLevel = 300;
8 8
9 9 export const WarnLevel = 200;
10 10
11 11 export const ErrorLevel = 100;
12 12
13 13 export const SilentLevel = 0;
14 14
15 15 export interface TraceEvent {
16 16 readonly source: TraceSource;
17 17
18 18 readonly level: number;
19 19
20 20 readonly arg: any;
21 21 }
22 22
23 23 export class TraceSource {
24 24 readonly id: any;
25 25
26 26 level: number;
27 27
28 28 readonly events: Observable<TraceEvent>;
29 29
30 30 _notifyNext: (arg: TraceEvent) => void;
31 31
32 32 constructor(id: any) {
33 33
34 34 this.id = id || new Object();
35 35 this.events = new Observable(next => {
36 36 this._notifyNext = next;
37 37 });
38 38 }
39 39
40 40 protected emit(level: number, arg: any) {
41 41 this._notifyNext({ source: this, level, arg });
42 42 }
43 43
44 44 isDebugEnabled() {
45 45 return this.level >= DebugLevel;
46 46 }
47 47
48 48 debug(msg: string, ...args: any[]) {
49 49 if (this.isEnabled(DebugLevel))
50 this.emit(DebugLevel, format(msg, args));
50 this.emit(DebugLevel, format.apply(null, arguments));
51 51 }
52 52
53 53 isLogEnabled() {
54 54 return this.level >= LogLevel;
55 55 }
56 56
57 57 log(msg: string, ...args: any[]) {
58 58 if (this.isEnabled(LogLevel))
59 this.emit(LogLevel, format(msg, args));
59 this.emit(LogLevel, format.apply(null, arguments));
60 60 }
61 61
62 62 isWarnEnabled() {
63 63 return this.level >= WarnLevel;
64 64 }
65 65
66 66 warn(msg: string, ...args: any[]) {
67 67 if (this.isEnabled(WarnLevel))
68 this.emit(WarnLevel, format(msg, args));
68 this.emit(WarnLevel, format.apply(null, arguments));
69 69 }
70 70
71 71 /**
72 72 * returns true if errors will be recorded.
73 73 */
74 74 isErrorEnabled() {
75 75 return this.level >= ErrorLevel;
76 76 }
77 77
78 78 /**
79 79 * Traces a error.
80 80 *
81 81 * @param msg the message.
82 82 * @param args parameters which will be substituted in the message.
83 83 */
84 84 error(msg: string, ...args: any[]) {
85 85 if (this.isEnabled(ErrorLevel))
86 86 this.emit(ErrorLevel, format.apply(null, arguments));
87 87 }
88 88
89 89 /**
90 90 * Checks whether the specified level is enabled for this
91 91 * trace source.
92 92 *
93 93 * @param level the trace level which should be checked.
94 94 */
95 95 isEnabled(level: number) {
96 96 return (this.level >= level);
97 97 }
98 98
99 99 /**
100 100 * Traces a raw event, passing data as it is to the underlying listeners
101 101 *
102 102 * @param level the level of the event
103 103 * @param arg the data of the event, can be a simple string or any object.
104 104 */
105 105 traceEvent(level: number, arg: any) {
106 106 if (this.isEnabled(level))
107 107 this.emit(level, arg);
108 108 }
109 109
110 110 /**
111 111 * Register the specified handler to be called for every new and already
112 112 * created trace source.
113 113 *
114 114 * @param handler the handler which will be called for each trace source
115 115 */
116 116 static on(handler: (source: TraceSource) => void) {
117 117 return Registry.instance.on(handler);
118 118 }
119 119
120 120 /**
121 121 * Creates or returns already created trace source for the specified id.
122 122 *
123 123 * @param id the id for the trace source
124 124 */
125 125 static get(id: any) {
126 126 return Registry.instance.get(id);
127 127 }
128 128 }
@@ -1,36 +1,49
1 1 import { IObservable, IDestroyable, ICancellation } from "../../interfaces";
2 2 import { TraceEvent, LogLevel, WarnLevel, DebugLevel } from "../TraceSource";
3 3 import { Cancellation } from "../../Cancellation";
4 4 import { destroy } from "../../safe";
5 5
6 function hasConsole() {
7 try {
8 // tslint:disable-next-line:no-console
9 return (typeof console !== "undefined" && typeof console.log === "function");
10 } catch {
11 return false;
12 }
13 }
14
6 15 export class ConsoleWriter implements IDestroyable {
7 16 readonly _subscriptions = new Array<IDestroyable>();
8 17
9 18 writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) {
10 19 const subscription = source.on(this.writeEvent.bind(this));
11 20 if (ct.isSupported()) {
12 21 ct.register(subscription.destroy.bind(subscription));
13 22 }
14 23 this._subscriptions.push(subscription);
15 24 }
16 25
17 26 writeEvent(next: TraceEvent) {
27 // IE will create console only when devepoler tools are activated
28 if (!hasConsole())
29 return;
30
18 31 if (next.level >= DebugLevel) {
19 // tslint:disable-next-line
32 // tslint:disable-next-line:no-console
20 33 console.debug(next.source.id.toString(), next.arg);
21 34 } else if (next.level >= LogLevel) {
22 // tslint:disable-next-line
35 // tslint:disable-next-line:no-console
23 36 console.log(next.source.id.toString(), next.arg);
24 37 } else if (next.level >= WarnLevel) {
25 // tslint:disable-next-line
38 // tslint:disable-next-line:no-console
26 39 console.warn(next.source.id.toString(), next.arg);
27 40 } else {
28 // tslint:disable-next-line
41 // tslint:disable-next-line:no-console
29 42 console.error(next.source.id.toString(), next.arg);
30 43 }
31 44 }
32 45
33 46 destroy() {
34 47 this._subscriptions.forEach(destroy);
35 48 }
36 49 }
@@ -1,94 +1,93
1 import { test, TapeWriter } from "./TestTraits";
1 import { test } from "./TestTraits";
2 2 import { Container } from "@implab/core/di/Container";
3 3 import { ReferenceDescriptor } from "@implab/core/di/ReferenceDescriptor";
4 4 import { AggregateDescriptor } from "@implab/core/di/AggregateDescriptor";
5 5 import { ValueDescriptor } from "@implab/core/di/ValueDescriptor";
6 import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource";
7 6 import { Foo } from "./mock/Foo";
8 7 import { Bar } from "./mock/Bar";
9 8 import { isNull } from "@implab/core/safe";
10 9
11 10 test("Container register/resolve tests", async t => {
12 11 const container = new Container();
13 12
14 13 const connection1 = "db://localhost";
15 14
16 15 t.throws(
17 16 () => container.register("bla-bla", "bla-bla"),
18 17 "Do not allow to register anything other than descriptors"
19 18 );
20 19
21 20 t.doesNotThrow(
22 21 () => container.register("connection", new ValueDescriptor(connection1)),
23 22 "register ValueDescriptor"
24 23 );
25 24
26 25 t.equals(container.resolve("connection"), connection1, "resolve string value");
27 26
28 27 t.doesNotThrow(
29 28 () => container.register(
30 29 "dbParams",
31 30 new AggregateDescriptor({
32 31 timeout: 10,
33 32 connection: new ReferenceDescriptor({ name: "connection" })
34 33 })
35 34 ),
36 35 "register AggregateDescriptor"
37 36 );
38 37
39 38 const dbParams = container.resolve("dbParams");
40 39 t.equals(dbParams.connection, connection1, "should get string value 'dbParams.connection'");
41 40 });
42 41
43 42 test("Container configure/resolve tests", async t => {
44 43
45 44 const container = new Container();
46 45
47 46 await container.configure({
48 47 foo: {
49 48 $type: Foo
50 49 },
51 50
52 51 box: {
53 52 $type: Bar,
54 53 params: {
55 54 $dependency: "foo"
56 55 }
57 56 },
58 57
59 58 bar: {
60 59 $type: Bar,
61 60 params: {
62 61 db: {
63 62 provider: {
64 63 $dependency: "db"
65 64 }
66 65 }
67 66 }
68 67 }
69 68 });
70 69 t.pass("should configure from js object");
71 70
72 71 const f1 = container.resolve("foo");
73 72
74 73 t.assert(!isNull(f1), "foo should be not null");
75 74
76 75 t.throws(() => container.resolve("bar"), "should not resolve dependency 'db'");
77 76
78 77 });
79 78
80 79 test("Load configuration from module", async t => {
81 80 const container = new Container();
82 81
83 82 await container.configure("test/mock/config1");
84 83 t.pass("The configuration should load");
85 84
86 85 const f1 = container.resolve("foo");
87 86
88 87 t.assert(!isNull(f1), "foo should be not null");
89 88
90 89 const b1 = container.resolve("bar") as Bar;
91 90
92 91 t.assert(!isNull(b1), "bar should not be null");
93 92 t.assert(!isNull(b1.foo), "bar.foo should not be null");
94 93 });
@@ -1,89 +1,88
1 1 import { IObservable, ICancellation, IDestroyable } from "@implab/core/interfaces";
2 2 import { Cancellation } from "@implab/core/Cancellation";
3 3 import { TraceEvent, LogLevel, WarnLevel, DebugLevel, TraceSource } from "@implab/core/log/TraceSource";
4 4 import * as tape from "tape";
5 import { argumentNotNull } from "@implab/core/safe";
5 import { argumentNotNull, destroy } from "@implab/core/safe";
6 6
7 7 export class TapeWriter implements IDestroyable {
8 8 readonly _tape: tape.Test;
9 9
10 10 _subscriptions = new Array<IDestroyable>();
11 11
12 12 constructor(t: tape.Test) {
13 13 argumentNotNull(t, "tape");
14 14 this._tape = t;
15 15 }
16 16
17 17 writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) {
18 18 const subscription = source.on(this.writeEvent.bind(this));
19 19 if (ct.isSupported()) {
20 20 ct.register(subscription.destroy.bind(subscription));
21 21 }
22 22 this._subscriptions.push(subscription);
23 23 }
24 24
25 25 writeEvent(next: TraceEvent) {
26 26 if (next.level >= DebugLevel) {
27 27 this._tape.comment(`DEBUG ${next.source.id} ${next.arg}`);
28 28 } else if (next.level >= LogLevel) {
29 29 this._tape.comment(`LOG ${next.source.id} ${next.arg}`);
30 30 } else if (next.level >= WarnLevel) {
31 31 this._tape.comment(`WARN ${next.source.id} ${next.arg}`);
32 32 } else {
33 33 this._tape.comment(`ERROR ${next.source.id} ${next.arg}`);
34 34 }
35 35 }
36 36
37 37 destroy() {
38 this._subscriptions.forEach(x => x.destroy());
38 this._subscriptions.forEach(destroy);
39 39 }
40 40 }
41 41
42 42 export async function delay(timeout: number, ct: ICancellation = Cancellation.none) {
43 43 let un: IDestroyable;
44 44
45 45 try {
46 46 await new Promise((resolve, reject) => {
47 47 if (ct.isRequested()) {
48 48 un = ct.register(reject);
49 49 } else {
50 50 const ht = setTimeout(() => {
51 51 resolve();
52 52 }, timeout);
53 53
54 54 un = ct.register(e => {
55 55 clearTimeout(ht);
56 56 reject(e);
57 57 });
58 58 }
59 59 });
60 60 } finally {
61 if (un)
62 un.destroy();
61 destroy(un);
63 62 }
64 63 }
65 64
66 65 export function test(name: string, cb: (t: tape.Test) => any) {
67 66 tape(name, async t => {
68 67 const writer = new TapeWriter(t);
69 68
70 69 TraceSource.on(ts => {
71 70 ts.level = DebugLevel;
72 71 writer.writeEvents(ts.events);
73 72 });
74 73
75 74 try {
76 75 await cb(t);
77 76 } catch (e) {
78 77
79 78 // verbose error information
80 79 // tslint:disable-next-line
81 80 console.error(e);
82 81 t.fail(e);
83 82
84 83 } finally {
85 84 t.end();
86 writer.destroy();
85 destroy(writer);
87 86 }
88 87 });
89 88 }
@@ -1,15 +1,15
1 1 {
2 2 "extends": "../tsconfig",
3 3 "compilerOptions": {
4 4 "rootDir": "ts",
5 5 "baseUrl": ".",
6 6 "paths": {
7 7 "@implab/core/*": [
8 "../../build/dist/amd/*"
8 "../../build/dist/*"
9 9 ]
10 10 }
11 11 },
12 12 "include" : [
13 13 "ts/**/*.ts"
14 14 ]
15 15 } No newline at end of file
@@ -1,18 +1,10
1 1 {
2 2 "compilerOptions": {
3 "target": "es3",
4 "sourceMap": true,
5 "declaration": true,
6 3 "moduleResolution": "node",
7 4 "noEmitOnError": true,
8 5 "listFiles": true,
9 "lib": [
10 "es5",
11 "es2015.promise",
12 "es2015.symbol",
13 "dom"
14 ],
15 "types": []
6 "types": [],
7 "lib": ["es5", "es2015.promise", "es2015.symbol", "dom", "scripthost"]
16 8 },
17 9 "files": []
18 10 } No newline at end of file
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now