##// END OF EJS Templates
working on lifetime management
cin -
r129:c13384c6c1ac ioc ts support
parent child
Show More
@@ -0,0 +1,70
1 import { IDestroyable, MapOf } from "../interfaces";
2 import { argumentNotNull, isDestroyable } from "../safe";
3 import { ILifetimeManager } from "./interfaces";
4
5 function safeCall(item: () => void) {
6 try {
7 item();
8 } catch {
9 // silence
10 }
11 }
12
13 export class LifetimeManager implements IDestroyable, ILifetimeManager {
14 private _cleanup: (() => void)[] = [];
15 private _cache: MapOf<any> = {};
16 private _destroyed = false;
17
18 has(id: string) {
19 return id in this._cache;
20 }
21
22 get(id: string) {
23 const t = this._cache[id];
24 if (t === undefined)
25 throw new Error(`The item with with the key ${id} isn't found`);
26 return t;
27 }
28
29 register(id: string, item: any, cleanup?: (item: any) => void) {
30 argumentNotNull(id, "id");
31 argumentNotNull(item, "item");
32 if (this.has(id))
33 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
34 this._cache[id] = item;
35
36 if (this._destroyed)
37 throw new Error("Lifetime manager is destroyed");
38 if (cleanup) {
39 this._cleanup.push(() => cleanup(item));
40 } else if (isDestroyable(item)) {
41 this._cleanup.push(() => item.destroy());
42 }
43 }
44
45 destroy() {
46 if (!this._destroyed) {
47 this._destroyed = true;
48 this._cleanup.forEach(safeCall);
49 this._cleanup.length = 0;
50 }
51 }
52
53 static readonly empty: ILifetimeManager = {
54 has() {
55 return false;
56 },
57
58 get() {
59 throw new Error("The specified item isn't registered with this lifetime manager");
60 },
61
62 register() {
63 // does nothing
64 },
65
66 destroy() {
67 throw new Error("Trying to destroy empty lifetime manager, this is a bug.");
68 }
69 };
70 }
@@ -1,36 +1,40
1 import { Descriptor } 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 import { isDescriptor } from "./traits";
5
5
6 export class AggregateDescriptor<S extends object, T> implements Descriptor<S, T> {
6 export class AggregateDescriptor<S extends object, T> implements Descriptor<S, T> {
7 _value: any;
7 _value: any;
8
8
9 constructor(value: any) {
9 constructor(value: any) {
10 this._value = value;
10 this._value = value;
11 }
11 }
12
12
13 activate(context: ActivationContext<S>): T {
13 activate(context: ActivationContext<S>): T {
14 return this._parse(this._value, context, "$value");
14 return this._parse(this._value, context, "$value");
15 }
15 }
16
16
17 _parse(value: any, context: ActivationContext<S>, path: string): any {
17 _parse(value: any, context: ActivationContext<S>, path: string): any {
18 if (isPrimitive(value))
18 if (isPrimitive(value))
19 return value as any;
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}]`)) as any;
25 return value.map((x, i) => this._parse(x, context, `${path}[${i}]`)) as any;
26
26
27 const t: any = {};
27 const t: any = {};
28 for (const p in 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 }
31 }
32
32
33 toString() {
33 toString() {
34 return "@walk";
34 return "@walk";
35 }
35 }
36
37 clone() {
38 return this;
36 }
39 }
40 }
@@ -1,140 +1,143
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 { ServiceMap, Descriptor, PartialServiceMap, ContainerProvided, Resolver, ContainerServiceMap, ContainerKeys, ContainerResolve } from "./interfaces";
4 import { ServiceMap, Descriptor, PartialServiceMap, ContainerProvided, Resolver, ContainerServiceMap, ContainerKeys, ContainerResolve } from "./interfaces";
5 import { TraceSource } from "../log/TraceSource";
5 import { TraceSource } from "../log/TraceSource";
6 import { Configuration, RegistrationMap } from "./Configuration";
6 import { Configuration, RegistrationMap } from "./Configuration";
7 import { Cancellation } from "../Cancellation";
7 import { Cancellation } from "../Cancellation";
8 import { MapOf } from "../interfaces";
8 import { MapOf, IDestroyable } from "../interfaces";
9 import { isDescriptor } from "./traits";
9 import { isDescriptor } from "./traits";
10
10
11 const trace = TraceSource.get("@implab/core/di/ActivationContext");
11 const trace = TraceSource.get("@implab/core/di/ActivationContext");
12
12
13 export class Container<S extends object = any> implements Resolver<S> {
13 export class Container<S extends object = any> implements Resolver<S>, IDestroyable {
14 readonly _services: ContainerServiceMap<S>;
14 readonly _services: ContainerServiceMap<S>;
15
15
16 readonly _cache: MapOf<any>;
16 readonly _cache: MapOf<any>;
17
17
18 readonly _cleanup: (() => void)[];
18 readonly _cleanup: (() => void)[];
19
19
20 readonly _root: Container<S>;
20 readonly _root: Container<S>;
21
21
22 readonly _parent?: Container<S>;
22 readonly _parent?: Container<S>;
23
23
24 _disposed: boolean;
24 _disposed: boolean;
25
25
26 constructor(parent?: Container<S>) {
26 constructor(parent?: Container<S>) {
27 this._parent = parent;
27 this._parent = parent;
28 this._services = parent ? Object.create(parent._services) : {};
28 this._services = parent ? Object.create(parent._services) : {};
29 this._cache = {};
29 this._cache = {};
30 this._cleanup = [];
30 this._cleanup = [];
31 this._root = parent ? parent.getRootContainer() : this;
31 this._root = parent ? parent.getRootContainer() : this;
32 this._services.container = new ValueDescriptor(this) as any;
32 this._services.container = new ValueDescriptor(this) as any;
33 this._disposed = false;
33 this._disposed = false;
34 }
34 }
35
35
36 getRootContainer() {
36 getRootContainer() {
37 return this._root;
37 return this._root;
38 }
38 }
39
39
40 getParent() {
40 getParent() {
41 return this._parent;
41 return this._parent;
42 }
42 }
43
43
44 resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>): ContainerResolve<S, K> {
44 resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>): ContainerResolve<S, K> {
45 trace.debug("resolve {0}", name);
45 trace.debug("resolve {0}", name);
46 const d = this._services[name];
46 const d = this._services[name];
47 if (d === undefined) {
47 if (d === undefined) {
48 if (def !== undefined)
48 if (def !== undefined)
49 return def;
49 return def;
50 else
50 else
51 throw new Error("Service '" + name + "' isn't found");
51 throw new Error("Service '" + name + "' isn't found");
52 } else {
52 } else {
53
53
54 const context = new ActivationContext<S>(this, this._services);
54 const context = new ActivationContext<S>(this, this._services);
55 try {
55 try {
56 return context.activate(d, name.toString());
56 return context.activate(d, name.toString());
57 } catch (error) {
57 } catch (error) {
58 throw new ActivationError(name.toString(), context.getStack(), error);
58 throw new ActivationError(name.toString(), context.getStack(), error);
59 }
59 }
60 }
60 }
61 }
61 }
62
62
63 /**
63 /**
64 * @deprecated use resolve() method
64 * @deprecated use resolve() method
65 */
65 */
66 getService<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>) {
66 getService<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>) {
67 return this.resolve(name, def);
67 return this.resolve(name, def);
68 }
68 }
69
69
70 register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>): this;
70 register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>): this;
71 register(services: PartialServiceMap<S>): this;
71 register(services: PartialServiceMap<S>): this;
72 register<K extends keyof S>(nameOrCollection: K | ServiceMap<S>, service?: Descriptor<S, S[K]>) {
72 register<K extends keyof S>(nameOrCollection: K | ServiceMap<S>, service?: Descriptor<S, S[K]>) {
73 if (arguments.length === 1) {
73 if (arguments.length === 1) {
74 const data = nameOrCollection as ServiceMap<S>;
74 const data = nameOrCollection as ServiceMap<S>;
75
75
76 for (const name in data) {
76 for (const name in data) {
77 if (Object.prototype.hasOwnProperty.call(data, name)) {
77 if (Object.prototype.hasOwnProperty.call(data, name)) {
78 this.register(name, data[name] as Descriptor<S, S[keyof S]>);
78 this.register(name, data[name] as Descriptor<S, S[keyof S]>);
79 }
79 }
80 }
80 }
81 } else {
81 } else {
82 if (!isDescriptor(service))
82 if (!isDescriptor(service))
83 throw new Error("The service parameter must be a descriptor");
83 throw new Error("The service parameter must be a descriptor");
84
84
85 this._services[nameOrCollection as K] = service as any;
85 this._services[nameOrCollection as K] = service as any;
86 }
86 }
87 return this;
87 return this;
88 }
88 }
89
89
90 onDispose(callback: () => void) {
90 onDispose(callback: () => void) {
91 if (!(callback instanceof Function))
91 if (!(callback instanceof Function))
92 throw new Error("The callback must be a function");
92 throw new Error("The callback must be a function");
93 this._cleanup.push(callback);
93 this._cleanup.push(callback);
94 }
94 }
95
95
96 destroy() {
97 return this.dispose();
98 }
96 dispose() {
99 dispose() {
97 if (this._disposed)
100 if (this._disposed)
98 return;
101 return;
99 this._disposed = true;
102 this._disposed = true;
100 for (const f of this._cleanup)
103 for (const f of this._cleanup)
101 f();
104 f();
102 }
105 }
103
106
104 /**
107 /**
105 * @param{String|Object} config
108 * @param{String|Object} config
106 * The configuration of the contaier. Can be either a string or an object,
109 * The configuration of the contaier. Can be either a string or an object,
107 * if the configuration is an object it's treated as a collection of
110 * if the configuration is an object it's treated as a collection of
108 * services which will be registed in the contaier.
111 * services which will be registed in the contaier.
109 *
112 *
110 * @param{Function} opts.contextRequire
113 * @param{Function} opts.contextRequire
111 * The function which will be used to load a configuration or types for services.
114 * The function which will be used to load a configuration or types for services.
112 *
115 *
113 */
116 */
114 async configure(config: string | RegistrationMap<S>, opts?: any, ct = Cancellation.none) {
117 async configure(config: string | RegistrationMap<S>, opts?: any, ct = Cancellation.none) {
115 const c = new Configuration<S>(this);
118 const c = new Configuration<S>(this);
116
119
117 if (typeof (config) === "string") {
120 if (typeof (config) === "string") {
118 return c.loadConfiguration(config, opts && opts.contextRequire, ct);
121 return c.loadConfiguration(config, opts && opts.contextRequire, ct);
119 } else {
122 } else {
120 return c.applyConfiguration(config, opts && opts.contextRequire, ct);
123 return c.applyConfiguration(config, opts && opts.contextRequire, ct);
121 }
124 }
122 }
125 }
123
126
124 createChildContainer<S2 extends object = S>(): Container<S & S2> {
127 createChildContainer<S2 extends object = S>(): Container<S & S2> {
125 return new Container<S & S2>(this as any);
128 return new Container<S & S2>(this as any);
126 }
129 }
127
130
128 has(id: string | number) {
131 has(id: string | number) {
129 return id in this._cache;
132 return id in this._cache;
130 }
133 }
131
134
132 get(id: string | number) {
135 get(id: string | number) {
133 return this._cache[id];
136 return this._cache[id];
134 }
137 }
135
138
136 store(id: string | number, value: any) {
139 store(id: string | number, value: any) {
137 return (this._cache[id] = value);
140 return (this._cache[id] = value);
138 }
141 }
139
142
140 }
143 }
@@ -1,21 +1,18
1 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
1 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
2 import { argumentNotNull, oid } from "../safe";
2 import { argumentNotNull, oid } from "../safe";
3
3
4 export interface FactoryServiceDescriptorParams<S extends object, T, P extends any[]> extends ServiceDescriptorParams<S, T, P> {
4 export interface FactoryServiceDescriptorParams<S extends object, T, P extends any[]> extends ServiceDescriptorParams<S, T, P> {
5 factory: (...args: P) => T;
5 factory: (...args: P) => T;
6 }
6 }
7
7
8 export class FactoryServiceDescriptor<S extends object, T, P extends any[]> extends ServiceDescriptor<S, T, P> {
8 export class FactoryServiceDescriptor<S extends object, T, P extends any[]> extends ServiceDescriptor<S, T, P> {
9 constructor(opts: FactoryServiceDescriptorParams<S, T, P>) {
9 constructor(opts: FactoryServiceDescriptorParams<S, T, P>) {
10 super(opts);
10 super(opts);
11
11
12 argumentNotNull(opts && opts.factory, "opts.factory");
12 argumentNotNull(opts && opts.factory, "opts.factory");
13
13
14 // bind to null
14 // bind to null
15 this._factory = (...args) => opts.factory.apply(null, args as any);
15 this._factory = (...args) => opts.factory.apply(null, args as any);
16
16
17 if (opts.activation === "singleton") {
18 this._cacheId = oid(opts.factory);
19 }
17 }
20 }
18 }
21 }
@@ -1,239 +1,155
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ServiceMap, PartialServiceMap, ActivationType } from "./interfaces";
2 import { Descriptor, ServiceMap, PartialServiceMap, ActivationType, ILifetimeManager } from "./interfaces";
3 import { Container } from "./Container";
4 import { argumentNotNull, isPrimitive, keys, isNull } from "../safe";
3 import { argumentNotNull, isPrimitive, keys, isNull } from "../safe";
5 import { TraceSource } from "../log/TraceSource";
4 import { TraceSource } from "../log/TraceSource";
6 import { isDescriptor } from "./traits";
5 import { isDescriptor } from "./traits";
6 import { LifetimeManager } from "./LifetimeManager";
7 import { MatchingMemberKeys } from "../interfaces";
7
8
8 let cacheId = 0;
9 let cacheId = 0;
9
10
10 const trace = TraceSource.get("@implab/core/di/ActivationContext");
11 const trace = TraceSource.get("@implab/core/di/ActivationContext");
11
12
12 function injectMethod<T, M extends keyof T, S extends object, A>(target: T, method: M, context: ActivationContext<S>, args: A) {
13 function injectMethod<T, M extends keyof T, S extends object, A>(target: T, method: M, context: ActivationContext<S>, args: A) {
13
14
14 const m = target[method];
15 const m = target[method];
15 if (!m || typeof m !== "function")
16 if (!m || typeof m !== "function")
16 throw new Error("Method '" + method + "' not found");
17 throw new Error("Method '" + method + "' not found");
17
18
18 if (args instanceof Array)
19 if (args instanceof Array)
19 return m.apply(target, _parse(args, context, "." + method));
20 return m.apply(target, _parse(args, context, "." + method));
20 else
21 else
21 return m.call(target, _parse(args, context, "." + method));
22 return m.call(target, _parse(args, context, "." + method));
22 }
23 }
23
24
24 function makeClenupCallback<T>(target: T, method: Cleaner<T>): () => void;
25 function makeClenupCallback<T>(method: Cleaner<T>) {
25 function makeClenupCallback(target: any, method: any) {
26 if (typeof (method) === "function") {
26 if (typeof (method) === "string") {
27 return (target: T) => {
27 return () => {
28 method(target);
28 target[method]();
29 };
29 };
30 } else {
30 } else {
31 return () => {
31 return (target: T) => {
32 method(target);
32 (target[method] as any)();
33 };
33 };
34 }
34 }
35 }
35 }
36
36
37 function _parse(value: any, context: ActivationContext<any>, path: string): any {
37 function _parse(value: any, context: ActivationContext<any>, path: string): any {
38 if (isPrimitive(value))
38 if (isPrimitive(value))
39 return value as any;
39 return value as any;
40
40
41 trace.debug("parse {0}", path);
41 trace.debug("parse {0}", path);
42
42
43 if (isDescriptor(value))
43 if (isDescriptor(value))
44 return context.activate(value, path);
44 return context.activate(value, path);
45
45
46 if (value instanceof Array)
46 if (value instanceof Array)
47 return value.map((x, i) => _parse(x, context, `${path}[${i}]`)) as any;
47 return value.map((x, i) => _parse(x, context, `${path}[${i}]`)) as any;
48
48
49 const t: any = {};
49 const t: any = {};
50
50
51 keys(value).forEach(p => t[p] = _parse(value[p], context, `${path}.${p}`));
51 keys(value).forEach(p => t[p] = _parse(value[p], context, `${path}.${p}`));
52
52
53 return t;
53 return t;
54 }
54 }
55
55
56 export type Cleaner<T> = ((x: T) => void) | keyof Extract<T, { [M in keyof T]: () => void }>;
56 export type Cleaner<T> = ((x: T) => void) | MatchingMemberKeys<() => void, T>;
57
57
58 export type InjectionSpec<T> = {
58 export type InjectionSpec<T> = {
59 [m in keyof T]?: any;
59 [m in keyof T]?: any;
60 };
60 };
61
61
62 export interface ServiceDescriptorParams<S extends object, T, P extends any[]> {
62 export interface ServiceDescriptorParams<S extends object, T, P extends any[]> {
63 activation?: ActivationType;
63 lifetime: ILifetimeManager;
64
65 owner: Container<S>;
66
64
67 params?: P;
65 params?: P;
68
66
69 inject?: InjectionSpec<T>[];
67 inject?: InjectionSpec<T>[];
70
68
71 services?: PartialServiceMap<S>;
69 services?: PartialServiceMap<S>;
72
70
73 cleanup?: Cleaner<T>;
71 cleanup?: Cleaner<T>;
74 }
72 }
75
73
76 export class ServiceDescriptor<S extends object, T, P extends any[]> implements Descriptor<S, T> {
74 export class ServiceDescriptor<S extends object, T, P extends any[]> implements Descriptor<S, T> {
77 _instance: T | undefined;
78
79 _hasInstance = false;
80
81 _activationType: ActivationType = "call";
82
83 _services: ServiceMap<S>;
75 _services: ServiceMap<S>;
84
76
85 _params: P | undefined;
77 _params: P | undefined;
86
78
87 _inject: InjectionSpec<T>[];
79 _inject: InjectionSpec<T>[];
88
80
89 _cleanup: Cleaner<T> | undefined;
81 _cleanup: ((item: T) => void) | undefined;
90
82
91 _cacheId: any;
83 _cacheId = String(++cacheId);
92
84
93 _owner: Container<S>;
85 _lifetime = LifetimeManager.empty;
94
86
95 constructor(opts: ServiceDescriptorParams<S, T, P>) {
87 constructor(opts: ServiceDescriptorParams<S, T, P>) {
96 argumentNotNull(opts, "opts");
88 argumentNotNull(opts, "opts");
97 argumentNotNull(opts.owner, "owner");
98
89
99 this._owner = opts.owner;
90 if (opts.lifetime)
100
91 this._lifetime = opts.lifetime;
101 if (!isNull(opts.activation))
102 this._activationType = opts.activation;
103
92
104 if (!isNull(opts.params))
93 if (!isNull(opts.params))
105 this._params = opts.params;
94 this._params = opts.params;
106
95
107 this._inject = opts.inject || [];
96 this._inject = opts.inject || [];
108
97
109 this._services = (opts.services || {}) as ServiceMap<S>;
98 this._services = (opts.services || {}) as ServiceMap<S>;
110
99
111 if (opts.cleanup) {
100 if (opts.cleanup) {
112 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
101 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
113 throw new Error(
102 throw new Error(
114 "The cleanup parameter must be either a function or a function name");
103 "The cleanup parameter must be either a function or a function name");
115
104
116 this._cleanup = opts.cleanup;
105 this._cleanup = makeClenupCallback(opts.cleanup);
117 }
106 }
118 }
107 }
119
108
120 activate(context: ActivationContext<S>) {
109 activate(context: ActivationContext<S>) {
121 // if we have a local service records, register them first
110 const lifetime = this._lifetime.initialize(this._cacheId, context);
122 let instance: T;
123
124 // ensure we have a cache id
125 if (!this._cacheId)
126 this._cacheId = ++cacheId;
127
128 switch (this._activationType) {
129 case "singleton": // SINGLETON
130 // if the value is cached return it
131 if (this._hasInstance)
132 return this._instance;
133
134 // singletons are bound to the root container
135 const container = context.container.getRootContainer();
136
137 if (container.has(this._cacheId)) {
138 instance = container.get(this._cacheId);
139 } else {
140 instance = this._create(context);
141 container.store(this._cacheId, instance);
142 if (this._cleanup)
143 container.onDispose(
144 makeClenupCallback(instance, this._cleanup));
145 }
146
147 this._hasInstance = true;
148 return (this._instance = instance);
149
150 case "container": // CONTAINER
151 // return a cached value
152
153 if (this._hasInstance)
154 return this._instance;
155
156 // create an instance
157 instance = this._create(context);
158
111
159 // the instance is bound to the container
112 if (lifetime.has()) {
160 if (this._cleanup)
113 return lifetime.get();
161 this._owner.onDispose(
114 } else {
162 makeClenupCallback(instance, this._cleanup));
115 const instance = this._create(context);
163
116 lifetime.store(this._cacheId, this._cleanup);
164 // cache and return the instance
117 return instance;
165 this._hasInstance = true;
166 return (this._instance = instance);
167 case "context": // CONTEXT
168 // return a cached value if one exists
169
170 if (context.has(this._cacheId))
171 return context.get(this._cacheId);
172 // context context activated instances are controlled by callers
173 return context.store(this._cacheId, this._create(context));
174 case "call": // CALL
175 // per-call created instances are controlled by callers
176 return this._create(context);
177 case "hierarchy": // HIERARCHY
178 // hierarchy activated instances are behave much like container activated
179 // except they are created and bound to the child container
180
181 // return a cached value
182 if (context.container.has(this._cacheId))
183 return context.container.get(this._cacheId);
184
185 instance = this._create(context);
186
187 if (this._cleanup)
188 context.container.onDispose(makeClenupCallback(
189 instance,
190 this._cleanup));
191
192 return context.container.store(this._cacheId, instance);
193 default:
194 throw new Error("Invalid activation type: " + this._activationType);
195 }
118 }
196 }
119 }
197
120
198 isInstanceCreated() {
199 return this._hasInstance;
200 }
201
202 getInstance() {
203 return this._instance;
204 }
205
206 _factory(...params: any[]): T {
121 _factory(...params: any[]): T {
207 throw Error("Not implemented");
122 throw Error("Not implemented");
208 }
123 }
209
124
210 _create(context: ActivationContext<S>) {
125 _create(context: ActivationContext<S>) {
211 trace.debug(`constructing ${context._name}`);
126 trace.debug(`constructing ${context._name}`);
212
127
213 if (this._activationType !== "call" &&
214 context.visit(this._cacheId) > 0)
215 throw new Error("Recursion detected");
216
217 if (this._services) {
128 if (this._services) {
218 keys(this._services).forEach(p => context.register(p, this._services[p]));
129 keys(this._services).forEach(p => context.register(p, this._services[p]));
219 }
130 }
220
131
221 let instance: T;
132 let instance: T;
222
133
223 if (this._params === undefined) {
134 if (this._params === undefined) {
224 instance = this._factory();
135 instance = this._factory();
225 } else if (this._params instanceof Array) {
136 } else if (this._params instanceof Array) {
226 instance = this._factory.apply(this, _parse(this._params, context, "args"));
137 instance = this._factory.apply(this, _parse(this._params, context, "args"));
227 } else {
138 } else {
228 instance = this._factory(_parse(this._params, context, "args"));
139 instance = this._factory(_parse(this._params, context, "args"));
229 }
140 }
230
141
231 if (this._inject) {
142 if (this._inject) {
232 this._inject.forEach(spec => {
143 this._inject.forEach(spec => {
233 for (const m in spec)
144 for (const m in spec)
234 injectMethod(instance, m, context, spec[m]);
145 injectMethod(instance, m, context, spec[m]);
235 });
146 });
236 }
147 }
237 return instance;
148 return instance;
238 }
149 }
150
151 clone() {
152 return Object.create(this);
239 }
153 }
154
155 }
@@ -1,90 +1,88
1 import { primitive } from "../../safe";
1 import { primitive } from "../../safe";
2 import { ActivationType } from "../interfaces";
2 import { ActivationType } from "../interfaces";
3 import { AnnotaionBuilder } from "../Annotations";
3 import { AnnotaionBuilder } from "../Annotations";
4 import { LazyDependencyRegistration, DependencyRegistration } from "../Configuration";
4 import { LazyDependencyRegistration, DependencyRegistration } from "../Configuration";
5 import { Container } from "../Container";
5 import { Container } from "../Container";
6
6
7 export interface DependencyOptions<T> {
7 export interface DependencyOptions<T> {
8 optional?: boolean;
8 optional?: boolean;
9 default?: T;
9 default?: T;
10 }
10 }
11
11
12 export interface LazyDependencyOptions<T> extends DependencyOptions<T> {
12 export interface LazyDependencyOptions<T> extends DependencyOptions<T> {
13 lazy: true;
13 lazy: true;
14 }
14 }
15
15
16 export type ExtractService<K, S> = K extends keyof S ? S[K] : K;
16 export type ExtractService<K, S> = K extends keyof S ? S[K] : K;
17
17
18 export type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
18 export type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
19 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
19 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
20 D extends { $type: new (...args: any[]) => infer I } ? I :
20 D extends { $type: new (...args: any[]) => infer I } ? I :
21 D extends { $factory: (...args: any[]) => infer R } ? R :
21 D extends { $factory: (...args: any[]) => infer R } ? R :
22 WalkDependencies<D, S>;
22 WalkDependencies<D, S>;
23
23
24 export type WalkDependencies<D, S> = D extends primitive ? D :
24 export type WalkDependencies<D, S> = D extends primitive ? D :
25 { [K in keyof D]: ExtractDependency<D[K], S> };
25 { [K in keyof D]: ExtractDependency<D[K], S> };
26
26
27 export type ServiceModule<T, S extends object, M extends keyof any = "service"> = {
27 export type ServiceModule<T, S extends object, M extends keyof any = "service"> = {
28 [m in M]: AnnotaionBuilder<T, S>;
28 [m in M]: AnnotaionBuilder<T, S>;
29 };
29 };
30
30
31 export interface ServiceRecordBuilder<T, S extends object> {
31 export interface ServiceRecordBuilder<T, S extends object> {
32 type<P extends any[], C extends new (...args: ExtractDependency<P, S>) => T>(
32 type<P extends any[], C extends new (...args: ExtractDependency<P, S>) => T>(
33 target: C, ...params: P): ConstructorBuilder<C, S>;
33 target: C, ...params: P): ConstructorBuilder<C, S>;
34 factory<P extends any[], F extends (...args: ExtractDependency<P, S>) => T>(
34 factory<P extends any[], F extends (...args: ExtractDependency<P, S>) => T>(
35 target: F, ...params: P): FactoryBuilder<F, S>;
35 target: F, ...params: P): FactoryBuilder<F, S>;
36 wired<M extends keyof any>(module: ServiceModule<T, S, M>, m: M): RegistrationBuilder<T, S>;
36 wired<M extends keyof any>(module: ServiceModule<T, S, M>, m: M): RegistrationBuilder<T, S>;
37 wired(module: ServiceModule<T, S>): RegistrationBuilder<T, S>;
37 wired(module: ServiceModule<T, S>): RegistrationBuilder<T, S>;
38 }
38 }
39
39
40 export interface RegistrationVisitor<S extends object> {
40 export interface RegistrationVisitor<S extends object> {
41 visitDependency(): void;
41 visitDependency(): void;
42
42
43 visitObject(): void;
43 visitObject(): void;
44
44
45 visitTypeRegistration(): void;
45 visitTypeRegistration(): void;
46
46
47 visitFactoryRegistration(): void;
47 visitFactoryRegistration(): void;
48
48
49 visitBuilder<T>(builder: (t: ServiceRecordBuilder<T, S>) => void): void;
50
51 }
49 }
52
50
53 export interface RegistrationBuilder<T, S extends object> {
51 export interface RegistrationBuilder<T, S extends object> {
54 override<K extends keyof S>(name: K, builder: S[K], raw: true): this;
52 override<K extends keyof S>(name: K, builder: S[K], raw: true): this;
55 override<K extends keyof S>(name: K, builder: (t: ServiceRecordBuilder<S[K], S>) => void): this;
53 override<K extends keyof S>(name: K, builder: (t: ServiceRecordBuilder<S[K], S>) => void): this;
56 override<K extends keyof S, V>(name: S[K] extends ExtractDependency<V, S> ? K : never, value: V): this;
54 override<K extends keyof S, V>(name: S[K] extends ExtractDependency<V, S> ? K : never, value: V): this;
57
55
58 activate(activation: ActivationType): this;
56 activate(activation: ActivationType): this;
59 inject<M extends keyof T, P extends any[]>(member: T[M] extends (...params: ExtractDependency<P, S>) => any ? M : never, ...params: P): this;
57 inject<M extends keyof T, P extends any[]>(member: T[M] extends (...params: ExtractDependency<P, S>) => any ? M : never, ...params: P): this;
60
58
61 visit(visitor: RegistrationVisitor<S>): void;
59 visit(visitor: RegistrationVisitor<S>): void;
62 }
60 }
63
61
64 export interface ConstructorBuilder<C extends new (...args: any[]) => any, S extends object> extends RegistrationBuilder<InstanceType<C>, S> {
62 export interface ConstructorBuilder<C extends new (...args: any[]) => any, S extends object> extends RegistrationBuilder<InstanceType<C>, S> {
65 $type: C;
63 $type: C;
66 }
64 }
67
65
68 export interface FactoryBuilder<F extends (...args: any[]) => any, S extends object> extends RegistrationBuilder<ReturnType<F>, S> {
66 export interface FactoryBuilder<F extends (...args: any[]) => any, S extends object> extends RegistrationBuilder<ReturnType<F>, S> {
69 $factory: F;
67 $factory: F;
70 }
68 }
71
69
72 export interface ConfigBuilder<S extends object, Y extends keyof S = keyof S> {
70 export interface ConfigBuilder<S extends object, Y extends keyof S = keyof S> {
73 register<K extends Y>(name: K, builder: (t: ServiceRecordBuilder<S[K], S>) => void | Promise<void>): ConfigBuilder<S, Exclude<Y, K>>;
71 register<K extends Y>(name: K, builder: (t: ServiceRecordBuilder<S[K], S>) => void | Promise<void>): ConfigBuilder<S, Exclude<Y, K>>;
74 register<K extends Y, V>(name: S[K] extends ExtractDependency<V, S> ? K : never, value: V): ConfigBuilder<S, Exclude<Y, K>>;
72 register<K extends Y, V>(name: S[K] extends ExtractDependency<V, S> ? K : never, value: V): ConfigBuilder<S, Exclude<Y, K>>;
75 register<K extends Y>(name: K, value: S[K], raw: true): ConfigBuilder<S, Exclude<Y, K>>;
73 register<K extends Y>(name: K, value: S[K], raw: true): ConfigBuilder<S, Exclude<Y, K>>;
76
74
77 apply(container: Container<S>): Promise<void>;
75 apply(container: Container<S>): Promise<void>;
78 }
76 }
79
77
80 interface ServicesDeclaration<S extends object> {
78 interface ServicesDeclaration<S extends object> {
81 build<T>(this: void): ServiceRecordBuilder<T, S>;
79 build<T>(this: void): ServiceRecordBuilder<T, S>;
82 annotate<T>(this: void): AnnotaionBuilder<T, S>;
80 annotate<T>(this: void): AnnotaionBuilder<T, S>;
83
81
84 dependency<K extends keyof S>(this: void, name: K, opts: LazyDependencyOptions<S[K]>): LazyDependencyRegistration<S, K>;
82 dependency<K extends keyof S>(this: void, name: K, opts: LazyDependencyOptions<S[K]>): LazyDependencyRegistration<S, K>;
85 dependency<K extends keyof S>(this: void, name: K, opts?: DependencyOptions<S[K]>): DependencyRegistration<S, K>;
83 dependency<K extends keyof S>(this: void, name: K, opts?: DependencyOptions<S[K]>): DependencyRegistration<S, K>;
86
84
87 configure(): ConfigBuilder<S>;
85 configure(): ConfigBuilder<S>;
88 }
86 }
89
87
90 export declare function declare<S extends object>(): ServicesDeclaration<S>;
88 export declare function declare<S extends object>(): ServicesDeclaration<S>;
@@ -1,38 +1,51
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { IDestroyable } from "../interfaces";
2
3
3 export interface Descriptor<S extends object = any, T = any> {
4 export interface Descriptor<S extends object = any, T = any> {
4 activate(context: ActivationContext<S>): T;
5 activate(context: ActivationContext<S>): T;
6
7 clone(): this;
5 }
8 }
6
9
7 export type ServiceMap<S extends object> = {
10 export type ServiceMap<S extends object> = {
8 [k in keyof S]: Descriptor<S, S[k]>;
11 [k in keyof S]: Descriptor<S, S[k]>;
9 };
12 };
10
13
11 export type ContainerKeys<S extends object> = keyof S | keyof ContainerProvided<S>;
14 export type ContainerKeys<S extends object> = keyof S | keyof ContainerProvided<S>;
12
15
13 export type ContainerResolve<S extends object, K> =
16 export type ContainerResolve<S extends object, K> =
14 K extends keyof ContainerProvided<S> ? ContainerProvided<S>[K] :
17 K extends keyof ContainerProvided<S> ? ContainerProvided<S>[K] :
15 K extends keyof S ? S[K] : never;
18 K extends keyof S ? S[K] : never;
16
19
17 export type ContainerServiceMap<S extends object> = {
20 export type ContainerServiceMap<S extends object> = {
18 [K in ContainerKeys<S>]: Descriptor<S, ContainerResolve<S, K>>;
21 [K in ContainerKeys<S>]: Descriptor<S, ContainerResolve<S, K>>;
19 };
22 };
20
23
21 export type PartialServiceMap<S extends object> = {
24 export type PartialServiceMap<S extends object> = {
22 [k in keyof S]?: Descriptor<S, S[k]>;
25 [k in keyof S]?: Descriptor<S, S[k]>;
23 };
26 };
24
27
25 export interface Resolver<S extends object> {
28 export interface Resolver<S extends object> {
26 resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>): ContainerResolve<S, K>;
29 resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>): ContainerResolve<S, K>;
27 }
30 }
28
31
29 export interface ContainerProvided<S extends object> {
32 export interface ContainerProvided<S extends object> {
30 container: Resolver<S>;
33 container: Resolver<S>;
31 }
34 }
32
35
33 export type ContainerRegistered<S extends object> = /*{
36 export type ContainerRegistered<S extends object> = /*{
34 [K in Exclude<keyof S, keyof ContainerProvided<S>>]: S[K];
37 [K in Exclude<keyof S, keyof ContainerProvided<S>>]: S[K];
35 };*/
38 };*/
36 Exclude<S, ContainerProvided<S>>;
39 Exclude<S, ContainerProvided<S>>;
37
40
38 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
41 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
42
43 export interface ILifetimeManager extends IDestroyable {
44 initialize(id: string, context: ActivationContext<any>): ILifetime;
45 }
46
47 export interface ILifetime {
48 has(): boolean;
49 get(): any;
50 store(item: any, cleanup?: (item: any) => void): void;
51 } No newline at end of file
@@ -1,118 +1,126
1 export interface Constructor<T = {}> {
1 export interface Constructor<T = {}> {
2 new(...args: any[]): T;
2 new(...args: any[]): T;
3 prototype: T;
3 prototype: T;
4 }
4 }
5
5
6 export type PromiseOrValue<T> = T | PromiseLike<T>;
6 export type PromiseOrValue<T> = T | PromiseLike<T>;
7
7
8 export type Factory<T = {}> = (...args: any[]) => T;
8 export type Factory<T = {}> = (...args: any[]) => T;
9
9
10 export type Predicate<T = any> = (x: T) => boolean;
10 export type Predicate<T = any> = (x: T) => boolean;
11
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
12 export interface MapOf<T> {
20 export interface MapOf<T> {
13 [key: string]: T;
21 [key: string]: T;
14 }
22 }
15
23
16 export interface IDestroyable {
24 export interface IDestroyable {
17 destroy(): void;
25 destroy(): void;
18 }
26 }
19
27
20 export interface IRemovable {
28 export interface IRemovable {
21 remove(): void;
29 remove(): void;
22 }
30 }
23
31
24 export interface ICancellation {
32 export interface ICancellation {
25 throwIfRequested(): void;
33 throwIfRequested(): void;
26 isRequested(): boolean;
34 isRequested(): boolean;
27 isSupported(): boolean;
35 isSupported(): boolean;
28 register(cb: (e: any) => void): IDestroyable;
36 register(cb: (e: any) => void): IDestroyable;
29 }
37 }
30
38
31 /**
39 /**
32 * Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉ Π°ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½Π½ΡƒΡŽ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΡŽ
40 * Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉ Π°ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½Π½ΡƒΡŽ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΡŽ
33 */
41 */
34 export interface IActivatable {
42 export interface IActivatable {
35 /**
43 /**
36 * @returns Boolean indicates the current state
44 * @returns Boolean indicates the current state
37 */
45 */
38 isActive(): boolean;
46 isActive(): boolean;
39
47
40 /**
48 /**
41 * Starts the component activation
49 * Starts the component activation
42 * @param ct cancellation token for this operation
50 * @param ct cancellation token for this operation
43 */
51 */
44 activate(ct?: ICancellation): Promise<void>;
52 activate(ct?: ICancellation): Promise<void>;
45
53
46 /**
54 /**
47 * Starts the component deactivation
55 * Starts the component deactivation
48 * @param ct cancellation token for this operation
56 * @param ct cancellation token for this operation
49 */
57 */
50 deactivate(ct?: ICancellation): Promise<void>;
58 deactivate(ct?: ICancellation): Promise<void>;
51
59
52 /**
60 /**
53 * Sets the activation controller for this component
61 * Sets the activation controller for this component
54 * @param controller The activation controller
62 * @param controller The activation controller
55 *
63 *
56 * Activation controller checks whether this component
64 * Activation controller checks whether this component
57 * can be activated and manages the active state of the
65 * can be activated and manages the active state of the
58 * component
66 * component
59 */
67 */
60 setActivationController(controller: IActivationController): void;
68 setActivationController(controller: IActivationController): void;
61
69
62 /** Indicates whether this component has an activation controller */
70 /** Indicates whether this component has an activation controller */
63 hasActivationController(): boolean;
71 hasActivationController(): boolean;
64
72
65 /**
73 /**
66 * Gets the current activation controller for this component
74 * Gets the current activation controller for this component
67 */
75 */
68 getActivationController(): IActivationController;
76 getActivationController(): IActivationController;
69 }
77 }
70
78
71 export interface IActivationController {
79 export interface IActivationController {
72 activating(component: IActivatable, ct?: ICancellation): Promise<void>;
80 activating(component: IActivatable, ct?: ICancellation): Promise<void>;
73
81
74 activated(component: IActivatable, ct?: ICancellation): Promise<void>;
82 activated(component: IActivatable, ct?: ICancellation): Promise<void>;
75
83
76 deactivating(component: IActivatable, ct?: ICancellation): Promise<void>;
84 deactivating(component: IActivatable, ct?: ICancellation): Promise<void>;
77
85
78 deactivated(component: IActivatable, ct?: ICancellation): Promise<void>;
86 deactivated(component: IActivatable, ct?: ICancellation): Promise<void>;
79
87
80 deactivate(ct?: ICancellation): Promise<void>;
88 deactivate(ct?: ICancellation): Promise<void>;
81
89
82 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
90 activate(component: IActivatable, ct?: ICancellation): Promise<void>;
83
91
84 hasActive(): boolean;
92 hasActive(): boolean;
85
93
86 getActive(): IActivatable;
94 getActive(): IActivatable;
87 }
95 }
88
96
89 export interface IAsyncComponent {
97 export interface IAsyncComponent {
90 getCompletion(): Promise<void>;
98 getCompletion(): Promise<void>;
91 }
99 }
92
100
93 export interface ICancellable {
101 export interface ICancellable {
94 cancel(reason?: any): void;
102 cancel(reason?: any): void;
95 }
103 }
96
104
97 export interface IObservable<T> {
105 export interface IObservable<T> {
98 on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable;
106 on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable;
99 next(ct?: ICancellation): Promise<T>;
107 next(ct?: ICancellation): Promise<T>;
100 }
108 }
101
109
102 export interface IObserver<T> {
110 export interface IObserver<T> {
103 next(event: T): void;
111 next(event: T): void;
104
112
105 error(e: any): void;
113 error(e: any): void;
106
114
107 complete(): void;
115 complete(): void;
108 }
116 }
109
117
110 export interface TextWriter {
118 export interface TextWriter {
111 write(obj: any): void;
119 write(obj: any): void;
112 write(format: string, ...args: any[]): void;
120 write(format: string, ...args: any[]): void;
113
121
114 writeLine(obj?: any): void;
122 writeLine(obj?: any): void;
115 writeLine(format: string, ...args: any[]): void;
123 writeLine(format: string, ...args: any[]): void;
116
124
117 writeValue(value: any, spec?: string): void;
125 writeValue(value: any, spec?: string): void;
118 }
126 }
@@ -1,499 +1,505
1 import { ICancellable, Constructor } from "./interfaces";
1 import { ICancellable, Constructor, IDestroyable } from "./interfaces";
2 import { Cancellation } from "./Cancellation";
2 import { Cancellation } from "./Cancellation";
3
3
4 let _nextOid = 0;
4 let _nextOid = 0;
5 const _oid = typeof Symbol === "function" ?
5 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: any): string | undefined {
9 export function oid(instance: any): string | undefined {
10 if (isNull(instance))
10 if (isNull(instance))
11 return undefined;
11 return undefined;
12
12
13 if (_oid in instance)
13 if (_oid in instance)
14 return instance[_oid];
14 return instance[_oid];
15 else
15 else
16 return (instance[_oid] = "oid_" + (++_nextOid));
16 return (instance[_oid] = "oid_" + (++_nextOid));
17 }
17 }
18
18
19 export function keys<T>(arg: T): (Extract<keyof T, string>)[] {
19 export function keys<T>(arg: T): (Extract<keyof T, string>)[] {
20 return isObject(arg) && arg ? Object.keys(arg) as (Extract<keyof T, string>)[] : [];
20 return isObject(arg) && arg ? Object.keys(arg) as (Extract<keyof T, string>)[] : [];
21 }
21 }
22
22
23 export function isKeyof<T>(k: string, target: T): k is Extract<keyof T, string> {
23 export function isKeyof<T>(k: string, target: T): k is Extract<keyof T, string> {
24 return target && typeof target === "object" && k in target;
24 return target && typeof target === "object" && k in target;
25 }
25 }
26
26
27 export function argumentNotNull(arg: any, name: string) {
27 export function argumentNotNull(arg: any, name: string) {
28 if (arg === null || arg === undefined)
28 if (arg === null || arg === undefined)
29 throw new Error("The argument " + name + " can't be null or undefined");
29 throw new Error("The argument " + name + " can't be null or undefined");
30 }
30 }
31
31
32 export function argumentNotEmptyString(arg: any, name: string) {
32 export function argumentNotEmptyString(arg: any, name: string) {
33 if (typeof (arg) !== "string" || !arg.length)
33 if (typeof (arg) !== "string" || !arg.length)
34 throw new Error("The argument '" + name + "' must be a not empty string");
34 throw new Error("The argument '" + name + "' must be a not empty string");
35 }
35 }
36
36
37 export function argumentNotEmptyArray(arg: any, name: string) {
37 export function argumentNotEmptyArray(arg: any, name: string) {
38 if (!(arg instanceof Array) || !arg.length)
38 if (!(arg instanceof Array) || !arg.length)
39 throw new Error("The argument '" + name + "' must be a not empty array");
39 throw new Error("The argument '" + name + "' must be a not empty array");
40 }
40 }
41
41
42 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
42 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
43 if (!(arg instanceof type))
43 if (!(arg instanceof type))
44 throw new Error("The argument '" + name + "' type doesn't match");
44 throw new Error("The argument '" + name + "' type doesn't match");
45 }
45 }
46
46
47 export function isObject(val: any): val is object {
47 export function isObject(val: any): val is object {
48 return typeof val === "object";
48 return typeof val === "object";
49 }
49 }
50
50
51 export function isNull(val: any): val is null | undefined {
51 export function isNull(val: any): val is null | undefined {
52 return (val === null || val === undefined);
52 return (val === null || val === undefined);
53 }
53 }
54
54
55 export type primitive = symbol | string | number | boolean | undefined | null;
55 export type primitive = symbol | string | number | boolean | undefined | null;
56
56
57 export function isPrimitive(val: any): val is primitive {
57 export function isPrimitive(val: any): val is primitive {
58 return (val === null || val === undefined || typeof (val) === "string" ||
58 return (val === null || val === undefined || typeof (val) === "string" ||
59 typeof (val) === "number" || typeof (val) === "boolean");
59 typeof (val) === "number" || typeof (val) === "boolean");
60 }
60 }
61
61
62 export function isInteger(val: any): val is number {
62 export function isInteger(val: any): val is number {
63 return parseInt(val, 10) === val;
63 return parseInt(val, 10) === val;
64 }
64 }
65
65
66 export function isNumber(val: any): val is number {
66 export function isNumber(val: any): val is number {
67 return parseFloat(val) === val;
67 return parseFloat(val) === val;
68 }
68 }
69
69
70 export function isString(val: any): val is string {
70 export function isString(val: any): val is string {
71 return typeof (val) === "string" || val instanceof String;
71 return typeof (val) === "string" || val instanceof String;
72 }
72 }
73
73
74 export function isPromise<T = any>(val: any): val is PromiseLike<T> {
74 export function isPromise<T = any>(val: any): val is PromiseLike<T> {
75 return val && typeof val.then === "function";
75 return val && typeof val.then === "function";
76 }
76 }
77
77
78 export function isCancellable(val: any): val is ICancellable {
78 export function isCancellable(val: any): val is ICancellable {
79 return val && typeof val.cancel === "function";
79 return val && typeof val.cancel === "function";
80 }
80 }
81
81
82 export function isNullOrEmptyString(val: any): val is ("" | null | undefined) {
82 export function isNullOrEmptyString(val: any): val is ("" | null | undefined) {
83 return (val === null || val === undefined ||
83 return (val === null || val === undefined ||
84 ((typeof (val) === "string" || val instanceof String) && val.length === 0));
84 ((typeof (val) === "string" || val instanceof String) && val.length === 0));
85 }
85 }
86
86
87 export function isNotEmptyArray<T = any>(arg: any): arg is T[] {
87 export function isNotEmptyArray<T = any>(arg: any): arg is T[] {
88 return (arg instanceof Array && arg.length > 0);
88 return (arg instanceof Array && arg.length > 0);
89 }
89 }
90
90
91 function _isStrictMode(this: any) {
91 function _isStrictMode(this: any) {
92 return !this;
92 return !this;
93 }
93 }
94
94
95 function _getNonStrictGlobal(this: any) {
95 function _getNonStrictGlobal(this: any) {
96 return this;
96 return this;
97 }
97 }
98
98
99 export function getGlobal() {
99 export function getGlobal() {
100 // in es3 we can't use indirect call to eval, since it will
100 // in es3 we can't use indirect call to eval, since it will
101 // be executed in the current call context.
101 // be executed in the current call context.
102 if (!_isStrictMode()) {
102 if (!_isStrictMode()) {
103 return _getNonStrictGlobal();
103 return _getNonStrictGlobal();
104 } else {
104 } else {
105 // tslint:disable-next-line:no-eval
105 // tslint:disable-next-line:no-eval
106 return eval.call(null, "this");
106 return eval.call(null, "this");
107 }
107 }
108 }
108 }
109
109
110 export function get(member: string, context?: object) {
110 export function get(member: string, context?: object) {
111 argumentNotEmptyString(member, "member");
111 argumentNotEmptyString(member, "member");
112 let that = context || getGlobal();
112 let that = context || getGlobal();
113 const parts = member.split(".");
113 const parts = member.split(".");
114 for (const m of parts) {
114 for (const m of parts) {
115 if (!m)
115 if (!m)
116 continue;
116 continue;
117 if (isNull(that = that[m]))
117 if (isNull(that = that[m]))
118 break;
118 break;
119 }
119 }
120 return that;
120 return that;
121 }
121 }
122
122
123 /**
123 /**
124 * ВыполняСт ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива, останавливаСтся, ΠΊΠΎΠ³Π΄Π°
124 * ВыполняСт ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива, останавливаСтся, ΠΊΠΎΠ³Π΄Π°
125 * Π»ΠΈΠ±ΠΎ достигнут ΠΊΠΎΠ½Π΅Ρ† массива, Π»ΠΈΠ±ΠΎ функция <c>cb</c> Π²Π΅Ρ€Π½ΡƒΠ»Π°
125 * Π»ΠΈΠ±ΠΎ достигнут ΠΊΠΎΠ½Π΅Ρ† массива, Π»ΠΈΠ±ΠΎ функция <c>cb</c> Π²Π΅Ρ€Π½ΡƒΠ»Π°
126 * Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
126 * Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
127 *
127 *
128 * @param {Array | Object} obj массив элСмСнтов для просмотра
128 * @param {Array | Object} obj массив элСмСнтов для просмотра
129 * @param {Function} cb функция, вызываСмая для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта
129 * @param {Function} cb функция, вызываСмая для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта
130 * @param {Object} thisArg Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ Π² качСствС
130 * @param {Object} thisArg Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ Π² качСствС
131 * <c>this</c> Π² <c>cb</c>.
131 * <c>this</c> Π² <c>cb</c>.
132 * @returns Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π²Ρ‹Π·ΠΎΠ²Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ <c>cb</c>, Π»ΠΈΠ±ΠΎ <c>undefined</c>
132 * @returns Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Π²Ρ‹Π·ΠΎΠ²Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ <c>cb</c>, Π»ΠΈΠ±ΠΎ <c>undefined</c>
133 * Ссли достигнут ΠΊΠΎΠ½Π΅Ρ† массива.
133 * Ссли достигнут ΠΊΠΎΠ½Π΅Ρ† массива.
134 */
134 */
135 export function each<T>(obj: T, cb: <X extends keyof T>(v: NonNullable<T[X]>, k: X) => void): void;
135 export function each<T>(obj: T, cb: <X extends keyof T>(v: NonNullable<T[X]>, k: X) => void): void;
136 export function each<T>(array: T[], cb: (v: T, i: number) => void): void;
136 export function each<T>(array: T[], cb: (v: T, i: number) => void): void;
137 export function each(obj: any, cb: any, thisArg?: any): any;
137 export function each(obj: any, cb: any, thisArg?: any): any;
138 export function each(obj: any, cb: any, thisArg?: any) {
138 export function each(obj: any, cb: any, thisArg?: any) {
139 argumentNotNull(cb, "cb");
139 argumentNotNull(cb, "cb");
140 if (obj instanceof Array) {
140 if (obj instanceof Array) {
141 for (let i = 0; i < obj.length; i++) {
141 for (let i = 0; i < obj.length; i++) {
142 const x = cb.call(thisArg, obj[i], i);
142 const x = cb.call(thisArg, obj[i], i);
143 if (x !== undefined)
143 if (x !== undefined)
144 return x;
144 return x;
145 }
145 }
146 } else {
146 } else {
147 const _keys = Object.keys(obj);
147 const _keys = Object.keys(obj);
148 for (const k of _keys) {
148 for (const k of _keys) {
149 const x = cb.call(thisArg, obj[k], k);
149 const x = cb.call(thisArg, obj[k], k);
150 if (x !== undefined)
150 if (x !== undefined)
151 return x;
151 return x;
152 }
152 }
153 }
153 }
154 }
154 }
155
155
156 /** Copies property values from a source object to the destination and returns
156 /** Copies property values from a source object to the destination and returns
157 * the destination onject.
157 * the destination onject.
158 *
158 *
159 * @param dest The destination object into which properties from the source
159 * @param dest The destination object into which properties from the source
160 * object will be copied.
160 * object will be copied.
161 * @param source The source of values which will be copied to the destination
161 * @param source The source of values which will be copied to the destination
162 * object.
162 * object.
163 * @param template An optional parameter specifies which properties should be
163 * @param template An optional parameter specifies which properties should be
164 * copied from the source and how to map them to the destination. If the
164 * copied from the source and how to map them to the destination. If the
165 * template is an array it contains the list of property names to copy from the
165 * template is an array it contains the list of property names to copy from the
166 * source to the destination. In case of object the templates contains the map
166 * source to the destination. In case of object the templates contains the map
167 * where keys are property names in the source and the values are property
167 * where keys are property names in the source and the values are property
168 * names in the destination object. If the template isn't specified then the
168 * names in the destination object. If the template isn't specified then the
169 * own properties of the source are entirely copied to the destination.
169 * own properties of the source are entirely copied to the destination.
170 *
170 *
171 */
171 */
172 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: keyof S[]): T & S;
172 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: keyof S[]): T & S;
173 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;
173 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;
174 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: any): any {
174 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: any): any {
175 argumentNotNull(dest, "dest");
175 argumentNotNull(dest, "dest");
176 const _res: any = dest as any;
176 const _res: any = dest as any;
177
177
178 if (isPrimitive(source))
178 if (isPrimitive(source))
179 return _res;
179 return _res;
180
180
181 if (template instanceof Array) {
181 if (template instanceof Array) {
182 template.forEach(p => {
182 template.forEach(p => {
183 if (isKeyof(p, source))
183 if (isKeyof(p, source))
184 _res[p] = source[p];
184 _res[p] = source[p];
185 });
185 });
186 } else if (template) {
186 } else if (template) {
187 keys(source).forEach(p => {
187 keys(source).forEach(p => {
188 if (isKeyof(p, template))
188 if (isKeyof(p, template))
189 _res[template[p]] = source[p];
189 _res[template[p]] = source[p];
190 });
190 });
191 } else {
191 } else {
192 keys(source).forEach(p => _res[p] = source[p]);
192 keys(source).forEach(p => _res[p] = source[p]);
193 }
193 }
194
194
195 return _res;
195 return _res;
196 }
196 }
197
197
198 /** Wraps the specified function to emulate an asynchronous execution.
198 /** Wraps the specified function to emulate an asynchronous execution.
199 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
199 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
200 * @param{Function|String} fn [Required] Function wich will be wrapped.
200 * @param{Function|String} fn [Required] Function wich will be wrapped.
201 */
201 */
202 export function async<T, F extends (...args: any[]) => T | PromiseLike<T>>(
202 export function async<T, F extends (...args: any[]) => T | PromiseLike<T>>(
203 fn: F,
203 fn: F,
204 thisArg?: ThisParameterType<F>
204 thisArg?: ThisParameterType<F>
205 ): (...args: Parameters<F>) => PromiseLike<T>;
205 ): (...args: Parameters<F>) => PromiseLike<T>;
206 export function async<T, M extends string, O extends { [m in M]?: (...args: any[]) => T | PromiseLike<T> }>(
206 export function async<T, M extends string, O extends { [m in M]?: (...args: any[]) => T | PromiseLike<T> }>(
207 fn: M,
207 fn: M,
208 thisArg: O
208 thisArg: O
209 ): (...args: Parameters<NonNullable<O[M]>>) => PromiseLike<T>;
209 ): (...args: Parameters<NonNullable<O[M]>>) => PromiseLike<T>;
210 export function async(_fn: any, thisArg: any): (...args: any[]) => PromiseLike<any> {
210 export function async(_fn: any, thisArg: any): (...args: any[]) => PromiseLike<any> {
211 let fn = _fn;
211 let fn = _fn;
212
212
213 if (arguments.length === 2 && !(fn instanceof Function))
213 if (arguments.length === 2 && !(fn instanceof Function))
214 fn = thisArg[fn];
214 fn = thisArg[fn];
215
215
216 if (fn == null)
216 if (fn == null)
217 throw new Error("The function must be specified");
217 throw new Error("The function must be specified");
218
218
219 function wrapresult(x: any, e?: any): PromiseLike<any> {
219 function wrapresult(x: any, e?: any): PromiseLike<any> {
220 if (e) {
220 if (e) {
221 return {
221 return {
222 then(cb, eb) {
222 then(cb, eb) {
223 try {
223 try {
224 return eb ? wrapresult(eb(e)) : this;
224 return eb ? wrapresult(eb(e)) : this;
225 } catch (e2) {
225 } catch (e2) {
226 return wrapresult(null, e2);
226 return wrapresult(null, e2);
227 }
227 }
228 }
228 }
229 };
229 };
230 } else {
230 } else {
231 if (x && x.then)
231 if (x && x.then)
232 return x;
232 return x;
233 return {
233 return {
234 then(cb) {
234 then(cb) {
235 try {
235 try {
236 return cb ? wrapresult(cb(x)) : this;
236 return cb ? wrapresult(cb(x)) : this;
237 } catch (e2) {
237 } catch (e2) {
238 return wrapresult(e2);
238 return wrapresult(e2);
239 }
239 }
240 }
240 }
241 };
241 };
242 }
242 }
243 }
243 }
244
244
245 return (...args) => {
245 return (...args) => {
246 try {
246 try {
247 return wrapresult(fn.apply(thisArg, args));
247 return wrapresult(fn.apply(thisArg, args));
248 } catch (e) {
248 } catch (e) {
249 return wrapresult(null, e);
249 return wrapresult(null, e);
250 }
250 }
251 };
251 };
252 }
252 }
253
253
254 export function delegate<T extends object, F extends (this: T, ...args: any[]) => any>(
254 export function delegate<T extends object, F extends (this: T, ...args: any[]) => any>(
255 target: T,
255 target: T,
256 method: F
256 method: F
257 ): OmitThisParameter<F>;
257 ): OmitThisParameter<F>;
258 export function delegate<M extends string, T extends { [m in M]?: (...args: any[]) => any; }>(
258 export function delegate<M extends string, T extends { [m in M]?: (...args: any[]) => any; }>(
259 target: T,
259 target: T,
260 method: M
260 method: M
261 ): OmitThisParameter<T[M]>;
261 ): OmitThisParameter<T[M]>;
262 export function delegate(target: any, _method: any): (...args: any[]) => any {
262 export function delegate(target: any, _method: any): (...args: any[]) => any {
263 let method: any;
263 let method: any;
264 if (!(_method instanceof Function)) {
264 if (!(_method instanceof Function)) {
265 argumentNotNull(target, "target");
265 argumentNotNull(target, "target");
266 method = target[_method];
266 method = target[_method];
267 if (!(method instanceof Function))
267 if (!(method instanceof Function))
268 throw new Error("'method' argument must be a Function or a method name");
268 throw new Error("'method' argument must be a Function or a method name");
269 } else {
269 } else {
270 method = _method;
270 method = _method;
271 }
271 }
272
272
273 return (...args) => {
273 return (...args) => {
274 return method.apply(target, args);
274 return method.apply(target, args);
275 };
275 };
276 }
276 }
277
277
278 export function delay(timeMs: number, ct = Cancellation.none) {
278 export function delay(timeMs: number, ct = Cancellation.none) {
279 ct.throwIfRequested();
279 ct.throwIfRequested();
280 return new Promise((resolve, reject) => {
280 return new Promise((resolve, reject) => {
281 const h = ct.register(e => {
281 const h = ct.register(e => {
282 clearTimeout(id);
282 clearTimeout(id);
283 reject(e);
283 reject(e);
284 // we don't nedd to unregister h, since ct is already disposed
284 // we don't nedd to unregister h, since ct is already disposed
285 });
285 });
286 const id = setTimeout(() => {
286 const id = setTimeout(() => {
287 h.destroy();
287 h.destroy();
288 resolve();
288 resolve();
289 }, timeMs);
289 }, timeMs);
290
290
291 });
291 });
292 }
292 }
293
293
294 /** Returns resolved promise, awaiting this method will cause the asynchronous
294 /** Returns resolved promise, awaiting this method will cause the asynchronous
295 * completion of the rest of the code.
295 * completion of the rest of the code.
296 */
296 */
297 export function fork() {
297 export function fork() {
298 return Promise.resolve();
298 return Promise.resolve();
299 }
299 }
300
300
301 /** Always throws Error, can be used as a stub for the methods which should be
301 /** Always throws Error, can be used as a stub for the methods which should be
302 * assigned later and are required to be not null.
302 * assigned later and are required to be not null.
303 */
303 */
304 export function notImplemented(): never {
304 export function notImplemented(): never {
305 throw new Error("Not implemeted");
305 throw new Error("Not implemeted");
306 }
306 }
307 /**
307 /**
308 * Iterates over the specified array of items and calls the callback `cb`, if
308 * Iterates over the specified array of items and calls the callback `cb`, if
309 * the result of the callback is a promise the next item from the array will be
309 * the result of the callback is a promise the next item from the array will be
310 * proceeded after the promise is resolved.
310 * proceeded after the promise is resolved.
311 *
311 *
312 */
312 */
313 export function pmap<T, T2>(
313 export function pmap<T, T2>(
314 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
314 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
315 cb: (item: T, i: number) => T2 | PromiseLike<T2>
315 cb: (item: T, i: number) => T2 | PromiseLike<T2>
316 ): T2[] | PromiseLike<T2[]> {
316 ): T2[] | PromiseLike<T2[]> {
317 argumentNotNull(cb, "cb");
317 argumentNotNull(cb, "cb");
318
318
319 if (isPromise(items)) {
319 if (isPromise(items)) {
320 return items.then(data => pmap(data, cb));
320 return items.then(data => pmap(data, cb));
321 } else {
321 } else {
322
322
323 if (isNull(items) || !items.length)
323 if (isNull(items) || !items.length)
324 return [];
324 return [];
325
325
326 let i = 0;
326 let i = 0;
327 const result = new Array<T2>();
327 const result = new Array<T2>();
328
328
329 const next = (): any => {
329 const next = (): any => {
330 while (i < items.length) {
330 while (i < items.length) {
331 const r = cb(items[i], i);
331 const r = cb(items[i], i);
332 const ri = i;
332 const ri = i;
333 i++;
333 i++;
334 if (isPromise(r)) {
334 if (isPromise(r)) {
335 return r.then(x => {
335 return r.then(x => {
336 result[ri] = x;
336 result[ri] = x;
337 return next();
337 return next();
338 });
338 });
339 } else {
339 } else {
340 result[ri] = r;
340 result[ri] = r;
341 }
341 }
342 }
342 }
343 return result;
343 return result;
344 };
344 };
345
345
346 return next();
346 return next();
347 }
347 }
348 }
348 }
349
349
350 export function pfor<T>(
350 export function pfor<T>(
351 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
351 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
352 cb: (item: T, i: number) => any
352 cb: (item: T, i: number) => any
353 ): void | PromiseLike<void> {
353 ): void | PromiseLike<void> {
354 argumentNotNull(cb, "cb");
354 argumentNotNull(cb, "cb");
355
355
356 if (isPromise(items)) {
356 if (isPromise(items)) {
357 return items.then(data => pfor(data, cb));
357 return items.then(data => pfor(data, cb));
358 } else {
358 } else {
359 if (isNull(items) || !items.length)
359 if (isNull(items) || !items.length)
360 return;
360 return;
361
361
362 let i = 0;
362 let i = 0;
363
363
364 const next = (): any => {
364 const next = (): any => {
365 while (i < items.length) {
365 while (i < items.length) {
366 const r = cb(items[i], i);
366 const r = cb(items[i], i);
367 i++;
367 i++;
368 if (isPromise(r))
368 if (isPromise(r))
369 return r.then(next);
369 return r.then(next);
370 }
370 }
371 };
371 };
372
372
373 return next();
373 return next();
374 }
374 }
375 }
375 }
376
376
377 export function first<T>(sequence: ArrayLike<T>): T;
377 export function first<T>(sequence: ArrayLike<T>): T;
378 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
378 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
379 export function first<T>(
379 export function first<T>(
380 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
380 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
381 cb?: (x: T) => void,
381 cb?: (x: T) => void,
382 err?: (x: Error) => void
382 err?: (x: Error) => void
383 ): void;
383 ): void;
384 /**
384 /**
385 * Π’Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт ΠΈΠ· ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, ΠΈΠ»ΠΈ обСщания, Ссли Π²
385 * Π’Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт ΠΈΠ· ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, ΠΈΠ»ΠΈ обСщания, Ссли Π²
386 * качСствС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΎΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ массив.
386 * качСствС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΎΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ массив.
387 *
387 *
388 * @param {Function} cb ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, Π΅ΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ
388 * @param {Function} cb ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, Π΅ΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ
389 * элСмСнт ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π² случаС успСха
389 * элСмСнт ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π² случаС успСха
390 * @param {Function} err ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Ссли массив пустой, Π»ΠΈΠ±ΠΎ
390 * @param {Function} err ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Ссли массив пустой, Π»ΠΈΠ±ΠΎ
391 * нС массив
391 * нС массив
392 *
392 *
393 * @remarks Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π½ΠΈ cb Π½ΠΈ err, Ρ‚ΠΎΠ³Π΄Π° функция Π²Π΅Ρ€Π½Π΅Ρ‚ Π»ΠΈΠ±ΠΎ
393 * @remarks Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π½ΠΈ cb Π½ΠΈ err, Ρ‚ΠΎΠ³Π΄Π° функция Π²Π΅Ρ€Π½Π΅Ρ‚ Π»ΠΈΠ±ΠΎ
394 * ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт.
394 * ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт.
395 * @async
395 * @async
396 */
396 */
397 export function first<T>(
397 export function first<T>(
398 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
398 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
399 cb?: (x: T) => void,
399 cb?: (x: T) => void,
400 err?: (x: Error) => void
400 err?: (x: Error) => void
401 ) {
401 ) {
402 if (isPromise(sequence)) {
402 if (isPromise(sequence)) {
403 return sequence.then(res => first(res, cb as any /* force to pass undefined cb */, err));
403 return sequence.then(res => first(res, cb as any /* force to pass undefined cb */, err));
404 } else if (sequence && "length" in sequence) {
404 } else if (sequence && "length" in sequence) {
405 if (sequence.length === 0) {
405 if (sequence.length === 0) {
406 if (err)
406 if (err)
407 return err(new Error("The sequence is empty"));
407 return err(new Error("The sequence is empty"));
408 else
408 else
409 throw new Error("The sequence is empty");
409 throw new Error("The sequence is empty");
410 } else if (cb) {
410 } else if (cb) {
411 return cb(sequence[0]);
411 return cb(sequence[0]);
412 } else {
412 } else {
413 return sequence[0];
413 return sequence[0];
414 }
414 }
415 } else {
415 } else {
416 if (err)
416 if (err)
417 return err(new Error("The sequence is required"));
417 return err(new Error("The sequence is required"));
418 else
418 else
419 throw new Error("The sequence is required");
419 throw new Error("The sequence is required");
420 }
420 }
421 }
421 }
422
422
423 export function firstWhere<T>(
423 export function firstWhere<T>(
424 sequence: ArrayLike<T>,
424 sequence: ArrayLike<T>,
425 predicate: (x: T) => boolean
425 predicate: (x: T) => boolean
426 ): T;
426 ): T;
427 export function firstWhere<T>(
427 export function firstWhere<T>(
428 sequence: PromiseLike<ArrayLike<T>>,
428 sequence: PromiseLike<ArrayLike<T>>,
429 predicate: (x: T) => boolean
429 predicate: (x: T) => boolean
430 ): PromiseLike<T>;
430 ): PromiseLike<T>;
431 export function firstWhere<T>(
431 export function firstWhere<T>(
432 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
432 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
433 predicate: (x: T) => boolean,
433 predicate: (x: T) => boolean,
434 cb: (x: T) => void,
434 cb: (x: T) => void,
435 err?: (x: Error) => void
435 err?: (x: Error) => void
436 ): void;
436 ): void;
437
437
438 export function firstWhere<T>(
438 export function firstWhere<T>(
439 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
439 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
440 predicate?: (x: T) => boolean,
440 predicate?: (x: T) => boolean,
441 cb?: (x: T) => any,
441 cb?: (x: T) => any,
442 err?: (x: Error) => any
442 err?: (x: Error) => any
443 ) {
443 ) {
444 if (isPromise(sequence)) {
444 if (isPromise(sequence)) {
445 return sequence.then(res => firstWhere(
445 return sequence.then(res => firstWhere(
446 res,
446 res,
447 predicate as any /* force to pass undefined predicate */,
447 predicate as any /* force to pass undefined predicate */,
448 cb as any /* force to pass undefined cb */,
448 cb as any /* force to pass undefined cb */,
449 err)
449 err)
450 );
450 );
451 } else if (sequence && "length" in sequence) {
451 } else if (sequence && "length" in sequence) {
452 if (sequence.length === 0) {
452 if (sequence.length === 0) {
453 if (err)
453 if (err)
454 err(new Error("The sequence is empty"));
454 err(new Error("The sequence is empty"));
455 else
455 else
456 throw new Error("The sequence is empty");
456 throw new Error("The sequence is empty");
457 } else {
457 } else {
458 if (!predicate) {
458 if (!predicate) {
459 return cb ? cb(sequence[0]) && void (0) : sequence[0];
459 return cb ? cb(sequence[0]) && void (0) : sequence[0];
460 } else {
460 } else {
461 for (let i = 0; i < sequence.length; i++) {
461 for (let i = 0; i < sequence.length; i++) {
462 const v = sequence[i];
462 const v = sequence[i];
463 if (predicate(v))
463 if (predicate(v))
464 return cb ? cb(v) : v;
464 return cb ? cb(v) : v;
465 }
465 }
466 if (err)
466 if (err)
467 err(new Error("The sequence doesn't contain matching items"));
467 err(new Error("The sequence doesn't contain matching items"));
468 else
468 else
469 throw new Error("The sequence doesn't contain matching items");
469 throw new Error("The sequence doesn't contain matching items");
470 }
470 }
471 }
471 }
472 } else {
472 } else {
473 if (err)
473 if (err)
474 err(new Error("The sequence is required"));
474 err(new Error("The sequence is required"));
475 else
475 else
476 throw new Error("The sequence is required");
476 throw new Error("The sequence is required");
477 }
477 }
478 }
478 }
479
479
480 export function isDestroyable(d: any): d is IDestroyable {
481 if (d && "destroy" in d && typeof(destroy) === "function")
482 return true;
483 return false;
484 }
485
480 export function destroy(d: any) {
486 export function destroy(d: any) {
481 if (d && "destroy" in d)
487 if (d && "destroy" in d)
482 d.destroy();
488 d.destroy();
483 }
489 }
484
490
485 /**
491 /**
486 * Used to mark that the async operation isn't awaited intentionally.
492 * Used to mark that the async operation isn't awaited intentionally.
487 * @param p The promise which represents the async operation.
493 * @param p The promise which represents the async operation.
488 */
494 */
489 export function nowait(p: Promise<any>) {
495 export function nowait(p: Promise<any>) {
490 }
496 }
491
497
492 /** represents already destroyed object.
498 /** represents already destroyed object.
493 */
499 */
494 export const destroyed = {
500 export const destroyed = {
495 /** Calling to this method doesn't affect anything, noop.
501 /** Calling to this method doesn't affect anything, noop.
496 */
502 */
497 destroy() {
503 destroy() {
498 }
504 }
499 };
505 };
General Comments 0
You need to be logged in to leave comments. Login now