##// END OF EJS Templates
Merge with ioc ts support
cin -
r138:a2fb9af6341c merge v1.4.0-rc1 default
parent child
Show More
@@ -0,0 +1,41
1 import { IDestroyable } from "./interfaces";
2 import { Observable } from "./Observable";
3
4 /**
5 * Event proviers are used to produce events, throug this object you can feed
6 * the Observable with input events. Once the EventProvider is destroyed the
7 * bound obsevable is disconnected and marked as 'done'.
8 */
9 export class EventProvider<T> implements IDestroyable {
10
11 _observable: Observable<T> | undefined;
12
13 _next: ((evt: T) => void) | undefined;
14 _done: (() => void) | undefined;
15
16 constructor() {
17 this._observable = new Observable<T>((next, _error, done) => {
18 this._next = next;
19 this._done = done;
20 });
21 }
22
23 destroy(): void {
24 if (this._observable) {
25 // break all references
26 this._observable = undefined;
27 this._next = undefined;
28 this._done = undefined;
29 }
30 }
31 post(event: T) {
32 return this._next && this._next(event);
33 }
34
35 getObservable() {
36 if (!this._observable)
37 throw new Error("The object is destroyed");
38
39 return this._observable;
40 }
41 }
@@ -0,0 +1,83
1 import { argumentNotEmptyString, each } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
3 import { Descriptor, PartialServiceMap, TypeOfService, ContainerKeys } from "./interfaces";
4 import { ActivationError } from "./ActivationError";
5
6 export interface ReferenceDescriptorParams<S extends object, K extends ContainerKeys<S>> {
7 name: K;
8 optional?: boolean;
9 default?: TypeOfService<S, K>;
10 services?: PartialServiceMap<S>;
11 }
12
13 export class LazyReferenceDescriptor<S extends object = any, K extends ContainerKeys<S> = ContainerKeys<S>>
14 implements Descriptor<S, ((args?: PartialServiceMap<S>) => TypeOfService<S, K>)> {
15
16 _name: K;
17
18 _optional = false;
19
20 _default: TypeOfService<S, K> | undefined;
21
22 _services: PartialServiceMap<S>;
23
24 constructor(opts: ReferenceDescriptorParams<S, K>) {
25 argumentNotEmptyString(opts && opts.name, "opts.name");
26 this._name = opts.name;
27 this._optional = !!opts.optional;
28 this._default = opts.default;
29
30 this._services = (opts.services || {}) as PartialServiceMap<S>;
31 }
32
33 activate(context: ActivationContext<S>) {
34 // добавляем сервисы
35 if (this._services) {
36 each(this._services, (v, k) => context.register(k, v));
37 }
38
39 const saved = context.clone();
40
41 return (cfg?: PartialServiceMap<S>): any => {
42 // защищаем контекст на случай исключения в процессе
43 // активации
44 const ct = cfg ? saved.clone() : saved;
45 try {
46 if (cfg) {
47 each(cfg, (v, k) => ct.register(k, v));
48 }
49
50 return this._optional ? ct.resolve(this._name, this._default) : ct
51 .resolve(this._name);
52 } catch (error) {
53 throw new ActivationError(this._name.toString(), ct.getStack(), error);
54 }
55 };
56 }
57
58 toString() {
59 const opts = [];
60 if (this._optional)
61 opts.push("optional");
62
63 opts.push("lazy");
64
65 const parts = [
66 "@ref "
67 ];
68 if (opts.length) {
69 parts.push("{");
70 parts.push(opts.join());
71 parts.push("} ");
72 }
73
74 parts.push(this._name.toString());
75
76 if (this._default !== undefined && this._default !== null) {
77 parts.push(" = ");
78 parts.push(String(this._default));
79 }
80
81 return parts.join("");
82 }
83 }
@@ -0,0 +1,198
1 import { IDestroyable, MapOf } from "../interfaces";
2 import { argumentNotNull, isDestroyable, primitive, isNull, argumentNotEmptyString } from "../safe";
3 import { ILifetime } from "./interfaces";
4 import { ActivationContext } from "./ActivationContext";
5 import { Container } from "./Container";
6
7 function safeCall(item: () => void) {
8 try {
9 item();
10 } catch {
11 // silence!
12 }
13 }
14
15 const emptyLifetime: ILifetime = Object.freeze({
16 has() {
17 return false;
18 },
19
20 initialize() {
21
22 },
23
24 get() {
25 throw new Error("The specified item isn't registered with this lifetime manager");
26 },
27
28 store() {
29 // does nothing
30 }
31
32 });
33
34 const unknownLifetime: ILifetime = Object.freeze({
35 has() {
36 return false;
37 },
38 initialize() {
39 throw new Error("Can't call initialize on the unknown lifetime object");
40 },
41 get() {
42 throw new Error("The lifetime object isn't initialized");
43 },
44 store() {
45 throw new Error("Can't store a value in the unknown lifetime object");
46 }
47 });
48
49 let nextId = 0;
50
51 const singletons: { [k in keyof any]: any; } = {};
52
53 export class LifetimeManager implements IDestroyable {
54 private _cleanup: (() => void)[] = [];
55 private _cache: MapOf<any> = {};
56 private _destroyed = false;
57
58 private _pending: MapOf<boolean> = {};
59
60 create(): ILifetime {
61 const self = this;
62 const id = ++nextId;
63 return {
64 has() {
65 return (id in self._cache);
66 },
67
68 get() {
69 const t = self._cache[id];
70 if (t === undefined)
71 throw new Error(`The item with with the key ${id} isn't found`);
72 return t;
73 },
74
75 initialize() {
76 if (self._pending[id])
77 throw Error(`Cyclic reference detected: the item with the key ${id} is already activating.`);
78 self._pending[id] = true;
79 },
80
81 store(item: any, cleanup?: (item: any) => void) {
82 argumentNotNull(id, "id");
83 argumentNotNull(item, "item");
84
85 if (this.has())
86 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
87 delete self._pending[id];
88
89 self._cache[id] = item;
90
91 if (self._destroyed)
92 throw new Error("Lifetime manager is destroyed");
93 if (cleanup) {
94 self._cleanup.push(() => cleanup(item));
95 } else if (isDestroyable(item)) {
96 self._cleanup.push(() => item.destroy());
97 }
98 }
99 };
100 }
101
102 destroy() {
103 if (!this._destroyed) {
104 this._destroyed = true;
105 this._cleanup.forEach(safeCall);
106 this._cleanup.length = 0;
107 }
108 }
109
110 static empty(): ILifetime {
111 return emptyLifetime;
112 }
113
114 static hierarchyLifetime(): ILifetime {
115 let _lifetime = unknownLifetime;
116 return {
117 initialize(context: ActivationContext<any>) {
118 if (_lifetime !== unknownLifetime)
119 throw new Error("Cyclic reference activation detected");
120
121 _lifetime = context.getContainer().getLifetimeManager().create();
122 },
123 get() {
124 return _lifetime.get();
125 },
126 has() {
127 return _lifetime.has();
128 },
129 store(item: any, cleanup?: (item: any) => void) {
130 return _lifetime.store(item, cleanup);
131 }
132 };
133 }
134
135 static contextLifetime(): ILifetime {
136 let _lifetime = unknownLifetime;
137 return {
138 initialize(context: ActivationContext<any>) {
139 if (_lifetime !== unknownLifetime)
140 throw new Error("Cyclic reference detected");
141 _lifetime = context.createLifetime();
142 },
143 get() {
144 return _lifetime.get();
145 },
146 has() {
147 return _lifetime.has();
148 },
149 store(item: any) {
150 _lifetime.store(item);
151 }
152 };
153 }
154
155 static singletonLifetime(typeId: string): ILifetime {
156 argumentNotEmptyString(typeId, "typeId");
157 let pending = false;
158 return {
159 has() {
160 return typeId in singletons;
161 },
162 get() {
163 if (!this.has())
164 throw new Error(`The instance ${typeId} doesn't exists`);
165 return singletons[typeId];
166 },
167 initialize() {
168 if (pending)
169 throw new Error("Cyclic reference detected");
170 pending = true;
171 },
172 store(item: any) {
173 singletons[typeId] = item;
174 pending = false;
175 }
176 };
177 }
178
179 static containerLifetime(container: Container<any>) {
180 let _lifetime = unknownLifetime;
181 return {
182 initialize(context: ActivationContext<any>) {
183 if (_lifetime !== unknownLifetime)
184 throw new Error("Cyclic reference detected");
185 _lifetime = container.getLifetimeManager().create();
186 },
187 get() {
188 return _lifetime.get();
189 },
190 has() {
191 return _lifetime.has();
192 },
193 store(item: any) {
194 _lifetime.store(item);
195 }
196 };
197 }
198 }
@@ -0,0 +1,147
1 import { Resolver, RegistrationBuilder } from "./interfaces";
2 import { Container } from "../Container";
3 import { Descriptor, ILifetime, ActivationType, PartialServiceMap } from "../interfaces";
4 import { DescriptorImpl } from "./DescriptorImpl";
5 import { LifetimeManager } from "../LifetimeManager";
6 import { isString, each, isPrimitive, isPromise, oid } from "../../safe";
7
8 export class DescriptorBuilder<S extends object, T> {
9 private readonly _container: Container<S>;
10 private readonly _cb: (d: Descriptor<S, T>) => void;
11
12 private readonly _eb: (err: any) => void;
13
14 private _lifetime = LifetimeManager.empty();
15
16 private _overrides?: PartialServiceMap<S>;
17
18 private _cleanup?: (item: T) => void;
19
20 private _factory?: (resolve: Resolver<S>) => T;
21
22 private _pending = 1;
23
24 private _failed = false;
25
26 constructor(container: Container<S>, cb: (d: Descriptor<S, T>) => void, eb: (err: any) => void) {
27 this._container = container;
28 this._cb = cb;
29 this._eb = eb;
30 }
31
32 build<T2>(): DescriptorBuilder<S, T2> {
33 this._defer();
34 return new DescriptorBuilder<S, T2>(this._container, () => this._complete(), err => this._fail(err));
35 }
36
37 override<K extends keyof S>(name: K, builder: RegistrationBuilder<S, S[K]>): this;
38 override<K extends keyof S>(services: { [name in K]: RegistrationBuilder<S, S[K]> }): this;
39 override<K extends keyof S>(nameOrServices: K | { [name in K]: RegistrationBuilder<S, S[K]> }, builder?: RegistrationBuilder<S, S[K]>): this {
40 const overrides: PartialServiceMap<S> = this._overrides ?
41 this._overrides :
42 (this._overrides = {});
43
44 const guard = (v: void | Promise<void>) => {
45 if (isPromise(v))
46 v.catch(err => this._fail(err));
47 };
48
49 if (isPrimitive(nameOrServices)) {
50 if (builder) {
51 this._defer();
52 const d = new DescriptorBuilder<S, S[K]>(
53 this._container,
54 result => {
55 overrides[nameOrServices] = result;
56 this._complete();
57 },
58 err => this._fail(err)
59 );
60
61 try {
62 guard(builder(d));
63 } catch (err) {
64 this._fail(err);
65 }
66 }
67 } else {
68 each(nameOrServices, (v, k) => this.override(k, v));
69 }
70 return this;
71 }
72
73 lifetime(lifetime: "singleton", typeId: string): this;
74 lifetime(lifetime: ILifetime | Exclude<ActivationType, "singleton">): this;
75 lifetime(lifetime: ILifetime | ActivationType, typeId?: string): this {
76 if (isString(lifetime)) {
77 this._lifetime = this._resolveLifetime(lifetime, typeId);
78 } else {
79 this._lifetime = lifetime;
80 }
81 return this;
82 }
83
84 cleanup(cb: (item: T) => void): this {
85 this._cleanup = cb;
86 return this;
87 }
88
89 factory(f: (resolve: Resolver<S>) => T): void {
90 this._factory = f;
91 this._complete();
92 }
93
94 value(v: T): void {
95 this._cb({
96 activate() {
97 return v;
98 }
99 });
100 }
101
102 _resolveLifetime(activation: ActivationType, typeId?: string | object) {
103 switch (activation) {
104 case "container":
105 return LifetimeManager.containerLifetime(this._container);
106 case "hierarchy":
107 return LifetimeManager.hierarchyLifetime();
108 case "context":
109 return LifetimeManager.contextLifetime();
110 case "singleton":
111 if (!typeId)
112 throw Error("The singleton activation requires a typeId");
113
114 const _oid = isString(typeId) ? typeId : oid(typeId);
115
116 return LifetimeManager.singletonLifetime(_oid);
117 default:
118 return LifetimeManager.empty();
119 }
120 }
121
122 _defer() {
123 this._pending++;
124 }
125
126 _complete() {
127 if (--this._pending === 0) {
128 if (!this._factory)
129 throw new Error("The factory must be specified");
130
131 this._cb(new DescriptorImpl<S, T>({
132 lifetime: this._lifetime,
133 factory: this._factory,
134 overrides: this._overrides,
135 cleanup: this._cleanup
136 }));
137 }
138 }
139
140 _fail(err: any) {
141 if (!this._failed) {
142 this._failed = true;
143 this._eb.call(undefined, err);
144 }
145 }
146
147 }
@@ -0,0 +1,63
1 import { Descriptor, PartialServiceMap, ILifetime, ContainerKeys } from "../interfaces";
2 import { ActivationContext } from "../ActivationContext";
3 import { each } from "../../safe";
4 import { DependencyOptions, LazyDependencyOptions, Resolver } from "./interfaces";
5
6 export interface DescriptorImplArgs<S extends object, T> {
7 lifetime: ILifetime;
8
9 factory: (resolve: Resolver<S>) => T;
10
11 cleanup?: (item: T) => void;
12
13 overrides?: PartialServiceMap<S>;
14 }
15
16 export class DescriptorImpl<S extends object, T> implements Descriptor<S, T> {
17
18 private readonly _overrides?: PartialServiceMap<S>;
19
20 private readonly _lifetime: ILifetime;
21
22 private readonly _factory: (resolve: Resolver<S>) => T;
23
24 private readonly _cleanup?: (item: T) => void;
25
26 constructor(args: DescriptorImplArgs<S, T>) {
27 this._lifetime = args.lifetime;
28 this._factory = args.factory;
29 if (args.cleanup)
30 this._cleanup = args.cleanup;
31 if (args.overrides)
32 this._overrides = args.overrides;
33 }
34
35 activate(context: ActivationContext<S>): T {
36
37 if (this._lifetime.has())
38 return this._lifetime.get();
39
40 this._lifetime.initialize(context);
41
42 if (this._overrides)
43 each(this._overrides, (v, k) => context.register(k, v));
44
45 const resolve = (name: ContainerKeys<S>, opts?: DependencyOptions | LazyDependencyOptions) => {
46 if (opts && "lazy" in opts && opts.lazy) {
47 const c2 = context.clone();
48 return () => {
49 return opts.optional ? c2.resolve(name, opts.default) : c2.resolve(name);
50 };
51 } else {
52 return opts && opts.optional ? context.resolve(name, opts.default) : context.resolve(name);
53 }
54 };
55
56 const instance = this._factory.call(undefined, resolve);
57
58 this._lifetime.store(instance, this._cleanup);
59
60 return instance;
61 }
62
63 }
@@ -0,0 +1,60
1 import { Container } from "../Container";
2 import { argumentNotNull, each, isPrimitive, isPromise } from "../../safe";
3 import { DescriptorBuilder } from "./DescriptorBuilder";
4 import { RegistrationBuilder, FluentRegistrations } from "./interfaces";
5 import { Cancellation } from "../../Cancellation";
6
7 export class FluentConfiguration<S extends object, Y extends keyof S = keyof S> {
8
9 _builders: { [k in keyof S]?: RegistrationBuilder<S, S[k]> } = {};
10
11 register<K extends Y>(name: K, builder: RegistrationBuilder<S, S[K]>): FluentConfiguration<S, Exclude<Y, K>>;
12 register<K extends Y>(config: FluentRegistrations<K, S>): FluentConfiguration<S, Exclude<Y, K>>;
13 register<K extends Y>(nameOrConfig: K | FluentRegistrations<K, S>, builder?: RegistrationBuilder<S, S[K]>): FluentConfiguration<S, Exclude<Y, K>> {
14 if (isPrimitive(nameOrConfig)) {
15 argumentNotNull(builder, "builder");
16 this._builders[nameOrConfig] = builder;
17 } else {
18 each(nameOrConfig, (v, k) => this.register(k, v));
19 }
20
21 return this;
22 }
23
24 apply<SC extends object>(target: Container<SC>, ct = Cancellation.none) {
25
26 let pending = 1;
27
28 const _t2 = target as unknown as Container<SC & S>;
29
30 return new Promise<Container<SC & S>>((resolve, reject) => {
31 function guard(v: void | Promise<void>) {
32 if (isPromise(v))
33 v.catch(reject);
34 }
35
36 function complete() {
37 if (!--pending)
38 resolve(_t2);
39 }
40 each(this._builders, (v, k) => {
41 pending++;
42 const d = new DescriptorBuilder<SC & S, any>(_t2,
43 result => {
44 _t2.register(k, result);
45 complete();
46 },
47 reject
48 );
49
50 try {
51 guard(v(d, ct));
52 } catch (e) {
53 reject(e);
54 }
55 });
56 complete();
57 });
58 }
59
60 }
@@ -0,0 +1,52
1 import { primitive } from "../../safe";
2 import { TypeOfService, ContainerKeys, ActivationType, ILifetime } from "../interfaces";
3 import { ICancellation } from "../../interfaces";
4
5 export interface DependencyOptions {
6 optional?: boolean;
7 default?: any;
8 }
9
10 export interface LazyDependencyOptions extends DependencyOptions {
11 lazy: true;
12 }
13
14 export type ExtractService<K, S> = K extends keyof S ? S[K] : never;
15
16 export type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
17 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
18 D extends { $type: new (...args: any[]) => infer I } ? I :
19 D extends { $factory: (...args: any[]) => infer R } ? R :
20 WalkDependencies<D, S>;
21
22 export type WalkDependencies<D, S> = D extends primitive ? D :
23 { [K in keyof D]: ExtractDependency<D[K], S> };
24
25 export type InferReferenceType<S extends object, K extends ContainerKeys<S>, O> = O extends { default: infer X } ? (TypeOfService<S, K> | X) :
26 O extends { optional: true } ? (TypeOfService<S, K> | undefined) :
27 TypeOfService<S, K>;
28
29 export interface Resolver<S extends object> {
30 <K extends ContainerKeys<S>, O extends LazyDependencyOptions>(this: void, name: K, opts: O): () => InferReferenceType<S, K, O>;
31 <K extends ContainerKeys<S>, O extends DependencyOptions>(this: void, name: K, opts?: O): InferReferenceType<S, K, O>;
32 }
33
34 export interface DescriptorBuilder<S extends object, T> {
35 factory(f: (resolve: Resolver<S>) => T): void;
36
37 build<T2>(): DescriptorBuilder<S, T2>;
38
39 override<K extends keyof S>(name: K, builder: RegistrationBuilder<S, S[K]>): this;
40 override<K extends keyof S>(services: { [name in K]: RegistrationBuilder<S, S[K]> }): this;
41
42 lifetime(lifetime: "singleton", typeId: any): this;
43 lifetime(lifetime: ILifetime | Exclude<ActivationType, "singleton">): this;
44
45 cleanup(cb: (item: T) => void): this;
46
47 value(v: T): void;
48 }
49
50 export type RegistrationBuilder<S extends object, T> = (d: DescriptorBuilder<S, T>, ct?: ICancellation) => void | Promise<void>;
51
52 export type FluentRegistrations<K extends keyof S, S extends object> = { [k in K]: RegistrationBuilder<S, S[k]> };
@@ -0,0 +1,11
1 import { isPrimitive } from "../safe";
2 import { Descriptor } from "./interfaces";
3 import { FluentConfiguration } from "./fluent/FluentConfiguration";
4 export function isDescriptor(x: any): x is Descriptor {
5 return (!isPrimitive(x)) &&
6 (x.activate instanceof Function);
7 }
8
9 export function fluent<S extends object>() {
10 return new FluentConfiguration<S>();
11 }
@@ -0,0 +1,28
1 import { Bar } from "./Bar";
2
3 // export service descriptor
4 // через service передается информация о типе зависимости
5 // даже если это шаблон.
6 // export const service = annotate<Box<Bar>>();
7
8 // @service.wire()
9 export class Box<T> {
10 private _value: T | undefined;
11
12 constructor(value?: T) {
13 this._value = value;
14 }
15
16 // @service.inject(dependency("bar"))
17 setValue(value: T) {
18 this._value = value;
19 return value;
20 }
21
22 getValue() {
23 if (this._value === undefined)
24 throw new Error("Trying to get a value from the empty box");
25
26 return this._value;
27 }
28 }
@@ -0,0 +1,14
1 import { Services } from "./services";
2 import { fluent } from "../di/traits";
3 import { Box } from "./Box";
4
5 export default fluent<Services>().register({
6 host: it => it.value("example.com"),
7
8 foo: it => import("./Foo").then(({ Foo }) => it
9 .factory(() => new Foo())
10 ),
11
12 box: it => it
13 .factory($dependency => new Box($dependency("foo")))
14 });
@@ -0,0 +1,15
1 import { Foo } from "./Foo";
2 import { Bar } from "./Bar";
3 import { Box } from "./Box";
4
5 /**
6 * Сервисы доступные внутри контейнера
7 */
8 export interface Services {
9 foo: Foo;
10
11 box: Box<Foo>;
12
13 host: string;
14
15 }
@@ -0,0 +1,63
1 import { test } from "./TestTraits";
2 import { fluent } from "../di/traits";
3 import { Bar } from "../mock/Bar";
4 import { Container } from "../di/Container";
5 import { Foo } from "../mock/Foo";
6 import { Box } from "../mock/Box";
7 import { delay } from "../safe";
8 import { Services } from "../mock/services";
9
10 test("Simple fluent config", async t => {
11 const config = fluent<{ host: string; bar: Bar; foo: Foo }>()
12 .register({
13 host: it => it.value("example.com"),
14 bar: it => it.factory(resolve => new Bar({ host: resolve("host") }, "s-bar")),
15 foo: it => import("../mock/Foo").then(m => it.lifetime("container").factory(() => new m.Foo()))
16 });
17
18 const c1 = new Container<{}>();
19 const container = await config.apply(c1);
20
21 t.equal(container.resolve("host"), "example.com", "The value should be resolved");
22 t.assert(container.resolve("bar"), "The service should de activated");
23 t.equal(container.resolve("foo"), container.resolve("foo"), "The service should be activated once");
24 });
25
26 test("Nested async configuration", async t => {
27 const container = await new Container<{
28 foo: Foo;
29 box: Box<Foo>
30 }>().fluent({
31 foo: it => delay(0).then(() => it.factory(() => new Foo())),
32 box: it => it.lifetime("context").factory($dependency => new Box($dependency("foo")))
33 });
34
35 t.assert(container.resolve("box").getValue(), "The dependency should be set");
36 t.equals(container.resolve("box").getValue(), container.resolve("box").getValue(), "The service should be activated once")
37 });
38
39 test("Bad fluent config", async t => {
40 try {
41 await new Container<{
42 foo: Foo;
43 box: Box<Foo>
44 }>().fluent({
45 foo: it => delay(0).then(() => it.factory(() => new Foo())),
46 box: it => it.lifetime("context")
47 .override("foo", () => { throw new Error("bad override"); })
48 .factory($dependency => new Box($dependency("foo")))
49 });
50 t.fail("Should throw");
51 } catch (e) {
52 t.pass("The configuration should fail");
53 t.equal(e.message, "bad override", "the error should pass");
54 }
55 });
56
57 test("Load fluent config", async t => {
58 const container = new Container<Services>();
59
60 await container.configure("../mock/config", { contextRequire: require });
61
62 t.assert(container.resolve("host"), "Should resolve simple value");
63 });
@@ -14,4 +14,15
14 <natures>
14 <natures>
15 <nature>org.eclipse.buildship.core.gradleprojectnature</nature>
15 <nature>org.eclipse.buildship.core.gradleprojectnature</nature>
16 </natures>
16 </natures>
17 <filteredResources>
18 <filter>
19 <id>1599549685358</id>
20 <name></name>
21 <type>30</type>
22 <matcher>
23 <id>org.eclipse.core.resources.regexFilterMatcher</id>
24 <arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
25 </matcher>
26 </filter>
27 </filteredResources>
17 </projectDescription>
28 </projectDescription>
@@ -1,2 +1,13
1 arguments=
2 auto.sync=false
3 build.scans.enabled=false
4 connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
1 connection.project.dir=
5 connection.project.dir=
2 eclipse.preferences.version=1
6 eclipse.preferences.version=1
7 gradle.user.home=
8 java.home=/usr/lib64/jvm/java
9 jvm.arguments=
10 offline.mode=false
11 override.workspace.settings=true
12 show.console.view=true
13 show.executions.view=true
@@ -1,5 +1,5
1 plugins {
1 plugins {
2 id "org.implab.gradle-typescript" version "1.3.0"
2 id "org.implab.gradle-typescript" version "1.3.3"
3 id "org.implab.gradle-hg"
3 id "org.implab.gradle-hg"
4 id "ivy-publish"
4 id "ivy-publish"
5 }
5 }
@@ -49,10 +49,14 typescript {
49 compilerOptions {
49 compilerOptions {
50 types = []
50 types = []
51 declaration = true
51 declaration = true
52 experimentalDecorators = true
53 strict = true
54 // dojo-typings are sick
55 skipLibCheck = true
52
56
53 if(symbols != 'none') {
57 if(symbols != 'none') {
54 sourceMap = true
58 sourceMap = true
55 sourceRoot = "_src"
59 sourceRoot = packageName
56 }
60 }
57
61
58 if (flavour == 'node') {
62 if (flavour == 'node') {
@@ -5,7 +5,7
5 самостоятельных событий, например, связанных с действиями пользователя.
5 самостоятельных событий, например, связанных с действиями пользователя.
6
6
7 Является реализацией классического шаблона наблюдателя с возможность сообщить
7 Является реализацией классического шаблона наблюдателя с возможность сообщить
8 о коце потока событий. Данная реализация не содержит никаких дополнительных
8 о конце потока событий. Данная реализация не содержит никаких дополнительных
9 функций, таких как фильтрация, канал с состоянием, преобразования сообщений и
9 функций, таких как фильтрация, канал с состоянием, преобразования сообщений и
10 т.п. Это сделано специально, чтобы реализация оставалась максимально простой.
10 т.п. Это сделано специально, чтобы реализация оставалась максимально простой.
11
11
@@ -20,7 +20,7 var events = new Observable(async (notif
20 notify(i);
20 notify(i);
21 }
21 }
22 // по окончании последовательности информируем, что событий больше не будет
22 // по окончании последовательности информируем, что событий больше не будет
23 compelte();
23 complete();
24 });
24 });
25
25
26 // создаем окно с отображением хода событий
26 // создаем окно с отображением хода событий
@@ -49,9 +49,9 let firstEvent = await events.next();
49 `Observable` можно создавать из событий другого объекта, например, виджета:
49 `Observable` можно создавать из событий другого объекта, например, виджета:
50
50
51 ```ts
51 ```ts
52 // клсс
52 // класс
53 class Canvas {
53 class Canvas {
54 readonly mouseMove: IObservable<[number,number]>
54 mouseMove: IObservable<[number,number]>;
55
55
56 postCreate() {
56 postCreate() {
57 // превращаем события виджета в Observable
57 // превращаем события виджета в Observable
@@ -98,7 +98,7 class PositionTracker implements IDestro
98 }
98 }
99 ```
99 ```
100
100
101 Существует также несколько варинатов получения сообщений
101 Существует также несколько вариантов получения сообщений
102
102
103 ```ts
103 ```ts
104 // регистрация метода для получений событий
104 // регистрация метода для получений событий
@@ -128,7 +128,7 class Map {
128 let evt = this.viewport.click.next(ct);
128 let evt = this.viewport.click.next(ct);
129
129
130 // преобразуем позицию на экране в координаты карты
130 // преобразуем позицию на экране в координаты карты
131 return this.clientToCoodinates([evt.clientx,evt.clientY]);
131 return this.clientToCoordinates([evt.clientX,evt.clientY]);
132 }
132 }
133 }
133 }
134
134
@@ -142,8 +142,8 let coords = await map.peekCoordinates()
142
142
143 ## Observable и последовательности
143 ## Observable и последовательности
144
144
145 Можно сичтать, что `Observable` это некоторая аналогия итератора только в
145 Можно считать, что `Observable` это некоторая аналогия итератора только в
146 парадигме событийного (или реактивного) программировния. Следует также понимать,
146 парадигме событийного (или реактивного) программирования. Следует также понимать,
147 что при переходе от синхронного процедурного программирования к событийному так
147 что при переходе от синхронного процедурного программирования к событийному так
148 же меняется и направление управления (Inverse Of Control), что означает
148 же меняется и направление управления (Inverse Of Control), что означает
149 следующее:
149 следующее:
@@ -153,7 +153,7 let coords = await map.peekCoordinates()
153 * при работе с `Observable` клиенты вынуждены обрабатывать эти события по мере
153 * при работе с `Observable` клиенты вынуждены обрабатывать эти события по мере
154 их поступления и не могут на это повлиять.
154 их поступления и не могут на это повлиять.
155
155
156 Последний пункт можно изменить применив, например, буффер или канал с
156 Последний пункт можно изменить применив, например, буфер или канал с
157 состоянием, т.е. очередь, но данные механизмы выходят за рамки простого шаблона
157 состоянием, т.е. очередь, но данные механизмы выходят за рамки простого шаблона
158 наблюдателя.
158 наблюдателя.
159
159
@@ -179,4 +179,4 events.on((data) => {
179 // будет вызван для всех сообщений
179 // будет вызван для всех сообщений
180 processEvent(data);
180 processEvent(data);
181 });
181 });
182 ``` No newline at end of file
182 ```
@@ -71,7 +71,6 function(declare, safe, when, QueryResul
71 });
71 });
72 mapped.total = total;
72 mapped.total = total;
73 var results = new QueryResults(mapped);
73 var results = new QueryResults(mapped);
74 console.log(results);
75 return results;
74 return results;
76 });
75 });
77 },
76 },
@@ -12,7 +12,7 const trace = TraceSource.get(m.id);
12 trace.debug("globalRequire = {0}", globalRequire);
12 trace.debug("globalRequire = {0}", globalRequire);
13
13
14 class ModuleResolver {
14 class ModuleResolver {
15 _base: string;
15 _base: string | undefined;
16 _require: Require;
16 _require: Require;
17
17
18 constructor(req: Require, base?: string) {
18 constructor(req: Require, base?: string) {
@@ -9,7 +9,7 export = {
9 cb = filter;
9 cb = filter;
10 filter = undefined;
10 filter = undefined;
11 }
11 }
12 let test: Predicate<string>;
12 let test: Predicate<string> | undefined;
13 if (filter instanceof RegExp) {
13 if (filter instanceof RegExp) {
14 test = chId => filter.test(chId);
14 test = chId => filter.test(chId);
15 } else if (filter instanceof Function) {
15 } else if (filter instanceof Function) {
@@ -21,7 +21,7 export = {
21 if (test) {
21 if (test) {
22 TraceSource.on(source => {
22 TraceSource.on(source => {
23 source.level = this.level;
23 source.level = this.level;
24 if (test(source.id))
24 if (test && test(source.id))
25 source.events.on(cb);
25 source.events.on(cb);
26 });
26 });
27 } else {
27 } else {
@@ -2,6 +2,7 import * as format from "./format";
2 import { TraceSource, DebugLevel } from "../log/TraceSource";
2 import { TraceSource, DebugLevel } from "../log/TraceSource";
3 import { ITemplateParser, TokenType } from "./TemplateParser";
3 import { ITemplateParser, TokenType } from "./TemplateParser";
4 import m = require("module");
4 import m = require("module");
5 import { isKeyof } from "../safe";
5
6
6 const trace = TraceSource.get(m.id);
7 const trace = TraceSource.get(m.id);
7
8
@@ -21,7 +22,7 const htmlEscaper = /[&<>"'\/]/g;
21
22
22 // Escape a string for HTML interpolation.
23 // Escape a string for HTML interpolation.
23 function escapeHtml(string: any) {
24 function escapeHtml(string: any) {
24 return ("" + string).replace(htmlEscaper, match => htmlEscapes[match]);
25 return ("" + string).replace(htmlEscaper, match => isKeyof(match, htmlEscapes) ? htmlEscapes[match] : "");
25 }
26 }
26
27
27 export class TemplateCompiler {
28 export class TemplateCompiler {
@@ -38,7 +38,7 export class TemplateParser implements I
38 _tokens: string[];
38 _tokens: string[];
39 _pos = -1;
39 _pos = -1;
40 _type: TokenType;
40 _type: TokenType;
41 _value: string;
41 _value: string | undefined;
42
42
43 constructor(text: string) {
43 constructor(text: string) {
44 argumentNotEmptyString(text, "text");
44 argumentNotEmptyString(text, "text");
@@ -66,6 +66,8 export class TemplateParser implements I
66 }
66 }
67
67
68 value() {
68 value() {
69 if (!this._value)
70 throw new Error("The current token doesn't have a value");
69 return this._value;
71 return this._value;
70 }
72 }
71
73
@@ -2,29 +2,32 import { format as dojoFormatNumber } fr
2 import { format as dojoFormatDate } from "dojo/date/locale";
2 import { format as dojoFormatDate } from "dojo/date/locale";
3 import { Formatter, compile as _compile } from "./StringFormat";
3 import { Formatter, compile as _compile } from "./StringFormat";
4
4
5 import { isNumber, isNull } from "../safe";
5 import { isNumber, isNull, get } from "../safe";
6
6
7 interface NumberFormatOptions {
7 interface NumberFormatOptions {
8 round?: number;
8 round?: number;
9 pattern?: string;
9 pattern?: string;
10 }
10 }
11
11
12 function convertNumber(value: any, pattern: string) {
12 function convertNumber(value: any, _pattern?: string) {
13 if (isNumber(value)) {
13 if (isNumber(value)) {
14 const nopt = {} as NumberFormatOptions;
14 const nopt = {} as NumberFormatOptions;
15 if (pattern.indexOf("!") === 0) {
15 let pattern = _pattern;
16 if (pattern && pattern.indexOf("!") === 0) {
16 nopt.round = -1;
17 nopt.round = -1;
17 pattern = pattern.substr(1);
18 pattern = pattern.substr(1);
18 }
19 }
19 nopt.pattern = pattern;
20 nopt.pattern = pattern;
20
21
21 return dojoFormatNumber(value, nopt);
22 return dojoFormatNumber(value, nopt);
23 } else {
24 return "";
22 }
25 }
23 }
26 }
24
27
25 function convertDate(value: any, pattern: string) {
28 function convertDate(value: any, pattern?: string) {
26 if (value instanceof Date) {
29 if (value instanceof Date) {
27 const m = pattern.match(/^(\w+)-(\w+)$/);
30 const m = pattern && pattern.match(/^(\w+)-(\w+)$/);
28 if (m)
31 if (m)
29 return dojoFormatDate(value, {
32 return dojoFormatDate(value, {
30 selector: m[2],
33 selector: m[2],
@@ -37,6 +40,8 function convertDate(value: any, pattern
37 selector: "date",
40 selector: "date",
38 datePattern: pattern
41 datePattern: pattern
39 });
42 });
43 } else {
44 return "";
40 }
45 }
41 }
46 }
42
47
@@ -46,7 +51,7 function format(msg: string, ...args: an
46 return _formatter.format(msg, ...args);
51 return _formatter.format(msg, ...args);
47 }
52 }
48
53
49 function _convert(value: any, pattern: string) {
54 function _convert(value: any, pattern?: string) {
50 return _formatter.convert(value, pattern);
55 return _formatter.convert(value, pattern);
51 }
56 }
52
57
@@ -55,9 +60,9 namespace format {
55 export function compile(text: string) {
60 export function compile(text: string) {
56 const template = _compile(text);
61 const template = _compile(text);
57
62
58 return (...data) => {
63 return (...data: any[]) => {
59 return template((name, pattern) => {
64 return template((name, pattern) => {
60 const value = data[name];
65 const value = get(name, data);
61 return !isNull(value) ? convert(value, pattern) : "";
66 return !isNull(value) ? convert(value, pattern) : "";
62 });
67 });
63 };
68 };
@@ -34,7 +34,7 compile.load = (id: string, require: Req
34 callback(cache[url]);
34 callback(cache[url]);
35 } else {
35 } else {
36 trace.debug("{0} -> {1}: load", id, url);
36 trace.debug("{0} -> {1}: load", id, url);
37 request(url).then(compile).then((tc: TemplateFn) => {
37 request<string>(url).then(compile).then((tc: TemplateFn) => {
38 trace.debug("{0}: compiled", url);
38 trace.debug("{0}: compiled", url);
39 callback(cache[url] = tc);
39 callback(cache[url] = tc);
40 }, (err: any) => {
40 }, (err: any) => {
@@ -4,10 +4,10 import { TraceSource } from "../log/Trac
4 const trace = TraceSource.get(module.id);
4 const trace = TraceSource.get(module.id);
5
5
6 const mainModule = require.main;
6 const mainModule = require.main;
7 const mainRequire = (id: string) => mainModule.require(id);
7 const mainRequire = (id: string) => mainModule ? mainModule.require(id) : require;
8
8
9 class ModuleResolver {
9 class ModuleResolver {
10 _base: string;
10 _base: string | undefined;
11 _require: NodeRequireFunction;
11 _require: NodeRequireFunction;
12
12
13 constructor(req: NodeRequireFunction, base?: string) {
13 constructor(req: NodeRequireFunction, base?: string) {
@@ -3,7 +3,7 import { argumentNotNull, destroyed } fr
3
3
4 export class Cancellation implements ICancellation {
4 export class Cancellation implements ICancellation {
5 private _reason: any;
5 private _reason: any;
6 private _cbs: Array<(e: any) => void>;
6 private _cbs: Array<(e: any) => void> | undefined;
7
7
8 constructor(action: (cancel: (e?: any) => void) => void) {
8 constructor(action: (cancel: (e?: any) => void) => void) {
9 argumentNotNull(action, "action");
9 argumentNotNull(action, "action");
@@ -44,7 +44,7 export class Cancellation implements ICa
44 }
44 }
45 }
45 }
46
46
47 private _unregister(cb) {
47 private _unregister(cb: any) {
48 if (this._cbs) {
48 if (this._cbs) {
49 const i = this._cbs.indexOf(cb);
49 const i = this._cbs.indexOf(cb);
50 if (i >= 0)
50 if (i >= 0)
@@ -52,7 +52,7 export class Cancellation implements ICa
52 }
52 }
53 }
53 }
54
54
55 private _cancel(reason) {
55 private _cancel(reason: any) {
56 if (this._reason)
56 if (this._reason)
57 return;
57 return;
58
58
@@ -60,7 +60,7 export class Cancellation implements ICa
60
60
61 if (this._cbs) {
61 if (this._cbs) {
62 this._cbs.forEach(cb => cb(reason));
62 this._cbs.forEach(cb => cb(reason));
63 this._cbs = null;
63 this._cbs = undefined;
64 }
64 }
65 }
65 }
66
66
@@ -1,10 +1,10
1 import { IObservable, IDestroyable, ICancellation, IObserver } from "./interfaces";
1 import { IObservable, IDestroyable, ICancellation, IObserver } from "./interfaces";
2 import { Cancellation } from "./Cancellation";
2 import { Cancellation } from "./Cancellation";
3 import { argumentNotNull, destroyed } from "./safe";
3 import { argumentNotNull } from "./safe";
4
4
5 type Handler<T> = (x: T) => void;
5 type Handler<T> = (x: T) => void;
6
6
7 type Initializer<T> = (notify: Handler<T>, error?: (e: any) => void, complete?: () => void) => void;
7 type Initializer<T> = (notify: Handler<T>, error: (e: any) => void, complete: () => void) => void;
8
8
9 const noop = () => { };
9 const noop = () => { };
10
10
@@ -17,7 +17,7 export class Observable<T> implements IO
17
17
18 private _observers = new Array<IObserver<T>>();
18 private _observers = new Array<IObserver<T>>();
19
19
20 private _complete: boolean;
20 private _complete = false;
21
21
22 private _error: any;
22 private _error: any;
23
23
@@ -46,7 +46,7 export class Observable<T> implements IO
46 const me = this;
46 const me = this;
47
47
48 const observer: IObserver<T> & IDestroyable = {
48 const observer: IObserver<T> & IDestroyable = {
49 next,
49 next: next.bind(null),
50 error: error ? error.bind(null) : noop,
50 error: error ? error.bind(null) : noop,
51 complete: complete ? complete.bind(null) : noop,
51 complete: complete ? complete.bind(null) : noop,
52
52
@@ -62,30 +62,21 export class Observable<T> implements IO
62
62
63 subscribe(next: IObserver<T> | Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
63 subscribe(next: IObserver<T> | Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
64 if (isObserver(next)) {
64 if (isObserver(next)) {
65 const me = this;
65 this._addObserver(next);
66 const subscription = {
66 return {
67 destroy() {
67 destroy: () => this._removeObserver(next)
68 me._removeObserver(next);
69 }
70 };
68 };
71 this._addObserver(next);
69 } else {
72 return subscription;
73 } else if (next) {
74 const observer = {
70 const observer = {
75 next,
71 next: next.bind(null),
76 error,
72 error: error ? error.bind(null) : noop,
77 complete
73 complete: complete ? complete.bind(null) : noop
78 };
74 };
79 const me = this;
75
80 const subscription = {
76 this._addObserver(observer);
81 destroy() {
77 return {
82 me._removeObserver(observer);
78 destroy: () => this._removeObserver(observer)
83 }
84 };
79 };
85 this._addObserver(observer);
86 return subscription;
87 } else {
88 return destroyed;
89 }
80 }
90 }
81 }
91
82
@@ -17,9 +17,11 export class ObservableValue<T> extends
17 }
17 }
18
18
19 setValue(value: T) {
19 setValue(value: T) {
20 if (this._value !== value) {
20 this._value = value;
21 this._value = value;
21 this._notifyNext(value);
22 this._notifyNext(value);
22 }
23 }
24 }
23
25
24 on(next: Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
26 on(next: Handler<T>, error?: Handler<any>, complete?: () => void): IDestroyable {
25 argumentNotNull(next, "next");
27 argumentNotNull(next, "next");
@@ -6,18 +6,25
6 // Copyright (c) 2010-2012 Robert Kieffer
6 // Copyright (c) 2010-2012 Robert Kieffer
7 // MIT License - http://opensource.org/licenses/mit-license.php
7 // MIT License - http://opensource.org/licenses/mit-license.php
8
8
9 import { MapOf } from "./interfaces";
10
9 declare const window: any;
11 declare const window: any;
10 declare const require;
12 declare const require: any;
11 declare const Buffer;
13 declare const Buffer: any;
12
14
13 const _window: any = "undefined" !== typeof window ? window : null;
15 const _window: any = "undefined" !== typeof window ? window : null;
14
16
17 interface WritableArrayLike<T> {
18 length: number;
19 [n: number]: T;
20 }
21
15 // Unique ID creation requires a high quality random # generator. We
22 // Unique ID creation requires a high quality random # generator. We
16 // feature
23 // feature
17 // detect to determine the best RNG source, normalizing to a function
24 // detect to determine the best RNG source, normalizing to a function
18 // that
25 // that
19 // returns 128-bits of randomness, since that's what's usually required
26 // returns 128-bits of randomness, since that's what's usually required
20 let _rng;
27 let _rng: () => WritableArrayLike<number> = () => [];
21
28
22 function setupBrowser() {
29 function setupBrowser() {
23 // Allow for MSIE11 msCrypto
30 // Allow for MSIE11 msCrypto
@@ -43,9 +50,9 function setupBrowser() {
43 // If all else fails, use Math.random(). It's fast, but is of
50 // If all else fails, use Math.random(). It's fast, but is of
44 // unspecified
51 // unspecified
45 // quality.
52 // quality.
46 const _rnds = new Array(16);
53 const _rnds = new Array<number>(16);
47 _rng = () => {
54 _rng = () => {
48 for (let i = 0, r; i < 16; i++) {
55 for (let i = 0, r = 0; i < 16; i++) {
49 if ((i & 0x03) === 0) {
56 if ((i & 0x03) === 0) {
50 r = Math.random() * 0x100000000;
57 r = Math.random() * 0x100000000;
51 }
58 }
@@ -84,22 +91,22 if (_window) {
84 const BufferClass = ("function" === typeof Buffer) ? Buffer : Array;
91 const BufferClass = ("function" === typeof Buffer) ? Buffer : Array;
85
92
86 // Maps for number <-> hex string conversion
93 // Maps for number <-> hex string conversion
87 const _byteToHex = [];
94 const _byteToHex: string[] = [];
88 const _hexToByte = {};
95 const _hexToByte: MapOf<number> = {};
89 for (let i = 0; i < 256; i++) {
96 for (let i = 0; i < 256; i++) {
90 _byteToHex[i] = (i + 0x100).toString(16).substr(1);
97 _byteToHex[i] = (i + 0x100).toString(16).substr(1);
91 _hexToByte[_byteToHex[i]] = i;
98 _hexToByte[_byteToHex[i]] = i;
92 }
99 }
93
100
94 // **`parse()` - Parse a UUID into it's component bytes**
101 // **`parse()` - Parse a UUID into it's component bytes**
95 function _parse(s, buf?, offset?): Array<string> {
102 function _parse(s: string, buf: number[] = [], offset?: number): number[] {
96 const i = (buf && offset) || 0; let ii = 0;
103 const i = (buf && offset) || 0; let ii = 0;
97
104
98 buf = buf || [];
99 s.toLowerCase().replace(/[0-9a-f]{2}/g, oct => {
105 s.toLowerCase().replace(/[0-9a-f]{2}/g, oct => {
100 if (ii < 16) { // Don't overflow!
106 if (ii < 16) { // Don't overflow!
101 buf[i + ii++] = _hexToByte[oct];
107 buf[i + ii++] = _hexToByte[oct];
102 }
108 }
109 return "";
103 });
110 });
104
111
105 // Zero out remaining bytes if string was short
112 // Zero out remaining bytes if string was short
@@ -111,7 +118,7 function _parse(s, buf?, offset?): Array
111 }
118 }
112
119
113 // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
120 // **`unparse()` - Convert UUID byte array (ala parse()) into a string**
114 function _unparse(buf, offset?): string {
121 function _unparse(buf: ArrayLike<number>, offset?: number): string {
115 let i = offset || 0; const bth = _byteToHex;
122 let i = offset || 0; const bth = _byteToHex;
116 return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] +
123 return bth[buf[i++]] + bth[buf[i++]] + bth[buf[i++]] +
117 bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] + "-" +
124 bth[buf[i++]] + "-" + bth[buf[i++]] + bth[buf[i++]] + "-" +
@@ -145,13 +152,20 let _clockseq = (_seedBytes[6] << 8 | _s
145 // Previous uuid creation time
152 // Previous uuid creation time
146 let _lastMSecs = 0; let _lastNSecs = 0;
153 let _lastMSecs = 0; let _lastNSecs = 0;
147
154
155 interface V1Options {
156 clockseq?: number;
157 msecs?: number;
158 nsecs?: number;
159 node?: number[];
160 }
161
148 // See https://github.com/broofa/node-uuid for API details
162 // See https://github.com/broofa/node-uuid for API details
149 function _v1(options?, buf?, offset?): string {
163 function _v1(options?: V1Options): string;
164 function _v1(options: V1Options, buf: number[], offset?: number): number[];
165 function _v1(options: V1Options = {}, buf?: number[], offset?: number): string | number[] {
150 let i = buf && offset || 0;
166 let i = buf && offset || 0;
151 const b = buf || [];
167 const b = buf || [];
152
168
153 options = options || {};
154
155 let clockseq = (options.clockseq != null) ? options.clockseq : _clockseq;
169 let clockseq = (options.clockseq != null) ? options.clockseq : _clockseq;
156
170
157 // UUID timestamps are 100 nano-second units since the Gregorian
171 // UUID timestamps are 100 nano-second units since the Gregorian
@@ -228,19 +242,21 function _v1(options?, buf?, offset?): s
228 return buf ? buf : _unparse(b);
242 return buf ? buf : _unparse(b);
229 }
243 }
230
244
245 interface V4Opptions {
246 rng?: () => WritableArrayLike<number>;
247
248 random?: number[];
249 }
250
231 // **`v4()` - Generate random UUID**
251 // **`v4()` - Generate random UUID**
232
252
233 // See https://github.com/broofa/node-uuid for API details
253 // See https://github.com/broofa/node-uuid for API details
234 function _v4(options?, buf?, offset?): string {
254 function _v4(options?: V4Opptions): string;
255 function _v4(options: V4Opptions, buf: number[], offset?: number): number[];
256 function _v4(options: V4Opptions = {}, buf?: number[], offset?: number): string | number[] {
235 // Deprecated - 'format' argument, as supported in v1.2
257 // Deprecated - 'format' argument, as supported in v1.2
236 const i = buf && offset || 0;
258 const i = buf && offset || 0;
237
259
238 if (typeof (options) === "string") {
239 buf = (options === "binary") ? new BufferClass(16) : null;
240 options = null;
241 }
242 options = options || {};
243
244 const rnds = options.random || (options.rng || _rng)();
260 const rnds = options.random || (options.rng || _rng)();
245
261
246 // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
262 // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
@@ -9,15 +9,21 const log = TraceSource.get("@implab/cor
9
9
10 export function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
10 export function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
11 return class extends Base implements IActivatable {
11 return class extends Base implements IActivatable {
12 _controller: IActivationController;
12 _controller: IActivationController | undefined;
13
13
14 _active: boolean;
14 _active = false;
15
15
16 isActive() {
16 isActive() {
17 return this._active;
17 return this._active;
18 }
18 }
19
19
20 hasActivationController() {
21 return !!this._controller;
22 }
23
20 getActivationController() {
24 getActivationController() {
25 if (!this._controller)
26 throw Error("Activation controller isn't set");
21 return this._controller;
27 return this._controller;
22 }
28 }
23
29
@@ -2,8 +2,10 import { Cancellation } from "../Cancell
2 import { IAsyncComponent, ICancellation, ICancellable, IDestroyable } from "../interfaces";
2 import { IAsyncComponent, ICancellation, ICancellable, IDestroyable } from "../interfaces";
3 import { destroy } from "../safe";
3 import { destroy } from "../safe";
4
4
5 const noop = () => void (0);
6
5 export class AsyncComponent implements IAsyncComponent, ICancellable {
7 export class AsyncComponent implements IAsyncComponent, ICancellable {
6 _cancel: (e: any) => void;
8 _cancel: ((e: any) => void) = noop;
7
9
8 _completion: Promise<void> = Promise.resolve();
10 _completion: Promise<void> = Promise.resolve();
9
11
@@ -26,7 +28,7 export class AsyncComponent implements I
26 // after the operation is complete we need to cleanup the
28 // after the operation is complete we need to cleanup the
27 // resources
29 // resources
28 destroy(h);
30 destroy(h);
29 this._cancel = null;
31 this._cancel = noop;
30 }
32 }
31 };
33 };
32
34
@@ -34,7 +36,6 export class AsyncComponent implements I
34 }
36 }
35
37
36 cancel(reason: any) {
38 cancel(reason: any) {
37 if (this._cancel)
38 this._cancel(reason);
39 this._cancel(reason);
39 }
40 }
40 }
41 }
@@ -1,7 +1,8
1 import { TraceSource } from "../log/TraceSource";
1 import { TraceSource } from "../log/TraceSource";
2 import { argumentNotNull, argumentNotEmptyString, isPrimitive, each, isNull } from "../safe";
2 import { argumentNotEmptyString } from "../safe";
3 import { Descriptor, ServiceMap } from "./interfaces";
3 import { Descriptor, ContainerServiceMap, ContainerKeys, TypeOfService, ILifetime } from "./interfaces";
4 import { Container } from "./Container";
4 import { Container } from "./Container";
5 import { MapOf } from "../interfaces";
5
6
6 const trace = TraceSource.get("@implab/core/di/ActivationContext");
7 const trace = TraceSource.get("@implab/core/di/ActivationContext");
7
8
@@ -10,50 +11,85 export interface ActivationContextInfo {
10
11
11 service: string;
12 service: string;
12
13
13 scope: ServiceMap;
14 }
14 }
15
15
16 export class ActivationContext {
16 let nextId = 1;
17 _cache: object;
18
17
19 _services: ServiceMap;
18 /** This class is created once per `Container.resolve` method call and used to
19 * cache dependencies and to track created instances. The activation context
20 * tracks services with `context` activation type.
21 */
22 export class ActivationContext<S extends object> {
23 _cache: MapOf<any>;
20
24
21 _stack: ActivationContextInfo[];
25 _services: ContainerServiceMap<S>;
22
26
23 _visited: object;
27 _visited: MapOf<any>;
24
28
25 _name: string;
29 _name: string;
26
30
27 _localized: boolean;
31 _service: Descriptor<S, any>;
28
32
29 container: Container;
33 _container: Container<S>;
34
35 _parent: ActivationContext<S> | undefined;
30
36
31 constructor(container: Container, services: ServiceMap, name?: string, cache?: object, visited?) {
37 /** Creates a new activation context with the specified parameters.
32 argumentNotNull(container, "container");
38 * @param container the container which starts the activation process
33 argumentNotNull(services, "services");
39 * @param services the initial service registrations
34
40 * @param name the name of the service being activated, this parameter is
41 * used for the debug purpose.
42 * @param service the service to activate, this parameter is used for the
43 * debug purpose.
44 */
45 constructor(container: Container<S>, services: ContainerServiceMap<S>, name: string, service: Descriptor<S, any>) {
35 this._name = name;
46 this._name = name;
36 this._visited = visited || {};
47 this._service = service;
37 this._stack = [];
48 this._visited = {};
38 this._cache = cache || {};
49 this._cache = {};
39 this._services = services;
50 this._services = services;
40 this.container = container;
51 this._container = container;
41 }
52 }
42
53
54 /** the name of the current resolving dependency */
43 getName() {
55 getName() {
44 return this._name;
56 return this._name;
45 }
57 }
46
58
47 resolve(name, def?): any {
59 /** Returns the container for which 'resolve' method was called */
60 getContainer() {
61 return this._container;
62 }
63
64 /** Resolves the specified dependency in the current context
65 * @param name The name of the dependency being resolved
66 */
67 resolve<K extends ContainerKeys<S>>(name: K): TypeOfService<S, K>;
68 /** Resolves the specified dependency with the specified default value if
69 * the dependency is missing.
70 *
71 * @param name The name of the dependency being resolved
72 * @param def A default value to return in case of the specified dependency
73 * is missing.
74 */
75 resolve<K extends ContainerKeys<S>, T>(name: K, def: T): TypeOfService<S, K> | T;
76 /** Resolves the specified dependency and returns undefined in case if the
77 * dependency is missing.
78 *
79 * @param name The name of the dependency being resolved
80 */
81 resolve<K extends ContainerKeys<S>>(name: K, def: undefined): TypeOfService<S, K> | undefined;
82 resolve<K extends ContainerKeys<S>, T>(name: K, def?: T): TypeOfService<S, K> | T | undefined {
48 const d = this._services[name];
83 const d = this._services[name];
49
84
50 if (!d)
85 if (d !== undefined) {
86 return this.activate(d, name.toString());
87 } else {
51 if (arguments.length > 1)
88 if (arguments.length > 1)
52 return def;
89 return def;
53 else
90 else
54 throw new Error(`Service ${name} not found`);
91 throw new Error(`Service ${name} not found`);
55
92 }
56 return this.activate(d, name);
57 }
93 }
58
94
59 /**
95 /**
@@ -62,41 +98,36 export class ActivationContext {
62 * @name{string} the name of the service
98 * @name{string} the name of the service
63 * @service{string} the service descriptor to register
99 * @service{string} the service descriptor to register
64 */
100 */
65 register(name: string, service: Descriptor) {
101 register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>) {
66 argumentNotEmptyString(name, "name");
102 argumentNotEmptyString(name, "name");
67
103
68 this._services[name] = service;
104 this._services[name] = service as any;
69 }
105 }
70
106
71 clone() {
107 createLifetime(): ILifetime {
72 return new ActivationContext(
108 const id = nextId++;
73 this.container,
109 const me = this;
74 this._services,
110 return {
75 this._name,
111 initialize() {
76 this._cache,
112 },
77 this._visited
113 has() {
78 );
114 return id in me._cache;
115 },
116 get() {
117 return me._cache[id];
118 },
119 store(item: any) {
120 me._cache[id] = item;
121 }
122 };
79 }
123 }
80
124
81 has(id: string) {
125 activate<T>(d: Descriptor<S, T>, name: string) {
82 return id in this._cache;
83 }
84
85 get(id: string) {
86 return this._cache[id];
87 }
88
89 store(id: string, value) {
90 return (this._cache[id] = value);
91 }
92
93 activate(d: Descriptor, name: string) {
94 if (trace.isLogEnabled())
126 if (trace.isLogEnabled())
95 trace.log(`enter ${name} ${d}`);
127 trace.log(`enter ${name} ${d}`);
96
128
97 this.enter(name, d.toString());
129 const ctx = this.enter(d, name);
98 const v = d.activate(this);
130 const v = d.activate(ctx);
99 this.leave();
100
131
101 if (trace.isLogEnabled())
132 if (trace.isLogEnabled())
102 trace.log(`leave ${name}`);
133 trace.log(`leave ${name}`);
@@ -110,23 +141,30 export class ActivationContext {
110 return count;
141 return count;
111 }
142 }
112
143
113 getStack() {
144 getStack(): ActivationContextInfo[] {
114 return this._stack.slice().reverse();
145 const stack = [{
146 name: this._name,
147 service: this._service.toString()
148 }];
149
150 return this._parent ?
151 stack.concat(this._parent.getStack()) :
152 stack;
115 }
153 }
116
154
117 private enter(name: string, service: string) {
155 private enter(service: Descriptor<S, any>, name: string): this {
118 this._stack.push({
156 const clone = Object.create(this);
119 name,
157 clone._name = name;
120 service,
158 clone._services = Object.create(this._services);
121 scope: this._services
159 clone._parent = this;
122 });
160 clone._service = service;
123 this._name = name;
161 return clone;
124 this._services = Object.create(this._services);
125 }
162 }
126
163
127 private leave() {
164 /** Creates a clone for the current context, used to protect it from modifications */
128 const ctx = this._stack.pop();
165 clone(): this {
129 this._services = ctx.scope;
166 const clone = Object.create(this);
130 this._name = ctx.name;
167 clone._services = Object.create(this._services);
168 return clone;
131 }
169 }
132 }
170 }
@@ -1,7 +1,10
1 import { ActivationContextInfo } from "./ActivationContext";
1 export interface ActivationItem {
2 name: string;
3 service: string;
4 }
2
5
3 export class ActivationError {
6 export class ActivationError {
4 activationStack: ActivationContextInfo[];
7 activationStack: ActivationItem[];
5
8
6 service: string;
9 service: string;
7
10
@@ -9,7 +12,7 export class ActivationError {
9
12
10 message: string;
13 message: string;
11
14
12 constructor(service: string, activationStack: ActivationContextInfo[], innerException) {
15 constructor(service: string, activationStack: ActivationItem[], innerException: any) {
13 this.message = "Failed to activate the service";
16 this.message = "Failed to activate the service";
14 this.activationStack = activationStack;
17 this.activationStack = activationStack;
15 this.service = service;
18 this.service = service;
@@ -1,37 +1,40
1 import { Descriptor, isDescriptor } from "./interfaces";
1 import { Descriptor } from "./interfaces";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3 import { isPrimitive } from "../safe";
3 import { isPrimitive } from "../safe";
4 import { isDescriptor } from "./traits";
4
5
5 export class AggregateDescriptor implements Descriptor {
6 export class AggregateDescriptor<S extends object, T> implements Descriptor<S, T> {
6 _value: object;
7 _value: any;
7
8
8 constructor(value: object) {
9 constructor(value: any) {
9 this._value = value;
10 this._value = value;
10 }
11 }
11
12
12 activate(context: ActivationContext) {
13 activate(context: ActivationContext<S>): T {
13 return this._parse(this._value, context, "$value");
14 return this._parse(this._value, context, "$value");
14 }
15 }
15
16
16 // TODO: make async
17 _parse(value: any, context: ActivationContext<S>, path: string): any {
17 _parse(value, context: ActivationContext, path: string) {
18 if (isPrimitive(value))
18 if (isPrimitive(value))
19 return value;
19 return value as any;
20
20
21 if (isDescriptor(value))
21 if (isDescriptor(value))
22 return context.activate(value, path);
22 return context.activate(value, path);
23
23
24 if (value instanceof Array)
24 if (value instanceof Array)
25 return value.map((x, i) => this._parse(x, context, `${path}[${i}]`));
25 return value.map((x, i) => this._parse(x, context, `${path}[${i}]`)) as any;
26
26
27 const t = {};
27 const t: any = {};
28 for (const p of Object.keys(value))
28 for (const p in value)
29 t[p] = this._parse(value[p], context, `${path}.${p}`);
29 t[p] = this._parse(value[p], context, `${path}.${p}`);
30 return t;
30 return t;
31
32 }
31 }
33
32
34 toString() {
33 toString() {
35 return "@walk";
34 return "@walk";
36 }
35 }
36
37 clone() {
38 return this;
37 }
39 }
40 }
@@ -1,11 +1,11
1 export class ConfigError extends Error {
1 export class ConfigError extends Error {
2 inner: any;
2 inner?: {};
3
3
4 path: string;
4 path?: string;
5
5
6 configName: string;
6 configName?: string;
7
7
8 constructor(message: string, inner?: any) {
8 constructor(message: string, inner?: {}) {
9 super(message);
9 super(message);
10 this.inner = inner;
10 this.inner = inner;
11 }
11 }
@@ -1,19 +1,12
1 import {
1 import {
2 ServiceRegistration,
2 PartialServiceMap,
3 TypeRegistration,
4 FactoryRegistration,
5 ServiceMap,
6 isDescriptor,
7 isDependencyRegistration,
8 DependencyRegistration,
9 ValueRegistration,
10 ActivationType,
3 ActivationType,
11 isValueRegistration,
4 ContainerKeys,
12 isTypeRegistration,
5 TypeOfService,
13 isFactoryRegistration
6 ILifetime
14 } from "./interfaces";
7 } from "./interfaces";
15
8
16 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get } from "../safe";
9 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
17 import { AggregateDescriptor } from "./AggregateDescriptor";
10 import { AggregateDescriptor } from "./AggregateDescriptor";
18 import { ValueDescriptor } from "./ValueDescriptor";
11 import { ValueDescriptor } from "./ValueDescriptor";
19 import { Container } from "./Container";
12 import { Container } from "./Container";
@@ -25,10 +18,115 import { ConfigError } from "./ConfigErr
25 import { Cancellation } from "../Cancellation";
18 import { Cancellation } from "../Cancellation";
26 import { makeResolver } from "./ResolverHelper";
19 import { makeResolver } from "./ResolverHelper";
27 import { ICancellation } from "../interfaces";
20 import { ICancellation } from "../interfaces";
21 import { isDescriptor } from "./traits";
22 import { LazyReferenceDescriptor } from "./LazyReferenceDescriptor";
23 import { LifetimeManager } from "./LifetimeManager";
24
25 export interface RegistrationScope<S extends object> {
26
27 /** сервисы, которые регистрируются в контексте активации и таким образом
28 * могут переопределять ранее зарегистрированные сервисы. за это свойство
29 * нужно платить, кроме того порядок активации будет влиять на результат
30 * разрешения зависимостей.
31 */
32 services?: RegistrationMap<S>;
33 }
34
35 /**
36 * Базовый интерфейс конфигурации сервисов
37 */
38 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
39
40 activation?: ActivationType;
41
42 params?: any;
43
44 /** Специальный идентификатор используется при активации singleton, если
45 * не указан для TypeRegistration вычисляется как oid($type)
46 */
47 typeId?: string;
48
49 inject?: object | object[];
50
51 cleanup?: ((instance: T) => void) | string;
52 }
53
54 export interface TypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
55 $type: string | C;
56 params?: Registration<ConstructorParameters<C>, S>;
57 }
58
59 export interface StrictTypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
60 $type: C;
61 params?: Registration<ConstructorParameters<C>, S>;
62 }
63
64 export interface FactoryRegistration<F extends (...args: any[]) => any, S extends object> extends ServiceRegistration<ReturnType<F>, S> {
65 $factory: string | F;
66 }
67
68 export interface ValueRegistration<T> {
69 $value: T;
70 parse?: boolean;
71 }
72
73 export interface DependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends RegistrationScope<S> {
74 $dependency: K;
75 lazy?: boolean;
76 optional?: boolean;
77 default?: TypeOfService<S, K>;
78 }
79
80 export interface LazyDependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends DependencyRegistration<S, K> {
81 lazy: true;
82 }
83
84 export type Registration<T, S extends object> = T extends primitive ? T :
85 (
86 T |
87 { [k in keyof T]: Registration<T[k], S> } |
88 TypeRegistration<new (...args: any[]) => T, S> |
89 FactoryRegistration<(...args: any[]) => T, S> |
90 ValueRegistration<any> |
91 DependencyRegistration<S, keyof S>
92 );
93
94 export type RegistrationMap<S extends object> = {
95 [k in keyof S]?: Registration<S[k], S>;
96 };
97
98 const _activationTypes: { [k in ActivationType]: number; } = {
99 singleton: 1,
100 container: 2,
101 hierarchy: 3,
102 context: 4,
103 call: 5
104 };
105
106 export function isTypeRegistration(x: any): x is TypeRegistration<new () => any, any> {
107 return (!isPrimitive(x)) && ("$type" in x);
108 }
109
110 export function isFactoryRegistration(x: any): x is FactoryRegistration<() => any, any> {
111 return (!isPrimitive(x)) && ("$factory" in x);
112 }
113
114 export function isValueRegistration(x: any): x is ValueRegistration<any> {
115 return (!isPrimitive(x)) && ("$value" in x);
116 }
117
118 export function isDependencyRegistration<S extends object>(x: any): x is DependencyRegistration<S, keyof S> {
119 return (!isPrimitive(x)) && ("$dependency" in x);
120 }
121
122 export function isActivationType(x: string): x is ActivationType {
123 return typeof x === "string" && x in _activationTypes;
124 }
28
125
29 const trace = TraceSource.get("@implab/core/di/Configuration");
126 const trace = TraceSource.get("@implab/core/di/Configuration");
30
127 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
31 async function mapAll(data: object | any[], map?: (v, k) => any): Promise<any> {
128 async function mapAll(data: any, map?: (v: any, k: string) => any): Promise<any>;
129 async function mapAll(data: any, map?: (v: any, k: any) => any): Promise<any> {
32 if (data instanceof Array) {
130 if (data instanceof Array) {
33 return Promise.all(map ? data.map(map) : data);
131 return Promise.all(map ? data.map(map) : data);
34 } else {
132 } else {
@@ -47,21 +145,19 async function mapAll(data: object | any
47
145
48 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
146 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
49
147
50 type _key = string | number;
148 export class Configuration<S extends object> {
51
52 export class Configuration {
53
149
54 _hasInnerDescriptors = false;
150 _hasInnerDescriptors = false;
55
151
56 _container: Container;
152 readonly _container: Container<S>;
57
153
58 _path: Array<_key>;
154 _path: Array<string>;
59
155
60 _configName: string;
156 _configName: string | undefined;
61
157
62 _require: ModuleResolver;
158 _require: ModuleResolver | undefined;
63
159
64 constructor(container: Container) {
160 constructor(container: Container<S>) {
65 argumentNotNull(container, "container");
161 argumentNotNull(container, "container");
66 this._container = container;
162 this._container = container;
67 this._path = [];
163 this._path = [];
@@ -78,7 +174,7 export class Configuration {
78
174
79 this._configName = moduleName;
175 this._configName = moduleName;
80
176
81 const r = await makeResolver(null, contextRequire);
177 const r = await makeResolver(undefined, contextRequire);
82
178
83 const config = await r(moduleName, ct);
179 const config = await r(moduleName, ct);
84
180
@@ -89,13 +185,14 export class Configuration {
89 );
185 );
90 }
186 }
91
187
92 async applyConfiguration(data: object, contextRequire?: any, ct = Cancellation.none) {
188 async applyConfiguration(data: RegistrationMap<S>, opts: { contextRequire?: any; baseModule?: string }, ct = Cancellation.none) {
93 argumentNotNull(data, "data");
189 argumentNotNull(data, "data");
190 const _opts = opts || {};
94
191
95 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
192 await this._applyConfiguration(data, await makeResolver(_opts.baseModule, _opts.contextRequire), ct);
96 }
193 }
97
194
98 async _applyConfiguration(data: object, resolver?: ModuleResolver, ct = Cancellation.none) {
195 async _applyConfiguration(data: RegistrationMap<S>, resolver?: ModuleResolver, ct = Cancellation.none) {
99 trace.log("applyConfiguration");
196 trace.log("applyConfiguration");
100
197
101 this._configName = "$";
198 this._configName = "$";
@@ -103,7 +200,7 export class Configuration {
103 if (resolver)
200 if (resolver)
104 this._require = resolver;
201 this._require = resolver;
105
202
106 let services: ServiceMap;
203 let services: PartialServiceMap<S>;
107
204
108 try {
205 try {
109 services = await this._visitRegistrations(data, "$");
206 services = await this._visitRegistrations(data, "$");
@@ -114,9 +211,9 export class Configuration {
114 this._container.register(services);
211 this._container.register(services);
115 }
212 }
116
213
117 _makeError(inner) {
214 _makeError(inner: any) {
118 const e = new ConfigError("Failed to load configuration", inner);
215 const e = new ConfigError("Failed to load configuration", inner);
119 e.configName = this._configName;
216 e.configName = this._configName || "<inline>";
120 e.path = this._makePath();
217 e.path = this._makePath();
121 return e;
218 return e;
122 }
219 }
@@ -135,7 +232,15 export class Configuration {
135 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
232 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
136 try {
233 try {
137 const m = await this._loadModule(moduleName);
234 const m = await this._loadModule(moduleName);
138 return localName ? get(localName, m) : m;
235 if (localName) {
236 return get(localName, m);
237 } else {
238 if (m instanceof Function)
239 return m;
240 if ("default" in m)
241 return m.default;
242 return m;
243 }
139 } catch (e) {
244 } catch (e) {
140 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
245 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
141 throw e;
246 throw e;
@@ -144,32 +249,31 export class Configuration {
144
249
145 _loadModule(moduleName: string) {
250 _loadModule(moduleName: string) {
146 trace.debug("loadModule {0}", moduleName);
251 trace.debug("loadModule {0}", moduleName);
252 if (!this._require)
253 throw new Error("Module loader isn't specified");
147
254
148 return this._require(moduleName);
255 return this._require(moduleName);
149 }
256 }
150
257
151 async _visitRegistrations(data, name: _key) {
258 async _visitRegistrations(data: RegistrationMap<S>, name: string) {
152 this._enter(name);
259 this._enter(name);
153
260
154 if (data.constructor &&
261 if (data.constructor &&
155 data.constructor.prototype !== Object.prototype)
262 data.constructor.prototype !== Object.prototype)
156 throw new Error("Configuration must be a simple object");
263 throw new Error("Configuration must be a simple object");
157
264
158 const o: ServiceMap = {};
159 const keys = Object.keys(data);
160
161 const services = await mapAll(data, async (v, k) => {
265 const services = await mapAll(data, async (v, k) => {
162 const d = await this._visit(v, k);
266 const d = await this._visit(v, k.toString());
163 return isDescriptor(d) ? d : new AggregateDescriptor(d);
267 return isDescriptor(d) ? d : new AggregateDescriptor(d);
164 }) as ServiceMap;
268 }) as PartialServiceMap<S>;
165
269
166 this._leave();
270 this._leave();
167
271
168 return services;
272 return services;
169 }
273 }
170
274
171 _enter(name: _key) {
275 _enter(name: string) {
172 this._path.push(name);
276 this._path.push(name.toString());
173 trace.debug(">{0}", name);
277 trace.debug(">{0}", name);
174 }
278 }
175
279
@@ -178,11 +282,13 export class Configuration {
178 trace.debug("<{0}", name);
282 trace.debug("<{0}", name);
179 }
283 }
180
284
181 async _visit(data, name: string) {
285 _visit(data: any, name: string): Promise<any> {
182 if (isPrimitive(data) || isDescriptor(data))
286 if (isPrimitive(data))
183 return data;
287 return Promise.resolve(new ValueDescriptor(data));
288 if (isDescriptor(data))
289 return Promise.resolve(data);
184
290
185 if (isDependencyRegistration(data)) {
291 if (isDependencyRegistration<S>(data)) {
186 return this._visitDependencyRegistration(data, name);
292 return this._visitDependencyRegistration(data, name);
187 } else if (isValueRegistration(data)) {
293 } else if (isValueRegistration(data)) {
188 return this._visitValueRegistration(data, name);
294 return this._visitValueRegistration(data, name);
@@ -197,7 +303,7 export class Configuration {
197 return this._visitObject(data, name);
303 return this._visitObject(data, name);
198 }
304 }
199
305
200 async _visitObject(data: object, name: _key) {
306 async _visitObject(data: any, name: string) {
201 if (data.constructor &&
307 if (data.constructor &&
202 data.constructor.prototype !== Object.prototype)
308 data.constructor.prototype !== Object.prototype)
203 return new ValueDescriptor(data);
309 return new ValueDescriptor(data);
@@ -220,7 +326,7 export class Configuration {
220 return v;
326 return v;
221 }
327 }
222
328
223 async _visitArray(data: any[], name: _key) {
329 async _visitArray(data: any[], name: string) {
224 if (data.constructor &&
330 if (data.constructor &&
225 data.constructor.prototype !== Array.prototype)
331 data.constructor.prototype !== Array.prototype)
226 return new ValueDescriptor(data);
332 return new ValueDescriptor(data);
@@ -233,9 +339,8 export class Configuration {
233 return v;
339 return v;
234 }
340 }
235
341
236 _makeServiceParams(data: ServiceRegistration) {
342 _makeServiceParams(data: ServiceRegistration<any, S>) {
237 const opts: any = {
343 const opts: any = {
238 owner: this._container
239 };
344 };
240 if (data.services)
345 if (data.services)
241 opts.services = this._visitRegistrations(data.services, "services");
346 opts.services = this._visitRegistrations(data.services, "services");
@@ -257,30 +362,7 export class Configuration {
257 this._visit(data.params, "params");
362 this._visit(data.params, "params");
258
363
259 if (data.activation) {
364 if (data.activation) {
260 if (typeof (data.activation) === "string") {
365 opts.activation = this._getLifetimeManager(data.activation, data.typeId);
261 switch (data.activation.toLowerCase()) {
262 case "singleton":
263 opts.activation = ActivationType.Singleton;
264 break;
265 case "container":
266 opts.activation = ActivationType.Container;
267 break;
268 case "hierarchy":
269 opts.activation = ActivationType.Hierarchy;
270 break;
271 case "context":
272 opts.activation = ActivationType.Context;
273 break;
274 case "call":
275 opts.activation = ActivationType.Call;
276 break;
277 default:
278 throw new Error("Unknown activation type: " +
279 data.activation);
280 }
281 } else {
282 opts.activation = Number(data.activation);
283 }
284 }
366 }
285
367
286 if (data.cleanup)
368 if (data.cleanup)
@@ -289,28 +371,28 export class Configuration {
289 return opts;
371 return opts;
290 }
372 }
291
373
292 async _visitValueRegistration(data: ValueRegistration, name: _key) {
374 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
293 this._enter(name);
375 this._enter(name);
294 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
376 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
295 this._leave();
377 this._leave();
296 return d;
378 return d;
297 }
379 }
298
380
299 async _visitDependencyRegistration(data: DependencyRegistration, name: _key) {
381 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
300 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
382 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
301 this._enter(name);
383 this._enter(name);
302 const d = new ReferenceDescriptor({
384 const options = {
303 name: data.$dependency,
385 name: data.$dependency,
304 lazy: data.lazy,
305 optional: data.optional,
386 optional: data.optional,
306 default: data.default,
387 default: data.default,
307 services: data.services && await this._visitRegistrations(data.services, "services")
388 services: data.services && await this._visitRegistrations(data.services, "services")
308 });
389 };
390 const d = data.lazy ? new LazyReferenceDescriptor<S, K>(options) : new ReferenceDescriptor<S, K>(options);
309 this._leave();
391 this._leave();
310 return d;
392 return d;
311 }
393 }
312
394
313 async _visitTypeRegistration(data: TypeRegistration, name: _key) {
395 async _visitTypeRegistration(data: TypeRegistration<new () => any, S>, name: string) {
314 argumentNotNull(data.$type, "data.$type");
396 argumentNotNull(data.$type, "data.$type");
315 this._enter(name);
397 this._enter(name);
316
398
@@ -319,10 +401,14 export class Configuration {
319 opts.type = data.$type;
401 opts.type = data.$type;
320 } else {
402 } else {
321 const [moduleName, typeName] = data.$type.split(":", 2);
403 const [moduleName, typeName] = data.$type.split(":", 2);
322 opts.type = this._resolveType(moduleName, typeName);
404 opts.type = this._resolveType(moduleName, typeName).then(t => {
405 if (!(t instanceof Function))
406 throw Error("$type (" + data.$type + ") is not a constructable");
407 return t;
408 });
323 }
409 }
324
410
325 const d = new TypeServiceDescriptor(
411 const d = new TypeServiceDescriptor<S, any, any[]>(
326 await mapAll(opts)
412 await mapAll(opts)
327 );
413 );
328
414
@@ -331,18 +417,35 export class Configuration {
331 return d;
417 return d;
332 }
418 }
333
419
334 async _visitFactoryRegistration(data: FactoryRegistration, name: _key) {
420 async _visitFactoryRegistration(data: FactoryRegistration<() => any, S>, name: string) {
335 argumentOfType(data.$factory, Function, "data.$factory");
421 argumentOfType(data.$factory, Function, "data.$factory");
336 this._enter(name);
422 this._enter(name);
337
423
338 const opts = this._makeServiceParams(data);
424 const opts = this._makeServiceParams(data);
339 opts.factory = data.$factory;
425 opts.factory = data.$factory;
340
426
341 const d = new FactoryServiceDescriptor(
427 const d = new FactoryServiceDescriptor<S, any, any[]>(
342 await mapAll(opts)
428 await mapAll(opts)
343 );
429 );
344
430
345 this._leave();
431 this._leave();
346 return d;
432 return d;
347 }
433 }
434
435 _getLifetimeManager(activation: ActivationType, typeId: string | undefined): ILifetime {
436 switch (activation) {
437 case "container":
438 return LifetimeManager.containerLifetime(this._container);
439 case "hierarchy":
440 return LifetimeManager.hierarchyLifetime();
441 case "context":
442 return LifetimeManager.contextLifetime();
443 case "singleton":
444 if (typeId === undefined)
445 throw Error("The singleton activation requires a typeId");
446 return LifetimeManager.singletonLifetime(typeId);
447 default:
448 return LifetimeManager.empty();
348 }
449 }
450 }
451 }
@@ -1,31 +1,40
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { ValueDescriptor } from "./ValueDescriptor";
2 import { ValueDescriptor } from "./ValueDescriptor";
3 import { ActivationError } from "./ActivationError";
3 import { ActivationError } from "./ActivationError";
4 import { isDescriptor, ServiceMap } from "./interfaces";
4 import { ServiceMap, Descriptor, PartialServiceMap, ServiceLocator, ContainerServiceMap, ContainerKeys, TypeOfService } from "./interfaces";
5 import { TraceSource } from "../log/TraceSource";
5 import { TraceSource } from "../log/TraceSource";
6 import { Configuration } from "./Configuration";
6 import { Configuration, RegistrationMap } from "./Configuration";
7 import { Cancellation } from "../Cancellation";
7 import { Cancellation } from "../Cancellation";
8 import { MapOf, IDestroyable } from "../interfaces";
9 import { isDescriptor } from "./traits";
10 import { LifetimeManager } from "./LifetimeManager";
11 import { each } from "../safe";
12 import { FluentRegistrations } from "./fluent/interfaces";
13 import { FluentConfiguration } from "./fluent/FluentConfiguration";
8
14
9 const trace = TraceSource.get("@implab/core/di/ActivationContext");
15 const trace = TraceSource.get("@implab/core/di/ActivationContext");
10
16
11 export class Container {
17 export class Container<S extends object = any> implements ServiceLocator<S>, IDestroyable {
12 _services: ServiceMap;
18 readonly _services: ContainerServiceMap<S>;
13
19
14 _cache: object;
20 readonly _lifetimeManager: LifetimeManager;
21
22 readonly _cleanup: (() => void)[];
15
23
16 _cleanup: (() => void)[];
24 readonly _root: Container<S>;
17
25
18 _root: Container;
26 readonly _parent?: Container<S>;
19
27
20 _parent: Container;
28 _disposed: boolean;
21
29
22 constructor(parent?: Container) {
30 constructor(parent?: Container<S>) {
23 this._parent = parent;
31 this._parent = parent;
24 this._services = parent ? Object.create(parent._services) : {};
32 this._services = parent ? Object.create(parent._services) : {};
25 this._cache = {};
26 this._cleanup = [];
33 this._cleanup = [];
27 this._root = parent ? parent.getRootContainer() : this;
34 this._root = parent ? parent.getRootContainer() : this;
28 this._services.container = new ValueDescriptor(this);
35 this._services.container = new ValueDescriptor(this) as any;
36 this._disposed = false;
37 this._lifetimeManager = new LifetimeManager();
29 }
38 }
30
39
31 getRootContainer() {
40 getRootContainer() {
@@ -36,93 +45,105 export class Container {
36 return this._parent;
45 return this._parent;
37 }
46 }
38
47
39 resolve(name: string, def?) {
48 getLifetimeManager() {
49 return this._lifetimeManager;
50 }
51
52 resolve<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>): TypeOfService<S, K> {
40 trace.debug("resolve {0}", name);
53 trace.debug("resolve {0}", name);
41 const d = this._services[name];
54 const d = this._services[name];
42 if (d === undefined) {
55 if (d === undefined) {
43 if (arguments.length > 1)
56 if (def !== undefined)
44 return def;
57 return def;
45 else
58 else
46 throw new Error("Service '" + name + "' isn't found");
59 throw new Error("Service '" + name + "' isn't found");
47 }
60 } else {
48
61
49 const context = new ActivationContext(this, this._services);
62 const context = new ActivationContext<S>(this, this._services, String(name), d);
50 try {
63 try {
51 return context.activate(d, name);
64 return d.activate(context);
52 } catch (error) {
65 } catch (error) {
53 throw new ActivationError(name, context.getStack(), error);
66 throw new ActivationError(name.toString(), context.getStack(), error);
67 }
54 }
68 }
55 }
69 }
56
70
57 /**
71 /**
58 * @deprecated use resolve() method
72 * @deprecated use resolve() method
59 */
73 */
60 getService() {
74 getService<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>) {
61 return this.resolve.apply(this, arguments);
75 return this.resolve(name, def);
62 }
76 }
63
77
64 register(nameOrCollection, service?) {
78 register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>): this;
79 register(services: PartialServiceMap<S>): this;
80 register<K extends keyof S>(nameOrCollection: K | ServiceMap<S>, service?: Descriptor<S, S[K]>) {
65 if (arguments.length === 1) {
81 if (arguments.length === 1) {
66 const data = nameOrCollection;
82 const data = nameOrCollection as ServiceMap<S>;
67 for (const name in data)
83
68 this.register(name, data[name]);
84 each(data, (v, k) => this.register(k, v));
69 } else {
85 } else {
70 if (!isDescriptor(service))
86 if (!isDescriptor(service))
71 throw new Error("The service parameter must be a descriptor");
87 throw new Error("The service parameter must be a descriptor");
72
88
73 this._services[nameOrCollection] = service;
89 this._services[nameOrCollection as K] = service as any;
74 }
90 }
75 return this;
91 return this;
76 }
92 }
77
93
78 onDispose(callback) {
94 onDispose(callback: () => void) {
79 if (!(callback instanceof Function))
95 if (!(callback instanceof Function))
80 throw new Error("The callback must be a function");
96 throw new Error("The callback must be a function");
81 this._cleanup.push(callback);
97 this._cleanup.push(callback);
82 }
98 }
83
99
100 destroy() {
101 return this.dispose();
102 }
84 dispose() {
103 dispose() {
85 if (this._cleanup) {
104 if (this._disposed)
105 return;
106 this._disposed = true;
86 for (const f of this._cleanup)
107 for (const f of this._cleanup)
87 f();
108 f();
88 this._cleanup = null;
89 }
90 }
109 }
91
110
92 /**
111 /**
93 * @param{String|Object} config
112 * @param{String|Object} config
94 * The configuration of the contaier. Can be either a string or an object,
113 * The configuration of the container. Can be either a string or an object,
95 * if the configuration is an object it's treated as a collection of
114 * if the configuration is an object it's treated as a collection of
96 * services which will be registed in the contaier.
115 * services which will be registered in the container.
97 *
116 *
98 * @param{Function} opts.contextRequire
117 * @param{Function} opts.contextRequire
99 * The function which will be used to load a configuration or types for services.
118 * The function which will be used to load a configuration or types for services.
100 *
119 *
101 */
120 */
102 async configure(config: string | object, opts?: any, ct = Cancellation.none) {
121 async configure(config: string | RegistrationMap<S>, opts?: { contextRequire: any; baseModule?: string }, ct = Cancellation.none) {
103 const c = new Configuration(this);
122 const _opts = Object.create(opts || null);
104
123
105 if (typeof (config) === "string") {
124 if (typeof (config) === "string") {
106 return c.loadConfiguration(config, opts && opts.contextRequire, ct);
125 _opts.baseModule = config;
126
127 const module = await import(config);
128 if (module && module.default && typeof (module.default.apply) === "function")
129 return module.default.apply(this);
130 else
131 return this._applyLegacyConfig(module, _opts, ct);
107 } else {
132 } else {
108 return c.applyConfiguration(config, opts && opts.contextRequire, ct);
133 return this._applyLegacyConfig(config, _opts, ct);
109 }
134 }
110 }
135 }
111
136
112 createChildContainer() {
137 async _applyLegacyConfig(config: RegistrationMap<S>, opts: { contextRequire: any; baseModule?: string }, ct = Cancellation.none) {
113 return new Container(this);
138 return new Configuration<S>(this).applyConfiguration(config, opts);
114 }
115
116 has(id) {
117 return id in this._cache;
118 }
139 }
119
140
120 get(id) {
141 async fluent<K extends keyof S>(config: FluentRegistrations<K, S>, ct = Cancellation.none): Promise<this> {
121 return this._cache[id];
142 await new FluentConfiguration<S>().register(config).apply(this, ct);
143 return this;
122 }
144 }
123
145
124 store(id, value) {
146 createChildContainer<S2 extends object = S>(): Container<S & S2> {
125 return (this._cache[id] = value);
147 return new Container<S & S2>(this as any);
126 }
148 }
127
128 }
149 }
@@ -1,23 +1,18
1 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
1 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
2 import { Factory } from "../interfaces";
3 import { argumentNotNull, oid } from "../safe";
2 import { argumentNotNull, oid } from "../safe";
4 import { ActivationType } from "./interfaces";
5
3
6 export interface FactoryServiceDescriptorParams extends ServiceDescriptorParams {
4 export interface FactoryServiceDescriptorParams<S extends object, T, P extends any[]> extends ServiceDescriptorParams<S, T, P> {
7 factory: Factory;
5 factory: (...args: P) => T;
8 }
6 }
9
7
10 export class FactoryServiceDescriptor extends ServiceDescriptor {
8 export class FactoryServiceDescriptor<S extends object, T, P extends any[]> extends ServiceDescriptor<S, T, P> {
11 constructor(opts: FactoryServiceDescriptorParams) {
9 constructor(opts: FactoryServiceDescriptorParams<S, T, P>) {
12 super(opts);
10 super(opts);
13
11
14 argumentNotNull(opts && opts.factory, "opts.factory");
12 argumentNotNull(opts && opts.factory, "opts.factory");
15
13
16 // bind to null
14 // bind to null
17 this._factory = (...args) => opts.factory.apply(null, args);
15 this._factory = (...args) => opts.factory.apply(null, args as any);
18
16
19 if (opts.activation === ActivationType.Singleton) {
20 this._cacheId = oid(opts.factory);
21 }
17 }
22 }
18 }
23 }
@@ -1,83 +1,71
1 import { isNull, argumentNotEmptyString, each } from "../safe";
1 import { argumentNotEmptyString, each } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3 import { ServiceMap, Descriptor } from "./interfaces";
3 import { Descriptor, PartialServiceMap, TypeOfService, ContainerKeys } from "./interfaces";
4 import { ActivationError } from "./ActivationError";
4
5 export interface ReferenceDescriptorParams<S extends object, K extends ContainerKeys<S>> {
6 /**
7 * The name of the descriptor
8 */
9 name: K;
5
10
6 export interface ReferenceDescriptorParams {
11 /**
7 name: string;
12 * The flag that indicates that the referenced service isn't required to exist.
8 lazy?: boolean;
13 * If the reference is optional and the referenced service doesn't exist,
14 * the undefined or a default value will be returned.
15 */
9 optional?: boolean;
16 optional?: boolean;
10 default?;
17
11 services?: ServiceMap;
18 /**
19 * a default value for the reference when the referenced service doesn't exist.
20 */
21 default?: TypeOfService<S, K>;
22
23 /**
24 * The service overrides
25 */
26 services?: PartialServiceMap<S>;
12 }
27 }
13
28
14 export class ReferenceDescriptor implements Descriptor {
29 export class ReferenceDescriptor<S extends object = any, K extends ContainerKeys<S> = ContainerKeys<S>>
15 _name: string;
30 implements Descriptor<S, TypeOfService<S, K>> {
16
31
17 _lazy = false;
32 _name: K;
18
33
19 _optional = false;
34 _optional = false;
20
35
21 _default: any;
36 _default: TypeOfService<S, K> | undefined;
22
37
23 _services: ServiceMap;
38 _services: PartialServiceMap<S>;
24
39
25 constructor(opts: ReferenceDescriptorParams) {
40 constructor(opts: ReferenceDescriptorParams<S, K>) {
26 argumentNotEmptyString(opts && opts.name, "opts.name");
41 argumentNotEmptyString(opts && opts.name, "opts.name");
27 this._name = opts.name;
42 this._name = opts.name;
28 this._lazy = !!opts.lazy;
29 this._optional = !!opts.optional;
43 this._optional = !!opts.optional;
30 this._default = opts.default;
44 this._default = opts.default;
31 this._services = opts.services;
32 }
33
45
34 activate(context: ActivationContext, name: string) {
46 this._services = (opts.services || {}) as PartialServiceMap<S>;
35 // добавляем сервисы
36 if (this._services) {
37 for (const p of Object.keys(this._services))
38 context.register(p, this._services[p]);
39 }
47 }
40
48
41 if (this._lazy) {
49 /** This method activates the referenced service if one exists
42 const saved = context.clone();
50 * @param context activation context which is used during current activation
43
51 */
44 return (cfg: ServiceMap) => {
52 activate(context: ActivationContext<S>): any {
45 // защищаем контекст на случай исключения в процессе
53 // добавляем сервисы
46 // активации
54 if (this._services) {
47 const ct = saved.clone();
55 each(this._services, (v, k) => context.register(k, v));
48 try {
49 if (cfg) {
50 for (const k in cfg)
51 ct.register(k, cfg[k]);
52 }
56 }
53
57
54 return this._optional ? ct.resolve(this._name, this._default) : ct
58 const res = this._optional ?
55 .resolve(this._name);
56 } catch (error) {
57 throw new ActivationError(this._name, ct.getStack(), error);
58 }
59 };
60 } else {
61 // добавляем сервисы
62 if (this._services) {
63 for (const p of Object.keys(this._services))
64 context.register(p, this._services[p]);
65 }
66
67 const v = this._optional ?
68 context.resolve(this._name, this._default) :
59 context.resolve(this._name, this._default) :
69 context.resolve(this._name);
60 context.resolve(this._name);
70
61
71 return v;
62 return res;
72 }
73 }
63 }
74
64
75 toString() {
65 toString() {
76 const opts = [];
66 const opts = [];
77 if (this._optional)
67 if (this._optional)
78 opts.push("optional");
68 opts.push("optional");
79 if (this._lazy)
80 opts.push("lazy");
81
69
82 const parts = [
70 const parts = [
83 "@ref "
71 "@ref "
@@ -88,11 +76,11 export class ReferenceDescriptor impleme
88 parts.push("} ");
76 parts.push("} ");
89 }
77 }
90
78
91 parts.push(this._name);
79 parts.push(this._name.toString());
92
80
93 if (!isNull(this._default)) {
81 if (this._default !== undefined && this._default !== null) {
94 parts.push(" = ");
82 parts.push(" = ");
95 parts.push(this._default);
83 parts.push(String(this._default));
96 }
84 }
97
85
98 return parts.join("");
86 return parts.join("");
@@ -1,16 +1,17
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ActivationType, ServiceMap, isDescriptor } from "./interfaces";
2 import { Descriptor, ServiceMap, PartialServiceMap, ILifetime } from "./interfaces";
3 import { Container } from "./Container";
3 import { isPrimitive, keys, isNull } from "../safe";
4 import { argumentNotNull, isPrimitive } from "../safe";
5 import { TraceSource } from "../log/TraceSource";
4 import { TraceSource } from "../log/TraceSource";
6
5 import { isDescriptor } from "./traits";
7 let cacheId = 0;
6 import { LifetimeManager } from "./LifetimeManager";
7 import { MatchingMemberKeys } from "../interfaces";
8
8
9 const trace = TraceSource.get("@implab/core/di/ActivationContext");
9 const trace = TraceSource.get("@implab/core/di/ActivationContext");
10
10
11 function injectMethod(target, method, context, args) {
11 function injectMethod<T, M extends keyof T, S extends object, A>(target: T, method: M, context: ActivationContext<S>, args: A) {
12
12 const m = target[method];
13 const m = target[method];
13 if (!m)
14 if (!m || typeof m !== "function")
14 throw new Error("Method '" + method + "' not found");
15 throw new Error("Method '" + method + "' not found");
15
16
16 if (args instanceof Array)
17 if (args instanceof Array)
@@ -19,22 +20,22 function injectMethod(target, method, co
19 return m.call(target, _parse(args, context, "." + method));
20 return m.call(target, _parse(args, context, "." + method));
20 }
21 }
21
22
22 function makeClenupCallback(target, method: ((instance) => void) | string) {
23 function makeCleanupCallback<T>(method: Cleaner<T>) {
23 if (typeof (method) === "string") {
24 if (typeof (method) === "function") {
24 return () => {
25 return (target: T) => {
25 target[method]();
26 method(target);
26 };
27 };
27 } else {
28 } else {
28 return () => {
29 return (target: T) => {
29 method(target);
30 const m = target[method] as any;
31 m.apply(target);
30 };
32 };
31 }
33 }
32 }
34 }
33
35
34 // TODO: make async
36 function _parse(value: any, context: ActivationContext<any>, path: string): any {
35 function _parse(value, context: ActivationContext, path: string) {
36 if (isPrimitive(value))
37 if (isPrimitive(value))
37 return value;
38 return value as any;
38
39
39 trace.debug("parse {0}", path);
40 trace.debug("parse {0}", path);
40
41
@@ -42,178 +43,92 function _parse(value, context: Activati
42 return context.activate(value, path);
43 return context.activate(value, path);
43
44
44 if (value instanceof Array)
45 if (value instanceof Array)
45 return value.map((x, i) => _parse(x, context, `${path}[${i}]`));
46 return value.map((x, i) => _parse(x, context, `${path}[${i}]`)) as any;
46
47
47 const t = {};
48 const t: any = {};
48 for (const p of Object.keys(value))
49
49 t[p] = _parse(value[p], context, `${path}.${p}`);
50 keys(value).forEach(p => t[p] = _parse(value[p], context, `${path}.${p}`));
50
51
51 return t;
52 return t;
52 }
53 }
53
54
54 export interface ServiceDescriptorParams {
55 export type Cleaner<T> = ((x: T) => void) | MatchingMemberKeys<() => void, T>;
55 activation?: ActivationType;
56
56
57 owner: Container;
57 export type InjectionSpec<T> = {
58 [m in keyof T]?: any;
59 };
58
60
59 params?;
61 export interface ServiceDescriptorParams<S extends object, T, P extends any[]> {
62 lifetime?: ILifetime;
60
63
61 inject?: object[];
64 params?: P;
62
65
63 services?: ServiceMap;
66 inject?: InjectionSpec<T>[];
64
67
65 cleanup?: ((x) => void) | string;
68 services?: PartialServiceMap<S>;
69
70 cleanup?: Cleaner<T>;
66 }
71 }
67
72
68 export class ServiceDescriptor implements Descriptor {
73 export class ServiceDescriptor<S extends object, T, P extends any[]> implements Descriptor<S, T> {
69 _instance;
74 _services: ServiceMap<S>;
70
71 _hasInstance = false;
72
73 _activationType = ActivationType.Call;
74
75
75 _services: ServiceMap;
76 _params: P | undefined;
76
77 _params;
78
77
79 _inject: object[];
78 _inject: InjectionSpec<T>[];
80
79
81 _cleanup: ((x) => void) | string;
80 _cleanup: ((item: T) => void) | undefined;
82
81
83 _cacheId: any;
82 _lifetime = LifetimeManager.empty();
84
83
85 _owner: Container;
84 _objectLifetime: ILifetime | undefined;
86
85
87 constructor(opts: ServiceDescriptorParams) {
86 constructor(opts: ServiceDescriptorParams<S, T, P>) {
88 argumentNotNull(opts, "opts");
89 argumentNotNull(opts.owner, "owner");
90
87
91 this._owner = opts.owner;
88 if (opts.lifetime)
89 this._lifetime = opts.lifetime;
92
90
93 if ("activation" in opts)
91 if (!isNull(opts.params))
94 this._activationType = opts.activation;
95
96 if ("params" in opts)
97 this._params = opts.params;
92 this._params = opts.params;
98
93
99 if (opts.inject)
94 this._inject = opts.inject || [];
100 this._inject = opts.inject;
101
95
102 if (opts.services)
96 this._services = (opts.services || {}) as ServiceMap<S>;
103 this._services = opts.services;
104
97
105 if (opts.cleanup) {
98 if (opts.cleanup) {
106 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
99 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
107 throw new Error(
100 throw new Error(
108 "The cleanup parameter must be either a function or a function name");
101 "The cleanup parameter must be either a function or a function name");
109
102
110 this._cleanup = opts.cleanup;
103 this._cleanup = makeCleanupCallback(opts.cleanup);
111 }
104 }
112 }
105 }
113
106
114 activate(context: ActivationContext) {
107 activate(context: ActivationContext<S>) {
115 // if we have a local service records, register them first
108 const lifetime = this._lifetime;
116 let instance;
117
118 // ensure we have a cache id
119 if (!this._cacheId)
120 this._cacheId = ++cacheId;
121
122 switch (this._activationType) {
123 case ActivationType.Singleton: // SINGLETON
124 // if the value is cached return it
125 if (this._hasInstance)
126 return this._instance;
127
128 // singletons are bound to the root container
129 const container = context.container.getRootContainer();
130
131 if (container.has(this._cacheId)) {
132 instance = container.get(this._cacheId);
133 } else {
134 instance = this._create(context);
135 container.store(this._cacheId, instance);
136 if (this._cleanup)
137 container.onDispose(
138 makeClenupCallback(instance, this._cleanup));
139 }
140
141 this._hasInstance = true;
142 return (this._instance = instance);
143
144 case ActivationType.Container: // CONTAINER
145 // return a cached value
146
147 if (this._hasInstance)
148 return this._instance;
149
109
150 // create an instance
110 if (lifetime.has()) {
151 instance = this._create(context);
111 return lifetime.get();
152
112 } else {
153 // the instance is bound to the container
113 lifetime.initialize(context);
154 if (this._cleanup)
114 const instance = this._create(context);
155 this._owner.onDispose(
115 lifetime.store(instance, this._cleanup);
156 makeClenupCallback(instance, this._cleanup));
116 return instance;
157
158 // cache and return the instance
159 this._hasInstance = true;
160 return (this._instance = instance);
161 case ActivationType.Context: // CONTEXT
162 // return a cached value if one exists
163
164 if (context.has(this._cacheId))
165 return context.get(this._cacheId);
166 // context context activated instances are controlled by callers
167 return context.store(this._cacheId, this._create(context));
168 case ActivationType.Call: // CALL
169 // per-call created instances are controlled by callers
170 return this._create(context);
171 case ActivationType.Hierarchy: // HIERARCHY
172 // hierarchy activated instances are behave much like container activated
173 // except they are created and bound to the child container
174
175 // return a cached value
176 if (context.container.has(this._cacheId))
177 return context.container.get(this._cacheId);
178
179 instance = this._create(context);
180
181 if (this._cleanup)
182 context.container.onDispose(makeClenupCallback(
183 instance,
184 this._cleanup));
185
186 return context.container.store(this._cacheId, instance);
187 default:
188 throw new Error("Invalid activation type: " + this._activationType);
189 }
117 }
190 }
118 }
191
119
192 isInstanceCreated() {
120 _factory(...params: any[]): T {
193 return this._hasInstance;
194 }
195
196 getInstance() {
197 return this._instance;
198 }
199
200 _factory(...params: any[]): any {
201 throw Error("Not implemented");
121 throw Error("Not implemented");
202 }
122 }
203
123
204 _create(context: ActivationContext) {
124 _create(context: ActivationContext<S>) {
205 trace.debug(`constructing ${context._name}`);
125 trace.debug(`constructing ${context._name}`);
206
126
207 if (this._activationType !== ActivationType.Call &&
208 context.visit(this._cacheId) > 0)
209 throw new Error("Recursion detected");
210
211 if (this._services) {
127 if (this._services) {
212 for (const p in this._services)
128 keys(this._services).forEach(p => context.register(p, this._services[p]));
213 context.register(p, this._services[p]);
214 }
129 }
215
130
216 let instance;
131 let instance: T;
217
132
218 if (this._params === undefined) {
133 if (this._params === undefined) {
219 instance = this._factory();
134 instance = this._factory();
@@ -231,4 +146,9 export class ServiceDescriptor implement
231 }
146 }
232 return instance;
147 return instance;
233 }
148 }
149
150 clone() {
151 return Object.create(this);
234 }
152 }
153
154 }
@@ -1,15 +1,14
1 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
1 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
2 import { Constructor, Factory } from "../interfaces";
3 import { argumentNotNull, isPrimitive } from "../safe";
2 import { argumentNotNull, isPrimitive } from "../safe";
4
3
5 export interface TypeServiceDescriptorParams extends ServiceDescriptorParams {
4 export interface TypeServiceDescriptorParams<S extends object, T extends object, P extends any[]> extends ServiceDescriptorParams<S, T, P> {
6 type: Constructor;
5 type: new (...args: any[]) => T;
7 }
6 }
8
7
9 export class TypeServiceDescriptor extends ServiceDescriptor {
8 export class TypeServiceDescriptor<S extends object, T extends object, P extends any[]> extends ServiceDescriptor<S, T, P> {
10 _type: Constructor;
9 _type: new (...args: any[]) => T;
11
10
12 constructor(opts: TypeServiceDescriptorParams) {
11 constructor(opts: TypeServiceDescriptorParams<S, T, P>) {
13 super(opts);
12 super(opts);
14 argumentNotNull(opts && opts.type, "opts.type");
13 argumentNotNull(opts && opts.type, "opts.type");
15
14
@@ -18,9 +17,10 export class TypeServiceDescriptor exten
18 if (this._params) {
17 if (this._params) {
19 if (this._params.length) {
18 if (this._params.length) {
20 this._factory = (...args) => {
19 this._factory = (...args) => {
21 const t = Object.create(ctor.prototype);
20 /*const t = Object.create(ctor.prototype);
22 const inst = ctor.apply(t, args);
21 const inst = ctor.apply(t, args);
23 return isPrimitive(inst) ? t : inst;
22 return isPrimitive(inst) ? t : inst;*/
23 return new ctor(...args);
24 };
24 };
25 } else {
25 } else {
26 this._factory = arg => {
26 this._factory = arg => {
@@ -1,9 +1,9
1 import { Descriptor } from "./interfaces";
1 import { Descriptor } from "./interfaces";
2
2
3 export class ValueDescriptor implements Descriptor {
3 export class ValueDescriptor<T> implements Descriptor<any, T> {
4 _value;
4 _value: T;
5
5
6 constructor(value) {
6 constructor(value: T) {
7 this._value = value;
7 this._value = value;
8 }
8 }
9
9
@@ -1,75 +1,53
1 import { isNull, isPrimitive } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
3 import { Constructor, Factory } from "../interfaces";
4
5 export interface Descriptor {
6 activate(context: ActivationContext, name?: string);
7 }
8
9 export function isDescriptor(x): x is Descriptor {
10 return (!isPrimitive(x)) &&
11 (x.activate instanceof Function);
12 }
13
2
14 export interface ServiceMap {
3 export interface Descriptor<S extends object = any, T = any> {
15 [s: string]: Descriptor;
4 activate(context: ActivationContext<S>): T;
16 }
17
18 export enum ActivationType {
19 Singleton = 1,
20 Container,
21 Hierarchy,
22 Context,
23 Call
24 }
25
26 export interface RegistrationWithServices {
27 services?: object;
28 }
5 }
29
6
30 export interface ServiceRegistration extends RegistrationWithServices {
7 export type ServiceMap<S extends object> = {
31
8 [k in keyof S]: Descriptor<S, S[k]>;
32 activation?: "singleton" | "container" | "hierarchy" | "context" | "call";
9 };
33
10
34 params?;
11 export type ContainerKeys<S extends object> = keyof S | keyof ContainerProvided<S>;
35
12
36 inject?: object | object[];
13 export type TypeOfService<S extends object, K> =
14 K extends keyof ContainerProvided<S> ? ContainerProvided<S>[K] :
15 K extends keyof S ? S[K] : never;
37
16
38 cleanup?: (instance) => void | string;
17 export type ContainerServiceMap<S extends object> = {
39 }
18 [K in ContainerKeys<S>]: Descriptor<S, TypeOfService<S, K>>;
19 };
40
20
41 export interface TypeRegistration extends ServiceRegistration {
21 export type PartialServiceMap<S extends object> = {
42 $type: string | Constructor;
22 [k in keyof S]?: Descriptor<S, S[k]>;
43 }
23 };
44
24
45 export interface FactoryRegistration extends ServiceRegistration {
25 export interface ServiceLocator<S extends object> {
46 $factory: string | Factory;
26 resolve<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>): TypeOfService<S, K>;
47 }
27 }
48
28
49 export interface ValueRegistration {
29 export interface ContainerProvided<S extends object> {
50 $value;
30 container: ServiceLocator<S>;
51 parse?: boolean;
52 }
53
54 export interface DependencyRegistration extends RegistrationWithServices {
55 $dependency: string;
56 lazy?: boolean;
57 optional?: boolean;
58 default?;
59 }
31 }
60
32
61 export function isTypeRegistration(x): x is TypeRegistration {
33 export type ContainerRegistered<S extends object> = /*{
62 return (!isPrimitive(x)) && ("$type" in x);
34 [K in Exclude<keyof S, keyof ContainerProvided<S>>]: S[K];
63 }
35 };*/
36 Exclude<S, ContainerProvided<S>>;
64
37
65 export function isFactoryRegistration(x): x is FactoryRegistration {
38 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
66 return (!isPrimitive(x)) && ("$factory" in x);
67 }
68
39
69 export function isValueRegistration(x): x is ValueRegistration {
40 /**
70 return (!isPrimitive(x)) && ("$value" in x);
41 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
71 }
42 * свой собственный объект `ILifetime`, который создается при первой активации
43 */
44 export interface ILifetime {
45 /** Проверяет, что уже создан экземпляр объекта */
46 has(): boolean;
72
47
73 export function isDependencyRegistration(x): x is DependencyRegistration {
48 get(): any;
74 return (!isPrimitive(x)) && ("$dependency" in x);
49
50 initialize(context: ActivationContext<any>): void;
51
52 store(item: any, cleanup?: (item: any) => void): void;
75 }
53 }
@@ -3,10 +3,20 export interface Constructor<T = {}> {
3 prototype: T;
3 prototype: T;
4 }
4 }
5
5
6 export type PromiseOrValue<T> = T | PromiseLike<T>;
7
6 export type Factory<T = {}> = (...args: any[]) => T;
8 export type Factory<T = {}> = (...args: any[]) => T;
7
9
8 export type Predicate<T = any> = (x: T) => boolean;
10 export type Predicate<T = any> = (x: T) => boolean;
9
11
12 export type MatchingMemberKeys<T, From> = { [K in keyof From]: From[K] extends T ? K : never}[keyof From];
13
14 export type NotMatchingMemberKeys<T, From> = { [K in keyof From]: From[K] extends T ? never : K}[keyof From];
15
16 export type ExtractMembers<T, From> = Pick<From, MatchingMemberKeys<T, From>>;
17
18 export type ExcludeMembers<T, From> = Pick<From, NotMatchingMemberKeys<T, From>>;
19
10 export interface MapOf<T> {
20 export interface MapOf<T> {
11 [key: string]: T;
21 [key: string]: T;
12 }
22 }
@@ -55,7 +65,10 export interface IActivatable {
55 * can be activated and manages the active state of the
65 * can be activated and manages the active state of the
56 * component
66 * component
57 */
67 */
58 setActivationController(controller: IActivationController);
68 setActivationController(controller: IActivationController): void;
69
70 /** Indicates whether this component has an activation controller */
71 hasActivationController(): boolean;
59
72
60 /**
73 /**
61 * Gets the current activation controller for this component
74 * Gets the current activation controller for this component
@@ -76,6 +89,8 export interface IActivationController {
76
89
77 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
90 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
78
91
92 hasActive(): boolean;
93
79 getActive(): IActivatable;
94 getActive(): IActivatable;
80 }
95 }
81
96
@@ -1,15 +1,15
1 import { TraceSource } from "./TraceSource";
1 import { TraceSource } from "./TraceSource";
2 import { argumentNotNull } from "../safe";
2 import { argumentNotNull } from "../safe";
3 import { IDestroyable } from "../interfaces";
3 import { IDestroyable, MapOf } from "../interfaces";
4
4
5 export class Registry {
5 export class Registry {
6 static readonly instance = new Registry();
6 static readonly instance = new Registry();
7
7
8 private _registry: object = new Object();
8 private _registry: MapOf<TraceSource> = {};
9 private _listeners: object = new Object();
9 private _listeners: MapOf<(source: TraceSource) => void> = {};
10 private _nextCookie: number = 1;
10 private _nextCookie: number = 1;
11
11
12 get(id: any): TraceSource {
12 get(id: string): TraceSource {
13 argumentNotNull(id, "id");
13 argumentNotNull(id, "id");
14
14
15 if (this._registry[id])
15 if (this._registry[id])
@@ -5,7 +5,7 export class TraceEventData implements T
5 source: TraceSource;
5 source: TraceSource;
6 level: number;
6 level: number;
7 message: any;
7 message: any;
8 args?: any[];
8 args: any[];
9
9
10 constructor(source: TraceSource, level: number, message: any, args: any[]) {
10 constructor(source: TraceSource, level: number, message: any, args: any[]) {
11 this.source = source;
11 this.source = source;
@@ -1,6 +1,7
1 import { Observable } from "../Observable";
1 import { Observable } from "../Observable";
2 import { Registry } from "./Registry";
2 import { Registry } from "./Registry";
3 import { TraceEventData } from "./TraceEventData";
3 import { TraceEventData } from "./TraceEventData";
4 import { EventProvider } from "../EventProvider";
4
5
5 export const DebugLevel = 400;
6 export const DebugLevel = 400;
6
7
@@ -25,22 +26,21 export interface TraceEvent {
25 export class TraceSource {
26 export class TraceSource {
26 readonly id: any;
27 readonly id: any;
27
28
28 level: number;
29 level = 0;
29
30
30 readonly events: Observable<TraceEvent>;
31 readonly events: Observable<TraceEvent>;
31
32
32 _notifyNext: (arg: TraceEvent) => void;
33 private readonly _provider: EventProvider<TraceEvent>;
33
34
34 constructor(id: any) {
35 constructor(id?: any) {
35
36
36 this.id = id || new Object();
37 this.id = id || new Object();
37 this.events = new Observable(next => {
38 this._provider = new EventProvider();
38 this._notifyNext = next;
39 this.events = this._provider.getObservable();
39 });
40 }
40 }
41
41
42 protected emit(level: number, message: any, args?: any[]) {
42 protected emit(level: number, message: any, args: any[]) {
43 this._notifyNext(new TraceEventData(this, level, message, args));
43 this._provider.post(new TraceEventData(this, level, message, args));
44 }
44 }
45
45
46 isDebugEnabled() {
46 isDebugEnabled() {
@@ -32,7 +32,11 export class ConsoleLogger implements ID
32 this._writer.setLogLevel("error");
32 this._writer.setLogLevel("error");
33 }
33 }
34 this._writer.write("{0}: ", next.source.id);
34 this._writer.write("{0}: ", next.source.id);
35
36 if (next.args)
35 this._writer.writeLine(next.message, ...next.args);
37 this._writer.writeLine(next.message, ...next.args);
38 else
39 this._writer.writeLine(next.message);
36 }
40 }
37
41
38 destroy() {
42 destroy() {
@@ -1,1 +1,1
1 export { ConsoleLogger as ConsoleWriter } from "./ConsoleLogger"; No newline at end of file
1 export { ConsoleLogger as ConsoleWriter } from "./ConsoleLogger";
@@ -1,4 +1,4
1 import { ICancellable, Constructor } from "./interfaces";
1 import { ICancellable, Constructor, IDestroyable, PromiseOrValue } from "./interfaces";
2 import { Cancellation } from "./Cancellation";
2 import { Cancellation } from "./Cancellation";
3
3
4 let _nextOid = 0;
4 let _nextOid = 0;
@@ -6,9 +6,11 const _oid = typeof Symbol === "function
6 Symbol("__implab__oid__") :
6 Symbol("__implab__oid__") :
7 "__implab__oid__";
7 "__implab__oid__";
8
8
9 export function oid(instance: object): string {
9 export function oid(instance: null | undefined): undefined;
10 export function oid(instance: NonNullable<any>): string;
11 export function oid(instance: any): string | undefined {
10 if (isNull(instance))
12 if (isNull(instance))
11 return null;
13 return undefined;
12
14
13 if (_oid in instance)
15 if (_oid in instance)
14 return instance[_oid];
16 return instance[_oid];
@@ -16,6 +18,14 export function oid(instance: object): s
16 return (instance[_oid] = "oid_" + (++_nextOid));
18 return (instance[_oid] = "oid_" + (++_nextOid));
17 }
19 }
18
20
21 export function keys<T>(arg: T): (Extract<keyof T, string>)[] {
22 return isObject(arg) && arg ? Object.keys(arg) as (Extract<keyof T, string>)[] : [];
23 }
24
25 export function isKeyof<T>(k: string, target: T): k is Extract<keyof T, string> {
26 return target && typeof target === "object" && k in target;
27 }
28
19 export function argumentNotNull(arg: any, name: string) {
29 export function argumentNotNull(arg: any, name: string) {
20 if (arg === null || arg === undefined)
30 if (arg === null || arg === undefined)
21 throw new Error("The argument " + name + " can't be null or undefined");
31 throw new Error("The argument " + name + " can't be null or undefined");
@@ -36,11 +46,17 export function argumentOfType(arg: any,
36 throw new Error("The argument '" + name + "' type doesn't match");
46 throw new Error("The argument '" + name + "' type doesn't match");
37 }
47 }
38
48
39 export function isNull(val: any) {
49 export function isObject(val: any): val is object {
50 return typeof val === "object";
51 }
52
53 export function isNull(val: any): val is null | undefined {
40 return (val === null || val === undefined);
54 return (val === null || val === undefined);
41 }
55 }
42
56
43 export function isPrimitive(val: any): val is string | number | boolean | undefined | null {
57 export type primitive = symbol | string | number | boolean | undefined | null;
58
59 export function isPrimitive(val: any): val is primitive {
44 return (val === null || val === undefined || typeof (val) === "string" ||
60 return (val === null || val === undefined || typeof (val) === "string" ||
45 typeof (val) === "number" || typeof (val) === "boolean");
61 typeof (val) === "number" || typeof (val) === "boolean");
46 }
62 }
@@ -57,7 +73,7 export function isString(val: any): val
57 return typeof (val) === "string" || val instanceof String;
73 return typeof (val) === "string" || val instanceof String;
58 }
74 }
59
75
60 export function isPromise(val: any): val is PromiseLike<any> {
76 export function isPromise<T = any>(val: any): val is PromiseLike<T> {
61 return val && typeof val.then === "function";
77 return val && typeof val.then === "function";
62 }
78 }
63
79
@@ -65,21 +81,20 export function isCancellable(val: any):
65 return val && typeof val.cancel === "function";
81 return val && typeof val.cancel === "function";
66 }
82 }
67
83
68 export function isNullOrEmptyString(val: any): val is string | null | undefined {
84 export function isNullOrEmptyString(val: any): val is ("" | null | undefined) {
69 if (val === null || val === undefined ||
85 return (val === null || val === undefined ||
70 ((typeof (val) === "string" || val instanceof String) && val.length === 0))
86 ((typeof (val) === "string" || val instanceof String) && val.length === 0));
71 return true;
72 }
87 }
73
88
74 export function isNotEmptyArray(arg: any): arg is Array<any> {
89 export function isNotEmptyArray<T = any>(arg: any): arg is T[] {
75 return (arg instanceof Array && arg.length > 0);
90 return (arg instanceof Array && arg.length > 0);
76 }
91 }
77
92
78 function _isStrictMode() {
93 function _isStrictMode(this: any) {
79 return !this;
94 return !this;
80 }
95 }
81
96
82 function _getNonStrictGlobal() {
97 function _getNonStrictGlobal(this: any) {
83 return this;
98 return this;
84 }
99 }
85
100
@@ -116,24 +131,22 export function get(member: string, cont
116 * @param {Function} cb функция, вызываемая для каждого элемента
131 * @param {Function} cb функция, вызываемая для каждого элемента
117 * @param {Object} thisArg значение, которое будет передано в качестве
132 * @param {Object} thisArg значение, которое будет передано в качестве
118 * <c>this</c> в <c>cb</c>.
133 * <c>this</c> в <c>cb</c>.
119 * @returns Результат вызова функции <c>cb</c>, либо <c>undefined</c>
134 * @returns {void}
120 * если достигнут конец массива.
121 */
135 */
122 export function each(obj, cb, thisArg?) {
136 export function each<T>(obj: T, cb: <X extends keyof T>(v: NonNullable<T[X]>, k: X) => void): void;
137 export function each<T>(array: T[], cb: (v: T, i: number) => void): void;
138 export function each(obj: any, cb: any, thisArg?: any): any;
139 export function each(obj: any, cb: any, thisArg?: any) {
123 argumentNotNull(cb, "cb");
140 argumentNotNull(cb, "cb");
124 if (obj instanceof Array) {
141 if (obj instanceof Array) {
142 let v: any;
125 for (let i = 0; i < obj.length; i++) {
143 for (let i = 0; i < obj.length; i++) {
126 const x = cb.call(thisArg, obj[i], i);
144 v = obj[i];
127 if (x !== undefined)
145 if (v !== undefined)
128 return x;
146 cb.call(thisArg, v, i);
129 }
147 }
130 } else {
148 } else {
131 const keys = Object.keys(obj);
149 Object.keys(obj).forEach(k => obj[k] !== undefined && cb.call(thisArg, obj[k], k));
132 for (const k of keys) {
133 const x = cb.call(thisArg, obj[k], k);
134 if (x !== undefined)
135 return x;
136 }
137 }
150 }
138 }
151 }
139
152
@@ -153,28 +166,27 export function each(obj, cb, thisArg?)
153 * own properties of the source are entirely copied to the destination.
166 * own properties of the source are entirely copied to the destination.
154 *
167 *
155 */
168 */
156 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: string[] | object): T & S {
169 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: keyof S[]): T & S;
157 argumentNotNull(dest, "to");
170 export function mixin<T extends object, S extends object, R extends object = T>(dest: T, source: S, template: { [p in keyof S]?: keyof R; }): T & R;
158 const _res = dest as T & S;
171 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: any): any {
172 argumentNotNull(dest, "dest");
173 const _res: any = dest as any;
159
174
160 if (isPrimitive(source))
175 if (isPrimitive(source))
161 return _res;
176 return _res;
162
177
163 if (template instanceof Array) {
178 if (template instanceof Array) {
164 for (const p of template) {
179 template.forEach(p => {
165 if (p in source)
180 if (isKeyof(p, source))
166 _res[p] = source[p];
181 _res[p] = source[p];
167 }
182 });
168 } else if (template) {
183 } else if (template) {
169 const keys = Object.keys(source);
184 keys(source).forEach(p => {
170 for (const p of keys) {
185 if (isKeyof(p, template))
171 if (p in template)
172 _res[template[p]] = source[p];
186 _res[template[p]] = source[p];
173 }
187 });
174 } else {
188 } else {
175 const keys = Object.keys(source);
189 keys(source).forEach(p => _res[p] = source[p]);
176 for (const p of keys)
177 _res[p] = source[p];
178 }
190 }
179
191
180 return _res;
192 return _res;
@@ -184,7 +196,15 export function mixin<T extends object,
184 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
196 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
185 * @param{Function|String} fn [Required] Function wich will be wrapped.
197 * @param{Function|String} fn [Required] Function wich will be wrapped.
186 */
198 */
187 export function async(_fn: (...args: any[]) => any, thisArg): (...args: any[]) => PromiseLike<any> {
199 export function async<T, F extends (...args: any[]) => T | PromiseLike<T>>(
200 fn: F,
201 thisArg?: ThisParameterType<F>
202 ): (...args: Parameters<F>) => PromiseLike<T>;
203 export function async<T, M extends string, O extends { [m in M]?: (...args: any[]) => T | PromiseLike<T> }>(
204 fn: M,
205 thisArg: O
206 ): (...args: Parameters<NonNullable<O[M]>>) => PromiseLike<T>;
207 export function async(_fn: any, thisArg: any): (...args: any[]) => PromiseLike<any> {
188 let fn = _fn;
208 let fn = _fn;
189
209
190 if (arguments.length === 2 && !(fn instanceof Function))
210 if (arguments.length === 2 && !(fn instanceof Function))
@@ -193,7 +213,7 export function async(_fn: (...args: any
193 if (fn == null)
213 if (fn == null)
194 throw new Error("The function must be specified");
214 throw new Error("The function must be specified");
195
215
196 function wrapresult(x, e?): PromiseLike<any> {
216 function wrapresult(x: any, e?: any): PromiseLike<any> {
197 if (e) {
217 if (e) {
198 return {
218 return {
199 then(cb, eb) {
219 then(cb, eb) {
@@ -228,10 +248,16 export function async(_fn: (...args: any
228 };
248 };
229 }
249 }
230
250
231 type _AnyFn = (...args) => any;
251 export function delegate<T extends object, F extends (this: T, ...args: any[]) => any>(
232
252 target: T,
233 export function delegate<T, K extends keyof T>(target: T, _method: (K | _AnyFn)) {
253 method: F
234 let method;
254 ): OmitThisParameter<F>;
255 export function delegate<M extends string, T extends { [m in M]?: (...args: any[]) => any; }>(
256 target: T,
257 method: M
258 ): OmitThisParameter<T[M]>;
259 export function delegate(target: any, _method: any): (...args: any[]) => any {
260 let method: any;
235 if (!(_method instanceof Function)) {
261 if (!(_method instanceof Function)) {
236 argumentNotNull(target, "target");
262 argumentNotNull(target, "target");
237 method = target[_method];
263 method = target[_method];
@@ -262,6 +288,19 export function delay(timeMs: number, ct
262 });
288 });
263 }
289 }
264
290
291 /** Returns resolved promise, awaiting this method will cause the asynchronous
292 * completion of the rest of the code.
293 */
294 export function fork() {
295 return Promise.resolve();
296 }
297
298 /** Always throws Error, can be used as a stub for the methods which should be
299 * assigned later and are required to be not null.
300 */
301 export function notImplemented(): never {
302 throw new Error("Not implemeted");
303 }
265 /**
304 /**
266 * Iterates over the specified array of items and calls the callback `cb`, if
305 * Iterates over the specified array of items and calls the callback `cb`, if
267 * the result of the callback is a promise the next item from the array will be
306 * the result of the callback is a promise the next item from the array will be
@@ -284,7 +323,7 export function pmap<T, T2>(
284 let i = 0;
323 let i = 0;
285 const result = new Array<T2>();
324 const result = new Array<T2>();
286
325
287 const next = () => {
326 const next = (): any => {
288 while (i < items.length) {
327 while (i < items.length) {
289 const r = cb(items[i], i);
328 const r = cb(items[i], i);
290 const ri = i;
329 const ri = i;
@@ -319,7 +358,7 export function pfor<T>(
319
358
320 let i = 0;
359 let i = 0;
321
360
322 const next = () => {
361 const next = (): any => {
323 while (i < items.length) {
362 while (i < items.length) {
324 const r = cb(items[i], i);
363 const r = cb(items[i], i);
325 i++;
364 i++;
@@ -336,7 +375,7 export function first<T>(sequence: Array
336 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
375 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
337 export function first<T>(
376 export function first<T>(
338 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
377 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
339 cb: (x: T) => void,
378 cb?: (x: T) => void,
340 err?: (x: Error) => void
379 err?: (x: Error) => void
341 ): void;
380 ): void;
342 /**
381 /**
@@ -358,7 +397,7 export function first<T>(
358 err?: (x: Error) => void
397 err?: (x: Error) => void
359 ) {
398 ) {
360 if (isPromise(sequence)) {
399 if (isPromise(sequence)) {
361 return sequence.then(res => first(res, cb, err));
400 return sequence.then(res => first(res, cb as any /* force to pass undefined cb */, err));
362 } else if (sequence && "length" in sequence) {
401 } else if (sequence && "length" in sequence) {
363 if (sequence.length === 0) {
402 if (sequence.length === 0) {
364 if (err)
403 if (err)
@@ -400,7 +439,12 export function firstWhere<T>(
400 err?: (x: Error) => any
439 err?: (x: Error) => any
401 ) {
440 ) {
402 if (isPromise(sequence)) {
441 if (isPromise(sequence)) {
403 return sequence.then(res => firstWhere(res, predicate, cb, err));
442 return sequence.then(res => firstWhere(
443 res,
444 predicate as any /* force to pass undefined predicate */,
445 cb as any /* force to pass undefined cb */,
446 err)
447 );
404 } else if (sequence && "length" in sequence) {
448 } else if (sequence && "length" in sequence) {
405 if (sequence.length === 0) {
449 if (sequence.length === 0) {
406 if (err)
450 if (err)
@@ -430,6 +474,12 export function firstWhere<T>(
430 }
474 }
431 }
475 }
432
476
477 export function isDestroyable(d: any): d is IDestroyable {
478 if (d && "destroy" in d && typeof (destroy) === "function")
479 return true;
480 return false;
481 }
482
433 export function destroy(d: any) {
483 export function destroy(d: any) {
434 if (d && "destroy" in d)
484 if (d && "destroy" in d)
435 d.destroy();
485 d.destroy();
@@ -3,9 +3,9 import { isPrimitive, isNull } from "../
3 export class Converter {
3 export class Converter {
4 static readonly default = new Converter();
4 static readonly default = new Converter();
5
5
6 convert(value: any, pattern: string) {
6 convert(value: any, pattern?: string) {
7 if (pattern && pattern.toLocaleLowerCase() === "json") {
7 if (pattern && pattern.toLocaleLowerCase() === "json") {
8 const seen = [];
8 const seen: any[] = [];
9 return JSON.stringify(value, (k, v) => {
9 return JSON.stringify(value, (k, v) => {
10 if (!isPrimitive(v)) {
10 if (!isPrimitive(v)) {
11 const id = seen.indexOf(v);
11 const id = seen.indexOf(v);
@@ -6,20 +6,20 type CompiledPattern = (writer: TextWrit
6
6
7 export class FormatCompiler {
7 export class FormatCompiler {
8 _scanner: FormatScanner;
8 _scanner: FormatScanner;
9 _cache: MapOf<CompiledPattern> = {};
9 static _cache: MapOf<CompiledPattern> = {};
10
10
11 _parts: Array<string | { name: string; format: string; }>;
11 _parts: Array<string | { name: string; format?: string; }>;
12
12
13 compile(pattern: string) {
13 constructor(scanner: FormatScanner) {
14 let compiledPattern = this._cache && this._cache[pattern];
14 this._scanner = scanner;
15 if (!compiledPattern) {
16 this._scanner = new FormatScanner(pattern);
17 this._parts = [];
15 this._parts = [];
16 }
18
17
18 compile() {
19 this.visitText();
19 this.visitText();
20 const parts = this._parts;
20 const parts = this._parts;
21
21
22 compiledPattern = (writer: TextWriter, args: any) => {
22 return (writer: TextWriter, args: any) => {
23 parts.forEach(x => {
23 parts.forEach(x => {
24 if (isPrimitive(x))
24 if (isPrimitive(x))
25 writer.writeValue(x);
25 writer.writeValue(x);
@@ -27,7 +27,15 export class FormatCompiler {
27 writer.writeValue(get(x.name, args), x.format);
27 writer.writeValue(get(x.name, args), x.format);
28 });
28 });
29 };
29 };
30 if (this._cache)
30 }
31
32 static compile(pattern: string) {
33 let compiledPattern = this._cache && this._cache[pattern];
34 if (!compiledPattern) {
35 const compiler = new this(new FormatScanner(pattern));
36
37 compiledPattern = compiler.compile();
38
31 this._cache[pattern] = compiledPattern;
39 this._cache[pattern] = compiledPattern;
32 }
40 }
33 return compiledPattern;
41 return compiledPattern;
@@ -73,7 +81,7 export class FormatCompiler {
73 this.dieUnexpectedToken("TEXT");
81 this.dieUnexpectedToken("TEXT");
74
82
75 const fieldName = this._scanner.getTokenValue();
83 const fieldName = this._scanner.getTokenValue();
76 const filedFormat = this.readColon() ? this.readFieldFormat() : null;
84 const filedFormat = this.readColon() ? this.readFieldFormat() : undefined;
77
85
78 if (this._scanner.getTokenType() !== TokeType.CurlClose)
86 if (this._scanner.getTokenType() !== TokeType.CurlClose)
79 this.dieUnexpectedToken("}");
87 this.dieUnexpectedToken("}");
@@ -104,7 +112,7 export class FormatCompiler {
104 return true;
112 return true;
105 }
113 }
106
114
107 pushSubst(fieldName: string, filedFormat: string) {
115 pushSubst(fieldName: string, filedFormat?: string) {
108 // console.log("pushSubst ", fieldName, filedFormat);
116 // console.log("pushSubst ", fieldName, filedFormat);
109 this._parts.push({ name: fieldName, format: filedFormat });
117 this._parts.push({ name: fieldName, format: filedFormat });
110 }
118 }
@@ -113,14 +121,14 export class FormatCompiler {
113 this._parts.push(text);
121 this._parts.push(text);
114 }
122 }
115
123
116 dieUnexpectedToken(expected?: string) {
124 dieUnexpectedToken(expected?: string): never {
117 throw new Error(isNullOrEmptyString(expected) ?
125 throw new Error(isNullOrEmptyString(expected) ?
118 `Unexpected token ${this._scanner.getTokenValue()}` :
126 `Unexpected token ${this._scanner.getTokenValue()}` :
119 `Unexpected token ${this._scanner.getTokenValue()}, expected ${expected}`
127 `Unexpected token ${this._scanner.getTokenValue()}, expected ${expected}`
120 );
128 );
121 }
129 }
122
130
123 dieUnexpectedEnd(expected?: string) {
131 dieUnexpectedEnd(expected?: string): never {
124 throw new Error(isNullOrEmptyString(expected) ? "Unexpected end of the string" : `Unexpected end of the string, expected ${expected}`);
132 throw new Error(isNullOrEmptyString(expected) ? "Unexpected end of the string" : `Unexpected end of the string, expected ${expected}`);
125 }
133 }
126 }
134 }
@@ -16,8 +16,8 const typeMap = {
16
16
17 export class FormatScanner {
17 export class FormatScanner {
18 private _text: string;
18 private _text: string;
19 private _tokenType: TokeType;
19 private _tokenType: TokeType | undefined;
20 private _tokenValue: string;
20 private _tokenValue: string | undefined;
21 private _rx = /[^{}:]+|(.)/g;
21 private _rx = /[^{}:]+|(.)/g;
22
22
23 constructor(text: string) {
23 constructor(text: string) {
@@ -30,6 +30,9 export class FormatScanner {
30 return false;
30 return false;
31
31
32 const match = this._rx.exec(this._text);
32 const match = this._rx.exec(this._text);
33 if (match === null)
34 return false;
35
33 this._tokenType = typeMap[match[1]] || TokeType.Text;
36 this._tokenType = typeMap[match[1]] || TokeType.Text;
34 this._tokenValue = match[0];
37 this._tokenValue = match[0];
35
38
@@ -37,10 +40,15 export class FormatScanner {
37 }
40 }
38
41
39 getTokenValue() {
42 getTokenValue() {
43 if (this._tokenValue === undefined)
44 throw new Error("The scanner is before the first element");
40 return this._tokenValue;
45 return this._tokenValue;
41 }
46 }
42
47
43 getTokenType() {
48 getTokenType() {
49
50 if (this._tokenType === undefined)
51 throw new Error("The scanner is before the first element");
44 return this._tokenType;
52 return this._tokenType;
45 }
53 }
46 }
54 }
@@ -26,6 +26,6 const sb = new StringBuilder();
26 export function format(format: string, ...args: any): string;
26 export function format(format: string, ...args: any): string;
27 export function format() {
27 export function format() {
28 sb.clear();
28 sb.clear();
29 sb.write.apply(sb, arguments);
29 sb.write.apply<StringBuilder, any, void>(sb, arguments);
30 return sb.toString();
30 return sb.toString();
31 }
31 }
@@ -1,8 +1,8
1 import { isPrimitive, isNull, each } from "../safe";
1 import { isPrimitive, isNull, isKeyof, get } from "../safe";
2 import { MapOf } from "../interfaces";
2 import { MapOf } from "../interfaces";
3
3
4 type SubstFn = (name: string, format?: string) => string;
4 type SubstFn = (name: string, format?: string) => string;
5 type TemplateFn = (subst: SubstFn) => string;
5 type TemplateFn = (subst: SubstFn) => string | undefined;
6 type ConvertFn = (value: any, format?: string) => string;
6 type ConvertFn = (value: any, format?: string) => string;
7
7
8 const map = {
8 const map = {
@@ -28,19 +28,19 function espaceString(s: string) {
28 function encode(s: string) {
28 function encode(s: string) {
29 if (!s)
29 if (!s)
30 return s;
30 return s;
31 return s.replace(/\\{|\\}|&|\\:|\n/g, m => map[m] || m);
31 return s.replace(/\\{|\\}|&|\\:|\n/g, m => isKeyof(m, map) ? map[m] : m);
32 }
32 }
33
33
34 function decode(s: string) {
34 function decode(s: string) {
35 if (!s)
35 if (!s)
36 return s;
36 return s;
37 return s.replace(/&(\w+);/g, (m, $1) => rev[$1] || m);
37 return s.replace(/&(\w+);/g, (m, $1) => isKeyof($1, rev) ? rev[$1] : m);
38 }
38 }
39
39
40 function subst(s: string) {
40 function subst(s: string) {
41 const i = s.indexOf(":");
41 const i = s.indexOf(":");
42 let name: string;
42 let name: string;
43 let pattern: string;
43 let pattern: string | undefined;
44 if (i >= 0) {
44 if (i >= 0) {
45 name = s.substr(0, i);
45 name = s.substr(0, i);
46 pattern = s.substr(i + 1);
46 pattern = s.substr(i + 1);
@@ -51,7 +51,8 function subst(s: string) {
51 if (pattern)
51 if (pattern)
52 return [
52 return [
53 espaceString(decode(name)),
53 espaceString(decode(name)),
54 espaceString(decode(pattern))];
54 espaceString(decode(pattern))
55 ];
55 else
56 else
56 return [espaceString(decode(name))];
57 return [espaceString(decode(name))];
57 }
58 }
@@ -103,9 +104,9 export function compile(template: string
103 return compiled;
104 return compiled;
104 }
105 }
105
106
106 function defaultConverter(value: any, pattern: string) {
107 function defaultConverter(value: any, pattern?: string) {
107 if (pattern && pattern.toLocaleLowerCase() === "json") {
108 if (pattern && pattern.toLocaleLowerCase() === "json") {
108 const seen = [];
109 const seen: any = [];
109 return JSON.stringify(value, (k, v) => {
110 return JSON.stringify(value, (k, v) => {
110 if (!isPrimitive(v)) {
111 if (!isPrimitive(v)) {
111 const id = seen.indexOf(v);
112 const id = seen.indexOf(v);
@@ -113,10 +114,10 function defaultConverter(value: any, pa
113 return "@ref-" + id;
114 return "@ref-" + id;
114 else {
115 else {
115 seen.push(v);
116 seen.push(v);
116 return v;
117 return v.toString() as string;
117 }
118 }
118 } else {
119 } else {
119 return v;
120 return isNull(v) ? "" : v.toString();
120 }
121 }
121 }, 2);
122 }, 2);
122 } else if (isNull(value)) {
123 } else if (isNull(value)) {
@@ -124,7 +125,7 function defaultConverter(value: any, pa
124 } else if (value instanceof Date) {
125 } else if (value instanceof Date) {
125 return value.toISOString();
126 return value.toISOString();
126 } else {
127 } else {
127 return value.toString();
128 return value.toString() as string;
128 }
129 }
129 }
130 }
130
131
@@ -136,7 +137,7 export class Formatter {
136 this._converters.push(defaultConverter);
137 this._converters.push(defaultConverter);
137 }
138 }
138
139
139 convert(value: any, pattern: string) {
140 convert(value: any, pattern?: string) {
140 for (const c of this._converters) {
141 for (const c of this._converters) {
141 const res = c(value, pattern);
142 const res = c(value, pattern);
142 if (!isNull(res))
143 if (!isNull(res))
@@ -149,7 +150,7 export class Formatter {
149 const template = compile(msg);
150 const template = compile(msg);
150
151
151 return template((name, pattern) => {
152 return template((name, pattern) => {
152 const value = args[name];
153 const value = get(name, args);
153 return !isNull(value) ? this.convert(value, pattern) : "";
154 return !isNull(value) ? this.convert(value, pattern) : "";
154 });
155 });
155
156
@@ -159,7 +160,7 export class Formatter {
159 const template = compile(msg);
160 const template = compile(msg);
160 return (...args: any[]) => {
161 return (...args: any[]) => {
161 return template((name, pattern) => {
162 return template((name, pattern) => {
162 const value = args[name];
163 const value = get(name, args);
163 return !isNull(value) ? this.convert(value, pattern) : "";
164 return !isNull(value) ? this.convert(value, pattern) : "";
164 });
165 });
165 };
166 };
@@ -3,8 +3,6 import { FormatCompiler } from "./Format
3 import { isString, argumentNotNull } from "../safe";
3 import { isString, argumentNotNull } from "../safe";
4 import { Converter } from "./Converter";
4 import { Converter } from "./Converter";
5
5
6 const compiler = new FormatCompiler();
7
8 export abstract class TextWriterBase implements TextWriter {
6 export abstract class TextWriterBase implements TextWriter {
9 private _converter: Converter;
7 private _converter: Converter;
10
8
@@ -21,7 +19,7 export abstract class TextWriterBase imp
21 write(format: string, ...args: any[]): void;
19 write(format: string, ...args: any[]): void;
22 write(format: any, ...args: any[]): void {
20 write(format: any, ...args: any[]): void {
23 if (args.length) {
21 if (args.length) {
24 const compiled = compiler.compile(format);
22 const compiled = FormatCompiler.compile(format);
25 compiled(this, args);
23 compiled(this, args);
26 } else {
24 } else {
27 this.writeValue(format);
25 this.writeValue(format);
@@ -32,11 +30,11 export abstract class TextWriterBase imp
32 writeLine(format: string, ...args: any[]): void;
30 writeLine(format: string, ...args: any[]): void;
33 writeLine(): void {
31 writeLine(): void {
34 if (arguments.length)
32 if (arguments.length)
35 this.write.apply(this, arguments);
33 this.write.apply<this, any, void>(this, arguments);
36 this.writeNewLine();
34 this.writeNewLine();
37 }
35 }
38
36
39 writeValue(value: any, spec?: string): void {
37 writeValue(value: any, spec?: string) {
40 this.writeText(
38 this.writeText(
41 isString(value) ?
39 isString(value) ?
42 value :
40 value :
@@ -44,5 +42,5 export abstract class TextWriterBase imp
44 );
42 );
45 }
43 }
46
44
47 abstract writeText(text: string);
45 abstract writeText(text: string): void;
48 }
46 }
@@ -1,12 +1,43
1 import { Foo } from "./Foo";
1 import { Foo } from "./Foo";
2
2
3 /* export const service = annotate<Bar>();
4
5 @service.wire({
6 foo: dependency("foo"),
7 nested: {
8 lazy: dependency("foo", { lazy: true })
9 },
10 host: dependency("host")
11 }, "") */
3 export class Bar {
12 export class Bar {
4 name = "bar";
13 barName = "Twister";
14
15 _v: Foo | undefined;
5
16
6 foo: Foo;
17 constructor(
18 _opts: {
19 foo?: Foo;
20 nested?: {
21 lazy: () => Foo
22 },
23 host: string
24 },
25 s: string
26 ) {
7
27
8 constructor(_opts) {
9 if (_opts && _opts.foo)
28 if (_opts && _opts.foo)
10 this.foo = _opts.foo;
29 this._v = _opts.foo;
30 if (s)
31 this.barName = s;
32 }
33
34 setName(name: string) {
35
36 }
37
38 getFoo() {
39 if (this._v === undefined)
40 throw new Error("The foo isn't set");
41 return this._v;
11 }
42 }
12 }
43 }
@@ -1,3 +1,3
1 export class Foo {
1 export class Foo {
2 name = "foo";
2 fooName = "foo";
3 }
3 }
@@ -3,9 +3,15 import { Cancellation } from "../Cancell
3
3
4 export class MockActivationController implements IActivationController {
4 export class MockActivationController implements IActivationController {
5
5
6 _active: IActivatable = null;
6 _active: IActivatable | null = null;
7
8 hasActive() {
9 return !!this._active;
10 }
7
11
8 getActive(): IActivatable {
12 getActive(): IActivatable {
13 if (!this._active)
14 throw new Error("No active component is set");
9 return this._active;
15 return this._active;
10 }
16 }
11
17
@@ -20,8 +20,19 test("controller activation", async t =>
20 const c = new MockActivationController();
20 const c = new MockActivationController();
21
21
22 t.false(a.isActive(), "the component is not active by default");
22 t.false(a.isActive(), "the component is not active by default");
23 t.assert(c.getActive() == null, "the activation controller doesn't have an active component by default");
23 t.false(c.hasActive(), "the activation controller doesn't have an active component by default");
24 t.assert(a.getActivationController() == null, "the component doesn't have an activation controller by default");
24 try {
25 c.getActive();
26 t.fail("Should fail when no active component is set");
27 } catch (e) {
28 }
29
30 t.false(a.hasActivationController(), "the component doesn't have an activation controller by default");
31 try {
32 a.getActivationController();
33 t.fail("Should fail when no activation controller is set");
34 } catch (e) {
35 }
25
36
26 t.comment("Active the component through the controller");
37 t.comment("Active the component through the controller");
27 await c.activate(a);
38 await c.activate(a);
@@ -33,7 +44,7 test("controller activation", async t =>
33 await c.deactivate();
44 await c.deactivate();
34
45
35 t.false(a.isActive(), "The component should successfully deactivate");
46 t.false(a.isActive(), "The component should successfully deactivate");
36 t.equal(c.getActive(), null, "The controller shouldn't point to any component");
47 t.false(c.hasActive(), "The controller shouldn't point to any component");
37 t.equal(a.getActivationController(), c, "The componet should point to it's controller");
48 t.equal(a.getActivationController(), c, "The componet should point to it's controller");
38 });
49 });
39
50
@@ -1,10 +1,10
1 import { Cancellation } from "../Cancellation";
1 import { Cancellation } from "../Cancellation";
2 import { delay } from "../safe";
2 import { delay, notImplemented } from "../safe";
3 import { test } from "./TestTraits";
3 import { test } from "./TestTraits";
4
4
5 test("standalone cancellation", async t => {
5 test("standalone cancellation", async t => {
6
6
7 let doCancel: (e) => void;
7 let doCancel: (e: any) => void = notImplemented;
8
8
9 const ct = new Cancellation(cancel => {
9 const ct = new Cancellation(cancel => {
10 doCancel = cancel;
10 doCancel = cancel;
@@ -8,12 +8,19 import { Bar } from "../mock/Bar";
8 import { isNull } from "../safe";
8 import { isNull } from "../safe";
9
9
10 test("Container register/resolve tests", async t => {
10 test("Container register/resolve tests", async t => {
11 const container = new Container();
11 const container = new Container<{
12 "bla-bla": string;
13 "connection": string;
14 "dbParams": {
15 timeout: number;
16 connection: string;
17 }
18 }>();
12
19
13 const connection1 = "db://localhost";
20 const connection1 = "db://localhost";
14
21
15 t.throws(
22 t.throws(
16 () => container.register("bla-bla", "bla-bla"),
23 () => container.register("bla-bla", "bla-bla" as any),
17 "Do not allow to register anything other than descriptors"
24 "Do not allow to register anything other than descriptors"
18 );
25 );
19
26
@@ -41,7 +48,12 test("Container register/resolve tests",
41
48
42 test("Container configure/resolve tests", async t => {
49 test("Container configure/resolve tests", async t => {
43
50
44 const container = new Container();
51 const container = new Container<{
52 foo: Foo;
53 box: Bar;
54 bar: Bar;
55 db: any;
56 }>();
45
57
46 await container.configure({
58 await container.configure({
47 foo: {
59 foo: {
@@ -57,13 +69,13 test("Container configure/resolve tests"
57
69
58 bar: {
70 bar: {
59 $type: Bar,
71 $type: Bar,
60 params: {
72 params: [{
61 db: {
73 db: {
62 provider: {
74 provider: {
63 $dependency: "db"
75 $dependency: "db"
64 }
76 }
65 }
77 }
66 }
78 }]
67 }
79 }
68 });
80 });
69 t.pass("should configure from js object");
81 t.pass("should configure from js object");
@@ -89,5 +101,6 test("Load configuration from module", a
89 const b1 = container.resolve("bar") as Bar;
101 const b1 = container.resolve("bar") as Bar;
90
102
91 t.assert(!isNull(b1), "bar should not be null");
103 t.assert(!isNull(b1), "bar should not be null");
92 t.assert(!isNull(b1.foo), "bar.foo should not be null");
104 t.assert(!isNull(b1._v), "bar.foo should not be null");
105
93 });
106 });
@@ -1,19 +1,19
1 import { TraceSource } from "../log/TraceSource";
1 import { TraceSource } from "../log/TraceSource";
2 import { Observable } from "../Observable";
2 import { Observable } from "../Observable";
3 import { IObservable } from "../interfaces";
3 import { IObservable } from "../interfaces";
4 import { delay } from "../safe";
4 import { delay, fork } from "../safe";
5 import { test } from "./TestTraits";
5 import { test } from "./TestTraits";
6
6
7 const trace = TraceSource.get("ObservableTests");
7 const trace = TraceSource.get("ObservableTests");
8
8
9 test("events sequence example", async t => {
9 test("events sequence example", async t => {
10
10
11 let events: IObservable<number>;
11 let events: IObservable<number> | undefined;
12
12
13 const done = new Promise<void>(resolve => {
13 const done = new Promise<void>(resolve => {
14 events = new Observable<number>(async (notify, fail, finish) => {
14 events = new Observable<number>(async (notify, fail, finish) => {
15 for (let i = 0; i < 10; i++) {
15 for (let i = 0; i < 10; i++) {
16 await delay(0);
16 await fork();
17 notify(i);
17 notify(i);
18 }
18 }
19 finish();
19 finish();
@@ -23,7 +23,9 test("events sequence example", async t
23
23
24 let count = 0;
24 let count = 0;
25 let complete = false;
25 let complete = false;
26 events.on(x => count = count + x, null, () => complete = true);
26 if (!events)
27 throw new Error("events === undefined");
28 events.on(x => count = count + x, undefined, () => complete = true);
27
29
28 const first = await events.next();
30 const first = await events.next();
29
31
@@ -37,11 +39,11 test("events sequence example", async t
37 });
39 });
38
40
39 test("event sequence termination", async t => {
41 test("event sequence termination", async t => {
40 let events: IObservable<number>;
42 let events: IObservable<number> | undefined;
41
43
42 const done = new Promise<void>(resolve => {
44 const done = new Promise<void>(resolve => {
43 events = new Observable<number>(async (notify, fail, complete) => {
45 events = new Observable<number>(async (notify, fail, complete) => {
44 await delay(0);
46 await fork();
45 notify(1);
47 notify(1);
46 complete();
48 complete();
47 notify(2);
49 notify(2);
@@ -51,6 +53,9 test("event sequence termination", async
51 });
53 });
52 });
54 });
53
55
56 if (!events)
57 throw new Error("events === undefined");
58
54 let count = 0;
59 let count = 0;
55 events.on(() => {}, e => count++, () => count++);
60 events.on(() => {}, e => count++, () => count++);
56
61
@@ -1,5 +1,5
1 import { Cancellation } from "../Cancellation";
1 import { Cancellation } from "../Cancellation";
2 import { first, isPromise, firstWhere, delay, nowait } from "../safe";
2 import { first, isPromise, firstWhere, delay, nowait, notImplemented } from "../safe";
3 import { test } from "./TestTraits";
3 import { test } from "./TestTraits";
4
4
5 test("await delay test", async t => {
5 test("await delay test", async t => {
@@ -13,7 +13,7 test("await delay test", async t => {
13 t.pass("await delay");
13 t.pass("await delay");
14
14
15 // create cancellation token
15 // create cancellation token
16 let cancel: (e?: any) => void;
16 let cancel: (e?: any) => void = notImplemented;
17 const ct = new Cancellation(c => cancel = c);
17 const ct = new Cancellation(c => cancel = c);
18
18
19 // schedule delay
19 // schedule delay
@@ -40,14 +40,14 test("await delay test", async t => {
40
40
41 test("sequemce test", async t => {
41 test("sequemce test", async t => {
42 const sequence = ["a", "b", "c"];
42 const sequence = ["a", "b", "c"];
43 const empty = [];
43 const empty: string[] = [];
44
44
45 // synchronous tests
45 // synchronous tests
46 t.equals(first(sequence), "a", "Should return the first element");
46 t.equals(first(sequence), "a", "Should return the first element");
47 t.equals(firstWhere(sequence, x => x === "b"), "b", "Should get the second element");
47 t.equals(firstWhere(sequence, x => x === "b"), "b", "Should get the second element");
48
48
49 let v: string;
49 let v: string | undefined;
50 let e: Error;
50 let e: Error | undefined;
51 first(sequence, x => v = x);
51 first(sequence, x => v = x);
52 t.equal(v, "a", "The callback should be called for the first element");
52 t.equal(v, "a", "The callback should be called for the first element");
53 firstWhere(sequence, x => x === "b", x => v = x);
53 firstWhere(sequence, x => x === "b", x => v = x);
@@ -77,7 +77,7 test("sequemce test", async t => {
77 firstWhere(sequence, x => x === "z", x => v = x);
77 firstWhere(sequence, x => x === "z", x => v = x);
78 }, "Should throw when the element isn't found");
78 }, "Should throw when the element isn't found");
79
79
80 first(empty, null, x => e = x);
80 first(empty, undefined, x => e = x);
81 t.true(e, "The errorback should be called for the empty sequence");
81 t.true(e, "The errorback should be called for the empty sequence");
82
82
83 // async tests
83 // async tests
@@ -8,7 +8,7 export class TapeWriter implements IDest
8 private readonly _tape: tape.Test;
8 private readonly _tape: tape.Test;
9
9
10 private readonly _subscriptions = new Array<IDestroyable>();
10 private readonly _subscriptions = new Array<IDestroyable>();
11 private _destroyed;
11 private _destroyed = false;
12
12
13 constructor(t: tape.Test) {
13 constructor(t: tape.Test) {
14 argumentNotNull(t, "tape");
14 argumentNotNull(t, "tape");
@@ -38,6 +38,9 export class TapeWriter implements IDest
38 }
38 }
39
39
40 destroy() {
40 destroy() {
41 if (this._destroyed)
42 return;
43 this._destroyed = true;
41 this._subscriptions.forEach(destroy);
44 this._subscriptions.forEach(destroy);
42 }
45 }
43 }
46 }
@@ -1,8 +1,6
1 {
1 {
2 "extends": "../tsconfig",
2 "extends": "../tsconfig",
3 "compilerOptions": {
3 "compilerOptions": {
4 "rootDir": "ts",
5 "baseUrl": ".",
6 "rootDirs": [
4 "rootDirs": [
7 "ts",
5 "ts",
8 "../main/ts"
6 "../main/ts"
@@ -6,5 +6,6 define([
6 "./ObservableTests",
6 "./ObservableTests",
7 "./ContainerTests",
7 "./ContainerTests",
8 "./SafeTests",
8 "./SafeTests",
9 "./TextTests"
9 "./TextTests",
10 "./FluentContainerTests"
10 ]); No newline at end of file
11 ]);
@@ -5,3 +5,4 import "./ObservableTests";
5 import "./ContainerTests";
5 import "./ContainerTests";
6 import "./SafeTests";
6 import "./SafeTests";
7 import "./TextTests";
7 import "./TextTests";
8 import "./FluentContainerTests";
@@ -1,9 +1,12
1 {
1 {
2 "compilerOptions": {
2 "compilerOptions": {
3 "moduleResolution": "node",
3 "moduleResolution": "node",
4 "experimentalDecorators": true,
4 "noEmitOnError": true,
5 "noEmitOnError": true,
5 "listFiles": true,
6 "listFiles": true,
7 "strict": true,
6 "types": [],
8 "types": [],
9 "target": "ES5",
7 "lib": ["es5", "es2015.promise", "es2015.symbol", "es2015.iterable", "dom", "scripthost"]
10 "lib": ["es5", "es2015.promise", "es2015.symbol", "es2015.iterable", "dom", "scripthost"]
8 }
11 }
9 } No newline at end of file
12 }
General Comments 0
You need to be logged in to leave comments. Login now