| @@ -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 | 2 | import { ActivationContext } from "./ActivationContext"; |
|
|
3 | 3 | import { isPrimitive } from "../safe"; |
|
|
4 | import { isDescriptor } from "./traits"; | |
|
|
4 | 5 | |
|
|
5 |
export class AggregateDescriptor<T> implements Descriptor< |
|
|
|
6 |
_value: |
|
|
|
6 | export class AggregateDescriptor<S, T> implements Descriptor<S, T> { | |
|
|
7 | _value: any; | |
|
|
7 | 8 | |
|
|
8 |
constructor(value: |
|
|
|
9 | constructor(value: any) { | |
|
|
9 | 10 | this._value = value; |
|
|
10 | 11 | } |
|
|
11 | 12 | |
|
|
12 |
activate |
|
|
|
13 | activate(context: ActivationContext<S>): T { | |
|
|
13 | 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 | 18 | if (isPrimitive(value)) |
|
|
18 | 19 | return value as any; |
|
|
19 | 20 | |
| @@ -1,4 +1,5 | |||
|
|
1 | 1 | import { Constructor } from "../interfaces"; |
|
|
2 | import { primitive } from "../safe"; | |
|
|
2 | 3 | |
|
|
3 | 4 | export interface InjectOptions { |
|
|
4 | 5 | lazy?: boolean; |
| @@ -14,25 +15,28 interface Lazy<K extends keyof any> exte | |||
|
|
14 | 15 | lazy: true; |
|
|
15 | 16 | } |
|
|
16 | 17 | |
|
|
17 | type Setter<T = any> = (v: T) => void; | |
|
|
18 | ||
|
|
19 | 18 | type Compatible<T1, T2> = T1 extends T2 ? any : never; |
|
|
20 | 19 | |
|
|
21 | 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 |
|
|
|
28 |
|
|
|
|
29 | interface Services<S> { | |
|
|
30 | get<K extends keyof S>(name: K): Dependency<K>; | |
|
|
29 | 31 | |
|
|
30 | 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 | 41 | export class Builder<T, S> { |
|
|
38 | 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 | 48 | // K = "bar" |
|
|
45 | 49 | // M = "setValue" |
|
|
46 | 50 | // S[K] = Bar |
|
|
47 | 51 | // T[M] = (value: string) => void |
|
|
48 | 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 | 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(); | |
|
|
68 | } | |
|
|
69 | ||
|
|
70 | lazy<K extends keyof S>(name: K): Lazy<K> { | |
|
|
71 | throw new Error(); | |
|
|
72 | } | |
|
|
73 | ||
|
|
74 | ||
|
|
58 | 75 | } |
| @@ -1,17 +1,6 | |||
|
|
1 | 1 | import { |
|
|
2 | ServiceRegistration, | |
|
|
3 | TypeRegistration, | |
|
|
4 | FactoryRegistration, | |
|
|
5 | ServiceMap, | |
|
|
6 | isDescriptor, | |
|
|
7 | isDependencyRegistration, | |
|
|
8 | DependencyRegistration, | |
|
|
9 | ValueRegistration, | |
|
|
10 | ActivationType, | |
|
|
11 | isValueRegistration, | |
|
|
12 | isTypeRegistration, | |
|
|
13 | isFactoryRegistration, | |
|
|
14 | PartialServiceMap | |
|
|
2 | PartialServiceMap, | |
|
|
3 | ActivationType | |
|
|
15 | 4 | } from "./interfaces"; |
|
|
16 | 5 | |
|
|
17 | 6 | import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get } from "../safe"; |
| @@ -25,7 +14,81 import { TraceSource } from "../log/Trac | |||
|
|
25 | 14 | import { ConfigError } from "./ConfigError"; |
|
|
26 | 15 | import { Cancellation } from "../Cancellation"; |
|
|
27 | 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 | 93 | const trace = TraceSource.get("@implab/core/di/Configuration"); |
|
|
31 | 94 | async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>; |
| @@ -180,7 +243,7 export class Configuration<S> { | |||
|
|
180 | 243 | trace.debug("<{0}", name); |
|
|
181 | 244 | } |
|
|
182 | 245 | |
|
|
183 |
async _visit |
|
|
|
246 | async _visit(data: any, name: string): Promise<any> { | |
|
|
184 | 247 | if (isPrimitive(data) || isDescriptor(data)) |
|
|
185 | 248 | return data; |
|
|
186 | 249 | |
| @@ -196,10 +259,10 export class Configuration<S> { | |||
|
|
196 | 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 | 266 | if (data.constructor && |
|
|
204 | 267 | data.constructor.prototype !== Object.prototype) |
|
|
205 | 268 | return new ValueDescriptor(data); |
| @@ -259,30 +322,7 export class Configuration<S> { | |||
|
|
259 | 322 | this._visit(data.params, "params"); |
|
|
260 | 323 | |
|
|
261 | 324 | if (data.activation) { |
|
|
262 | if (typeof (data.activation) === "string") { | |
|
|
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 | } | |
|
|
325 | opts.activation = data.activation; | |
|
|
286 | 326 | } |
|
|
287 | 327 | |
|
|
288 | 328 | if (data.cleanup) |
| @@ -312,7 +352,7 export class Configuration<S> { | |||
|
|
312 | 352 | return d; |
|
|
313 | 353 | } |
|
|
314 | 354 | |
|
|
315 |
async _visitTypeRegistration |
|
|
|
355 | async _visitTypeRegistration(data: TypeRegistration<any, any, S>, name: string) { | |
|
|
316 | 356 | argumentNotNull(data.$type, "data.$type"); |
|
|
317 | 357 | this._enter(name); |
|
|
318 | 358 | |
| @@ -324,7 +364,7 export class Configuration<S> { | |||
|
|
324 | 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 | 368 | await mapAll(opts) |
|
|
329 | 369 | ); |
|
|
330 | 370 | |
| @@ -333,14 +373,14 export class Configuration<S> { | |||
|
|
333 | 373 | return d; |
|
|
334 | 374 | } |
|
|
335 | 375 | |
|
|
336 |
async _visitFactoryRegistration |
|
|
|
376 | async _visitFactoryRegistration(data: FactoryRegistration<any, any, S>, name: string) { | |
|
|
337 | 377 | argumentOfType(data.$factory, Function, "data.$factory"); |
|
|
338 | 378 | this._enter(name); |
|
|
339 | 379 | |
|
|
340 | 380 | const opts = this._makeServiceParams(data); |
|
|
341 | 381 | opts.factory = data.$factory; |
|
|
342 | 382 | |
|
|
343 | const d = new FactoryServiceDescriptor( | |
|
|
383 | const d = new FactoryServiceDescriptor<S, any, any[]>( | |
|
|
344 | 384 | await mapAll(opts) |
|
|
345 | 385 | ); |
|
|
346 | 386 | |
| @@ -1,16 +1,17 | |||
|
|
1 | 1 | import { ActivationContext } from "./ActivationContext"; |
|
|
2 | 2 | import { ValueDescriptor } from "./ValueDescriptor"; |
|
|
3 | 3 | import { ActivationError } from "./ActivationError"; |
|
|
4 |
import { |
|
|
|
4 | import { ServiceMap, Descriptor, PartialServiceMap, ContainerServices, Resolver } from "./interfaces"; | |
|
|
5 | 5 | import { TraceSource } from "../log/TraceSource"; |
|
|
6 | 6 | import { Configuration } from "./Configuration"; |
|
|
7 | 7 | import { Cancellation } from "../Cancellation"; |
|
|
8 | 8 | import { MapOf } from "../interfaces"; |
|
|
9 | import { isDescriptor } from "./traits"; | |
|
|
9 | 10 | |
|
|
10 | 11 | const trace = TraceSource.get("@implab/core/di/ActivationContext"); |
|
|
11 | 12 | |
|
|
12 | 13 | export class Container<S = any> implements Resolver<S> { |
|
|
13 |
readonly _services: PartialServiceMap< |
|
|
|
14 | readonly _services: PartialServiceMap<ContainerServices<S>>; | |
|
|
14 | 15 | |
|
|
15 | 16 | readonly _cache: MapOf<any>; |
|
|
16 | 17 | |
| @@ -1,6 +1,5 | |||
|
|
1 | 1 | import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor"; |
|
|
2 | 2 | import { argumentNotNull, oid } from "../safe"; |
|
|
3 | import { ActivationType } from "./interfaces"; | |
|
|
4 | 3 | |
|
|
5 | 4 | export interface FactoryServiceDescriptorParams<S, T, P extends any[]> extends ServiceDescriptorParams<S, T, P> { |
|
|
6 | 5 | factory: (...args: P) => T; |
| @@ -15,7 +14,7 export class FactoryServiceDescriptor<S, | |||
|
|
15 | 14 | // bind to null |
|
|
16 | 15 | this._factory = (...args) => opts.factory.apply(null, args as any); |
|
|
17 | 16 | |
|
|
18 |
if (opts.activation === |
|
|
|
17 | if (opts.activation === "singleton") { | |
|
|
19 | 18 | this._cacheId = oid(opts.factory); |
|
|
20 | 19 | } |
|
|
21 | 20 | } |
| @@ -26,7 +26,7 export class ReferenceDescriptor<S = any | |||
|
|
26 | 26 | |
|
|
27 | 27 | _default: S[K] | undefined; |
|
|
28 | 28 | |
|
|
29 | _services: ServiceMap<S>; | |
|
|
29 | _services: PartialServiceMap<S>; | |
|
|
30 | 30 | |
|
|
31 | 31 | constructor(opts: ReferenceDescriptorParams<S, K>) { |
|
|
32 | 32 | argumentNotEmptyString(opts && opts.name, "opts.name"); |
| @@ -53,7 +53,7 export class ReferenceDescriptor<S = any | |||
|
|
53 | 53 | const ct = saved.clone(); |
|
|
54 | 54 | try { |
|
|
55 | 55 | if (cfg) { |
|
|
56 |
each(cfg |
|
|
|
56 | each(cfg, (v, k) => ct.register(k, v)); | |
|
|
57 | 57 | } |
|
|
58 | 58 | |
|
|
59 | 59 | return this._optional ? ct.resolve(this._name, this._default) : ct |
| @@ -1,8 +1,9 | |||
|
|
1 | 1 | import { ActivationContext } from "./ActivationContext"; |
|
|
2 |
import { Descriptor, |
|
|
|
2 | import { Descriptor, ServiceMap, PartialServiceMap, ActivationType } from "./interfaces"; | |
|
|
3 | 3 | import { Container } from "./Container"; |
|
|
4 |
import { argumentNotNull, isPrimitive |
|
|
|
4 | import { argumentNotNull, isPrimitive, keys, isNull } from "../safe"; | |
|
|
5 | 5 | import { TraceSource } from "../log/TraceSource"; |
|
|
6 | import { isDescriptor } from "./traits"; | |
|
|
6 | 7 | |
|
|
7 | 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 | 38 | if (isPrimitive(value)) |
|
|
38 | 39 | return value as any; |
|
|
39 | 40 | |
| @@ -77,7 +78,7 export class ServiceDescriptor<S, T, P e | |||
|
|
77 | 78 | |
|
|
78 | 79 | _hasInstance = false; |
|
|
79 | 80 | |
|
|
80 |
_activationType = |
|
|
|
81 | _activationType: ActivationType = "call"; | |
|
|
81 | 82 | |
|
|
82 | 83 | _services: ServiceMap<S>; |
|
|
83 | 84 | |
| @@ -125,7 +126,7 export class ServiceDescriptor<S, T, P e | |||
|
|
125 | 126 | this._cacheId = ++cacheId; |
|
|
126 | 127 | |
|
|
127 | 128 | switch (this._activationType) { |
|
|
128 |
case |
|
|
|
129 | case "singleton": // SINGLETON | |
|
|
129 | 130 | // if the value is cached return it |
|
|
130 | 131 | if (this._hasInstance) |
|
|
131 | 132 | return this._instance; |
| @@ -146,7 +147,7 export class ServiceDescriptor<S, T, P e | |||
|
|
146 | 147 | this._hasInstance = true; |
|
|
147 | 148 | return (this._instance = instance); |
|
|
148 | 149 | |
|
|
149 |
case |
|
|
|
150 | case "container": // CONTAINER | |
|
|
150 | 151 | // return a cached value |
|
|
151 | 152 | |
|
|
152 | 153 | if (this._hasInstance) |
| @@ -163,17 +164,17 export class ServiceDescriptor<S, T, P e | |||
|
|
163 | 164 | // cache and return the instance |
|
|
164 | 165 | this._hasInstance = true; |
|
|
165 | 166 | return (this._instance = instance); |
|
|
166 |
case |
|
|
|
167 | case "context": // CONTEXT | |
|
|
167 | 168 | // return a cached value if one exists |
|
|
168 | 169 | |
|
|
169 | 170 | if (context.has(this._cacheId)) |
|
|
170 | 171 | return context.get(this._cacheId); |
|
|
171 | 172 | // context context activated instances are controlled by callers |
|
|
172 | 173 | return context.store(this._cacheId, this._create(context)); |
|
|
173 |
case |
|
|
|
174 | case "call": // CALL | |
|
|
174 | 175 | // per-call created instances are controlled by callers |
|
|
175 | 176 | return this._create(context); |
|
|
176 |
case |
|
|
|
177 | case "hierarchy": // HIERARCHY | |
|
|
177 | 178 | // hierarchy activated instances are behave much like container activated |
|
|
178 | 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 | 210 | _create(context: ActivationContext<S>) { |
|
|
210 | 211 | trace.debug(`constructing ${context._name}`); |
|
|
211 | 212 | |
|
|
212 |
if (this._activationType !== |
|
|
|
213 | if (this._activationType !== "call" && | |
|
|
213 | 214 | context.visit(this._cacheId) > 0) |
|
|
214 | 215 | throw new Error("Recursion detected"); |
|
|
215 | 216 | |
| @@ -1,68 +1,16 | |||
|
|
1 | import { isPrimitive, primitive } from "../safe"; | |
|
|
2 | 1 |
|
|
|
3 | import { Constructor, Factory } from "../interfaces"; | |
|
|
4 | 2 | |
|
|
5 | 3 | export interface Descriptor<S = any, T = any> { |
|
|
6 | 4 | activate(context: ActivationContext<S>): T; |
|
|
7 | 5 | } |
|
|
8 | 6 | |
|
|
9 | export function isDescriptor(x: any): x is Descriptor { | |
|
|
10 | return (!isPrimitive(x)) && | |
|
|
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]>; | |
|
|
7 | export type ServiceMap<S> = { | |
|
|
8 | [k in keyof S]: Descriptor<S, S[k]>; | |
|
|
16 | 9 | }; |
|
|
17 | 10 | |
|
|
18 |
export type PartialServiceMap<S |
|
|
|
19 | ||
|
|
20 | export enum ActivationType { | |
|
|
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]> }; | |
|
|
11 | export type PartialServiceMap<S> = { | |
|
|
12 | [k in keyof S]?: Descriptor<S, S[k]>; | |
|
|
13 | }; | |
|
|
66 | 14 | |
|
|
67 | 15 | export interface Resolver<S> { |
|
|
68 | 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 | 18 | export type ContainerServices<S> = S & { |
|
|
71 | 19 | container: Resolver<S>; |
|
|
72 | 20 | }; |
|
|
73 | ||
|
|
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 | } | |
|
|
21 | export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call"; | |
| @@ -1,22 +1,22 | |||
|
|
1 | 1 | import { Foo } from "./Foo"; |
|
|
2 |
|
|
|
|
2 | import { build } from "./config"; | |
|
|
3 | 3 |
|
|
|
4 |
|
|
|
|
4 | const service = build<Bar>(); | |
|
|
5 | 5 |
|
|
|
6 |
|
|
|
|
7 | // f: config.dependency("foo"), | |
|
|
8 |
|
|
|
|
9 |
|
|
|
|
10 |
|
|
|
|
11 | // }) | |
|
|
6 | @service.consume({ | |
|
|
7 | foo: service.get("foo"), | |
|
|
8 | nested: { | |
|
|
9 | lazy: service.lazy("foo") | |
|
|
10 | } | |
|
|
11 | }) | |
|
|
12 | 12 | export class Bar { |
|
|
13 | 13 | barName = "bar"; |
|
|
14 | 14 | |
|
|
15 | 15 | _v: Foo | undefined; |
|
|
16 | 16 | |
|
|
17 | constructor(_opts: { | |
|
|
18 | foo: Foo; | |
|
|
19 | nested: { | |
|
|
17 | constructor(_opts?: { | |
|
|
18 | foo?: Foo; | |
|
|
19 | nested?: { | |
|
|
20 | 20 | lazy: () => Foo |
|
|
21 | 21 | } |
|
|
22 | 22 | }) { |
| @@ -1,15 +1,17 | |||
|
|
1 | 1 | import { services } from "../di/Annotations"; |
|
|
2 | 2 | import { Bar } from "./Bar"; |
|
|
3 | import { Foo } from "./Foo"; | |
|
|
3 | 4 | |
|
|
4 | 5 | // declare required dependencies |
|
|
5 | 6 | const config = services<{ |
|
|
6 | 7 | bar: Bar; |
|
|
8 | foo: Foo; | |
|
|
7 | 9 | }>(); |
|
|
8 | 10 | |
|
|
9 | 11 | // export service descriptor |
|
|
10 | 12 | export const service = config.build<Box<Bar>>(); |
|
|
11 | 13 | |
|
|
12 |
@service.consume(config. |
|
|
|
14 | @service.consume(config.get("bar")) | |
|
|
13 | 15 | export class Box<T> { |
|
|
14 | 16 | private _value: T | undefined; |
|
|
15 | 17 | |
| @@ -17,9 +19,10 export class Box<T> { | |||
|
|
17 | 19 | this._value = value; |
|
|
18 | 20 | } |
|
|
19 | 21 | |
|
|
20 |
|
|
|
|
21 |
|
|
|
|
22 | @service.inject( config.get("bar")) | |
|
|
23 | setValue(value?: T) { | |
|
|
22 | 24 | this._value = value; |
|
|
25 | return value; | |
|
|
23 | 26 | } |
|
|
24 | 27 | |
|
|
25 | 28 | setObj(value: object) { |
| @@ -1,8 +1,7 | |||
|
|
1 | 1 | import { Foo } from "./Foo"; |
|
|
2 | 2 | import { Bar } from "./Bar"; |
|
|
3 | 3 | import { Box } from "./Box"; |
|
|
4 |
import { |
|
|
|
5 | import { Constructor } from "../interfaces"; | |
|
|
4 | import { Builder } from "../di/Annotations"; | |
|
|
6 | 5 | |
|
|
7 | 6 | interface Services { |
|
|
8 | 7 | foo: Foo; |
| @@ -15,24 +14,14 interface Services { | |||
|
|
15 | 14 | |
|
|
16 | 15 | } |
|
|
17 | 16 | |
|
|
18 | interface TypeDescriptor<T, C extends Constructor<T>> { | |
|
|
19 | $type: C; | |
|
|
17 | const services = { | |
|
|
18 | build: <T>() => { | |
|
|
19 | return new Builder<T, Services>(); | |
|
|
20 | } | |
|
|
21 | }; | |
|
|
20 | 22 | |
|
|
21 | params: Wrap<ConstructorParameters<C>>; | |
|
|
22 | } | |
|
|
23 | namespace services { | |
|
|
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>}; | |
|
|
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 | }; | |
|
|
27 | export = services; | |
General Comments 0
You need to be logged in to leave comments.
Login now
