##// END OF EJS Templates
fixed bug in applying a lifetime while processing the json configuration...
cin -
r152:8bee6a6d5f46 v1.4.0-rc10 default
parent child
Show More
@@ -1,74 +1,74
1 import { argumentNotEmptyString } from "../safe";
1 import { argumentNotEmptyString } from "../safe";
2 import { MapOf } from "../interfaces";
2 import { MapOf } from "../interfaces";
3 import { TraceSource, DebugLevel } from "../log/TraceSource";
3 import { TraceSource, DebugLevel } from "../log/TraceSource";
4 import m = require("module");
4 import m = require("module");
5
5
6 const trace = TraceSource.get(m.id);
6 const trace = TraceSource.get(m.id);
7
7
8 const splitRx = /(<%=|<%~|\[%~|\[%=|<%|\[%|%\]|%>)/;
8 const splitRx = /(<%=|<%~|\[%~|\[%=|<%|\[%|%\]|%>)/;
9
9
10 export enum TokenType {
10 export enum TokenType {
11 None,
11 None,
12 Text,
12 Text,
13 OpenInlineBlock,
13 OpenInlineBlock,
14 OpenFilterBlock,
14 OpenFilterBlock,
15 OpenBlock,
15 OpenBlock,
16 CloseBlock
16 CloseBlock
17 }
17 }
18
18
19 const tokenMap: MapOf<TokenType> = {
19 const tokenMap: MapOf<TokenType> = {
20 "<%": TokenType.OpenBlock,
20 "<%": TokenType.OpenBlock,
21 "[%": TokenType.OpenBlock,
21 "[%": TokenType.OpenBlock,
22 "<%=": TokenType.OpenInlineBlock,
22 "<%=": TokenType.OpenInlineBlock,
23 "[%=": TokenType.OpenInlineBlock,
23 "[%=": TokenType.OpenInlineBlock,
24 "<%~": TokenType.OpenFilterBlock,
24 "<%~": TokenType.OpenFilterBlock,
25 "[%~": TokenType.OpenFilterBlock,
25 "[%~": TokenType.OpenFilterBlock,
26 "%>": TokenType.CloseBlock,
26 "%>": TokenType.CloseBlock,
27 "%]": TokenType.CloseBlock
27 "%]": TokenType.CloseBlock
28 };
28 };
29
29
30 export interface ITemplateParser {
30 export interface ITemplateParser {
31 next(): boolean;
31 next(): boolean;
32 token(): TokenType;
32 token(): TokenType;
33 value(): string;
33 value(): string;
34 }
34 }
35
35
36 export class TemplateParser implements ITemplateParser {
36 export class TemplateParser implements ITemplateParser {
37
37
38 _tokens: string[];
38 _tokens: string[];
39 _pos = -1;
39 _pos = -1;
40 _type: TokenType;
40 _type: TokenType;
41 _value: string | undefined;
41 _value: string | undefined;
42
42
43 constructor(text: string) {
43 constructor(text: string) {
44 argumentNotEmptyString(text, "text");
44 argumentNotEmptyString(text, "text");
45
45
46 this._tokens = text.split(splitRx);
46 this._tokens = text.split(splitRx);
47 this._type = TokenType.None;
47 this._type = TokenType.None;
48 }
48 }
49
49
50 next() {
50 next() {
51 this._pos++;
51 this._pos++;
52 if (this._pos < this._tokens.length) {
52 if (this._pos < this._tokens.length) {
53 this._value = this._tokens[this._pos];
53 this._value = this._tokens[this._pos];
54 this._type = tokenMap[this._value] || TokenType.Text;
54 this._type = tokenMap[this._value] || TokenType.Text;
55
55
56 return true;
56 return true;
57 } else {
57 } else {
58 this._type = TokenType.None;
58 this._type = TokenType.None;
59 this._value = undefined;
59 this._value = undefined;
60 return false;
60 return false;
61 }
61 }
62 }
62 }
63
63
64 token() {
64 token() {
65 return this._type;
65 return this._type;
66 }
66 }
67
67
68 value() {
68 value() {
69 if (!this._value)
69 if (this._value === undefined)
70 throw new Error("The current token doesn't have a value");
70 throw new Error("The current token doesn't have a value");
71 return this._value;
71 return this._value;
72 }
72 }
73
73
74 }
74 }
@@ -1,49 +1,56
1 import request = require("dojo/request");
1 import request = require("dojo/request");
2 import m = require("module");
2 import m = require("module");
3 import { TraceSource } from "../log/TraceSource";
3 import { TraceSource } from "../log/TraceSource";
4 import { TemplateCompiler } from "./TemplateCompiler";
4 import { TemplateCompiler } from "./TemplateCompiler";
5 import { TemplateParser } from "./TemplateParser";
5 import { TemplateParser } from "./TemplateParser";
6 import { isNullOrEmptyString } from "../safe";
6 import { isNullOrEmptyString } from "../safe";
7 import { MapOf } from "../interfaces";
7 import { MapOf } from "../interfaces";
8
8
9 type TemplateFn = (obj: object) => string;
9 type TemplateFn = (obj: object) => string;
10
10
11 const trace = TraceSource.get(m.id);
11 const trace = TraceSource.get(m.id);
12
12
13 function compile(str: string) {
13 function compile(str: string) {
14 if (isNullOrEmptyString(str))
14 if (isNullOrEmptyString(str))
15 return () => "";
15 return () => "";
16
16
17 const parser = new TemplateParser(str);
17 const parser = new TemplateParser(str);
18 const compiler = new TemplateCompiler();
18 const compiler = new TemplateCompiler();
19
19
20 return compiler.compile(parser);
20 return compiler.compile(parser);
21 }
21 }
22
22
23 const cache: MapOf<TemplateFn> = {};
23 const cache: MapOf<TemplateFn> = {};
24
24
25 interface OnLoadFn<T> {
25 interface OnLoadFn<T> {
26 (res: T): void;
26 (res: T): void;
27 error(e: any): void;
27 error(e: any): void;
28 }
28 }
29
29
30 compile.load = (id: string, require: Require, callback: OnLoadFn<TemplateFn>) => {
30 compile.load = (id: string, require: Require, callback: OnLoadFn<TemplateFn>) => {
31 const url = require.toUrl(id);
31 const url = require.toUrl(id);
32 if (url in cache) {
32 if (url in cache) {
33 trace.debug("{0} -> {1}: cached", id, url);
33 trace.debug("{0} -> {1}: cached", id, url);
34 callback(cache[url]);
34 callback(cache[url]);
35 } else {
35 } else {
36 trace.debug("{0} -> {1}: load", id, url);
36 trace.debug("{0} -> {1}: load", id, url);
37 request<string>(url).then(compile).then((tc: TemplateFn) => {
37 request<string>(url).then(compile).then((tc: TemplateFn) => {
38 trace.debug("{0}: compiled", url);
38 trace.debug("{0}: compiled", url);
39 callback(cache[url] = tc);
39 callback(cache[url] = tc);
40 }, (err: any) => {
40 }, (err: any) => {
41 if (callback.error)
41 callback.error({
42 callback.error({
42 inner: err,
43 inner: err,
43 src: "@implab/core/text/template-compile"
44 from: "@implab/core/text/template-compile"
45 });
46 else
47 trace.error({
48 message: `Failed to load: ${url}`,
49 error: err,
50 from: "@implab/core/text/template-compile"
44 });
51 });
45 });
52 });
46 }
53 }
47 };
54 };
48
55
49 export = compile;
56 export = compile;
@@ -1,12 +1,14
1 export class ConfigError extends Error {
1 export class ConfigError {
2 inner?: {};
2 inner?: {};
3
3
4 message: string;
5
4 path?: string;
6 path?: string;
5
7
6 configName?: string;
8 configName?: string;
7
9
8 constructor(message: string, inner?: {}) {
10 constructor(message: string, inner?: {}) {
9 super(message);
11 this.message = message;
10 this.inner = inner;
12 this.inner = inner;
11 }
13 }
12 }
14 }
@@ -1,450 +1,459
1 import {
1 import {
2 PartialServiceMap,
2 PartialServiceMap,
3 ActivationType,
3 ActivationType,
4 ContainerKeys,
4 ContainerKeys,
5 TypeOfService,
5 TypeOfService,
6 ILifetime, ServiceContainer
6 ILifetime, ServiceContainer
7 } from "./interfaces";
7 } from "./interfaces";
8
8
9 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
9 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive, oid, mixin } from "../safe";
10 import { AggregateDescriptor } from "./AggregateDescriptor";
10 import { AggregateDescriptor } from "./AggregateDescriptor";
11 import { ValueDescriptor } from "./ValueDescriptor";
11 import { ValueDescriptor } from "./ValueDescriptor";
12 import { ReferenceDescriptor } from "./ReferenceDescriptor";
12 import { ReferenceDescriptor } from "./ReferenceDescriptor";
13 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
13 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
14 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
14 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
15 import { TraceSource } from "../log/TraceSource";
15 import { TraceSource } from "../log/TraceSource";
16 import { ConfigError } from "./ConfigError";
16 import { ConfigError } from "./ConfigError";
17 import { Cancellation } from "../Cancellation";
17 import { Cancellation } from "../Cancellation";
18 import { makeResolver } from "./ResolverHelper";
18 import { makeResolver } from "./ResolverHelper";
19 import { ICancellation } from "../interfaces";
19 import { ICancellation } from "../interfaces";
20 import { isDescriptor } from "./traits";
20 import { isDescriptor } from "./traits";
21 import { LazyReferenceDescriptor } from "./LazyReferenceDescriptor";
21 import { LazyReferenceDescriptor } from "./LazyReferenceDescriptor";
22 import { LifetimeManager } from "./LifetimeManager";
22 import { LifetimeManager } from "./LifetimeManager";
23 import { ServiceDescriptorParams } from "./ServiceDescriptor";
23
24
24 export interface RegistrationScope<S extends object> {
25 export interface RegistrationScope<S extends object> {
25
26
26 /** сСрвисы, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Ρ€Π΅Π³ΠΈΡΡ‚Ρ€ΠΈΡ€ΡƒΡŽΡ‚ΡΡ Π² контСкстС Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ ΠΈ Ρ‚Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ
27 /** сСрвисы, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Ρ€Π΅Π³ΠΈΡΡ‚Ρ€ΠΈΡ€ΡƒΡŽΡ‚ΡΡ Π² контСкстС Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ ΠΈ Ρ‚Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ
27 * ΠΌΠΎΠ³ΡƒΡ‚ ΠΏΠ΅Ρ€Π΅ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡ‚ΡŒ Ρ€Π°Π½Π΅Π΅ зарСгистрированныС сСрвисы. Π·Π° это свойство
28 * ΠΌΠΎΠ³ΡƒΡ‚ ΠΏΠ΅Ρ€Π΅ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡ‚ΡŒ Ρ€Π°Π½Π΅Π΅ зарСгистрированныС сСрвисы. Π·Π° это свойство
28 * Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ»Π°Ρ‚ΠΈΡ‚ΡŒ, ΠΊΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ порядок Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ Π±ΡƒΠ΄Π΅Ρ‚ Π²Π»ΠΈΡΡ‚ΡŒ Π½Π° Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚
29 * Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ»Π°Ρ‚ΠΈΡ‚ΡŒ, ΠΊΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ порядок Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ Π±ΡƒΠ΄Π΅Ρ‚ Π²Π»ΠΈΡΡ‚ΡŒ Π½Π° Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚
29 * Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ зависимостСй.
30 * Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ зависимостСй.
30 */
31 */
31 services?: RegistrationMap<S>;
32 services?: RegistrationMap<S>;
32 }
33 }
33
34
34 /**
35 /**
35 * Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ интСрфСйс ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ сСрвисов
36 * Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ интСрфСйс ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ сСрвисов
36 */
37 */
37 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
38 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
38
39
39 activation?: ActivationType;
40 activation?: ActivationType;
40
41
41 params?: any;
42 params?: any;
42
43
43 /** Π‘ΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΏΡ€ΠΈ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ singleton, Ссли
44 /** Π‘ΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΏΡ€ΠΈ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ singleton, Ссли
44 * Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½ для TypeRegistration вычисляСтся ΠΊΠ°ΠΊ oid($type)
45 * Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½ для TypeRegistration вычисляСтся ΠΊΠ°ΠΊ oid($type)
45 */
46 */
46 typeId?: string;
47 typeId?: string;
47
48
48 inject?: object | object[];
49 inject?: object | object[];
49
50
50 cleanup?: ((instance: T) => void) | string;
51 cleanup?: ((instance: T) => void) | string;
51 }
52 }
52
53
53 export interface TypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
54 export interface TypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
54 $type: string | C;
55 $type: string | C;
55 params?: Registration<ConstructorParameters<C>, S>;
56 params?: Registration<ConstructorParameters<C>, S>;
56 }
57 }
57
58
58 export interface StrictTypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
59 export interface StrictTypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
59 $type: C;
60 $type: C;
60 params?: Registration<ConstructorParameters<C>, S>;
61 params?: Registration<ConstructorParameters<C>, S>;
61 }
62 }
62
63
63 export interface FactoryRegistration<F extends (...args: any[]) => any, S extends object> extends ServiceRegistration<ReturnType<F>, S> {
64 export interface FactoryRegistration<F extends (...args: any[]) => any, S extends object> extends ServiceRegistration<ReturnType<F>, S> {
64 $factory: string | F;
65 $factory: string | F;
65 }
66 }
66
67
67 export interface ValueRegistration<T> {
68 export interface ValueRegistration<T> {
68 $value: T;
69 $value: T;
69 parse?: boolean;
70 parse?: boolean;
70 }
71 }
71
72
72 export interface DependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends RegistrationScope<S> {
73 export interface DependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends RegistrationScope<S> {
73 $dependency: K;
74 $dependency: K;
74 lazy?: boolean;
75 lazy?: boolean;
75 optional?: boolean;
76 optional?: boolean;
76 default?: TypeOfService<S, K>;
77 default?: TypeOfService<S, K>;
77 }
78 }
78
79
79 export interface LazyDependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends DependencyRegistration<S, K> {
80 export interface LazyDependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends DependencyRegistration<S, K> {
80 lazy: true;
81 lazy: true;
81 }
82 }
82
83
83 export type Registration<T, S extends object> = T extends primitive ? T :
84 export type Registration<T, S extends object> = T extends primitive ? T :
84 (
85 (
85 T |
86 T |
86 { [k in keyof T]: Registration<T[k], S> } |
87 { [k in keyof T]: Registration<T[k], S> } |
87 TypeRegistration<new (...args: any[]) => T, S> |
88 TypeRegistration<new (...args: any[]) => T, S> |
88 FactoryRegistration<(...args: any[]) => T, S> |
89 FactoryRegistration<(...args: any[]) => T, S> |
89 ValueRegistration<any> |
90 ValueRegistration<any> |
90 DependencyRegistration<S, keyof S>
91 DependencyRegistration<S, keyof S>
91 );
92 );
92
93
93 export type RegistrationMap<S extends object> = {
94 export type RegistrationMap<S extends object> = {
94 [k in keyof S]?: Registration<S[k], S>;
95 [k in keyof S]?: Registration<S[k], S>;
95 };
96 };
96
97
97 const _activationTypes: { [k in ActivationType]: number; } = {
98 const _activationTypes: { [k in ActivationType]: number; } = {
98 singleton: 1,
99 singleton: 1,
99 container: 2,
100 container: 2,
100 hierarchy: 3,
101 hierarchy: 3,
101 context: 4,
102 context: 4,
102 call: 5
103 call: 5
103 };
104 };
104
105
105 export function isTypeRegistration(x: any): x is TypeRegistration<new () => any, any> {
106 export function isTypeRegistration(x: any): x is TypeRegistration<new () => any, any> {
106 return (!isPrimitive(x)) && ("$type" in x);
107 return (!isPrimitive(x)) && ("$type" in x);
107 }
108 }
108
109
109 export function isFactoryRegistration(x: any): x is FactoryRegistration<() => any, any> {
110 export function isFactoryRegistration(x: any): x is FactoryRegistration<() => any, any> {
110 return (!isPrimitive(x)) && ("$factory" in x);
111 return (!isPrimitive(x)) && ("$factory" in x);
111 }
112 }
112
113
113 export function isValueRegistration(x: any): x is ValueRegistration<any> {
114 export function isValueRegistration(x: any): x is ValueRegistration<any> {
114 return (!isPrimitive(x)) && ("$value" in x);
115 return (!isPrimitive(x)) && ("$value" in x);
115 }
116 }
116
117
117 export function isDependencyRegistration<S extends object>(x: any): x is DependencyRegistration<S, keyof S> {
118 export function isDependencyRegistration<S extends object>(x: any): x is DependencyRegistration<S, keyof S> {
118 return (!isPrimitive(x)) && ("$dependency" in x);
119 return (!isPrimitive(x)) && ("$dependency" in x);
119 }
120 }
120
121
121 export function isActivationType(x: string): x is ActivationType {
122 export function isActivationType(x: string): x is ActivationType {
122 return typeof x === "string" && x in _activationTypes;
123 return typeof x === "string" && x in _activationTypes;
123 }
124 }
124
125
125 const trace = TraceSource.get("@implab/core/di/Configuration");
126 const trace = TraceSource.get("@implab/core/di/Configuration");
126 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
127 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
127 async function mapAll(data: any, map?: (v: any, k: string) => any): Promise<any>;
128 async function mapAll(data: any, map?: (v: any, k: string) => any): Promise<any>;
128 async function mapAll(data: any, map?: (v: any, k: any) => any): Promise<any> {
129 async function mapAll(data: any, map?: (v: any, k: any) => any): Promise<any> {
129 if (data instanceof Array) {
130 if (data instanceof Array) {
130 return Promise.all(map ? data.map(map) : data);
131 return Promise.all(map ? data.map(map) : data);
131 } else {
132 } else {
132 const keys = Object.keys(data);
133 const keys = Object.keys(data);
133
134
134 const o: any = {};
135 const o: any = {};
135
136
136 await Promise.all(keys.map(async k => {
137 await Promise.all(keys.map(async k => {
137 const v = map ? map(data[k], k) : data[k];
138 const v = map ? map(data[k], k) : data[k];
138 o[k] = isPromise(v) ? await v : v;
139 o[k] = isPromise(v) ? await v : v;
139 }));
140 }));
140
141
141 return o;
142 return o;
142 }
143 }
143 }
144 }
144
145
145 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
146 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
146
147
147 export class Configuration<S extends object> {
148 export class Configuration<S extends object> {
148
149
149 _hasInnerDescriptors = false;
150 _hasInnerDescriptors = false;
150
151
151 readonly _container: ServiceContainer<S>;
152 readonly _container: ServiceContainer<S>;
152
153
153 _path: Array<string>;
154 _path: Array<string>;
154
155
155 _configName: string | undefined;
156 _configName: string | undefined;
156
157
157 _require: ModuleResolver | undefined;
158 _require: ModuleResolver | undefined;
158
159
159 constructor(container: ServiceContainer<S>) {
160 constructor(container: ServiceContainer<S>) {
160 argumentNotNull(container, "container");
161 argumentNotNull(container, "container");
161 this._container = container;
162 this._container = container;
162 this._path = [];
163 this._path = [];
163 }
164 }
164
165
165 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
166 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
166 argumentNotEmptyString(moduleName, "moduleName");
167 argumentNotEmptyString(moduleName, "moduleName");
167
168
168 trace.log(
169 trace.log(
169 "loadConfiguration moduleName={0}, contextRequire={1}",
170 "loadConfiguration moduleName={0}, contextRequire={1}",
170 moduleName,
171 moduleName,
171 contextRequire ? typeof (contextRequire) : "<nil>"
172 contextRequire ? typeof (contextRequire) : "<nil>"
172 );
173 );
173
174
174 this._configName = moduleName;
175 this._configName = moduleName;
175
176
176 const r = await makeResolver(undefined, contextRequire);
177 const r = await makeResolver(undefined, contextRequire);
177
178
178 const config = await r(moduleName, ct);
179 const config = await r(moduleName, ct);
179
180
180 await this._applyConfiguration(
181 await this._applyConfiguration(
181 config,
182 config,
182 await makeResolver(moduleName, contextRequire),
183 await makeResolver(moduleName, contextRequire),
183 ct
184 ct
184 );
185 );
185 }
186 }
186
187
187 async applyConfiguration(data: RegistrationMap<S>, opts: { contextRequire?: any; baseModule?: string }, ct = Cancellation.none) {
188 async applyConfiguration(data: RegistrationMap<S>, opts: { contextRequire?: any; baseModule?: string }, ct = Cancellation.none) {
188 argumentNotNull(data, "data");
189 argumentNotNull(data, "data");
189 const _opts = opts || {};
190 const _opts = opts || {};
190
191
191 await this._applyConfiguration(data, await makeResolver(_opts.baseModule, _opts.contextRequire), ct);
192 await this._applyConfiguration(data, await makeResolver(_opts.baseModule, _opts.contextRequire), ct);
192 }
193 }
193
194
194 async _applyConfiguration(data: RegistrationMap<S>, resolver?: ModuleResolver, ct = Cancellation.none) {
195 async _applyConfiguration(data: RegistrationMap<S>, resolver?: ModuleResolver, ct = Cancellation.none) {
195 trace.log("applyConfiguration");
196 trace.log("applyConfiguration");
196
197
197 this._configName = "$";
198 this._configName = "$";
198
199
199 if (resolver)
200 if (resolver)
200 this._require = resolver;
201 this._require = resolver;
201
202
202 let services: PartialServiceMap<S>;
203 let services: PartialServiceMap<S>;
203
204
204 try {
205 try {
205 services = await this._visitRegistrations(data, "$");
206 services = await this._visitRegistrations(data, "$");
206 } catch (e) {
207 } catch (e) {
207 throw this._makeError(e);
208 throw this._makeError(e);
208 }
209 }
209
210
210 this._container.register(services);
211 this._container.register(services);
211 }
212 }
212
213
213 _makeError(inner: any) {
214 _makeError(inner: any) {
214 const e = new ConfigError("Failed to load configuration", inner);
215 const e = new ConfigError("Failed to load configuration", inner);
215 e.configName = this._configName || "<inline>";
216 e.configName = this._configName || "<inline>";
216 e.path = this._makePath();
217 e.path = this._makePath();
217 return e;
218 return e;
218 }
219 }
219
220
220 _makePath() {
221 _makePath() {
221 return this._path
222 return this._path
222 .reduce(
223 .reduce(
223 (prev, cur) => typeof cur === "number" ?
224 (prev, cur) => typeof cur === "number" ?
224 `${prev}[${cur}]` :
225 `${prev}[${cur}]` :
225 `${prev}.${cur}`
226 `${prev}.${cur}`
226 )
227 )
227 .toString();
228 .toString();
228 }
229 }
229
230
230 async _resolveType(moduleName: string, localName: string) {
231 async _resolveType(moduleName: string, localName: string) {
231 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
232 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
232 try {
233 try {
233 const m = await this._loadModule(moduleName);
234 const m = await this._loadModule(moduleName);
234 if (localName) {
235 if (localName) {
235 return get(localName, m);
236 return get(localName, m);
236 } else {
237 } else {
237 if (m instanceof Function)
238 if (m instanceof Function)
238 return m;
239 return m;
239 if ("default" in m)
240 if ("default" in m)
240 return m.default;
241 return m.default;
241 return m;
242 return m;
242 }
243 }
243 } catch (e) {
244 } catch (e) {
244 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
245 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
245 throw e;
246 throw e;
246 }
247 }
247 }
248 }
248
249
249 _loadModule(moduleName: string) {
250 _loadModule(moduleName: string) {
250 trace.debug("loadModule {0}", moduleName);
251 trace.debug("loadModule {0}", moduleName);
251 if (!this._require)
252 if (!this._require)
252 throw new Error("Module loader isn't specified");
253 throw new Error("Module loader isn't specified");
253
254
254 return this._require(moduleName);
255 return this._require(moduleName);
255 }
256 }
256
257
257 async _visitRegistrations(data: RegistrationMap<S>, name: string) {
258 async _visitRegistrations(data: RegistrationMap<S>, name: string) {
258 this._enter(name);
259 this._enter(name);
259
260
260 if (data.constructor &&
261 if (data.constructor &&
261 data.constructor.prototype !== Object.prototype)
262 data.constructor.prototype !== Object.prototype)
262 throw new Error("Configuration must be a simple object");
263 throw new Error("Configuration must be a simple object");
263
264
264 const services = await mapAll(data, async (v, k) => {
265 const services = await mapAll(data, async (v, k) => {
265 const d = await this._visit(v, k.toString());
266 const d = await this._visit(v, k.toString());
266 return isDescriptor(d) ? d : new AggregateDescriptor(d);
267 return isDescriptor(d) ? d : new AggregateDescriptor(d);
267 }) as PartialServiceMap<S>;
268 }) as PartialServiceMap<S>;
268
269
269 this._leave();
270 this._leave();
270
271
271 return services;
272 return services;
272 }
273 }
273
274
274 _enter(name: string) {
275 _enter(name: string) {
275 this._path.push(name.toString());
276 this._path.push(name.toString());
276 trace.debug(">{0}", name);
277 trace.debug(">{0}", name);
277 }
278 }
278
279
279 _leave() {
280 _leave() {
280 const name = this._path.pop();
281 const name = this._path.pop();
281 trace.debug("<{0}", name);
282 trace.debug("<{0}", name);
282 }
283 }
283
284
284 _visit(data: any, name: string): Promise<any> {
285 _visit(data: any, name: string): Promise<any> {
285 if (isPrimitive(data))
286 if (isPrimitive(data))
286 return Promise.resolve(new ValueDescriptor(data));
287 return Promise.resolve(new ValueDescriptor(data));
287 if (isDescriptor(data))
288 if (isDescriptor(data))
288 return Promise.resolve(data);
289 return Promise.resolve(data);
289
290
290 if (isDependencyRegistration<S>(data)) {
291 if (isDependencyRegistration<S>(data)) {
291 return this._visitDependencyRegistration(data, name);
292 return this._visitDependencyRegistration(data, name);
292 } else if (isValueRegistration(data)) {
293 } else if (isValueRegistration(data)) {
293 return this._visitValueRegistration(data, name);
294 return this._visitValueRegistration(data, name);
294 } else if (isTypeRegistration(data)) {
295 } else if (isTypeRegistration(data)) {
295 return this._visitTypeRegistration(data, name);
296 return this._visitTypeRegistration(data, name);
296 } else if (isFactoryRegistration(data)) {
297 } else if (isFactoryRegistration(data)) {
297 return this._visitFactoryRegistration(data, name);
298 return this._visitFactoryRegistration(data, name);
298 } else if (data instanceof Array) {
299 } else if (data instanceof Array) {
299 return this._visitArray(data, name);
300 return this._visitArray(data, name);
300 }
301 }
301
302
302 return this._visitObject(data, name);
303 return this._visitObject(data, name);
303 }
304 }
304
305
305 async _visitObject(data: any, name: string) {
306 async _visitObject(data: any, name: string) {
306 if (data.constructor &&
307 if (data.constructor &&
307 data.constructor.prototype !== Object.prototype)
308 data.constructor.prototype !== Object.prototype)
308 return new ValueDescriptor(data);
309 return new ValueDescriptor(data);
309
310
310 this._enter(name);
311 this._enter(name);
311
312
312 const v = await mapAll(data, delegate(this, "_visit"));
313 const v = await mapAll(data, delegate(this, "_visit"));
313
314
314 // TODO: handle inline descriptors properly
315 // TODO: handle inline descriptors properly
315 // const ex = {
316 // const ex = {
316 // activate(ctx) {
317 // activate(ctx) {
317 // const value = ctx.activate(this.prop, "prop");
318 // const value = ctx.activate(this.prop, "prop");
318 // // some code
319 // // some code
319 // },
320 // },
320 // // will be turned to ReferenceDescriptor
321 // // will be turned to ReferenceDescriptor
321 // prop: { $dependency: "depName" }
322 // prop: { $dependency: "depName" }
322 // };
323 // };
323
324
324 this._leave();
325 this._leave();
325 return v;
326 return v;
326 }
327 }
327
328
328 async _visitArray(data: any[], name: string) {
329 async _visitArray(data: any[], name: string) {
329 if (data.constructor &&
330 if (data.constructor &&
330 data.constructor.prototype !== Array.prototype)
331 data.constructor.prototype !== Array.prototype)
331 return new ValueDescriptor(data);
332 return new ValueDescriptor(data);
332
333
333 this._enter(name);
334 this._enter(name);
334
335
335 const v = await mapAll(data, delegate(this, "_visit"));
336 const v = await mapAll(data, delegate(this, "_visit"));
336 this._leave();
337 this._leave();
337
338
338 return v;
339 return v;
339 }
340 }
340
341
341 _makeServiceParams(data: ServiceRegistration<any, S>) {
342 _makeServiceParams(data: ServiceRegistration<any, S>) {
342 const opts: any = {
343 const opts: any = {};
343 };
344
344 if (data.services)
345 if (data.services)
345 opts.services = this._visitRegistrations(data.services, "services");
346 opts.services = this._visitRegistrations(data.services, "services");
346
347
347 if (data.inject) {
348 if (data.inject) {
348 this._enter("inject");
349 this._enter("inject");
349 opts.inject = mapAll(
350 opts.inject = mapAll(
350 data.inject instanceof Array ?
351 data.inject instanceof Array ?
351 data.inject :
352 data.inject :
352 [data.inject],
353 [data.inject],
353 delegate(this, "_visitObject")
354 delegate(this, "_visitObject")
354 );
355 );
355 this._leave();
356 this._leave();
356 }
357 }
357
358
358 if ("params" in data)
359 if ("params" in data)
359 opts.params = data.params instanceof Array ?
360 opts.params = data.params instanceof Array ?
360 this._visitArray(data.params, "params") :
361 this._visitArray(data.params, "params") :
361 this._visit(data.params, "params");
362 this._visit(data.params, "params");
362
363
363 if (data.activation) {
364 if (data.activation) {
364 opts.activation = this._getLifetimeManager(data.activation, data.typeId);
365 opts.lifetime = this._getLifetimeManager(data.activation, data.typeId);
365 }
366 }
366
367
367 if (data.cleanup)
368 if (data.cleanup)
368 opts.cleanup = data.cleanup;
369 opts.cleanup = data.cleanup;
369
370
370 return opts;
371 return opts;
371 }
372 }
372
373
373 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
374 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
374 this._enter(name);
375 this._enter(name);
375 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
376 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
376 this._leave();
377 this._leave();
377 return d;
378 return d;
378 }
379 }
379
380
380 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
381 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
381 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
382 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
382 this._enter(name);
383 this._enter(name);
383 const options = {
384 const options = {
384 name: data.$dependency,
385 name: data.$dependency,
385 optional: data.optional,
386 optional: data.optional,
386 default: data.default,
387 default: data.default,
387 services: data.services && await this._visitRegistrations(data.services, "services")
388 services: data.services && await this._visitRegistrations(data.services, "services")
388 };
389 };
389 const d = data.lazy ? new LazyReferenceDescriptor<S, K>(options) : new ReferenceDescriptor<S, K>(options);
390 const d = data.lazy ? new LazyReferenceDescriptor<S, K>(options) : new ReferenceDescriptor<S, K>(options);
390 this._leave();
391 this._leave();
391 return d;
392 return d;
392 }
393 }
393
394
394 async _visitTypeRegistration(data: TypeRegistration<new () => any, S>, name: string) {
395 async _visitTypeRegistration(data: TypeRegistration<new () => any, S>, name: string) {
395 argumentNotNull(data.$type, "data.$type");
396 argumentNotNull(data.$type, "data.$type");
396 this._enter(name);
397 this._enter(name);
397
398
398 const opts = this._makeServiceParams(data);
399 const opts = {} as any;
399 if (data.$type instanceof Function) {
400 if (data.$type instanceof Function) {
400 opts.type = data.$type;
401 opts.type = data.$type;
401 } else {
402 } else {
402 const [moduleName, typeName] = data.$type.split(":", 2);
403 const [moduleName, typeName] = data.$type.split(":", 2);
403 opts.type = this._resolveType(moduleName, typeName).then(t => {
404 opts.type = this._resolveType(moduleName, typeName).then(t => {
404 if (!(t instanceof Function))
405 if (!(t instanceof Function))
405 throw Error("$type (" + data.$type + ") is not a constructable");
406 throw Error("$type (" + data.$type + ") is not a constructable");
406 return t;
407 return t;
407 });
408 });
408 }
409 }
409
410
411 if (!data.typeId && data.activation === "singleton")
412 data.typeId = oid(opts.type);
413
414 mixin(opts, this._makeServiceParams(data));
415
410 const d = new TypeServiceDescriptor<S, any, any[]>(
416 const d = new TypeServiceDescriptor<S, any, any[]>(
411 await mapAll(opts)
417 await mapAll(opts)
412 );
418 );
413
419
414 this._leave();
420 this._leave();
415
421
416 return d;
422 return d;
417 }
423 }
418
424
419 async _visitFactoryRegistration(data: FactoryRegistration<() => any, S>, name: string) {
425 async _visitFactoryRegistration(data: FactoryRegistration<() => any, S>, name: string) {
420 argumentOfType(data.$factory, Function, "data.$factory");
426 argumentOfType(data.$factory, Function, "data.$factory");
421 this._enter(name);
427 this._enter(name);
422
428
429 if (!data.typeId && data.activation === "singleton")
430 data.typeId = oid(data.$factory);
431
423 const opts = this._makeServiceParams(data);
432 const opts = this._makeServiceParams(data);
424 opts.factory = data.$factory;
433 opts.factory = data.$factory;
425
434
426 const d = new FactoryServiceDescriptor<S, any, any[]>(
435 const d = new FactoryServiceDescriptor<S, any, any[]>(
427 await mapAll(opts)
436 await mapAll(opts)
428 );
437 );
429
438
430 this._leave();
439 this._leave();
431 return d;
440 return d;
432 }
441 }
433
442
434 _getLifetimeManager(activation: ActivationType, typeId: string | undefined): ILifetime {
443 _getLifetimeManager(activation: ActivationType, typeId: string | undefined): ILifetime {
435 switch (activation) {
444 switch (activation) {
436 case "container":
445 case "container":
437 return LifetimeManager.containerLifetime(this._container);
446 return LifetimeManager.containerLifetime(this._container);
438 case "hierarchy":
447 case "hierarchy":
439 return LifetimeManager.hierarchyLifetime();
448 return LifetimeManager.hierarchyLifetime();
440 case "context":
449 case "context":
441 return LifetimeManager.contextLifetime();
450 return LifetimeManager.contextLifetime();
442 case "singleton":
451 case "singleton":
443 if (typeId === undefined)
452 if (typeId === undefined)
444 throw Error("The singleton activation requires a typeId");
453 throw Error("The singleton activation requires a typeId");
445 return LifetimeManager.singletonLifetime(typeId);
454 return LifetimeManager.singletonLifetime(typeId);
446 default:
455 default:
447 return LifetimeManager.empty();
456 return LifetimeManager.empty();
448 }
457 }
449 }
458 }
450 }
459 }
@@ -1,518 +1,518
1 import { ICancellable, Constructor, IDestroyable, ICancellation } from "./interfaces";
1 import { ICancellable, Constructor, IDestroyable, ICancellation } from "./interfaces";
2
2
3 let _nextOid = 0;
3 let _nextOid = 0;
4 const _oid = typeof Symbol === "function" ?
4 const _oid = typeof Symbol === "function" ?
5 Symbol("__implab__oid__") :
5 Symbol("__implab__oid__") :
6 "__implab__oid__";
6 "__implab__oid__";
7
7
8 export function oid(instance: null | undefined): undefined;
8 export function oid(instance: null | undefined): undefined;
9 export function oid(instance: NonNullable<any>): string;
9 export function oid(instance: NonNullable<any>): string;
10 export function oid(instance: any): string | undefined {
10 export function oid(instance: any): string | undefined {
11 if (isNull(instance))
11 if (isNull(instance))
12 return undefined;
12 return undefined;
13
13
14 if (_oid in instance)
14 if (_oid in instance)
15 return instance[_oid];
15 return instance[_oid];
16 else
16 else
17 return (instance[_oid] = "oid_" + (++_nextOid));
17 return (instance[_oid] = "oid_" + (++_nextOid));
18 }
18 }
19
19
20 const cancellationNone: ICancellation = {
20 const cancellationNone: ICancellation = {
21 isSupported(): boolean {
21 isSupported(): boolean {
22 return false;
22 return false;
23 },
23 },
24
24
25 throwIfRequested(): void {
25 throwIfRequested(): void {
26 },
26 },
27
27
28 isRequested(): boolean {
28 isRequested(): boolean {
29 return false;
29 return false;
30 },
30 },
31
31
32 register(_cb: (e: any) => void): IDestroyable {
32 register(_cb: (e: any) => void): IDestroyable {
33 return destroyed;
33 return destroyed;
34 }
34 }
35 };
35 };
36
36
37 export function keys<T>(arg: T): (Extract<keyof T, string>)[] {
37 export function keys<T>(arg: T): (Extract<keyof T, string>)[] {
38 return isObject(arg) && arg ? Object.keys(arg) as (Extract<keyof T, string>)[] : [];
38 return isObject(arg) && arg ? Object.keys(arg) as (Extract<keyof T, string>)[] : [];
39 }
39 }
40
40
41 export function isKeyof<T>(k: string, target: T): k is Extract<keyof T, string> {
41 export function isKeyof<T>(k: string, target: T): k is Extract<keyof T, string> {
42 return target && typeof target === "object" && k in target;
42 return target && typeof target === "object" && k in target;
43 }
43 }
44
44
45 export function argumentNotNull(arg: any, name: string) {
45 export function argumentNotNull(arg: any, name: string) {
46 if (arg === null || arg === undefined)
46 if (arg === null || arg === undefined)
47 throw new Error("The argument " + name + " can't be null or undefined");
47 throw new Error("The argument " + name + " can't be null or undefined");
48 }
48 }
49
49
50 export function argumentNotEmptyString(arg: any, name: string) {
50 export function argumentNotEmptyString(arg: any, name: string) {
51 if (typeof (arg) !== "string" || !arg.length)
51 if (typeof (arg) !== "string" || !arg.length)
52 throw new Error("The argument '" + name + "' must be a not empty string");
52 throw new Error("The argument '" + name + "' must be a not empty string");
53 }
53 }
54
54
55 export function argumentNotEmptyArray(arg: any, name: string) {
55 export function argumentNotEmptyArray(arg: any, name: string) {
56 if (!(arg instanceof Array) || !arg.length)
56 if (!(arg instanceof Array) || !arg.length)
57 throw new Error("The argument '" + name + "' must be a not empty array");
57 throw new Error("The argument '" + name + "' must be a not empty array");
58 }
58 }
59
59
60 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
60 export function argumentOfType(arg: any, type: Constructor<{}>, name: string) {
61 if (!(arg instanceof type))
61 if (!(arg instanceof type))
62 throw new Error("The argument '" + name + "' type doesn't match");
62 throw new Error("The argument '" + name + "' type doesn't match");
63 }
63 }
64
64
65 export function isObject(val: any): val is object {
65 export function isObject(val: any): val is object {
66 return typeof val === "object";
66 return typeof val === "object";
67 }
67 }
68
68
69 export function isNull(val: any): val is null | undefined {
69 export function isNull(val: any): val is null | undefined {
70 return (val === null || val === undefined);
70 return (val === null || val === undefined);
71 }
71 }
72
72
73 export type primitive = symbol | string | number | boolean | undefined | null;
73 export type primitive = symbol | string | number | boolean | undefined | null;
74
74
75 export function isPrimitive(val: any): val is primitive {
75 export function isPrimitive(val: any): val is primitive {
76 return (val === null || val === undefined || typeof (val) === "string" ||
76 return (val === null || val === undefined || typeof (val) === "string" ||
77 typeof (val) === "number" || typeof (val) === "boolean");
77 typeof (val) === "number" || typeof (val) === "boolean");
78 }
78 }
79
79
80 export function isInteger(val: any): val is number {
80 export function isInteger(val: any): val is number {
81 return parseInt(val, 10) === val;
81 return parseInt(val, 10) === val;
82 }
82 }
83
83
84 export function isNumber(val: any): val is number {
84 export function isNumber(val: any): val is number {
85 return parseFloat(val) === val;
85 return parseFloat(val) === val;
86 }
86 }
87
87
88 export function isString(val: any): val is string {
88 export function isString(val: any): val is string {
89 return typeof (val) === "string" || val instanceof String;
89 return typeof (val) === "string" || val instanceof String;
90 }
90 }
91
91
92 export function isPromise<T = any>(val: any): val is PromiseLike<T> {
92 export function isPromise<T = any>(val: any): val is PromiseLike<T> {
93 return val && typeof val.then === "function";
93 return val && typeof val.then === "function";
94 }
94 }
95
95
96 export function isCancellable(val: any): val is ICancellable {
96 export function isCancellable(val: any): val is ICancellable {
97 return val && typeof val.cancel === "function";
97 return val && typeof val.cancel === "function";
98 }
98 }
99
99
100 export function isNullOrEmptyString(val: any): val is ("" | null | undefined) {
100 export function isNullOrEmptyString(val: any): val is ("" | null | undefined) {
101 return (val === null || val === undefined ||
101 return (val === null || val === undefined ||
102 ((typeof (val) === "string" || val instanceof String) && val.length === 0));
102 ((typeof (val) === "string" || val instanceof String) && val.length === 0));
103 }
103 }
104
104
105 export function isNotEmptyArray<T = any>(arg: any): arg is T[] {
105 export function isNotEmptyArray<T = any>(arg: any): arg is T[] {
106 return (arg instanceof Array && arg.length > 0);
106 return (arg instanceof Array && arg.length > 0);
107 }
107 }
108
108
109 function _isStrictMode(this: any) {
109 function _isStrictMode(this: any) {
110 return !this;
110 return !this;
111 }
111 }
112
112
113 function _getNonStrictGlobal(this: any) {
113 function _getNonStrictGlobal(this: any) {
114 return this;
114 return this;
115 }
115 }
116
116
117 export function getGlobal() {
117 export function getGlobal() {
118 // in es3 we can't use indirect call to eval, since it will
118 // in es3 we can't use indirect call to eval, since it will
119 // be executed in the current call context.
119 // be executed in the current call context.
120 if (!_isStrictMode()) {
120 if (!_isStrictMode()) {
121 return _getNonStrictGlobal();
121 return _getNonStrictGlobal();
122 } else {
122 } else {
123 // tslint:disable-next-line:no-eval
123 // tslint:disable-next-line:no-eval
124 return eval.call(null, "this");
124 return eval.call(null, "this");
125 }
125 }
126 }
126 }
127
127
128 export function get(member: string, context?: object) {
128 export function get(member: string, context?: object) {
129 argumentNotEmptyString(member, "member");
129 argumentNotEmptyString(member, "member");
130 let that = context || getGlobal();
130 let that = context || getGlobal();
131 const parts = member.split(".");
131 const parts = member.split(".");
132 for (const m of parts) {
132 for (const m of parts) {
133 if (!m)
133 if (!m)
134 continue;
134 continue;
135 if (isNull(that = that[m]))
135 if (isNull(that = that[m]))
136 break;
136 break;
137 }
137 }
138 return that;
138 return that;
139 }
139 }
140
140
141 /**
141 /**
142 * ВыполняСт ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива, останавливаСтся, ΠΊΠΎΠ³Π΄Π°
142 * ВыполняСт ΠΌΠ΅Ρ‚ΠΎΠ΄ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта массива, останавливаСтся, ΠΊΠΎΠ³Π΄Π°
143 * Π»ΠΈΠ±ΠΎ достигнут ΠΊΠΎΠ½Π΅Ρ† массива, Π»ΠΈΠ±ΠΎ функция <c>cb</c> Π²Π΅Ρ€Π½ΡƒΠ»Π°
143 * Π»ΠΈΠ±ΠΎ достигнут ΠΊΠΎΠ½Π΅Ρ† массива, Π»ΠΈΠ±ΠΎ функция <c>cb</c> Π²Π΅Ρ€Π½ΡƒΠ»Π°
144 * Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
144 * Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.
145 *
145 *
146 * @param {Array | Object} obj массив элСмСнтов для просмотра
146 * @param {Array | Object} obj массив элСмСнтов для просмотра
147 * @param {Function} cb функция, вызываСмая для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта
147 * @param {Function} cb функция, вызываСмая для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ элСмСнта
148 * @param {Object} thisArg Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ Π² качСствС
148 * @param {Object} thisArg Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎ Π² качСствС
149 * <c>this</c> Π² <c>cb</c>.
149 * <c>this</c> Π² <c>cb</c>.
150 * @returns {void}
150 * @returns {void}
151 */
151 */
152 export function each<T>(obj: T, cb: <X extends keyof T>(v: NonNullable<T[X]>, k: X) => void): void;
152 export function each<T>(obj: T, cb: <X extends Extract<keyof T, string>>(v: NonNullable<T[X]>, k: X) => void): void;
153 export function each<T>(array: T[], cb: (v: T, i: number) => void): void;
153 export function each<T>(array: T[], cb: (v: T, i: number) => void): void;
154 export function each(obj: any, cb: any, thisArg?: any): any;
154 export function each(obj: any, cb: any, thisArg?: any): any;
155 export function each(obj: any, cb: any, thisArg?: any) {
155 export function each(obj: any, cb: any, thisArg?: any) {
156 argumentNotNull(cb, "cb");
156 argumentNotNull(cb, "cb");
157 if (obj instanceof Array) {
157 if (obj instanceof Array) {
158 let v: any;
158 let v: any;
159 for (let i = 0; i < obj.length; i++) {
159 for (let i = 0; i < obj.length; i++) {
160 v = obj[i];
160 v = obj[i];
161 if (v !== undefined)
161 if (v !== undefined)
162 cb.call(thisArg, v, i);
162 cb.call(thisArg, v, i);
163 }
163 }
164 } else {
164 } else {
165 Object.keys(obj).forEach(k => obj[k] !== undefined && cb.call(thisArg, obj[k], k));
165 Object.keys(obj).forEach(k => obj[k] !== undefined && cb.call(thisArg, obj[k], k));
166 }
166 }
167 }
167 }
168
168
169 /** Copies property values from a source object to the destination and returns
169 /** Copies property values from a source object to the destination and returns
170 * the destination object.
170 * the destination object.
171 *
171 *
172 * @param dest The destination object into which properties from the source
172 * @param dest The destination object into which properties from the source
173 * object will be copied.
173 * object will be copied.
174 * @param source The source of values which will be copied to the destination
174 * @param source The source of values which will be copied to the destination
175 * object.
175 * object.
176 * @param template An optional parameter specifies which properties should be
176 * @param template An optional parameter specifies which properties should be
177 * copied from the source and how to map them to the destination. If the
177 * copied from the source and how to map them to the destination. If the
178 * template is an array it contains the list of property names to copy from the
178 * template is an array it contains the list of property names to copy from the
179 * source to the destination. In case of object the templates contains the map
179 * source to the destination. In case of object the templates contains the map
180 * where keys are property names in the source and the values are property
180 * where keys are property names in the source and the values are property
181 * names in the destination object. If the template isn't specified then the
181 * names in the destination object. If the template isn't specified then the
182 * own properties of the source are entirely copied to the destination.
182 * own properties of the source are entirely copied to the destination.
183 *
183 *
184 */
184 */
185 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: (keyof S)[]): T & S;
185 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: (keyof S)[]): T & S;
186 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;
186 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;
187 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: any): any {
187 export function mixin<T extends object, S extends object>(dest: T, source: S, template?: any): any {
188 argumentNotNull(dest, "dest");
188 argumentNotNull(dest, "dest");
189 const _res: any = dest as any;
189 const _res: any = dest as any;
190
190
191 if (isPrimitive(source))
191 if (isPrimitive(source))
192 return _res;
192 return _res;
193
193
194 if (template instanceof Array) {
194 if (template instanceof Array) {
195 template.forEach(p => {
195 template.forEach(p => {
196 if (isKeyof(p, source))
196 if (isKeyof(p, source))
197 _res[p] = source[p];
197 _res[p] = source[p];
198 });
198 });
199 } else if (template) {
199 } else if (template) {
200 keys(source).forEach(p => {
200 keys(source).forEach(p => {
201 if (isKeyof(p, template))
201 if (isKeyof(p, template))
202 _res[template[p]] = source[p];
202 _res[template[p]] = source[p];
203 });
203 });
204 } else {
204 } else {
205 keys(source).forEach(p => _res[p] = source[p]);
205 keys(source).forEach(p => _res[p] = source[p]);
206 }
206 }
207
207
208 return _res;
208 return _res;
209 }
209 }
210
210
211 /** Wraps the specified function to emulate an asynchronous execution.
211 /** Wraps the specified function to emulate an asynchronous execution.
212 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
212 * @param{Object} thisArg [Optional] Object which will be passed as 'this' to the function.
213 * @param{Function|String} fn [Required] Function wich will be wrapped.
213 * @param{Function|String} fn [Required] Function wich will be wrapped.
214 */
214 */
215 export function async<T, F extends (...args: any[]) => T | PromiseLike<T>>(
215 export function async<T, F extends (...args: any[]) => T | PromiseLike<T>>(
216 fn: F,
216 fn: F,
217 thisArg?: ThisParameterType<F>
217 thisArg?: ThisParameterType<F>
218 ): (...args: Parameters<F>) => PromiseLike<T>;
218 ): (...args: Parameters<F>) => PromiseLike<T>;
219 export function async<T, M extends string, O extends { [m in M]?: (...args: any[]) => T | PromiseLike<T> }>(
219 export function async<T, M extends string, O extends { [m in M]?: (...args: any[]) => T | PromiseLike<T> }>(
220 fn: M,
220 fn: M,
221 thisArg: O
221 thisArg: O
222 ): (...args: Parameters<NonNullable<O[M]>>) => PromiseLike<T>;
222 ): (...args: Parameters<NonNullable<O[M]>>) => PromiseLike<T>;
223 export function async(_fn: any, thisArg: any): (...args: any[]) => PromiseLike<any> {
223 export function async(_fn: any, thisArg: any): (...args: any[]) => PromiseLike<any> {
224 let fn = _fn;
224 let fn = _fn;
225
225
226 if (arguments.length === 2 && !(fn instanceof Function))
226 if (arguments.length === 2 && !(fn instanceof Function))
227 fn = thisArg[fn];
227 fn = thisArg[fn];
228
228
229 if (fn == null)
229 if (fn == null)
230 throw new Error("The function must be specified");
230 throw new Error("The function must be specified");
231
231
232 function wrapresult(x: any, e?: any): PromiseLike<any> {
232 function wrapresult(x: any, e?: any): PromiseLike<any> {
233 if (e) {
233 if (e) {
234 return {
234 return {
235 then(cb, eb) {
235 then(cb, eb) {
236 try {
236 try {
237 return eb ? wrapresult(eb(e)) : this;
237 return eb ? wrapresult(eb(e)) : this;
238 } catch (e2) {
238 } catch (e2) {
239 return wrapresult(null, e2);
239 return wrapresult(null, e2);
240 }
240 }
241 }
241 }
242 };
242 };
243 } else {
243 } else {
244 if (x && x.then)
244 if (x && x.then)
245 return x;
245 return x;
246 return {
246 return {
247 then(cb) {
247 then(cb) {
248 try {
248 try {
249 return cb ? wrapresult(cb(x)) : this;
249 return cb ? wrapresult(cb(x)) : this;
250 } catch (e2) {
250 } catch (e2) {
251 return wrapresult(e2);
251 return wrapresult(e2);
252 }
252 }
253 }
253 }
254 };
254 };
255 }
255 }
256 }
256 }
257
257
258 return (...args) => {
258 return (...args) => {
259 try {
259 try {
260 return wrapresult(fn.apply(thisArg, args));
260 return wrapresult(fn.apply(thisArg, args));
261 } catch (e) {
261 } catch (e) {
262 return wrapresult(null, e);
262 return wrapresult(null, e);
263 }
263 }
264 };
264 };
265 }
265 }
266
266
267 export function delegate<T extends object, F extends (this: T, ...args: any[]) => any>(
267 export function delegate<T extends object, F extends (this: T, ...args: any[]) => any>(
268 target: T,
268 target: T,
269 method: F
269 method: F
270 ): OmitThisParameter<F>;
270 ): OmitThisParameter<F>;
271 export function delegate<M extends string, T extends { [m in M]?: (...args: any[]) => any; }>(
271 export function delegate<M extends string, T extends { [m in M]?: (...args: any[]) => any; }>(
272 target: T,
272 target: T,
273 method: M
273 method: M
274 ): OmitThisParameter<T[M]>;
274 ): OmitThisParameter<T[M]>;
275 export function delegate(target: any, _method: any): (...args: any[]) => any {
275 export function delegate(target: any, _method: any): (...args: any[]) => any {
276 let method: any;
276 let method: any;
277 if (!(_method instanceof Function)) {
277 if (!(_method instanceof Function)) {
278 argumentNotNull(target, "target");
278 argumentNotNull(target, "target");
279 method = target[_method];
279 method = target[_method];
280 if (!(method instanceof Function))
280 if (!(method instanceof Function))
281 throw new Error("'method' argument must be a Function or a method name");
281 throw new Error("'method' argument must be a Function or a method name");
282 } else {
282 } else {
283 method = _method;
283 method = _method;
284 }
284 }
285
285
286 return (...args) => {
286 return (...args) => {
287 return method.apply(target, args);
287 return method.apply(target, args);
288 };
288 };
289 }
289 }
290
290
291 export function delay(timeMs: number, ct = cancellationNone) {
291 export function delay(timeMs: number, ct = cancellationNone) {
292 ct.throwIfRequested();
292 ct.throwIfRequested();
293 return new Promise((resolve, reject) => {
293 return new Promise((resolve, reject) => {
294 const h = ct.register(e => {
294 const h = ct.register(e => {
295 clearTimeout(id);
295 clearTimeout(id);
296 reject(e);
296 reject(e);
297 // we don't nedd to unregister h, since ct is already disposed
297 // we don't nedd to unregister h, since ct is already disposed
298 });
298 });
299 const id = setTimeout(() => {
299 const id = setTimeout(() => {
300 h.destroy();
300 h.destroy();
301 resolve();
301 resolve();
302 }, timeMs);
302 }, timeMs);
303
303
304 });
304 });
305 }
305 }
306
306
307 /** Returns resolved promise, awaiting this method will cause the asynchronous
307 /** Returns resolved promise, awaiting this method will cause the asynchronous
308 * completion of the rest of the code.
308 * completion of the rest of the code.
309 */
309 */
310 export function fork() {
310 export function fork() {
311 return Promise.resolve();
311 return Promise.resolve();
312 }
312 }
313
313
314 /** Always throws Error, can be used as a stub for the methods which should be
314 /** Always throws Error, can be used as a stub for the methods which should be
315 * assigned later and are required to be not null.
315 * assigned later and are required to be not null.
316 */
316 */
317 export function notImplemented(): never {
317 export function notImplemented(): never {
318 throw new Error("Not implemeted");
318 throw new Error("Not implemeted");
319 }
319 }
320 /**
320 /**
321 * Iterates over the specified array of items and calls the callback `cb`, if
321 * Iterates over the specified array of items and calls the callback `cb`, if
322 * the result of the callback is a promise the next item from the array will be
322 * the result of the callback is a promise the next item from the array will be
323 * proceeded after the promise is resolved.
323 * proceeded after the promise is resolved.
324 *
324 *
325 */
325 */
326 export function pmap<T, T2>(
326 export function pmap<T, T2>(
327 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
327 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
328 cb: (item: T, i: number) => T2 | PromiseLike<T2>
328 cb: (item: T, i: number) => T2 | PromiseLike<T2>
329 ): T2[] | PromiseLike<T2[]> {
329 ): T2[] | PromiseLike<T2[]> {
330 argumentNotNull(cb, "cb");
330 argumentNotNull(cb, "cb");
331
331
332 if (isPromise(items)) {
332 if (isPromise(items)) {
333 return items.then(data => pmap(data, cb));
333 return items.then(data => pmap(data, cb));
334 } else {
334 } else {
335
335
336 if (isNull(items) || !items.length)
336 if (isNull(items) || !items.length)
337 return [];
337 return [];
338
338
339 let i = 0;
339 let i = 0;
340 const result = new Array<T2>();
340 const result = new Array<T2>();
341
341
342 const next = (): any => {
342 const next = (): any => {
343 while (i < items.length) {
343 while (i < items.length) {
344 const r = cb(items[i], i);
344 const r = cb(items[i], i);
345 const ri = i;
345 const ri = i;
346 i++;
346 i++;
347 if (isPromise(r)) {
347 if (isPromise(r)) {
348 return r.then(x => {
348 return r.then(x => {
349 result[ri] = x;
349 result[ri] = x;
350 return next();
350 return next();
351 });
351 });
352 } else {
352 } else {
353 result[ri] = r;
353 result[ri] = r;
354 }
354 }
355 }
355 }
356 return result;
356 return result;
357 };
357 };
358
358
359 return next();
359 return next();
360 }
360 }
361 }
361 }
362
362
363 export function pfor<T>(
363 export function pfor<T>(
364 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
364 items: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
365 cb: (item: T, i: number) => any
365 cb: (item: T, i: number) => any
366 ): void | PromiseLike<void> {
366 ): void | PromiseLike<void> {
367 argumentNotNull(cb, "cb");
367 argumentNotNull(cb, "cb");
368
368
369 if (isPromise(items)) {
369 if (isPromise(items)) {
370 return items.then(data => pfor(data, cb));
370 return items.then(data => pfor(data, cb));
371 } else {
371 } else {
372 if (isNull(items) || !items.length)
372 if (isNull(items) || !items.length)
373 return;
373 return;
374
374
375 let i = 0;
375 let i = 0;
376
376
377 const next = (): any => {
377 const next = (): any => {
378 while (i < items.length) {
378 while (i < items.length) {
379 const r = cb(items[i], i);
379 const r = cb(items[i], i);
380 i++;
380 i++;
381 if (isPromise(r))
381 if (isPromise(r))
382 return r.then(next);
382 return r.then(next);
383 }
383 }
384 };
384 };
385
385
386 return next();
386 return next();
387 }
387 }
388 }
388 }
389
389
390 export function first<T>(sequence: ArrayLike<T>): T;
390 export function first<T>(sequence: ArrayLike<T>): T;
391 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
391 export function first<T>(sequence: PromiseLike<ArrayLike<T>>): PromiseLike<T>;
392 export function first<T>(
392 export function first<T>(
393 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
393 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
394 cb?: (x: T) => void,
394 cb?: (x: T) => void,
395 err?: (x: Error) => void
395 err?: (x: Error) => void
396 ): void;
396 ): void;
397 /**
397 /**
398 * Π’Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт ΠΈΠ· ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, ΠΈΠ»ΠΈ обСщания, Ссли Π²
398 * Π’Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт ΠΈΠ· ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ, ΠΈΠ»ΠΈ обСщания, Ссли Π²
399 * качСствС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΎΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ массив.
399 * качСствС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, ΠΎΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ массив.
400 *
400 *
401 * @param {Function} cb ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, Π΅ΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ
401 * @param {Function} cb ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Π°, Π΅ΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ
402 * элСмСнт ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π² случаС успСха
402 * элСмСнт ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Π² случаС успСха
403 * @param {Function} err ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Ссли массив пустой, Π»ΠΈΠ±ΠΎ
403 * @param {Function} err ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ, Ссли массив пустой, Π»ΠΈΠ±ΠΎ
404 * нС массив
404 * нС массив
405 *
405 *
406 * @remarks Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π½ΠΈ cb Π½ΠΈ err, Ρ‚ΠΎΠ³Π΄Π° функция Π²Π΅Ρ€Π½Π΅Ρ‚ Π»ΠΈΠ±ΠΎ
406 * @remarks Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π½ΠΈ cb Π½ΠΈ err, Ρ‚ΠΎΠ³Π΄Π° функция Π²Π΅Ρ€Π½Π΅Ρ‚ Π»ΠΈΠ±ΠΎ
407 * ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт.
407 * ΠΎΠ±Π΅Ρ‰Π°Π½ΠΈΠ΅, Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ элСмСнт.
408 * @async
408 * @async
409 */
409 */
410 export function first<T>(
410 export function first<T>(
411 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
411 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
412 cb?: (x: T) => void,
412 cb?: (x: T) => void,
413 err?: (x: Error) => void
413 err?: (x: Error) => void
414 ) {
414 ) {
415 if (isPromise(sequence)) {
415 if (isPromise(sequence)) {
416 return sequence.then(res => first(res, cb as any /* force to pass undefined cb */, err));
416 return sequence.then(res => first(res, cb as any /* force to pass undefined cb */, err));
417 } else if (sequence && "length" in sequence) {
417 } else if (sequence && "length" in sequence) {
418 if (sequence.length === 0) {
418 if (sequence.length === 0) {
419 if (err)
419 if (err)
420 return err(new Error("The sequence is empty"));
420 return err(new Error("The sequence is empty"));
421 else
421 else
422 throw new Error("The sequence is empty");
422 throw new Error("The sequence is empty");
423 } else if (cb) {
423 } else if (cb) {
424 return cb(sequence[0]);
424 return cb(sequence[0]);
425 } else {
425 } else {
426 return sequence[0];
426 return sequence[0];
427 }
427 }
428 } else {
428 } else {
429 if (err)
429 if (err)
430 return err(new Error("The sequence is required"));
430 return err(new Error("The sequence is required"));
431 else
431 else
432 throw new Error("The sequence is required");
432 throw new Error("The sequence is required");
433 }
433 }
434 }
434 }
435
435
436 export function firstWhere<T>(
436 export function firstWhere<T>(
437 sequence: ArrayLike<T>,
437 sequence: ArrayLike<T>,
438 predicate: (x: T) => boolean
438 predicate: (x: T) => boolean
439 ): T;
439 ): T;
440 export function firstWhere<T>(
440 export function firstWhere<T>(
441 sequence: PromiseLike<ArrayLike<T>>,
441 sequence: PromiseLike<ArrayLike<T>>,
442 predicate: (x: T) => boolean
442 predicate: (x: T) => boolean
443 ): PromiseLike<T>;
443 ): PromiseLike<T>;
444 export function firstWhere<T>(
444 export function firstWhere<T>(
445 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
445 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
446 predicate: (x: T) => boolean,
446 predicate: (x: T) => boolean,
447 cb: (x: T) => void,
447 cb: (x: T) => void,
448 err?: (x: Error) => void
448 err?: (x: Error) => void
449 ): void;
449 ): void;
450
450
451 export function firstWhere<T>(
451 export function firstWhere<T>(
452 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
452 sequence: ArrayLike<T> | PromiseLike<ArrayLike<T>>,
453 predicate?: (x: T) => boolean,
453 predicate?: (x: T) => boolean,
454 cb?: (x: T) => any,
454 cb?: (x: T) => any,
455 err?: (x: Error) => any
455 err?: (x: Error) => any
456 ) {
456 ) {
457 if (isPromise(sequence)) {
457 if (isPromise(sequence)) {
458 return sequence.then(res => firstWhere(
458 return sequence.then(res => firstWhere(
459 res,
459 res,
460 predicate as any /* force to pass undefined predicate */,
460 predicate as any /* force to pass undefined predicate */,
461 cb as any /* force to pass undefined cb */,
461 cb as any /* force to pass undefined cb */,
462 err)
462 err)
463 );
463 );
464 } else if (sequence && "length" in sequence) {
464 } else if (sequence && "length" in sequence) {
465 if (sequence.length === 0) {
465 if (sequence.length === 0) {
466 if (err)
466 if (err)
467 err(new Error("The sequence is empty"));
467 err(new Error("The sequence is empty"));
468 else
468 else
469 throw new Error("The sequence is empty");
469 throw new Error("The sequence is empty");
470 } else {
470 } else {
471 if (!predicate) {
471 if (!predicate) {
472 return cb ? cb(sequence[0]) && void (0) : sequence[0];
472 return cb ? cb(sequence[0]) && void (0) : sequence[0];
473 } else {
473 } else {
474 for (let i = 0; i < sequence.length; i++) {
474 for (let i = 0; i < sequence.length; i++) {
475 const v = sequence[i];
475 const v = sequence[i];
476 if (predicate(v))
476 if (predicate(v))
477 return cb ? cb(v) : v;
477 return cb ? cb(v) : v;
478 }
478 }
479 if (err)
479 if (err)
480 err(new Error("The sequence doesn't contain matching items"));
480 err(new Error("The sequence doesn't contain matching items"));
481 else
481 else
482 throw new Error("The sequence doesn't contain matching items");
482 throw new Error("The sequence doesn't contain matching items");
483 }
483 }
484 }
484 }
485 } else {
485 } else {
486 if (err)
486 if (err)
487 err(new Error("The sequence is required"));
487 err(new Error("The sequence is required"));
488 else
488 else
489 throw new Error("The sequence is required");
489 throw new Error("The sequence is required");
490 }
490 }
491 }
491 }
492
492
493 export function isDestroyable(d: any): d is IDestroyable {
493 export function isDestroyable(d: any): d is IDestroyable {
494 if (d && "destroy" in d && typeof (destroy) === "function")
494 if (d && "destroy" in d && typeof (destroy) === "function")
495 return true;
495 return true;
496 return false;
496 return false;
497 }
497 }
498
498
499 export function destroy(d: any) {
499 export function destroy(d: any) {
500 if (d && "destroy" in d)
500 if (d && "destroy" in d)
501 d.destroy();
501 d.destroy();
502 }
502 }
503
503
504 /**
504 /**
505 * Used to mark that the async operation isn't awaited intentionally.
505 * Used to mark that the async operation isn't awaited intentionally.
506 * @param p The promise which represents the async operation.
506 * @param p The promise which represents the async operation.
507 */
507 */
508 export function nowait(p: Promise<any>) {
508 export function nowait(p: Promise<any>) {
509 }
509 }
510
510
511 /** represents already destroyed object.
511 /** represents already destroyed object.
512 */
512 */
513 export const destroyed = {
513 export const destroyed = {
514 /** Calling to this method doesn't affect anything, noop.
514 /** Calling to this method doesn't affect anything, noop.
515 */
515 */
516 destroy() {
516 destroy() {
517 }
517 }
518 };
518 };
General Comments 0
You need to be logged in to leave comments. Login now