##// END OF EJS Templates
fixed "singleton" activation type handling in container configuration...
cin -
r65:0c74a0572161 v1.2.13 default
parent child
Show More
@@ -1,236 +1,236
1 define(["./declare", "./log/trace!"], function (declare, trace) {
1 define(["dojo/_base/declare", "./log/trace!"], function (declare, trace) {
2 2 trace.warn("THIS MODULE IS DEPRECATED! use uri-js or similar alternatives.");
3 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 88 var balance = 0,
89 89 result = [],
90 90 isRoot;
91 91
92 92 for (var i = 0; i < parts.length; i++) {
93 93 var part = parts[i];
94 94 switch (part) {
95 95 case "..":
96 96 if (balance > 0) {
97 97 result.pop();
98 98 } else {
99 99 if (isRoot)
100 100 throw new Error("Unbalanced path: " + parts);
101 101
102 102 result.push(part);
103 103 }
104 104 balance--;
105 105 break;
106 106 case ".":
107 107 break;
108 108 case "":
109 109 if (i === 0) {
110 110 isRoot = true;
111 111 result.push(part);
112 112 }
113 113 break;
114 114 default:
115 115 result.push(part);
116 116 balance++;
117 117 break;
118 118 }
119 119 }
120 120
121 121 return result.join("/");
122 122 }
123 123
124 124 var meta = {
125 125 schema: null,
126 126 host: null,
127 127 port: null,
128 128 path: null,
129 129 query: null,
130 130 hash: null
131 131 };
132 132
133 133 var URI = declare(null, {
134 134 constructor: function (opts) {
135 135 trace.warn("This class is deprecated use uri-js or similar");
136 136 if (typeof (opts) == "string")
137 137 opts = parseURI(opts);
138 138 for (var p in meta)
139 139 if (p in opts)
140 140 this[p] = opts[p];
141 141 },
142 142
143 143 clone: function () {
144 144 return new URI(this);
145 145 },
146 146
147 147 combine: function (rel) {
148 148 var me = this;
149 149
150 150 if (typeof (rel) === "string")
151 151 rel = new URI(rel);
152 152 else
153 153 rel = rel.clone();
154 154
155 155 // //some.host:123/path?q=a#123
156 156 if (rel.host)
157 157 return rel;
158 158
159 159 // /abs/path?q=a#123
160 160 if (rel.path && rel.path[0] == "/") {
161 161 if (me.host) {
162 162 rel.schema = me.schema;
163 163 rel.host = me.host;
164 164 rel.port = me.port;
165 165 }
166 166 return rel;
167 167 }
168 168
169 169 var base = me.clone();
170 170
171 171 // rel/path?a=b#cd
172 172 if (rel.path) {
173 173 var segments = base.getSegments();
174 174 segments.pop();
175 175 segments.push.apply(segments, rel.getSegments());
176 176
177 177 base.path = reducePath(segments);
178 178 }
179 179
180 180 // ?q=a#123
181 181 if (rel.query)
182 182 base.query = rel.query;
183 183 if (rel.hash)
184 184 base.hase = rel.hash;
185 185
186 186 return base;
187 187 },
188 188
189 189 optimize: function () {
190 190 this.path = reducePath(this.getSegments());
191 191 },
192 192
193 193 getSegments: function () {
194 194 if (typeof (this.path) === "string")
195 195 return this.path.split("/");
196 196 else
197 197 return [];
198 198 },
199 199
200 200 toString: function () {
201 201 var uri = [],
202 202 me = this;
203 203
204 204 if (me.schema)
205 205 uri.push(me.schema, ":");
206 206 if (me.host)
207 207 uri.push("//", me.host);
208 208 if (me.host && me.port)
209 209 uri.push(":", me.port);
210 210
211 211 if (me.path) {
212 212 if (me.host && me.path[0] != "/")
213 213 uri.push("/");
214 214 uri.push(me.path);
215 215 } else if (me.host) {
216 216 uri.push("/");
217 217 }
218 218
219 219 if (me.query)
220 220 uri.push("?", me.query);
221 221 if (me.hash)
222 222 uri.push("#", me.hash);
223 223
224 224 return uri.join("");
225 225 }
226 226
227 227 });
228 228
229 229 URI.combine = function (base, rel) {
230 230 if (typeof (base) === "string")
231 231 base = new URI(base);
232 232 return base.combine(rel).toString();
233 233 };
234 234
235 235 return URI;
236 236 }); No newline at end of file
@@ -1,72 +1,74
1 1 import { Uuid } from "../Uuid";
2 import { argumentNotEmptyString, getGlobal } from "../safe";
2 import { argumentNotEmptyString, getGlobal, isNullOrEmptyString } from "../safe";
3 3 import { TraceSource, DebugLevel } from "../log/TraceSource";
4 4 import m = require("module");
5 5
6 6 const sandboxId = Uuid();
7 7 define(sandboxId, ["require"], r => r);
8 8
9 9 // tslint:disable-next-line:no-var-requires
10 const globalRequire = require(sandboxId);
10 const globalRequire = getGlobal().require as Require;
11 11
12 12 const trace = TraceSource.get(m.id);
13 13
14 14 export async function createContextRequire(moduleName: string): Promise<Require> {
15 15 argumentNotEmptyString(moduleName, "moduleName");
16 16
17 17 const parts = moduleName.split("/");
18 18 if (parts[0] === ".")
19 19 throw new Error("An absolute module path is required");
20 20
21 21 if (parts.length > 1)
22 22 parts.splice(-1, 1, Uuid());
23 23 else
24 24 parts.push(Uuid());
25 25
26 26 const shim = parts.join("/");
27 27
28 28 trace.debug(`define shim ${shim}`);
29 29
30 30 return new Promise<Require>(cb => {
31 31 define(shim, ["require"], r => {
32 32 trace.debug("shim resolved");
33 33 return r;
34 34 });
35 35 require([shim], cb);
36 36 });
37 37 }
38 38
39 39 class ModuleResolver {
40 40 _base: string;
41 41 _require: Require;
42 42
43 43 constructor(req: Require, base?: string) {
44 44 this._base = base;
45 45 this._require = req || globalRequire;
46 46 }
47 47
48 48 resolve(moduleName: string) {
49 49 argumentNotEmptyString(moduleName, "moduleName");
50 50 const resolvedName = moduleName[0] === "." && this._base ? [this._base, moduleName].join("/") : moduleName;
51 51 trace.debug(`${moduleName} -> ${resolvedName}`);
52 52
53 53 const req = this._require;
54 54
55 55 return new Promise<any>((cb, eb) => {
56 56 req([resolvedName], cb, eb);
57 57 });
58 58 }
59 59 }
60 60
61 export function makeResolver(moduleName: string, contextRequire: Require) {
61 export async function makeResolver(moduleName: string, contextRequire: Require) {
62 62 trace.debug(
63 63 "makeResolver moduleName={0}, contextRequire={1}",
64 64 moduleName || "<nil>",
65 65 contextRequire ? typeof (contextRequire) : "<nil>"
66 66 );
67 67
68 const base = moduleName && moduleName.split("/").slice(0, -1).join("/");
68 const nestedRequire = isNullOrEmptyString(moduleName) ? null : await createContextRequire(moduleName);
69 69
70 const resolver = new ModuleResolver(contextRequire, base);
70 // const base = moduleName && moduleName.split("/").slice(0, -1).join("/");
71
72 const resolver = new ModuleResolver(nestedRequire, null);
71 73 return (id: string) => resolver.resolve(id);
72 74 }
@@ -1,44 +1,48
1 1 import { TraceSource } from "./TraceSource";
2 2 import { Predicate } from "../interfaces";
3 3
4 4 export = {
5 on(filter: any , cb: any) {
5 level: 0,
6
7 on(filter: any, cb: any) {
6 8 if (arguments.length === 1) {
7 9 cb = filter;
8 10 filter = undefined;
9 11 }
10 12 let test: Predicate<string>;
11 13 if (filter instanceof RegExp) {
12 14 test = chId => filter.test(chId);
13 15 } else if (filter instanceof Function) {
14 16 test = filter;
15 17 } else if (filter) {
16 18 test = chId => chId === filter;
17 19 }
18 20
19 21 if (test) {
20 22 TraceSource.on(source => {
23 source.level = this.level;
21 24 if (test(source.id))
22 25 source.events.on(cb);
23 26 });
24 27 } else {
25 28 TraceSource.on(source => {
29 source.level = this.level;
26 30 source.events.on(cb);
27 31 });
28 32 }
29 33 },
30 34
31 35 load(id: string, require: any, cb: (trace: TraceSource) => void) {
32 36 if (id) {
33 37 cb(TraceSource.get(id));
34 38 } else if (require.module && require.module.mid) {
35 39 cb(TraceSource.get(require.module.mid));
36 40 } else {
37 41 require(["module"], (module: { id: any; }) => {
38 42 cb(TraceSource.get(module && module.id));
39 43 });
40 44 }
41 45 },
42 46
43 47 dynamic: true
44 48 };
@@ -1,104 +1,104
1 import { format } from "./StringFormat";
1 import * as format from "./format";
2 2 import { TraceSource, DebugLevel } from "../log/TraceSource";
3 3 import { ITemplateParser, TokenType } from "./TemplateParser";
4 4 import m = require("module");
5 5
6 6 const trace = TraceSource.get(m.id);
7 7
8 8 type TemplateFn = (obj: object) => string;
9 9
10 10 export class TemplateCompiler {
11 11
12 12 _data: string[];
13 13 _code: string[];
14 14 _wrapWith = true;
15 15
16 16 constructor() {
17 17 this._code = [];
18 18 this._data = [];
19 19 }
20 20
21 21 compile(parser: ITemplateParser): TemplateFn {
22 22 this.preamble();
23 23 this.visitTemplate(parser);
24 24 this.postamble();
25 25
26 26 const text = this._code.join("\n");
27 27
28 28 try {
29 29 // tslint:disable-next-line:function-constructor
30 30 const compiled = new Function("obj, format, $data", text);
31 31 /**
32 32 * Ѐункция форматирования ΠΏΠΎ ΡˆΠ°Π±Π»ΠΎΠ½Ρƒ
33 33 *
34 34 * @type{Function}
35 35 * @param{Object} obj ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ для подстановки
36 36 */
37 37 return (obj: object) => compiled(obj || {}, format, this._data);
38 38 } catch (e) {
39 39 trace.traceEvent(DebugLevel, [e, text, this._data]);
40 40 throw e;
41 41 }
42 42 }
43 43
44 44 preamble() {
45 45 this._code.push(
46 46 "var $p = [];",
47 47 "var print = function(){",
48 48 " $p.push(format.apply(null,arguments));",
49 49 "};"
50 50 );
51 51
52 52 if (this._wrapWith)
53 53 this._code.push("with(obj){");
54 54 }
55 55
56 56 postamble() {
57 57 if (this._wrapWith)
58 58 this._code.push("}");
59 59
60 60 this._code.push("return $p.join('');");
61 61 }
62 62
63 63 visitTemplate(parser: ITemplateParser) {
64 64 while (parser.next()) {
65 65 switch (parser.token()) {
66 66 case TokenType.OpenBlock:
67 67 this.visitCode(parser);
68 68 break;
69 69 case TokenType.OpenInlineBlock:
70 70 this.visitInline(parser);
71 71 break;
72 72 default:
73 73 this.visitTextFragment(parser);
74 74 break;
75 75 }
76 76 }
77 77 }
78 78
79 79 visitInline(parser: ITemplateParser) {
80 80 const code = ["$p.push("];
81 81 while (parser.next()) {
82 82 if (parser.token() === TokenType.CloseBlock)
83 83 break;
84 84 code.push(parser.value());
85 85 }
86 86 code.push(");");
87 87 this._code.push(code.join(""));
88 88 }
89 89
90 90 visitCode(parser: ITemplateParser) {
91 91 const code = [];
92 92 while (parser.next()) {
93 93 if (parser.token() === TokenType.CloseBlock)
94 94 break;
95 95 code.push(parser.value());
96 96 }
97 97 this._code.push(code.join(""));
98 98 }
99 99
100 100 visitTextFragment(parser: ITemplateParser) {
101 const i = this._data.push(parser.value());
101 const i = this._data.push(parser.value()) - 1;
102 102 this._code.push("$p.push($data[" + i + "]);");
103 103 }
104 104 }
@@ -1,64 +1,69
1 1 import { argumentNotEmptyString } from "../safe";
2 2 import { MapOf } from "../interfaces";
3 import { TraceSource, DebugLevel } from "../log/TraceSource";
4 import m = require("module");
5
6 const trace = TraceSource.get(m.id);
3 7
4 8 const splitRx = /(<%=|\[%=|<%|\[%|%\]|%>)/;
5 9
6 10 export enum TokenType {
7 11 None,
8 12 Text,
9 13 OpenInlineBlock,
10 14 OpenBlock,
11 15 CloseBlock
12 16 }
13 17
14 18 const tokenMap: MapOf<TokenType> = {
15 19 "<%": TokenType.OpenBlock,
16 20 "[%": TokenType.OpenBlock,
17 21 "<%=": TokenType.OpenInlineBlock,
18 22 "[%=": TokenType.OpenInlineBlock,
19 23 "%>": TokenType.CloseBlock,
20 24 "%]": TokenType.CloseBlock
21 25 };
22 26
23 27 export interface ITemplateParser {
24 28 next(): boolean;
25 29 token(): TokenType;
26 30 value(): string;
27 31 }
28 32
29 33 export class TemplateParser implements ITemplateParser {
30 34
31 35 _tokens: string[];
32 36 _pos = -1;
33 37 _type: TokenType;
34 38 _value: string;
35 39
36 40 constructor(text: string) {
37 41 argumentNotEmptyString(text, "text");
38 42
39 43 this._tokens = text.split(splitRx);
40 44 this._type = TokenType.None;
41 45 }
42 46
43 47 next() {
44 48 this._pos++;
45 49 if (this._pos < this._tokens.length) {
46 50 this._value = this._tokens[this._pos];
47 51 this._type = tokenMap[this._value] || TokenType.Text;
52
48 53 return true;
49 54 } else {
50 55 this._type = TokenType.None;
51 56 this._value = undefined;
52 57 return false;
53 58 }
54 59 }
55 60
56 61 token() {
57 62 return this._type;
58 63 }
59 64
60 65 value() {
61 66 return this._value;
62 67 }
63 68
64 69 }
@@ -1,8 +1,9
1 1 import * as module from "module";
2 2 import { TraceSource } from "../log/TraceSource";
3 import { compile } from "./StringFormat";
3 4
4 5 const logger = TraceSource.get(module.id);
5 6
6 7 logger.warn("The module is deprecated, use StringFormat.compile() method directly");
7 8
8 export { compile } from "./StringFormat";
9 export = compile;
@@ -1,47 +1,67
1 1 import { format as dojoFormatNumber } from "dojo/number";
2 2 import { format as dojoFormatDate } from "dojo/date/locale";
3 import { Formatter } from "./StringFormat";
3 import { Formatter, compile as _compile } from "./StringFormat";
4 4
5 import { isNumber } from "../safe";
5 import { isNumber, isNull } from "../safe";
6 6
7 7 interface NumberFormatOptions {
8 8 round?: number;
9 9 pattern?: string;
10 10 }
11 11
12 12 function convertNumber(value: any, pattern: string) {
13 13 if (isNumber(value)) {
14 14 const nopt = {} as NumberFormatOptions;
15 15 if (pattern.indexOf("!") === 0) {
16 16 nopt.round = -1;
17 17 pattern = pattern.substr(1);
18 18 }
19 19 nopt.pattern = pattern;
20 20
21 21 return dojoFormatNumber(value, nopt);
22 22 }
23 23 }
24 24
25 25 function convertDate(value: any, pattern: string) {
26 26 if (value instanceof Date) {
27 27 const m = pattern.match(/^(\w+)-(\w+)$/);
28 28 if (m)
29 29 return dojoFormatDate(value, {
30 30 selector: m[2],
31 31 formatLength: m[1]
32 32 });
33 33 else if (pattern === "iso")
34 34 return value.toISOString();
35 35 else
36 36 return dojoFormatDate(value, {
37 37 selector: "date",
38 38 datePattern: pattern
39 39 });
40 40 }
41 41 }
42 42
43 43 const _formatter = new Formatter([convertNumber, convertDate]);
44 44
45 export = function format(msg: string, ...args: any[]) {
46 return _formatter.format.apply(msg, ...args);
47 };
45 function format(msg: string, ...args: any[]) {
46 return _formatter.format(msg, ...args);
47 }
48
49 function _convert(value: any, pattern: string) {
50 return _formatter.convert(value, pattern);
51 }
52
53 namespace format {
54 export const convert = _convert;
55 export function compile(text: string) {
56 const template = _compile(text);
57
58 return (...data) => {
59 return template((name, pattern) => {
60 const value = data[name];
61 return !isNull(value) ? convert(value, pattern) : "";
62 });
63 };
64 }
65 }
66
67 export = format;
@@ -1,269 +1,272
1 1 // Typescript port of the uuid.js
2 2 // Copyright (c) 2018 Sergey Smirnov
3 3 // BSD-2-Clause License https://opensource.org/licenses/BSD-2-Clause
4 4 //
5 5 // uuid.js
6 6 // Copyright (c) 2010-2012 Robert Kieffer
7 7 // MIT License - http://opensource.org/licenses/mit-license.php
8 8
9 9 declare const window: any;
10 10 declare const require;
11 11 declare const Buffer;
12 12
13 13 const _window: any = "undefined" !== typeof window ? window : null;
14 14
15 15 // Unique ID creation requires a high quality random # generator. We
16 16 // feature
17 17 // detect to determine the best RNG source, normalizing to a function
18 18 // that
19 19 // returns 128-bits of randomness, since that's what's usually required
20 20 let _rng;
21 21
22 22 function setupBrowser() {
23 23 // Allow for MSIE11 msCrypto
24 24 const _crypto = _window.crypto || _window.msCrypto;
25 25
26 26 if (!_rng && _crypto && _crypto.getRandomValues) {
27 27 // WHATWG crypto-based RNG - http://wiki.whatwg.org/wiki/Crypto
28 28 //
29 29 // Moderately fast, high quality
30 30 try {
31 31 const _rnds8 = new Uint8Array(16);
32 32 _rng = function whatwgRNG() {
33 33 _crypto.getRandomValues(_rnds8);
34 34 return _rnds8;
35 35 };
36 36 _rng();
37 37 } catch (e) { /**/ }
38 38 }
39 39
40 40 if (!_rng) {
41 41 // Math.random()-based (RNG)
42 42 //
43 43 // If all else fails, use Math.random(). It's fast, but is of
44 44 // unspecified
45 45 // quality.
46 46 const _rnds = new Array(16);
47 47 _rng = () => {
48 48 for (let i = 0, r; i < 16; i++) {
49 49 if ((i & 0x03) === 0) {
50 50 r = Math.random() * 0x100000000;
51 51 }
52 52 _rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
53 53 }
54 54
55 55 return _rnds;
56 56 };
57 if ("undefined" !== typeof console && console.warn) {
58 console.warn("[SECURITY] node-uuid: crypto not usable, falling back to insecure Math.random()");
59 }
57 // if ("undefined" !== typeof console && console.warn) {
58 // console.warn("[SECURITY] node-uuid: crypto not usable, falling back to insecure Math.random()");
59 // }
60 60 }
61 61 }
62 62
63 63 function setupNode() {
64 64 // Node.js crypto-based RNG -
65 65 // http://nodejs.org/docs/v0.6.2/api/crypto.html
66 66 //
67 67 // Moderately fast, high quality
68 68 if ("function" === typeof require) {
69 69 try {
70 70 const _rb = require("crypto").randomBytes;
71 71 _rng = _rb && (() => _rb(16));
72 72 _rng();
73 73 } catch (e) { /**/ }
74 74 }
75 75 }
76 76
77 77 if (_window) {
78 78 setupBrowser();
79 79 } else {
80 80 setupNode();
81 81 }
82 82
83 83 // Buffer class to use
84 84 const BufferClass = ("function" === typeof Buffer) ? Buffer : Array;
85 85
86 86 // Maps for number <-> hex string conversion
87 87 const _byteToHex = [];
88 88 const _hexToByte = {};
89 89 for (let i = 0; i < 256; i++) {
90 90 _byteToHex[i] = (i + 0x100).toString(16).substr(1);
91 91 _hexToByte[_byteToHex[i]] = i;
92 92 }
93 93
94 94 // **`parse()` - Parse a UUID into it's component bytes**
95 export function _parse(s, buf?, offset?): Array<string> {
95 function _parse(s, buf?, offset?): Array<string> {
96 96 const i = (buf && offset) || 0; let ii = 0;
97 97
98 98 buf = buf || [];
99 99 s.toLowerCase().replace(/[0-9a-f]{2}/g, oct => {
100 100 if (ii < 16) { // Don't overflow!
101 101 buf[i + ii++] = _hexToByte[oct];
102 102 }
103 103 });
104 104
105 105 // Zero out remaining bytes if string was short
106 106 while (ii < 16) {
107 107 buf[i + ii++] = 0;
108 108 }
109 109
110 110 return buf;
111 111 }
112 112
113 113 // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
114 114 function _unparse(buf, offset?): string {
115 115 let i = offset || 0; const bth = _byteToHex;
116 116 return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] +
117 117 bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] + "-" +
118 118 bth[buf[i++]] + bth[buf[i++]] + "-" + bth[buf[i++]] +
119 119 bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] +
120 120 bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]];
121 121 }
122 122
123 123 // **`v1()` - Generate time-based UUID**
124 124 //
125 125 // Inspired by https://github.com/LiosK/UUID.js
126 126 // and http://docs.python.org/library/uuid.html
127 127
128 128 // random #'s we need to init node and clockseq
129 129 const _seedBytes = _rng();
130 130
131 131 // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit =
132 132 // 1)
133 133 const _nodeId = [
134 134 _seedBytes[0] | 0x01,
135 135 _seedBytes[1],
136 136 _seedBytes[2],
137 137 _seedBytes[3],
138 138 _seedBytes[4],
139 139 _seedBytes[5]
140 140 ];
141 141
142 142 // Per 4.2.2, randomize (14 bit) clockseq
143 143 let _clockseq = (_seedBytes[6] << 8 | _seedBytes[7]) & 0x3fff;
144 144
145 145 // Previous uuid creation time
146 146 let _lastMSecs = 0; let _lastNSecs = 0;
147 147
148 148 // See https://github.com/broofa/node-uuid for API details
149 export function _v1(options?, buf?, offset?): string {
149 function _v1(options?, buf?, offset?): string {
150 150 let i = buf && offset || 0;
151 151 const b = buf || [];
152 152
153 153 options = options || {};
154 154
155 155 let clockseq = (options.clockseq != null) ? options.clockseq : _clockseq;
156 156
157 157 // UUID timestamps are 100 nano-second units since the Gregorian
158 158 // epoch,
159 159 // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
160 160 // time is handled internally as 'msecs' (integer milliseconds) and
161 161 // 'nsecs'
162 162 // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01
163 163 // 00:00.
164 164 let msecs = (options.msecs != null) ? options.msecs : new Date()
165 165 .getTime();
166 166
167 167 // Per 4.2.1.2, use count of uuid's generated during the current
168 168 // clock
169 169 // cycle to simulate higher resolution clock
170 170 let nsecs = (options.nsecs != null) ? options.nsecs : _lastNSecs + 1;
171 171
172 172 // Time since last uuid creation (in msecs)
173 173 const dt = (msecs - _lastMSecs) + (nsecs - _lastNSecs) / 10000;
174 174
175 175 // Per 4.2.1.2, Bump clockseq on clock regression
176 176 if (dt < 0 && options.clockseq == null) {
177 177 clockseq = clockseq + 1 & 0x3fff;
178 178 }
179 179
180 180 // Reset nsecs if clock regresses (new clockseq) or we've moved onto
181 181 // a new
182 182 // time interval
183 183 if ((dt < 0 || msecs > _lastMSecs) && options.nsecs == null) {
184 184 nsecs = 0;
185 185 }
186 186
187 187 // Per 4.2.1.2 Throw error if too many uuids are requested
188 188 if (nsecs >= 10000) {
189 189 throw new Error(
190 190 "uuid.v1(): Can't create more than 10M uuids/sec");
191 191 }
192 192
193 193 _lastMSecs = msecs;
194 194 _lastNSecs = nsecs;
195 195 _clockseq = clockseq;
196 196
197 197 // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
198 198 msecs += 12219292800000;
199 199
200 200 // `time_low`
201 201 const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
202 202 b[i++] = tl >>> 24 & 0xff;
203 203 b[i++] = tl >>> 16 & 0xff;
204 204 b[i++] = tl >>> 8 & 0xff;
205 205 b[i++] = tl & 0xff;
206 206
207 207 // `time_mid`
208 208 const tmh = (msecs / 0x100000000 * 10000) & 0xfffffff;
209 209 b[i++] = tmh >>> 8 & 0xff;
210 210 b[i++] = tmh & 0xff;
211 211
212 212 // `time_high_and_version`
213 213 b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
214 214 b[i++] = tmh >>> 16 & 0xff;
215 215
216 216 // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
217 217 b[i++] = clockseq >>> 8 | 0x80;
218 218
219 219 // `clock_seq_low`
220 220 b[i++] = clockseq & 0xff;
221 221
222 222 // `node`
223 223 const node = options.node || _nodeId;
224 224 for (let n = 0; n < 6; n++) {
225 225 b[i + n] = node[n];
226 226 }
227 227
228 228 return buf ? buf : _unparse(b);
229 229 }
230 230
231 231 // **`v4()` - Generate random UUID**
232 232
233 233 // See https://github.com/broofa/node-uuid for API details
234 export function _v4(options?, buf?, offset?): string {
234 function _v4(options?, buf?, offset?): string {
235 235 // Deprecated - 'format' argument, as supported in v1.2
236 236 const i = buf && offset || 0;
237 237
238 238 if (typeof (options) === "string") {
239 239 buf = (options === "binary") ? new BufferClass(16) : null;
240 240 options = null;
241 241 }
242 242 options = options || {};
243 243
244 244 const rnds = options.random || (options.rng || _rng)();
245 245
246 246 // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
247 247 rnds[6] = (rnds[6] & 0x0f) | 0x40;
248 248 rnds[8] = (rnds[8] & 0x3f) | 0x80;
249 249
250 250 // Copy bytes to buffer, if provided
251 251 if (buf) {
252 252 for (let ii = 0; ii < 16; ii++) {
253 253 buf[i + ii] = rnds[ii];
254 254 }
255 255 }
256 256
257 257 return buf || _unparse(rnds);
258 258 }
259 259
260 export function Uuid() {
260 function _Uuid() {
261 261 return _v4();
262 262 }
263 263
264 export namespace Uuid {
264 namespace _Uuid {
265 265 export const v4 = _v4;
266 266 export const v1 = _v1;
267 267 export const empty = "00000000-0000-0000-0000-000000000000";
268 268 export const parse = _parse;
269 export const Uuid = _v4;
269 270 }
271
272 export = _Uuid;
@@ -1,369 +1,348
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 26 import { makeResolver } from "./ResolverHelper";
27 27 import { ICancellation } from "../interfaces";
28 28
29 29 const trace = TraceSource.get("@implab/core/di/Configuration");
30 30
31 declare const define;
32 declare const require;
33 declare const module;
34
35 function hasAmdLoader() {
36 try {
37 // es6 may throw the exception
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 }
50 }
51
52 31 async function mapAll(data: object | any[], map?: (v, k) => any): Promise<any> {
53 32 if (data instanceof Array) {
54 33 return Promise.all(map ? data.map(map) : data);
55 34 } else {
56 35 const keys = Object.keys(data);
57 36
58 37 const o: any = {};
59 38
60 39 await Promise.all(keys.map(async k => {
61 40 const v = map ? map(data[k], k) : data[k];
62 41 o[k] = isPromise(v) ? await v : v;
63 42 }));
64 43
65 44 return o;
66 45 }
67 46 }
68 47
69 48 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
70 49
71 50 type _key = string | number;
72 51
73 52 export class Configuration {
74 53
75 54 _hasInnerDescriptors = false;
76 55
77 56 _container: Container;
78 57
79 58 _path: Array<_key>;
80 59
81 60 _configName: string;
82 61
83 62 _require: ModuleResolver;
84 63
85 64 constructor(container: Container) {
86 65 argumentNotNull(container, container);
87 66 this._container = container;
88 67 this._path = [];
89 68 }
90 69
91 70 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
92 71 argumentNotEmptyString(moduleName, "moduleName");
93 72
94 73 trace.log(
95 74 "loadConfiguration moduleName={0}, contextRequire={1}",
96 75 moduleName,
97 76 contextRequire ? typeof (contextRequire) : "<nil>"
98 77 );
99 78
100 79 this._configName = moduleName;
101 80
102 const r = makeResolver(null, contextRequire);
81 const r = await makeResolver(null, contextRequire);
103 82
104 83 const config = await r(moduleName, ct);
105 84
106 85 await this._applyConfiguration(
107 86 config,
108 makeResolver(moduleName, contextRequire),
87 await makeResolver(moduleName, contextRequire),
109 88 ct
110 89 );
111 90 }
112 91
113 applyConfiguration(data: object, contextRequire?: any, ct = Cancellation.none) {
92 async applyConfiguration(data: object, contextRequire?: any, ct = Cancellation.none) {
114 93 argumentNotNull(data, "data");
115 94
116 return this._applyConfiguration(data, makeResolver(void (0), contextRequire), ct);
95 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
117 96 }
118 97
119 98 async _applyConfiguration(data: object, resolver?: ModuleResolver, ct = Cancellation.none) {
120 99 trace.log("applyConfiguration");
121 100
122 101 this._configName = "$";
123 102
124 103 if (resolver)
125 104 this._require = resolver;
126 105
127 106 let services: ServiceMap;
128 107
129 108 try {
130 109 services = await this._visitRegistrations(data, "$");
131 110 } catch (e) {
132 111 throw this._makeError(e);
133 112 }
134 113
135 114 this._container.register(services);
136 115 }
137 116
138 117 _makeError(inner) {
139 118 const e = new ConfigError("Failed to load configuration", inner);
140 119 e.configName = this._configName;
141 120 e.path = this._makePath();
142 121 return e;
143 122 }
144 123
145 124 _makePath() {
146 125 return this._path
147 126 .reduce(
148 127 (prev, cur) => typeof cur === "number" ?
149 128 `${prev}[${cur}]` :
150 129 `${prev}.${cur}`
151 130 )
152 131 .toString();
153 132 }
154 133
155 134 async _resolveType(moduleName: string, localName: string) {
156 135 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
157 136 try {
158 137 const m = await this._loadModule(moduleName);
159 138 return localName ? get(localName, m) : m;
160 139 } catch (e) {
161 140 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
162 141 throw e;
163 142 }
164 143 }
165 144
166 145 _loadModule(moduleName: string) {
167 146 trace.debug("loadModule {0}", moduleName);
168 147
169 148 return this._require(moduleName);
170 149 }
171 150
172 151 async _visitRegistrations(data, name: _key) {
173 152 this._enter(name);
174 153
175 154 if (data.constructor &&
176 155 data.constructor.prototype !== Object.prototype)
177 156 throw new Error("Configuration must be a simple object");
178 157
179 158 const o: ServiceMap = {};
180 159 const keys = Object.keys(data);
181 160
182 161 const services = await mapAll(data, async (v, k) => {
183 162 const d = await this._visit(v, k);
184 163 return isDescriptor(d) ? d : new AggregateDescriptor(d);
185 164 }) as ServiceMap;
186 165
187 166 this._leave();
188 167
189 168 return services;
190 169 }
191 170
192 171 _enter(name: _key) {
193 172 this._path.push(name);
194 173 trace.debug(">{0}", name);
195 174 }
196 175
197 176 _leave() {
198 177 const name = this._path.pop();
199 178 trace.debug("<{0}", name);
200 179 }
201 180
202 181 _visit(data, name: string): Promise<any> {
203 182 if (isPrimitive(data) || isDescriptor(data))
204 183 return data;
205 184
206 185 if (isDependencyRegistration(data)) {
207 186 return this._visitDependencyRegistration(data, name);
208 187 } else if (isValueRegistration(data)) {
209 188 return this._visitValueRegistration(data, name);
210 189 } else if (isTypeRegistration(data)) {
211 190 return this._visitTypeRegistration(data, name);
212 191 } else if (isFactoryRegistration(data)) {
213 192 return this._visitFactoryRegistration(data, name);
214 193 } else if (data instanceof Array) {
215 194 return this._visitArray(data, name);
216 195 }
217 196
218 197 return this._visitObject(data, name);
219 198 }
220 199
221 200 async _visitObject(data: object, name: _key) {
222 201 if (data.constructor &&
223 202 data.constructor.prototype !== Object.prototype)
224 203 return new ValueDescriptor(data);
225 204
226 205 this._enter(name);
227 206
228 207 const v = await mapAll(data, delegate(this, "_visit"));
229 208
230 209 // TODO: handle inline descriptors properly
231 210 // const ex = {
232 211 // activate(ctx) {
233 212 // const value = ctx.activate(this.prop, "prop");
234 213 // // some code
235 214 // },
236 215 // // will be turned to ReferenceDescriptor
237 216 // prop: { $dependency: "depName" }
238 217 // };
239 218
240 219 this._leave();
241 220 return v;
242 221 }
243 222
244 223 async _visitArray(data: any[], name: _key) {
245 224 if (data.constructor &&
246 225 data.constructor.prototype !== Array.prototype)
247 226 return new ValueDescriptor(data);
248 227
249 228 this._enter(name);
250 229
251 230 const v = await mapAll(data, delegate(this, "_visit"));
252 231 this._leave();
253 232
254 233 return v;
255 234 }
256 235
257 236 _makeServiceParams(data: ServiceRegistration) {
258 237 const opts: any = {
259 238 owner: this._container
260 239 };
261 240 if (data.services)
262 241 opts.services = this._visitRegistrations(data.services, "services");
263 242
264 243 if (data.inject) {
265 this._path.push("inject");
244 this._enter("inject");
266 245 opts.inject = mapAll(
267 246 data.inject instanceof Array ?
268 247 data.inject :
269 248 [data.inject],
270 249 delegate(this, "_visitObject")
271 250 );
272 251 this._leave();
273 252 }
274 253
275 254 if ("params" in data)
276 255 opts.params = data.params instanceof Array ?
277 256 this._visitArray(data.params, "params") :
278 257 this._visit(data.params, "params");
279 258
280 259 if (data.activation) {
281 260 if (typeof (data.activation) === "string") {
282 261 switch (data.activation.toLowerCase()) {
283 262 case "singleton":
284 263 opts.activation = ActivationType.Singleton;
285 264 break;
286 265 case "container":
287 266 opts.activation = ActivationType.Container;
288 267 break;
289 268 case "hierarchy":
290 269 opts.activation = ActivationType.Hierarchy;
291 270 break;
292 271 case "context":
293 272 opts.activation = ActivationType.Context;
294 273 break;
295 274 case "call":
296 275 opts.activation = ActivationType.Call;
297 276 break;
298 277 default:
299 278 throw new Error("Unknown activation type: " +
300 279 data.activation);
301 280 }
302 281 } else {
303 282 opts.activation = Number(data.activation);
304 283 }
305 284 }
306 285
307 286 if (data.cleanup)
308 287 opts.cleanup = data.cleanup;
309 288
310 289 return opts;
311 290 }
312 291
313 292 async _visitValueRegistration(data: ValueRegistration, name: _key) {
314 293 this._enter(name);
315 294 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
316 295 this._leave();
317 296 return d;
318 297 }
319 298
320 299 async _visitDependencyRegistration(data: DependencyRegistration, name: _key) {
321 300 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
322 301 this._enter(name);
323 302 const d = new ReferenceDescriptor({
324 303 name: data.$dependency,
325 304 lazy: data.lazy,
326 305 optional: data.optional,
327 306 default: data.default,
328 307 services: data.services && await this._visitRegistrations(data.services, "services")
329 308 });
330 309 this._leave();
331 310 return d;
332 311 }
333 312
334 313 async _visitTypeRegistration(data: TypeRegistration, name: _key) {
335 314 argumentNotNull(data.$type, "data.$type");
336 315 this._enter(name);
337 316
338 317 const opts = this._makeServiceParams(data);
339 318 if (data.$type instanceof Function) {
340 319 opts.type = data.$type;
341 320 } else {
342 321 const [moduleName, typeName] = data.$type.split(":", 2);
343 322 opts.type = this._resolveType(moduleName, typeName);
344 323 }
345 324
346 325 const d = new TypeServiceDescriptor(
347 326 await mapAll(opts)
348 327 );
349 328
350 329 this._leave();
351 330
352 331 return d;
353 332 }
354 333
355 334 async _visitFactoryRegistration(data: FactoryRegistration, name: _key) {
356 argumentOfType(data.$factory, Function, "data.$type");
335 argumentOfType(data.$factory, Function, "data.$factory");
357 336 this._enter(name);
358 337
359 338 const opts = this._makeServiceParams(data);
360 opts.factory = opts.$factory;
339 opts.factory = data.$factory;
361 340
362 341 const d = new FactoryServiceDescriptor(
363 342 await mapAll(opts)
364 343 );
365 344
366 345 this._leave();
367 346 return d;
368 347 }
369 348 }
@@ -1,23 +1,23
1 1 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
2 2 import { Factory } from "../interfaces";
3 3 import { argumentNotNull, oid } from "../safe";
4 4 import { ActivationType } from "./interfaces";
5 5
6 6 export interface FactoryServiceDescriptorParams extends ServiceDescriptorParams {
7 7 factory: Factory;
8 8 }
9 9
10 10 export class FactoryServiceDescriptor extends ServiceDescriptor {
11 11 constructor(opts: FactoryServiceDescriptorParams) {
12 12 super(opts);
13 13
14 14 argumentNotNull(opts && opts.factory, "opts.factory");
15 15
16 16 // bind to null
17 this._factory = () => opts.factory();
17 this._factory = (...args) => opts.factory.apply(null, args);
18 18
19 19 if (opts.activation === ActivationType.Singleton) {
20 20 this._cacheId = oid(opts.factory);
21 21 }
22 22 }
23 23 }
@@ -1,3 +1,3
1 1 import { ModuleResolver } from "./Configuration";
2 2
3 export declare function makeResolver(moduleName?: string, contextRequire?: any): ModuleResolver; No newline at end of file
3 export declare function makeResolver(moduleName?: string, contextRequire?: any): Promise<ModuleResolver>; No newline at end of file
@@ -1,234 +1,234
1 1 import { ActivationContext } from "./ActivationContext";
2 2 import { Descriptor, ActivationType, ServiceMap, isDescriptor } from "./interfaces";
3 3 import { Container } from "./Container";
4 4 import { argumentNotNull, isPrimitive } from "../safe";
5 5 import { TraceSource } from "../log/TraceSource";
6 6
7 7 let cacheId = 0;
8 8
9 9 const trace = TraceSource.get("@implab/core/di/ActivationContext");
10 10
11 11 function injectMethod(target, method, context, args) {
12 12 const m = target[method];
13 13 if (!m)
14 14 throw new Error("Method '" + method + "' not found");
15 15
16 16 if (args instanceof Array)
17 return m.apply(target, context.parse(args, "." + method));
17 return m.apply(target, _parse(args, context, "." + method));
18 18 else
19 return m.call(target, context.parse(args, "." + method));
19 return m.call(target, _parse(args, context, "." + method));
20 20 }
21 21
22 22 function makeClenupCallback(target, method: ((instance) => void) | string) {
23 23 if (typeof (method) === "string") {
24 24 return () => {
25 25 target[method]();
26 26 };
27 27 } else {
28 28 return () => {
29 29 method(target);
30 30 };
31 31 }
32 32 }
33 33
34 34 // TODO: make async
35 35 function _parse(value, context: ActivationContext, path: string) {
36 36 if (isPrimitive(value))
37 37 return value;
38 38
39 39 trace.debug("parse {0}", path);
40 40
41 41 if (isDescriptor(value))
42 42 return context.activate(value, path);
43 43
44 44 if (value instanceof Array)
45 45 return value.map((x, i) => _parse(x, context, `${path}[${i}]`));
46 46
47 47 const t = {};
48 48 for (const p of Object.keys(value))
49 49 t[p] = _parse(value[p], context, `${path}.${p}`);
50 50
51 51 return t;
52 52 }
53 53
54 54 export interface ServiceDescriptorParams {
55 55 activation?: ActivationType;
56 56
57 57 owner: Container;
58 58
59 59 params?;
60 60
61 61 inject?: object[];
62 62
63 63 services?: ServiceMap;
64 64
65 65 cleanup?: ((x) => void) | string;
66 66 }
67 67
68 68 export class ServiceDescriptor implements Descriptor {
69 69 _instance;
70 70
71 71 _hasInstance = false;
72 72
73 73 _activationType = ActivationType.Call;
74 74
75 75 _services: ServiceMap;
76 76
77 77 _params;
78 78
79 79 _inject: object[];
80 80
81 81 _cleanup: ((x) => void) | string;
82 82
83 83 _cacheId: any;
84 84
85 85 _owner: Container;
86 86
87 87 constructor(opts: ServiceDescriptorParams) {
88 88 argumentNotNull(opts, "opts");
89 89 argumentNotNull(opts.owner, "owner");
90 90
91 91 this._owner = opts.owner;
92 92
93 if (opts.activation)
93 if ("activation" in opts)
94 94 this._activationType = opts.activation;
95 95
96 if (opts.params)
96 if ("params" in opts)
97 97 this._params = opts.params;
98 98
99 99 if (opts.inject)
100 100 this._inject = opts.inject;
101 101
102 102 if (opts.services)
103 103 this._services = opts.services;
104 104
105 105 if (opts.cleanup) {
106 106 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
107 107 throw new Error(
108 108 "The cleanup parameter must be either a function or a function name");
109 109
110 110 this._cleanup = opts.cleanup;
111 111 }
112 112 }
113 113
114 114 activate(context: ActivationContext) {
115 115 // if we have a local service records, register them first
116 116 let instance;
117 117
118 118 // ensure we have a cache id
119 119 if (!this._cacheId)
120 120 this._cacheId = ++cacheId;
121 121
122 122 switch (this._activationType) {
123 123 case ActivationType.Singleton: // SINGLETON
124 124 // if the value is cached return it
125 125 if (this._hasInstance)
126 126 return this._instance;
127 127
128 128 // singletons are bound to the root container
129 129 const container = context.container.getRootContainer();
130 130
131 131 if (container.has(this._cacheId)) {
132 132 instance = container.get(this._cacheId);
133 133 } else {
134 134 instance = this._create(context);
135 135 container.store(this._cacheId, instance);
136 136 if (this._cleanup)
137 137 container.onDispose(
138 138 makeClenupCallback(instance, this._cleanup));
139 139 }
140 140
141 141 this._hasInstance = true;
142 142 return (this._instance = instance);
143 143
144 144 case ActivationType.Container: // CONTAINER
145 145 // return a cached value
146 146
147 147 if (this._hasInstance)
148 148 return this._instance;
149 149
150 150 // create an instance
151 151 instance = this._create(context);
152 152
153 153 // the instance is bound to the container
154 154 if (this._cleanup)
155 155 this._owner.onDispose(
156 156 makeClenupCallback(instance, this._cleanup));
157 157
158 158 // cache and return the instance
159 159 this._hasInstance = true;
160 160 return (this._instance = instance);
161 161 case ActivationType.Context: // CONTEXT
162 162 // return a cached value if one exists
163 163
164 164 if (context.has(this._cacheId))
165 165 return context.get(this._cacheId);
166 166 // context context activated instances are controlled by callers
167 167 return context.store(this._cacheId, this._create(context));
168 168 case ActivationType.Call: // CALL
169 169 // per-call created instances are controlled by callers
170 170 return this._create(context);
171 171 case ActivationType.Hierarchy: // HIERARCHY
172 172 // hierarchy activated instances are behave much like container activated
173 173 // except they are created and bound to the child container
174 174
175 175 // return a cached value
176 176 if (context.container.has(this._cacheId))
177 177 return context.container.get(this._cacheId);
178 178
179 179 instance = this._create(context);
180 180
181 181 if (this._cleanup)
182 182 context.container.onDispose(makeClenupCallback(
183 183 instance,
184 184 this._cleanup));
185 185
186 186 return context.container.store(this._cacheId, instance);
187 187 default:
188 188 throw new Error("Invalid activation type: " + this._activationType);
189 189 }
190 190 }
191 191
192 192 isInstanceCreated() {
193 193 return this._hasInstance;
194 194 }
195 195
196 196 getInstance() {
197 197 return this._instance;
198 198 }
199 199
200 200 _factory(...params: any[]): any {
201 201 throw Error("Not implemented");
202 202 }
203 203
204 204 _create(context: ActivationContext) {
205 205 trace.debug(`constructing ${context._name}`);
206 206
207 207 if (this._activationType !== ActivationType.Call &&
208 208 context.visit(this._cacheId) > 0)
209 209 throw new Error("Recursion detected");
210 210
211 211 if (this._services) {
212 212 for (const p in this._services)
213 213 context.register(p, this._services[p]);
214 214 }
215 215
216 216 let instance;
217 217
218 218 if (this._params === undefined) {
219 219 instance = this._factory();
220 220 } else if (this._params instanceof Array) {
221 221 instance = this._factory.apply(this, _parse(this._params, context, "args"));
222 222 } else {
223 223 instance = this._factory(_parse(this._params, context, "args"));
224 224 }
225 225
226 226 if (this._inject) {
227 227 this._inject.forEach(spec => {
228 228 for (const m in spec)
229 229 injectMethod(instance, m, context, spec[m]);
230 230 });
231 231 }
232 232 return instance;
233 233 }
234 234 }
@@ -1,75 +1,75
1 1 import { isNull, isPrimitive } from "../safe";
2 2 import { ActivationContext } from "./ActivationContext";
3 3 import { Constructor, Factory } from "../interfaces";
4 4
5 5 export interface Descriptor {
6 6 activate(context: ActivationContext, name?: string);
7 7 }
8 8
9 9 export function isDescriptor(x): x is Descriptor {
10 10 return (!isPrimitive(x)) &&
11 11 (x.activate instanceof Function);
12 12 }
13 13
14 14 export interface ServiceMap {
15 15 [s: string]: Descriptor;
16 16 }
17 17
18 18 export enum ActivationType {
19 Singleton,
19 Singleton = 1,
20 20 Container,
21 21 Hierarchy,
22 22 Context,
23 23 Call
24 24 }
25 25
26 26 export interface RegistrationWithServices {
27 27 services?: object;
28 28 }
29 29
30 30 export interface ServiceRegistration extends RegistrationWithServices {
31 31
32 32 activation?: "singleton" | "container" | "hierarchy" | "context" | "call";
33 33
34 34 params?;
35 35
36 36 inject?: object | object[];
37 37
38 38 cleanup?: (instance) => void | string;
39 39 }
40 40
41 41 export interface TypeRegistration extends ServiceRegistration {
42 42 $type: string | Constructor;
43 43 }
44 44
45 45 export interface FactoryRegistration extends ServiceRegistration {
46 46 $factory: string | Factory;
47 47 }
48 48
49 49 export interface ValueRegistration {
50 50 $value;
51 51 parse?: boolean;
52 52 }
53 53
54 54 export interface DependencyRegistration extends RegistrationWithServices {
55 55 $dependency: string;
56 56 lazy?: boolean;
57 57 optional?: boolean;
58 58 default?;
59 59 }
60 60
61 61 export function isTypeRegistration(x): x is TypeRegistration {
62 return (!isPrimitive(x)) && ("$type" in x || "$factory" in x);
62 return (!isPrimitive(x)) && ("$type" in x);
63 63 }
64 64
65 65 export function isFactoryRegistration(x): x is FactoryRegistration {
66 return (!isPrimitive(x)) && ("$type" in x || "$factory" in x);
66 return (!isPrimitive(x)) && ("$factory" in x);
67 67 }
68 68
69 69 export function isValueRegistration(x): x is ValueRegistration {
70 70 return (!isPrimitive(x)) && ("$value" in x);
71 71 }
72 72
73 73 export function isDependencyRegistration(x): x is DependencyRegistration {
74 74 return (!isPrimitive(x)) && ("$dependency" in x);
75 75 }
@@ -1,128 +1,134
1 1 import { Observable } from "../Observable";
2 2 import { Registry } from "./Registry";
3 import { format } from "../text/StringFormat";
3 import { format as _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 function format(msg) {
24 if (typeof(msg) !== "string" || arguments.length === 1)
25 return msg;
26 return _format.apply(null, arguments);
27 }
28
23 29 export class TraceSource {
24 30 readonly id: any;
25 31
26 32 level: number;
27 33
28 34 readonly events: Observable<TraceEvent>;
29 35
30 36 _notifyNext: (arg: TraceEvent) => void;
31 37
32 38 constructor(id: any) {
33 39
34 40 this.id = id || new Object();
35 41 this.events = new Observable(next => {
36 42 this._notifyNext = next;
37 43 });
38 44 }
39 45
40 46 protected emit(level: number, arg: any) {
41 47 this._notifyNext({ source: this, level, arg });
42 48 }
43 49
44 50 isDebugEnabled() {
45 51 return this.level >= DebugLevel;
46 52 }
47 53
48 54 debug(msg: string, ...args: any[]) {
49 55 if (this.isEnabled(DebugLevel))
50 56 this.emit(DebugLevel, format.apply(null, arguments));
51 57 }
52 58
53 59 isLogEnabled() {
54 60 return this.level >= LogLevel;
55 61 }
56 62
57 63 log(msg: string, ...args: any[]) {
58 64 if (this.isEnabled(LogLevel))
59 65 this.emit(LogLevel, format.apply(null, arguments));
60 66 }
61 67
62 68 isWarnEnabled() {
63 69 return this.level >= WarnLevel;
64 70 }
65 71
66 72 warn(msg: string, ...args: any[]) {
67 73 if (this.isEnabled(WarnLevel))
68 74 this.emit(WarnLevel, format.apply(null, arguments));
69 75 }
70 76
71 77 /**
72 78 * returns true if errors will be recorded.
73 79 */
74 80 isErrorEnabled() {
75 81 return this.level >= ErrorLevel;
76 82 }
77 83
78 84 /**
79 85 * Traces a error.
80 86 *
81 87 * @param msg the message.
82 88 * @param args parameters which will be substituted in the message.
83 89 */
84 90 error(msg: string, ...args: any[]) {
85 91 if (this.isEnabled(ErrorLevel))
86 92 this.emit(ErrorLevel, format.apply(null, arguments));
87 93 }
88 94
89 95 /**
90 96 * Checks whether the specified level is enabled for this
91 97 * trace source.
92 98 *
93 99 * @param level the trace level which should be checked.
94 100 */
95 101 isEnabled(level: number) {
96 102 return (this.level >= level);
97 103 }
98 104
99 105 /**
100 106 * Traces a raw event, passing data as it is to the underlying listeners
101 107 *
102 108 * @param level the level of the event
103 109 * @param arg the data of the event, can be a simple string or any object.
104 110 */
105 111 traceEvent(level: number, arg: any) {
106 112 if (this.isEnabled(level))
107 113 this.emit(level, arg);
108 114 }
109 115
110 116 /**
111 117 * Register the specified handler to be called for every new and already
112 118 * created trace source.
113 119 *
114 120 * @param handler the handler which will be called for each trace source
115 121 */
116 122 static on(handler: (source: TraceSource) => void) {
117 123 return Registry.instance.on(handler);
118 124 }
119 125
120 126 /**
121 127 * Creates or returns already created trace source for the specified id.
122 128 *
123 129 * @param id the id for the trace source
124 130 */
125 131 static get(id: any) {
126 132 return Registry.instance.get(id);
127 133 }
128 134 }
@@ -1,308 +1,354
1 1 let _nextOid = 0;
2 2 const _oid = typeof Symbol === "function" ?
3 3 Symbol("__implab__oid__") :
4 4 "__implab__oid__";
5 5
6 declare const window: any;
7 declare const global: any;
8
6 9 export function oid(instance: object): string {
7 10 if (isNull(instance))
8 11 return null;
9 12
10 13 if (_oid in instance)
11 14 return instance[_oid];
12 15 else
13 16 return (instance[_oid] = "oid_" + (++_nextOid));
14 17 }
15 18
16 19 export function argumentNotNull(arg, name) {
17 20 if (arg === null || arg === undefined)
18 21 throw new Error("The argument " + name + " can't be null or undefined");
19 22 }
20 23
21 24 export function argumentNotEmptyString(arg, name) {
22 25 if (typeof (arg) !== "string" || !arg.length)
23 26 throw new Error("The argument '" + name + "' must be a not empty string");
24 27 }
25 28
26 29 export function argumentNotEmptyArray(arg, name) {
27 30 if (!(arg instanceof Array) || !arg.length)
28 31 throw new Error("The argument '" + name + "' must be a not empty array");
29 32 }
30 33
31 34 export function argumentOfType(arg, type, name) {
32 35 if (!(arg instanceof type))
33 36 throw new Error("The argument '" + name + "' type doesn't match");
34 37 }
35 38
36 39 export function isNull(arg) {
37 40 return (arg === null || arg === undefined);
38 41 }
39 42
40 43 export function isPrimitive(arg) {
41 44 return (arg === null || arg === undefined || typeof (arg) === "string" ||
42 45 typeof (arg) === "number" || typeof (arg) === "boolean");
43 46 }
44 47
45 48 export function isInteger(arg) {
46 49 return parseInt(arg, 10) === arg;
47 50 }
48 51
49 52 export function isNumber(arg) {
50 53 return parseFloat(arg) === arg;
51 54 }
52 55
53 56 export function isString(val) {
54 57 return typeof (val) === "string" || val instanceof String;
55 58 }
56 59
57 60 export function isPromise(val): val is PromiseLike<any> {
58 return "then" in val && val.then instanceof Function;
61 return val && typeof val.then === "function";
59 62 }
60 63
61 64 export function isNullOrEmptyString(str) {
62 65 if (str === null || str === undefined ||
63 66 ((typeof (str) === "string" || str instanceof String) && str.length === 0))
64 67 return true;
65 68 }
66 69
67 70 export function isNotEmptyArray(arg): arg is Array<any> {
68 71 return (arg instanceof Array && arg.length > 0);
69 72 }
70 73
74 function _isStrictMode() {
75 return !this;
76 }
77
78 function _getNonStrictGlobal() {
79 return this;
80 }
81
71 82 export function getGlobal() {
72 return this;
83 // in es3 we can't use indirect call to eval, since it will
84 // be executed in the current call context.
85 if (!_isStrictMode()) {
86 return _getNonStrictGlobal();
87 } else {
88 // tslint:disable-next-line:no-eval
89 return eval.call(null, "this");
90 }
73 91 }
74 92
75 93 export function get(member: string, context?: object) {
76 94 argumentNotEmptyString(member, "member");
77 95 let that = context || getGlobal();
78 96 const parts = member.split(".");
79 97 for (const m of parts) {
80 98 if (!m)
81 99 continue;
82 100 if (isNull(that = that[m]))
83 101 break;
84 102 }
85 103 return that;
86 104 }
87 105
88 106 /**
89 107 * ВыполняСт ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива, останавливаСтся, ΠΊΠΎΠ³Π΄Π°
90 108 * Π»ΠΈΠ±ΠΎ достигнут ΠΊΠΎΠ½Π΅Ρ† массива, Π»ΠΈΠ±ΠΎ функция <c>cb</c> Π²Π΅Ρ€Π½ΡƒΠ»Π°
91 109 * Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
92 110 *
93 111 * @param {Array | Object} obj массив элСмСнтов для просмотра
94 112 * @param {Function} cb функция, вызываСмая для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта
95 113 * @param {Object} thisArg Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ Π² качСствС
96 114 * <c>this</c> Π² <c>cb</c>.
97 115 * @returns Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π²Ρ‹Π·ΠΎΠ²Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ <c>cb</c>, Π»ΠΈΠ±ΠΎ <c>undefined</c>
98 116 * Ссли достигнут ΠΊΠΎΠ½Π΅Ρ† массива.
99 117 */
100 118 export function each(obj, cb, thisArg?) {
101 119 argumentNotNull(cb, "cb");
102 120 if (obj instanceof Array) {
103 121 for (let i = 0; i < obj.length; i++) {
104 122 const x = cb.call(thisArg, obj[i], i);
105 123 if (x !== undefined)
106 124 return x;
107 125 }
108 126 } else {
109 127 const keys = Object.keys(obj);
110 128 for (const k of keys) {
111 129 const x = cb.call(thisArg, obj[k], k);
112 130 if (x !== undefined)
113 131 return x;
114 132 }
115 133 }
116 134 }
117 135
118 136 /** Copies property values from a source object to the destination and returns
119 137 * the destination onject.
120 138 *
121 139 * @param dest The destination object into which properties from the source
122 140 * object will be copied.
123 141 * @param source The source of values which will be copied to the destination
124 142 * object.
125 143 * @param template An optional parameter specifies which properties should be
126 144 * copied from the source and how to map them to the destination. If the
127 145 * template is an array it contains the list of property names to copy from the
128 146 * source to the destination. In case of object the templates contains the map
129 147 * where keys are property names in the source and the values are property
130 148 * names in the destination object. If the template isn't specified then the
131 149 * own properties of the source are entirely copied to the destination.
132 150 *
133 151 */
134 152 export function mixin<T, S>(dest: T, source: S, template?: string[] | object): T & S {
135 153 argumentNotNull(dest, "to");
136 154 const _res = dest as T & S;
137 155
156 if (isPrimitive(source))
157 return _res;
158
138 159 if (template instanceof Array) {
139 160 for (const p of template) {
140 161 if (p in source)
141 162 _res[p] = source[p];
142 163 }
143 164 } else if (template) {
144 165 const keys = Object.keys(source);
145 166 for (const p of keys) {
146 167 if (p in template)
147 168 _res[template[p]] = source[p];
148 169 }
149 170 } else {
150 171 const keys = Object.keys(source);
151 172 for (const p of keys)
152 173 _res[p] = source[p];
153 174 }
154 175
155 176 return _res;
156 177 }
157 178
158 179 /** Wraps the specified function to emulate an asynchronous execution.
159 180 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
160 181 * @param{Function|String} fn [Required] Function wich will be wrapped.
161 182 */
162 183 export function async(_fn: (...args: any[]) => any, thisArg): (...args: any[]) => PromiseLike<any> {
163 184 let fn = _fn;
164 185
165 186 if (arguments.length === 2 && !(fn instanceof Function))
166 187 fn = thisArg[fn];
167 188
168 189 if (fn == null)
169 190 throw new Error("The function must be specified");
170 191
171 192 function wrapresult(x, e?): PromiseLike<any> {
172 193 if (e) {
173 194 return {
174 195 then(cb, eb) {
175 196 try {
176 197 return eb ? wrapresult(eb(e)) : this;
177 198 } catch (e2) {
178 199 return wrapresult(null, e2);
179 200 }
180 201 }
181 202 };
182 203 } else {
183 204 if (x && x.then)
184 205 return x;
185 206 return {
186 207 then(cb) {
187 208 try {
188 209 return cb ? wrapresult(cb(x)) : this;
189 210 } catch (e2) {
190 211 return wrapresult(e2);
191 212 }
192 213 }
193 214 };
194 215 }
195 216 }
196 217
197 218 return (...args) => {
198 219 try {
199 220 return wrapresult(fn.apply(thisArg, args));
200 221 } catch (e) {
201 222 return wrapresult(null, e);
202 223 }
203 224 };
204 225 }
205 226
206 227 type _AnyFn = (...args) => any;
207 228
208 229 export function delegate<T, K extends keyof T>(target: T, _method: (K | _AnyFn)) {
209 230 let method;
210 231
211 232 if (!(_method instanceof Function)) {
212 233 argumentNotNull(target, "target");
213 234 method = target[_method];
214 235 if (!(method instanceof Function))
215 236 throw new Error("'method' argument must be a Function or a method name");
216 237 } else {
217 238 method = _method;
218 239 }
219 240
220 241 return (...args) => {
221 242 return method.apply(target, args);
222 243 };
223 244 }
224 245
225 246 /**
226 247 * Для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ ΡƒΠΊΠ°Π·Π°Π½Π½ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΈ сохраняСт
227 248 * Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π΅Π½Π½ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π² массивС Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ².
228 249 *
229 250 * @remarks cb ΠΌΠΎΠΆΠ΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒΡΡ асинхронно, ΠΏΡ€ΠΈ этом ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ Π±ΡƒΠ΄Π΅Ρ‚
230 251 * Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΎΠ΄Π½Π° опСрация.
231 252 *
232 253 * @async
233 254 */
234 255 export function pmap(items, cb) {
235 256 argumentNotNull(cb, "cb");
236 257
237 258 if (isPromise(items))
238 259 return items.then(data => pmap(data, cb));
239 260
240 261 if (isNull(items) || !items.length)
241 262 return items;
242 263
243 264 let i = 0;
244 265 const result = [];
245 266
246 267 function next() {
247 268 let r;
248 269 let ri;
249 270
250 271 function chain(x) {
251 272 result[ri] = x;
252 273 return next();
253 274 }
254 275
255 276 while (i < items.length) {
256 277 r = cb(items[i], i);
257 278 ri = i;
258 279 i++;
259 280 if (isPromise(r)) {
260 281 return r.then(chain);
261 282 } else {
262 283 result[ri] = r;
263 284 }
264 285 }
265 286 return result;
266 287 }
267 288
268 289 return next();
269 290 }
270 291
292 export function pfor(items, cb) {
293 argumentNotNull(cb, "cb");
294
295 if (isPromise(items))
296 return items.then(data => {
297 return pmap(data, cb);
298 });
299
300 if (isNull(items) || !items.length)
301 return items;
302
303 let i = 0;
304
305 function next() {
306 while (i < items.length) {
307 const r = cb(items[i], i);
308 i++;
309 if (isPromise(r))
310 return r.then(next);
311 }
312 }
313
314 return next();
315 }
316
271 317 /**
272 318 * Π’Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт ΠΈΠ· ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, ΠΈΠ»ΠΈ обСщания, Ссли Π²
273 319 * качСствС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΎΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ массив.
274 320 *
275 321 * @param {Function} cb ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, Π΅ΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ
276 322 * элСмСнт ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π² случаС успСха
277 323 * @param {Function} err ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Ссли массив пустой, Π»ΠΈΠ±ΠΎ
278 324 * нС массив
279 325 *
280 326 * @remarks Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π½ΠΈ cb Π½ΠΈ err, Ρ‚ΠΎΠ³Π΄Π° функция Π²Π΅Ρ€Π½Π΅Ρ‚ Π»ΠΈΠ±ΠΎ
281 327 * ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт.
282 328 * @async
283 329 */
284 330 export function first(sequence, cb: (x) => any, err: (x) => any) {
285 331 if (sequence) {
286 332 if (isPromise(sequence)) {
287 333 return sequence.then(res => first(res, cb, err));
288 334 } else if (sequence && "length" in sequence) {
289 335 if (sequence.length === 0) {
290 336 if (err)
291 337 return err(new Error("The sequence is empty"));
292 338 else
293 339 throw new Error("The sequence is empty");
294 340 }
295 341 return cb ? cb(sequence[0]) : sequence[0];
296 342 }
297 343 }
298 344
299 345 if (err)
300 346 return err(new Error("The sequence is required"));
301 347 else
302 348 throw new Error("The sequence is required");
303 349 }
304 350
305 351 export function destroy(d) {
306 352 if (d && "destroy" in d)
307 353 d.destroy();
308 354 }
@@ -1,173 +1,177
1 1 import { isPrimitive, isNull, each } from "../safe";
2 2 import { MapOf } from "../interfaces";
3 3
4 4 type SubstFn = (name: string, format?: string) => string;
5 5 type TemplateFn = (subst: SubstFn) => string;
6 6 type ConvertFn = (value: any, format?: string) => string;
7 7
8 8 const map = {
9 9 "\\{": "&curlopen;",
10 10 "\\}": "&curlclose;",
11 11 "&": "&amp;",
12 12 "\\:": "&colon;"
13 13 };
14 14
15 15 const rev = {
16 16 curlopen: "{",
17 17 curlclose: "}",
18 18 amp: "&",
19 19 colon: ":"
20 20 };
21 21
22 22 function espaceString(s: string) {
23 23 if (!s)
24 24 return s;
25 25 return "'" + s.replace(/('|\\)/g, "\\$1").replace("\n", "\\n") + "'";
26 26 }
27 27
28 28 function encode(s: string) {
29 29 if (!s)
30 30 return s;
31 31 return s.replace(/\\{|\\}|&|\\:|\n/g, m => map[m] || m);
32 32 }
33 33
34 34 function decode(s: string) {
35 35 if (!s)
36 36 return s;
37 37 return s.replace(/&(\w+);/g, (m, $1) => rev[$1] || m);
38 38 }
39 39
40 40 function subst(s: string) {
41 41 const i = s.indexOf(":");
42 42 let name: string;
43 43 let pattern: string;
44 44 if (i >= 0) {
45 45 name = s.substr(0, i);
46 46 pattern = s.substr(i + 1);
47 47 } else {
48 48 name = s;
49 49 }
50 50
51 51 if (pattern)
52 52 return [
53 53 espaceString(decode(name)),
54 54 espaceString(decode(pattern))];
55 55 else
56 56 return [espaceString(decode(name))];
57 57 }
58 58
59 59 function _compile(str: string) {
60 60 if (!str)
61 61 return () => void 0;
62 62
63 63 const chunks = encode(str).split("{");
64 64 let chunk: string;
65 65
66 66 const code = ["var result=[];"];
67 67
68 68 for (let i = 0; i < chunks.length; i++) {
69 69 chunk = chunks[i];
70 70
71 71 if (i === 0) {
72 72 if (chunk)
73 73 code.push("result.push(" + espaceString(decode(chunk)) +
74 74 ");");
75 75 } else {
76 76 const len = chunk.indexOf("}");
77 77 if (len < 0)
78 78 throw new Error("Unbalanced substitution #" + i);
79 79
80 80 code.push("result.push(subst(" +
81 81 subst(chunk.substr(0, len)).join(",") + "));");
82 82 if (chunk.length > len + 1)
83 83 code.push("result.push(" +
84 84 espaceString(decode(chunk.substr(len + 1))) + ");");
85 85 }
86 86 }
87 87
88 88 code.push("return result.join('');");
89 89
90 90 // the code for this function is generated from the template
91 91 // tslint:disable-next-line:function-constructor
92 92 return new Function("subst", code.join("\n")) as TemplateFn;
93 93 }
94 94
95 95 const cache: MapOf<TemplateFn> = {};
96 96
97 97 export function compile(template: string) {
98 98 let compiled = cache[template];
99 99 if (!compiled) {
100 100 compiled = _compile(template);
101 101 cache[template] = compiled;
102 102 }
103 103 return compiled;
104 104 }
105 105
106 106 function defaultConverter(value: any, pattern: string) {
107 107 if (pattern && pattern.toLocaleLowerCase() === "json") {
108 108 const seen = [];
109 109 return JSON.stringify(value, (k, v) => {
110 110 if (!isPrimitive(v)) {
111 111 const id = seen.indexOf(v);
112 112 if (id >= 0)
113 113 return "@ref-" + id;
114 114 else {
115 115 seen.push(v);
116 116 return v;
117 117 }
118 118 } else {
119 119 return v;
120 120 }
121 121 }, 2);
122 122 } else if (isNull(value)) {
123 123 return "";
124 124 } else if (value instanceof Date) {
125 125 return value.toISOString();
126 126 } else {
127 return pattern ? value.toString(pattern) : value.toString();
127 return value.toString();
128 128 }
129 129 }
130 130
131 131 export class Formatter {
132 132 _converters: ConvertFn[];
133 133
134 134 constructor(converters?: ConvertFn[]) {
135 135 this._converters = converters || [];
136 136 this._converters.push(defaultConverter);
137 137 }
138 138
139 139 convert(value: any, pattern: string) {
140 140 for (const c of this._converters) {
141 141 const res = c(value, pattern);
142 142 if (!isNull(res))
143 143 return res;
144 144 }
145 145 return "";
146 146 }
147 147
148 148 format(msg: string, ...args: any[]) {
149 149 const template = compile(msg);
150 150
151 151 return template((name, pattern) => {
152 152 const value = args[name];
153 153 return !isNull(value) ? this.convert(value, pattern) : "";
154 154 });
155 155
156 156 }
157 157
158 158 compile(msg: string) {
159 159 const template = compile(msg);
160 160 return (...args: any[]) => {
161 161 return template((name, pattern) => {
162 162 const value = args[name];
163 163 return !isNull(value) ? this.convert(value, pattern) : "";
164 164 });
165 165 };
166 166 }
167 167 }
168 168
169 169 const _default = new Formatter();
170 170
171 171 export function format(msg: string, ...args: any[]) {
172 172 return _default.format(msg, ...args);
173 173 }
174
175 export function convert(value: any, pattern: string) {
176 return _default.format(value, pattern);
177 }
General Comments 0
You need to be logged in to leave comments. Login now