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