| @@ -0,0 +1,7 | |||||
|
|
1 | import { isPrimitive } from "../safe"; | |||
|
|
2 | import { Descriptor } from "./interfaces"; | |||
|
|
3 | ||||
|
|
4 | export function isDescriptor(x: any): x is Descriptor { | |||
|
|
5 | return (!isPrimitive(x)) && | |||
|
|
6 | (x.activate instanceof Function); | |||
|
|
7 | } | |||
| @@ -1,19 +1,20 | |||||
| 1 |
import { Descriptor |
|
1 | import { Descriptor } from "./interfaces"; | |
| 2 | import { ActivationContext } from "./ActivationContext"; |
|
2 | import { ActivationContext } from "./ActivationContext"; | |
| 3 | import { isPrimitive } from "../safe"; |
|
3 | import { isPrimitive } from "../safe"; | |
|
|
4 | import { isDescriptor } from "./traits"; | |||
| 4 |
|
5 | |||
| 5 |
export class AggregateDescriptor<T> implements Descriptor< |
|
6 | export class AggregateDescriptor<S, T> implements Descriptor<S, T> { | |
| 6 |
_value: |
|
7 | _value: any; | |
| 7 |
|
8 | |||
| 8 |
constructor(value: |
|
9 | constructor(value: any) { | |
| 9 | this._value = value; |
|
10 | this._value = value; | |
| 10 | } |
|
11 | } | |
| 11 |
|
12 | |||
| 12 |
activate |
|
13 | activate(context: ActivationContext<S>): T { | |
| 13 | return this._parse(this._value, context, "$value"); |
|
14 | return this._parse(this._value, context, "$value"); | |
| 14 | } |
|
15 | } | |
| 15 |
|
16 | |||
| 16 |
_parse |
|
17 | _parse(value: any, context: ActivationContext<S>, path: string): any { | |
| 17 | if (isPrimitive(value)) |
|
18 | if (isPrimitive(value)) | |
| 18 | return value as any; |
|
19 | return value as any; | |
| 19 |
|
20 | |||
| @@ -1,4 +1,5 | |||||
| 1 | import { Constructor } from "../interfaces"; |
|
1 | import { Constructor } from "../interfaces"; | |
|
|
2 | import { primitive } from "../safe"; | |||
| 2 |
|
3 | |||
| 3 | export interface InjectOptions { |
|
4 | export interface InjectOptions { | |
| 4 | lazy?: boolean; |
|
5 | lazy?: boolean; | |
| @@ -14,25 +15,28 interface Lazy<K extends keyof any> exte | |||||
| 14 | lazy: true; |
|
15 | lazy: true; | |
| 15 | } |
|
16 | } | |
| 16 |
|
17 | |||
| 17 | type Setter<T = any> = (v: T) => void; |
|
|||
| 18 |
|
||||
| 19 | type Compatible<T1, T2> = T1 extends T2 ? any : never; |
|
18 | type Compatible<T1, T2> = T1 extends T2 ? any : never; | |
| 20 |
|
19 | |||
| 21 | type ExtractService<K, S> = K extends keyof S ? S[K] : K; |
|
20 | type ExtractService<K, S> = K extends keyof S ? S[K] : K; | |
| 22 |
|
21 | |||
| 23 | type ExtractDependency<D, S> = D extends { $dependency: infer K } ? D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> : VisitDependency<D, S>; |
|
22 | type ExtractDependency<D, S> = D extends { $dependency: infer K } ? | |
|
|
23 | D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> : | |||
|
|
24 | WalkDependencies<D, S>; | |||
| 24 |
|
25 | |||
| 25 | type VisitDependency<D, S> = D extends {} ? { [K in keyof D]: ExtractDependency<D[K], S> } : D; |
|
26 | type WalkDependencies<D, S> = D extends primitive ? D : | |
|
|
27 | { [K in keyof D]: ExtractDependency<D[K], S> }; | |||
| 26 |
|
28 | |||
| 27 |
interface |
|
29 | interface Services<S> { | |
| 28 |
|
|
30 | get<K extends keyof S>(name: K): Dependency<K>; | |
| 29 |
|
31 | |||
| 30 | lazy<K extends keyof S>(name: K): Lazy<K>; |
|
32 | lazy<K extends keyof S>(name: K): Lazy<K>; | |
| 31 |
|
33 | |||
| 32 | build<T>(): Builder<T, S>; |
|
34 | build<T extends object>(): Builder<T, S>; | |
| 33 | } |
|
35 | } | |
| 34 |
|
36 | |||
| 35 |
export declare function services<S extends object>(): |
|
37 | export declare function services<S extends object>(): Services<S>; | |
|
|
38 | ||||
|
|
39 | export declare function build<T = never, S = any>(): Builder<T, S>; | |||
| 36 |
|
40 | |||
| 37 | export class Builder<T, S> { |
|
41 | export class Builder<T, S> { | |
| 38 | consume<P extends any[]>(...args: P) { |
|
42 | consume<P extends any[]>(...args: P) { | |
| @@ -40,13 +44,17 export class Builder<T, S> { | |||||
| 40 | }; |
|
44 | }; | |
| 41 | } |
|
45 | } | |
| 42 |
|
46 | |||
| 43 |
inject< |
|
47 | inject<P extends any[]>(...args: P) { | |
| 44 | // K = "bar" |
|
48 | // K = "bar" | |
| 45 | // M = "setValue" |
|
49 | // M = "setValue" | |
| 46 | // S[K] = Bar |
|
50 | // S[K] = Bar | |
| 47 | // T[M] = (value: string) => void |
|
51 | // T[M] = (value: string) => void | |
| 48 | // P[m] = (value: V) => void |
|
52 | // P[m] = (value: V) => void | |
| 49 | return <P, M extends keyof (T | P)>(target: P, memberName: M, descriptor: TypedPropertyDescriptor<Compatible<T[M], Setter<S[K]>>>) => { |
|
53 | return <X extends { [m in M]: (...args: any) => any }, M extends keyof (T | X)>( | |
|
|
54 | target: X, | |||
|
|
55 | memberName: M, | |||
|
|
56 | descriptor: TypedPropertyDescriptor<Compatible<(...args: ExtractDependency<P, S>) => any, T[M]>> | |||
|
|
57 | ) => { | |||
| 50 |
|
58 | |||
| 51 | }; |
|
59 | }; | |
| 52 | } |
|
60 | } | |
| @@ -55,4 +63,13 export class Builder<T, S> { | |||||
| 55 | return this as Builder<T2, S>; |
|
63 | return this as Builder<T2, S>; | |
| 56 | } |
|
64 | } | |
| 57 |
|
65 | |||
|
|
66 | get<K extends keyof S>(name: K): Dependency<K> { | |||
|
|
67 | throw new Error(); | |||
| 58 | } |
|
68 | } | |
|
|
69 | ||||
|
|
70 | lazy<K extends keyof S>(name: K): Lazy<K> { | |||
|
|
71 | throw new Error(); | |||
|
|
72 | } | |||
|
|
73 | ||||
|
|
74 | ||||
|
|
75 | } | |||
| @@ -1,17 +1,6 | |||||
| 1 | import { |
|
1 | import { | |
| 2 | ServiceRegistration, |
|
2 | PartialServiceMap, | |
| 3 | TypeRegistration, |
|
3 | ActivationType | |
| 4 | FactoryRegistration, |
|
|||
| 5 | ServiceMap, |
|
|||
| 6 | isDescriptor, |
|
|||
| 7 | isDependencyRegistration, |
|
|||
| 8 | DependencyRegistration, |
|
|||
| 9 | ValueRegistration, |
|
|||
| 10 | ActivationType, |
|
|||
| 11 | isValueRegistration, |
|
|||
| 12 | isTypeRegistration, |
|
|||
| 13 | isFactoryRegistration, |
|
|||
| 14 | PartialServiceMap |
|
|||
| 15 | } from "./interfaces"; |
|
4 | } from "./interfaces"; | |
| 16 |
|
5 | |||
| 17 | import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get } from "../safe"; |
|
6 | import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get } from "../safe"; | |
| @@ -25,7 +14,81 import { TraceSource } from "../log/Trac | |||||
| 25 | import { ConfigError } from "./ConfigError"; |
|
14 | import { ConfigError } from "./ConfigError"; | |
| 26 | import { Cancellation } from "../Cancellation"; |
|
15 | import { Cancellation } from "../Cancellation"; | |
| 27 | import { makeResolver } from "./ResolverHelper"; |
|
16 | import { makeResolver } from "./ResolverHelper"; | |
| 28 | import { ICancellation } from "../interfaces"; |
|
17 | import { ICancellation, Constructor, Factory } from "../interfaces"; | |
|
|
18 | import { isDescriptor } from "./traits"; | |||
|
|
19 | ||||
|
|
20 | export interface RegistrationScope<S> { | |||
|
|
21 | ||||
|
|
22 | /** сервисы, которые регистрируются в контексте активации и таким образом | |||
|
|
23 | * могут переопределять ранее зарегистрированные сервисы. за это свойство | |||
|
|
24 | * нужно платить, кроме того порядок активации будет влиять на результат | |||
|
|
25 | * разрешения зависимостей. | |||
|
|
26 | */ | |||
|
|
27 | services?: PartialServiceMap<S>; | |||
|
|
28 | } | |||
|
|
29 | ||||
|
|
30 | /** | |||
|
|
31 | * Базовый интефейс конфигурации сервисов | |||
|
|
32 | */ | |||
|
|
33 | export interface ServiceRegistration<T, P, S> extends RegistrationScope<S> { | |||
|
|
34 | ||||
|
|
35 | activation?: ActivationType; | |||
|
|
36 | ||||
|
|
37 | params?: P; | |||
|
|
38 | ||||
|
|
39 | inject?: object | object[]; | |||
|
|
40 | ||||
|
|
41 | cleanup?: ((instance: T) => void) | string; | |||
|
|
42 | } | |||
|
|
43 | ||||
|
|
44 | export interface TypeRegistration<T, P extends any[], S> extends ServiceRegistration<T, P, S> { | |||
|
|
45 | $type: string | (new (...params: P) => T); | |||
|
|
46 | ||||
|
|
47 | } | |||
|
|
48 | ||||
|
|
49 | export interface FactoryRegistration<T, P extends any[], S> extends ServiceRegistration<T, P, S> { | |||
|
|
50 | $factory: string | ( (...params: P) => T); | |||
|
|
51 | } | |||
|
|
52 | ||||
|
|
53 | export interface ValueRegistration<T> { | |||
|
|
54 | $value: T; | |||
|
|
55 | parse?: boolean; | |||
|
|
56 | } | |||
|
|
57 | ||||
|
|
58 | export interface DependencyRegistration<S, K extends keyof S> extends RegistrationScope<S> { | |||
|
|
59 | $dependency: K; | |||
|
|
60 | lazy?: boolean; | |||
|
|
61 | optional?: boolean; | |||
|
|
62 | default?: S[K]; | |||
|
|
63 | } | |||
|
|
64 | ||||
|
|
65 | const _activationTypes: { [k in ActivationType]: number; } = { | |||
|
|
66 | singleton: 1, | |||
|
|
67 | container: 2, | |||
|
|
68 | hierarchy: 3, | |||
|
|
69 | context: 4, | |||
|
|
70 | call: 5 | |||
|
|
71 | }; | |||
|
|
72 | ||||
|
|
73 | export function isTypeRegistration(x: any): x is TypeRegistration<any, any, any> { | |||
|
|
74 | return (!isPrimitive(x)) && ("$type" in x); | |||
|
|
75 | } | |||
|
|
76 | ||||
|
|
77 | export function isFactoryRegistration(x: any): x is FactoryRegistration<any, any, any> { | |||
|
|
78 | return (!isPrimitive(x)) && ("$factory" in x); | |||
|
|
79 | } | |||
|
|
80 | ||||
|
|
81 | export function isValueRegistration(x: any): x is ValueRegistration<any> { | |||
|
|
82 | return (!isPrimitive(x)) && ("$value" in x); | |||
|
|
83 | } | |||
|
|
84 | ||||
|
|
85 | export function isDependencyRegistration<S>(x: any): x is DependencyRegistration<S, keyof S> { | |||
|
|
86 | return (!isPrimitive(x)) && ("$dependency" in x); | |||
|
|
87 | } | |||
|
|
88 | ||||
|
|
89 | export function isActivationType(x: string): x is ActivationType { | |||
|
|
90 | return typeof x === "string" && x in _activationTypes; | |||
|
|
91 | } | |||
| 29 |
|
92 | |||
| 30 | const trace = TraceSource.get("@implab/core/di/Configuration"); |
|
93 | const trace = TraceSource.get("@implab/core/di/Configuration"); | |
| 31 | async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>; |
|
94 | async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>; | |
| @@ -180,7 +243,7 export class Configuration<S> { | |||||
| 180 | trace.debug("<{0}", name); |
|
243 | trace.debug("<{0}", name); | |
| 181 | } |
|
244 | } | |
| 182 |
|
245 | |||
| 183 |
async _visit |
|
246 | async _visit(data: any, name: string): Promise<any> { | |
| 184 | if (isPrimitive(data) || isDescriptor(data)) |
|
247 | if (isPrimitive(data) || isDescriptor(data)) | |
| 185 | return data; |
|
248 | return data; | |
| 186 |
|
249 | |||
| @@ -196,10 +259,10 export class Configuration<S> { | |||||
| 196 | return this._visitArray(data, name); |
|
259 | return this._visitArray(data, name); | |
| 197 | } |
|
260 | } | |
| 198 |
|
261 | |||
| 199 |
return this._visitObject(data |
|
262 | return this._visitObject(data, name); | |
| 200 | } |
|
263 | } | |
| 201 |
|
264 | |||
| 202 |
async _visitObject |
|
265 | async _visitObject(data: any, name: string) { | |
| 203 | if (data.constructor && |
|
266 | if (data.constructor && | |
| 204 | data.constructor.prototype !== Object.prototype) |
|
267 | data.constructor.prototype !== Object.prototype) | |
| 205 | return new ValueDescriptor(data); |
|
268 | return new ValueDescriptor(data); | |
| @@ -259,30 +322,7 export class Configuration<S> { | |||||
| 259 | this._visit(data.params, "params"); |
|
322 | this._visit(data.params, "params"); | |
| 260 |
|
323 | |||
| 261 | if (data.activation) { |
|
324 | if (data.activation) { | |
| 262 | if (typeof (data.activation) === "string") { |
|
325 | opts.activation = data.activation; | |
| 263 | switch (data.activation.toLowerCase()) { |
|
|||
| 264 | case "singleton": |
|
|||
| 265 | opts.activation = ActivationType.Singleton; |
|
|||
| 266 | break; |
|
|||
| 267 | case "container": |
|
|||
| 268 | opts.activation = ActivationType.Container; |
|
|||
| 269 | break; |
|
|||
| 270 | case "hierarchy": |
|
|||
| 271 | opts.activation = ActivationType.Hierarchy; |
|
|||
| 272 | break; |
|
|||
| 273 | case "context": |
|
|||
| 274 | opts.activation = ActivationType.Context; |
|
|||
| 275 | break; |
|
|||
| 276 | case "call": |
|
|||
| 277 | opts.activation = ActivationType.Call; |
|
|||
| 278 | break; |
|
|||
| 279 | default: |
|
|||
| 280 | throw new Error("Unknown activation type: " + |
|
|||
| 281 | data.activation); |
|
|||
| 282 | } |
|
|||
| 283 | } else { |
|
|||
| 284 | opts.activation = Number(data.activation); |
|
|||
| 285 | } |
|
|||
| 286 | } |
|
326 | } | |
| 287 |
|
327 | |||
| 288 | if (data.cleanup) |
|
328 | if (data.cleanup) | |
| @@ -312,7 +352,7 export class Configuration<S> { | |||||
| 312 | return d; |
|
352 | return d; | |
| 313 | } |
|
353 | } | |
| 314 |
|
354 | |||
| 315 |
async _visitTypeRegistration |
|
355 | async _visitTypeRegistration(data: TypeRegistration<any, any, S>, name: string) { | |
| 316 | argumentNotNull(data.$type, "data.$type"); |
|
356 | argumentNotNull(data.$type, "data.$type"); | |
| 317 | this._enter(name); |
|
357 | this._enter(name); | |
| 318 |
|
358 | |||
| @@ -324,7 +364,7 export class Configuration<S> { | |||||
| 324 | opts.type = this._resolveType(moduleName, typeName); |
|
364 | opts.type = this._resolveType(moduleName, typeName); | |
| 325 | } |
|
365 | } | |
| 326 |
|
366 | |||
| 327 | const d = new TypeServiceDescriptor( |
|
367 | const d = new TypeServiceDescriptor<S, any, any[]>( | |
| 328 | await mapAll(opts) |
|
368 | await mapAll(opts) | |
| 329 | ); |
|
369 | ); | |
| 330 |
|
370 | |||
| @@ -333,14 +373,14 export class Configuration<S> { | |||||
| 333 | return d; |
|
373 | return d; | |
| 334 | } |
|
374 | } | |
| 335 |
|
375 | |||
| 336 |
async _visitFactoryRegistration |
|
376 | async _visitFactoryRegistration(data: FactoryRegistration<any, any, S>, name: string) { | |
| 337 | argumentOfType(data.$factory, Function, "data.$factory"); |
|
377 | argumentOfType(data.$factory, Function, "data.$factory"); | |
| 338 | this._enter(name); |
|
378 | this._enter(name); | |
| 339 |
|
379 | |||
| 340 | const opts = this._makeServiceParams(data); |
|
380 | const opts = this._makeServiceParams(data); | |
| 341 | opts.factory = data.$factory; |
|
381 | opts.factory = data.$factory; | |
| 342 |
|
382 | |||
| 343 | const d = new FactoryServiceDescriptor( |
|
383 | const d = new FactoryServiceDescriptor<S, any, any[]>( | |
| 344 | await mapAll(opts) |
|
384 | await mapAll(opts) | |
| 345 | ); |
|
385 | ); | |
| 346 |
|
386 | |||
| @@ -1,16 +1,17 | |||||
| 1 | import { ActivationContext } from "./ActivationContext"; |
|
1 | import { ActivationContext } from "./ActivationContext"; | |
| 2 | import { ValueDescriptor } from "./ValueDescriptor"; |
|
2 | import { ValueDescriptor } from "./ValueDescriptor"; | |
| 3 | import { ActivationError } from "./ActivationError"; |
|
3 | import { ActivationError } from "./ActivationError"; | |
| 4 |
import { |
|
4 | import { ServiceMap, Descriptor, PartialServiceMap, ContainerServices, Resolver } from "./interfaces"; | |
| 5 | import { TraceSource } from "../log/TraceSource"; |
|
5 | import { TraceSource } from "../log/TraceSource"; | |
| 6 | import { Configuration } from "./Configuration"; |
|
6 | import { Configuration } from "./Configuration"; | |
| 7 | import { Cancellation } from "../Cancellation"; |
|
7 | import { Cancellation } from "../Cancellation"; | |
| 8 | import { MapOf } from "../interfaces"; |
|
8 | import { MapOf } from "../interfaces"; | |
|
|
9 | import { isDescriptor } from "./traits"; | |||
| 9 |
|
10 | |||
| 10 | const trace = TraceSource.get("@implab/core/di/ActivationContext"); |
|
11 | const trace = TraceSource.get("@implab/core/di/ActivationContext"); | |
| 11 |
|
12 | |||
| 12 | export class Container<S = any> implements Resolver<S> { |
|
13 | export class Container<S = any> implements Resolver<S> { | |
| 13 |
readonly _services: PartialServiceMap< |
|
14 | readonly _services: PartialServiceMap<ContainerServices<S>>; | |
| 14 |
|
15 | |||
| 15 | readonly _cache: MapOf<any>; |
|
16 | readonly _cache: MapOf<any>; | |
| 16 |
|
17 | |||
| @@ -1,6 +1,5 | |||||
| 1 | import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor"; |
|
1 | import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor"; | |
| 2 | import { argumentNotNull, oid } from "../safe"; |
|
2 | import { argumentNotNull, oid } from "../safe"; | |
| 3 | import { ActivationType } from "./interfaces"; |
|
|||
| 4 |
|
3 | |||
| 5 | export interface FactoryServiceDescriptorParams<S, T, P extends any[]> extends ServiceDescriptorParams<S, T, P> { |
|
4 | export interface FactoryServiceDescriptorParams<S, T, P extends any[]> extends ServiceDescriptorParams<S, T, P> { | |
| 6 | factory: (...args: P) => T; |
|
5 | factory: (...args: P) => T; | |
| @@ -15,7 +14,7 export class FactoryServiceDescriptor<S, | |||||
| 15 | // bind to null |
|
14 | // bind to null | |
| 16 | this._factory = (...args) => opts.factory.apply(null, args as any); |
|
15 | this._factory = (...args) => opts.factory.apply(null, args as any); | |
| 17 |
|
16 | |||
| 18 |
if (opts.activation === |
|
17 | if (opts.activation === "singleton") { | |
| 19 | this._cacheId = oid(opts.factory); |
|
18 | this._cacheId = oid(opts.factory); | |
| 20 | } |
|
19 | } | |
| 21 | } |
|
20 | } | |
| @@ -26,7 +26,7 export class ReferenceDescriptor<S = any | |||||
| 26 |
|
26 | |||
| 27 | _default: S[K] | undefined; |
|
27 | _default: S[K] | undefined; | |
| 28 |
|
28 | |||
| 29 | _services: ServiceMap<S>; |
|
29 | _services: PartialServiceMap<S>; | |
| 30 |
|
30 | |||
| 31 | constructor(opts: ReferenceDescriptorParams<S, K>) { |
|
31 | constructor(opts: ReferenceDescriptorParams<S, K>) { | |
| 32 | argumentNotEmptyString(opts && opts.name, "opts.name"); |
|
32 | argumentNotEmptyString(opts && opts.name, "opts.name"); | |
| @@ -53,7 +53,7 export class ReferenceDescriptor<S = any | |||||
| 53 | const ct = saved.clone(); |
|
53 | const ct = saved.clone(); | |
| 54 | try { |
|
54 | try { | |
| 55 | if (cfg) { |
|
55 | if (cfg) { | |
| 56 |
each(cfg |
|
56 | each(cfg, (v, k) => ct.register(k, v)); | |
| 57 | } |
|
57 | } | |
| 58 |
|
58 | |||
| 59 | return this._optional ? ct.resolve(this._name, this._default) : ct |
|
59 | return this._optional ? ct.resolve(this._name, this._default) : ct | |
| @@ -1,8 +1,9 | |||||
| 1 | import { ActivationContext } from "./ActivationContext"; |
|
1 | import { ActivationContext } from "./ActivationContext"; | |
| 2 |
import { Descriptor, |
|
2 | import { Descriptor, ServiceMap, PartialServiceMap, ActivationType } from "./interfaces"; | |
| 3 | import { Container } from "./Container"; |
|
3 | import { Container } from "./Container"; | |
| 4 |
import { argumentNotNull, isPrimitive |
|
4 | import { argumentNotNull, isPrimitive, keys, isNull } from "../safe"; | |
| 5 | import { TraceSource } from "../log/TraceSource"; |
|
5 | import { TraceSource } from "../log/TraceSource"; | |
|
|
6 | import { isDescriptor } from "./traits"; | |||
| 6 |
|
7 | |||
| 7 | let cacheId = 0; |
|
8 | let cacheId = 0; | |
| 8 |
|
9 | |||
| @@ -33,7 +34,7 function makeClenupCallback(target: any, | |||||
| 33 | } |
|
34 | } | |
| 34 | } |
|
35 | } | |
| 35 |
|
36 | |||
| 36 |
function _parse |
|
37 | function _parse(value: any, context: ActivationContext<any>, path: string): any { | |
| 37 | if (isPrimitive(value)) |
|
38 | if (isPrimitive(value)) | |
| 38 | return value as any; |
|
39 | return value as any; | |
| 39 |
|
40 | |||
| @@ -77,7 +78,7 export class ServiceDescriptor<S, T, P e | |||||
| 77 |
|
78 | |||
| 78 | _hasInstance = false; |
|
79 | _hasInstance = false; | |
| 79 |
|
80 | |||
| 80 |
_activationType = |
|
81 | _activationType: ActivationType = "call"; | |
| 81 |
|
82 | |||
| 82 | _services: ServiceMap<S>; |
|
83 | _services: ServiceMap<S>; | |
| 83 |
|
84 | |||
| @@ -125,7 +126,7 export class ServiceDescriptor<S, T, P e | |||||
| 125 | this._cacheId = ++cacheId; |
|
126 | this._cacheId = ++cacheId; | |
| 126 |
|
127 | |||
| 127 | switch (this._activationType) { |
|
128 | switch (this._activationType) { | |
| 128 |
case |
|
129 | case "singleton": // SINGLETON | |
| 129 | // if the value is cached return it |
|
130 | // if the value is cached return it | |
| 130 | if (this._hasInstance) |
|
131 | if (this._hasInstance) | |
| 131 | return this._instance; |
|
132 | return this._instance; | |
| @@ -146,7 +147,7 export class ServiceDescriptor<S, T, P e | |||||
| 146 | this._hasInstance = true; |
|
147 | this._hasInstance = true; | |
| 147 | return (this._instance = instance); |
|
148 | return (this._instance = instance); | |
| 148 |
|
149 | |||
| 149 |
case |
|
150 | case "container": // CONTAINER | |
| 150 | // return a cached value |
|
151 | // return a cached value | |
| 151 |
|
152 | |||
| 152 | if (this._hasInstance) |
|
153 | if (this._hasInstance) | |
| @@ -163,17 +164,17 export class ServiceDescriptor<S, T, P e | |||||
| 163 | // cache and return the instance |
|
164 | // cache and return the instance | |
| 164 | this._hasInstance = true; |
|
165 | this._hasInstance = true; | |
| 165 | return (this._instance = instance); |
|
166 | return (this._instance = instance); | |
| 166 |
case |
|
167 | case "context": // CONTEXT | |
| 167 | // return a cached value if one exists |
|
168 | // return a cached value if one exists | |
| 168 |
|
169 | |||
| 169 | if (context.has(this._cacheId)) |
|
170 | if (context.has(this._cacheId)) | |
| 170 | return context.get(this._cacheId); |
|
171 | return context.get(this._cacheId); | |
| 171 | // context context activated instances are controlled by callers |
|
172 | // context context activated instances are controlled by callers | |
| 172 | return context.store(this._cacheId, this._create(context)); |
|
173 | return context.store(this._cacheId, this._create(context)); | |
| 173 |
case |
|
174 | case "call": // CALL | |
| 174 | // per-call created instances are controlled by callers |
|
175 | // per-call created instances are controlled by callers | |
| 175 | return this._create(context); |
|
176 | return this._create(context); | |
| 176 |
case |
|
177 | case "hierarchy": // HIERARCHY | |
| 177 | // hierarchy activated instances are behave much like container activated |
|
178 | // hierarchy activated instances are behave much like container activated | |
| 178 | // except they are created and bound to the child container |
|
179 | // except they are created and bound to the child container | |
| 179 |
|
180 | |||
| @@ -209,7 +210,7 export class ServiceDescriptor<S, T, P e | |||||
| 209 | _create(context: ActivationContext<S>) { |
|
210 | _create(context: ActivationContext<S>) { | |
| 210 | trace.debug(`constructing ${context._name}`); |
|
211 | trace.debug(`constructing ${context._name}`); | |
| 211 |
|
212 | |||
| 212 |
if (this._activationType !== |
|
213 | if (this._activationType !== "call" && | |
| 213 | context.visit(this._cacheId) > 0) |
|
214 | context.visit(this._cacheId) > 0) | |
| 214 | throw new Error("Recursion detected"); |
|
215 | throw new Error("Recursion detected"); | |
| 215 |
|
216 | |||
| @@ -1,68 +1,16 | |||||
| 1 | import { isPrimitive, primitive } from "../safe"; |
|
|||
| 2 |
|
|
1 | import { ActivationContext } from "./ActivationContext"; | |
| 3 | import { Constructor, Factory } from "../interfaces"; |
|
|||
| 4 |
|
2 | |||
| 5 | export interface Descriptor<S = any, T = any> { |
|
3 | export interface Descriptor<S = any, T = any> { | |
| 6 | activate(context: ActivationContext<S>): T; |
|
4 | activate(context: ActivationContext<S>): T; | |
| 7 | } |
|
5 | } | |
| 8 |
|
6 | |||
| 9 | export function isDescriptor(x: any): x is Descriptor { |
|
7 | export type ServiceMap<S> = { | |
| 10 | return (!isPrimitive(x)) && |
|
8 | [k in keyof S]: Descriptor<S, S[k]>; | |
| 11 | (x.activate instanceof Function); |
|
|||
| 12 | } |
|
|||
| 13 |
|
||||
| 14 | export type ServiceMap<S, S2 extends S = S> = { |
|
|||
| 15 | [k in keyof S2]: Descriptor<S, S2[k]>; |
|
|||
| 16 | }; |
|
9 | }; | |
| 17 |
|
10 | |||
| 18 |
export type PartialServiceMap<S |
|
11 | export type PartialServiceMap<S> = { | |
| 19 |
|
12 | [k in keyof S]?: Descriptor<S, S[k]>; | ||
| 20 | export enum ActivationType { |
|
13 | }; | |
| 21 | Singleton = 1, |
|
|||
| 22 | Container, |
|
|||
| 23 | Hierarchy, |
|
|||
| 24 | Context, |
|
|||
| 25 | Call |
|
|||
| 26 | } |
|
|||
| 27 |
|
||||
| 28 | export interface RegistrationWithServices<S> { |
|
|||
| 29 | services?: ServiceMap<S>; |
|
|||
| 30 | } |
|
|||
| 31 |
|
||||
| 32 | export interface ServiceRegistration<T, P, S> extends RegistrationWithServices<S> { |
|
|||
| 33 |
|
||||
| 34 | activation?: "singleton" | "container" | "hierarchy" | "context" | "call"; |
|
|||
| 35 |
|
||||
| 36 | params?: P; |
|
|||
| 37 |
|
||||
| 38 | inject?: object | object[]; |
|
|||
| 39 |
|
||||
| 40 | cleanup?: ((instance: T) => void) | string; |
|
|||
| 41 | } |
|
|||
| 42 |
|
||||
| 43 | export interface TypeRegistration<T, P, S> extends ServiceRegistration<T, P, S> { |
|
|||
| 44 | $type: string | Constructor<T>; |
|
|||
| 45 | } |
|
|||
| 46 |
|
||||
| 47 | export interface FactoryRegistration<T, P, S> extends ServiceRegistration<T, P, S> { |
|
|||
| 48 | $factory: string | Factory<T>; |
|
|||
| 49 | } |
|
|||
| 50 |
|
||||
| 51 | export interface ValueRegistration<T> { |
|
|||
| 52 | $value: T; |
|
|||
| 53 | parse?: boolean; |
|
|||
| 54 | } |
|
|||
| 55 |
|
||||
| 56 | export interface DependencyRegistration<S, K extends keyof S> extends RegistrationWithServices<S> { |
|
|||
| 57 | $dependency: K; |
|
|||
| 58 | lazy?: boolean; |
|
|||
| 59 | optional?: boolean; |
|
|||
| 60 | default?: S[K]; |
|
|||
| 61 | } |
|
|||
| 62 |
|
||||
| 63 | export type Parse<T> = T extends primitive ? T: |
|
|||
| 64 | T extends Descriptor<infer V> ? V : |
|
|||
| 65 | { [K in keyof T]: Parse<T[K]> }; |
|
|||
| 66 |
|
14 | |||
| 67 | export interface Resolver<S> { |
|
15 | export interface Resolver<S> { | |
| 68 | resolve<K extends keyof ContainerServices<S>, T extends ContainerServices<S>[K] = ContainerServices<S>[K]>(name: K, def?: T): T; |
|
16 | resolve<K extends keyof ContainerServices<S>, T extends ContainerServices<S>[K] = ContainerServices<S>[K]>(name: K, def?: T): T; | |
| @@ -70,19 +18,4 export interface Resolver<S> { | |||||
| 70 | export type ContainerServices<S> = S & { |
|
18 | export type ContainerServices<S> = S & { | |
| 71 | container: Resolver<S>; |
|
19 | container: Resolver<S>; | |
| 72 | }; |
|
20 | }; | |
| 73 |
|
21 | export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call"; | ||
| 74 | export function isTypeRegistration(x: any): x is TypeRegistration<any, any, any> { |
|
|||
| 75 | return (!isPrimitive(x)) && ("$type" in x); |
|
|||
| 76 | } |
|
|||
| 77 |
|
||||
| 78 | export function isFactoryRegistration(x: any): x is FactoryRegistration<any, any, any> { |
|
|||
| 79 | return (!isPrimitive(x)) && ("$factory" in x); |
|
|||
| 80 | } |
|
|||
| 81 |
|
||||
| 82 | export function isValueRegistration(x: any): x is ValueRegistration<any> { |
|
|||
| 83 | return (!isPrimitive(x)) && ("$value" in x); |
|
|||
| 84 | } |
|
|||
| 85 |
|
||||
| 86 | export function isDependencyRegistration<S>(x: any): x is DependencyRegistration<S, keyof S> { |
|
|||
| 87 | return (!isPrimitive(x)) && ("$dependency" in x); |
|
|||
| 88 | } |
|
|||
| @@ -1,22 +1,22 | |||||
| 1 | import { Foo } from "./Foo"; |
|
1 | import { Foo } from "./Foo"; | |
| 2 |
|
|
2 | import { build } from "./config"; | |
| 3 |
|
|
3 | ||
| 4 |
|
|
4 | const service = build<Bar>(); | |
| 5 |
|
|
5 | ||
| 6 |
|
|
6 | @service.consume({ | |
| 7 | // f: config.dependency("foo"), |
|
7 | foo: service.get("foo"), | |
| 8 |
|
|
8 | nested: { | |
| 9 |
|
|
9 | lazy: service.lazy("foo") | |
| 10 |
|
|
10 | } | |
| 11 | // }) |
|
11 | }) | |
| 12 | export class Bar { |
|
12 | export class Bar { | |
| 13 | barName = "bar"; |
|
13 | barName = "bar"; | |
| 14 |
|
14 | |||
| 15 | _v: Foo | undefined; |
|
15 | _v: Foo | undefined; | |
| 16 |
|
16 | |||
| 17 | constructor(_opts: { |
|
17 | constructor(_opts?: { | |
| 18 | foo: Foo; |
|
18 | foo?: Foo; | |
| 19 | nested: { |
|
19 | nested?: { | |
| 20 | lazy: () => Foo |
|
20 | lazy: () => Foo | |
| 21 | } |
|
21 | } | |
| 22 | }) { |
|
22 | }) { | |
| @@ -1,15 +1,17 | |||||
| 1 | import { services } from "../di/Annotations"; |
|
1 | import { services } from "../di/Annotations"; | |
| 2 | import { Bar } from "./Bar"; |
|
2 | import { Bar } from "./Bar"; | |
|
|
3 | import { Foo } from "./Foo"; | |||
| 3 |
|
4 | |||
| 4 | // declare required dependencies |
|
5 | // declare required dependencies | |
| 5 | const config = services<{ |
|
6 | const config = services<{ | |
| 6 | bar: Bar; |
|
7 | bar: Bar; | |
|
|
8 | foo: Foo; | |||
| 7 | }>(); |
|
9 | }>(); | |
| 8 |
|
10 | |||
| 9 | // export service descriptor |
|
11 | // export service descriptor | |
| 10 | export const service = config.build<Box<Bar>>(); |
|
12 | export const service = config.build<Box<Bar>>(); | |
| 11 |
|
13 | |||
| 12 |
@service.consume(config. |
|
14 | @service.consume(config.get("bar")) | |
| 13 | export class Box<T> { |
|
15 | export class Box<T> { | |
| 14 | private _value: T | undefined; |
|
16 | private _value: T | undefined; | |
| 15 |
|
17 | |||
| @@ -17,9 +19,10 export class Box<T> { | |||||
| 17 | this._value = value; |
|
19 | this._value = value; | |
| 18 | } |
|
20 | } | |
| 19 |
|
21 | |||
| 20 |
|
|
22 | @service.inject( config.get("bar")) | |
| 21 |
|
|
23 | setValue(value?: T) { | |
| 22 | this._value = value; |
|
24 | this._value = value; | |
|
|
25 | return value; | |||
| 23 | } |
|
26 | } | |
| 24 |
|
27 | |||
| 25 | setObj(value: object) { |
|
28 | setObj(value: object) { | |
| @@ -1,8 +1,7 | |||||
| 1 | import { Foo } from "./Foo"; |
|
1 | import { Foo } from "./Foo"; | |
| 2 | import { Bar } from "./Bar"; |
|
2 | import { Bar } from "./Bar"; | |
| 3 | import { Box } from "./Box"; |
|
3 | import { Box } from "./Box"; | |
| 4 |
import { |
|
4 | import { Builder } from "../di/Annotations"; | |
| 5 | import { Constructor } from "../interfaces"; |
|
|||
| 6 |
|
5 | |||
| 7 | interface Services { |
|
6 | interface Services { | |
| 8 | foo: Foo; |
|
7 | foo: Foo; | |
| @@ -15,24 +14,14 interface Services { | |||||
| 15 |
|
14 | |||
| 16 | } |
|
15 | } | |
| 17 |
|
16 | |||
| 18 | interface TypeDescriptor<T, C extends Constructor<T>> { |
|
17 | const services = { | |
| 19 | $type: C; |
|
18 | build: <T>() => { | |
|
|
19 | return new Builder<T, Services>(); | |||
|
|
20 | } | |||
|
|
21 | }; | |||
| 20 |
|
22 | |||
| 21 | params: Wrap<ConstructorParameters<C>>; |
|
23 | namespace services { | |
| 22 | } |
|
|||
| 23 |
|
24 | |||
| 24 | function typeRegistration<T, C extends Constructor<T>>(target: C, params: Wrap<ConstructorParameters<C>>): TypeDescriptor<T, C> { |
|
|||
| 25 | throw new Error(); |
|
|||
| 26 | } |
|
25 | } | |
| 27 |
|
26 | |||
| 28 | declare function register<T>(): { type<C extends Constructor<T>>(target: C, params: Wrap<ConstructorParameters<C>>): TypeDescriptor<T, C>}; |
|
27 | export = services; | |
| 29 |
|
||||
| 30 | type Wrap<T> = T extends primitive ? T : |
|
|||
| 31 | { [k in keyof T]: Wrap<T[k]> } | TypeDescriptor<T, Constructor<T>>; |
|
|||
| 32 |
|
||||
| 33 | const config: Wrap<Services> = { |
|
|||
| 34 | foo: typeRegistration(Foo, []), |
|
|||
| 35 | bar: typeRegistration(Bar, [{ foo: null as any, nested: null as any }]), |
|
|||
| 36 | box: register<Box<Foo>>().type(Box, [{ $type: Bar, params: [] }]), |
|
|||
| 37 | host: "" |
|
|||
| 38 | }; |
|
|||
| @@ -91,4 +91,5 test("Load configuration from module", a | |||||
| 91 |
|
91 | |||
| 92 | t.assert(!isNull(b1), "bar should not be null"); |
|
92 | t.assert(!isNull(b1), "bar should not be null"); | |
| 93 | t.assert(!isNull(b1._v), "bar.foo should not be null"); |
|
93 | t.assert(!isNull(b1._v), "bar.foo should not be null"); | |
|
|
94 | ||||
| 94 | }); |
|
95 | }); | |
General Comments 0
You need to be logged in to leave comments.
Login now
