| @@ -1,154 +1,154 | |||||
| 1 | import { ActivationContext } from "./ActivationContext"; |
|
1 | import { ActivationContext } from "./ActivationContext"; | |
| 2 | import { Descriptor, ServiceMap, PartialServiceMap, ILifetime } from "./interfaces"; |
|
2 | import { Descriptor, ServiceMap, PartialServiceMap, ILifetime } from "./interfaces"; | |
| 3 | import { isPrimitive, keys, isNull } from "../safe"; |
|
3 | import { isPrimitive, keys, isNull } from "../safe"; | |
| 4 | import { TraceSource } from "../log/TraceSource"; |
|
4 | import { TraceSource } from "../log/TraceSource"; | |
| 5 | import { isDescriptor } from "./traits"; |
|
5 | import { isDescriptor } from "./traits"; | |
| 6 | import { LifetimeManager } from "./LifetimeManager"; |
|
6 | import { LifetimeManager } from "./LifetimeManager"; | |
| 7 | import { MatchingMemberKeys } from "../interfaces"; |
|
7 | import { MatchingMemberKeys } from "../interfaces"; | |
| 8 |
|
8 | |||
| 9 | const trace = TraceSource.get("@implab/core/di/ActivationContext"); |
|
9 | const trace = TraceSource.get("@implab/core/di/ActivationContext"); | |
| 10 |
|
10 | |||
| 11 | function injectMethod<T, M extends keyof T, S extends object, A>(target: T, method: M, context: ActivationContext<S>, args: A) { |
|
11 | function injectMethod<T, M extends keyof T, S extends object, A>(target: T, method: M, context: ActivationContext<S>, args: A) { | |
| 12 |
|
12 | |||
| 13 | const m = target[method]; |
|
13 | const m = target[method]; | |
| 14 | if (!m || typeof m !== "function") |
|
14 | if (!m || typeof m !== "function") | |
| 15 | throw new Error("Method '" + method + "' not found"); |
|
15 | throw new Error("Method '" + method + "' not found"); | |
| 16 |
|
16 | |||
| 17 | if (args instanceof Array) |
|
17 | if (args instanceof Array) | |
| 18 | return m.apply(target, _parse(args, context, "." + method)); |
|
18 | return m.apply(target, _parse(args, context, "." + method)); | |
| 19 | else |
|
19 | else | |
| 20 | return m.call(target, _parse(args, context, "." + method)); |
|
20 | return m.call(target, _parse(args, context, "." + method)); | |
| 21 | } |
|
21 | } | |
| 22 |
|
22 | |||
| 23 | function makeCleanupCallback<T>(method: Cleaner<T>) { |
|
23 | function makeCleanupCallback<T>(method: Cleaner<T>) { | |
| 24 | if (typeof (method) === "function") { |
|
24 | if (typeof (method) === "function") { | |
| 25 | return (target: T) => { |
|
25 | return (target: T) => { | |
| 26 | method(target); |
|
26 | method(target); | |
| 27 | }; |
|
27 | }; | |
| 28 | } else { |
|
28 | } else { | |
| 29 | return (target: T) => { |
|
29 | return (target: T) => { | |
| 30 | const m = target[method] as any; |
|
30 | const m = target[method] as any; | |
| 31 | m.apply(target); |
|
31 | m.apply(target); | |
| 32 | }; |
|
32 | }; | |
| 33 | } |
|
33 | } | |
| 34 | } |
|
34 | } | |
| 35 |
|
35 | |||
| 36 | function _parse(value: any, context: ActivationContext<any>, path: string): any { |
|
36 | function _parse(value: any, context: ActivationContext<any>, path: string): any { | |
| 37 | if (isPrimitive(value)) |
|
37 | if (isPrimitive(value)) | |
| 38 | return value as any; |
|
38 | return value as any; | |
| 39 |
|
39 | |||
| 40 | trace.debug("parse {0}", path); |
|
40 | trace.debug("parse {0}", path); | |
| 41 |
|
41 | |||
| 42 | if (isDescriptor(value)) |
|
42 | if (isDescriptor(value)) | |
| 43 | return context.activate(value, path); |
|
43 | return context.activate(value, path); | |
| 44 |
|
44 | |||
| 45 | if (value instanceof Array) |
|
45 | if (value instanceof Array) | |
| 46 | return value.map((x, i) => _parse(x, context, `${path}[${i}]`)) as any; |
|
46 | return value.map((x, i) => _parse(x, context, `${path}[${i}]`)) as any; | |
| 47 |
|
47 | |||
| 48 | const t: any = {}; |
|
48 | const t: any = {}; | |
| 49 |
|
49 | |||
| 50 | keys(value).forEach(p => t[p] = _parse(value[p], context, `${path}.${p}`)); |
|
50 | keys(value).forEach(p => t[p] = _parse(value[p], context, `${path}.${p}`)); | |
| 51 |
|
51 | |||
| 52 | return t; |
|
52 | return t; | |
| 53 | } |
|
53 | } | |
| 54 |
|
54 | |||
| 55 |
export type Cleaner<T> = ((x: T) => void) | MatchingMemberKeys<() => void |
|
55 | export type Cleaner<T> = ((x: T) => void) | MatchingMemberKeys<T, () => void>; | |
| 56 |
|
56 | |||
| 57 | export type InjectionSpec<T> = { |
|
57 | export type InjectionSpec<T> = { | |
| 58 | [m in keyof T]?: any; |
|
58 | [m in keyof T]?: any; | |
| 59 | }; |
|
59 | }; | |
| 60 |
|
60 | |||
| 61 | export interface ServiceDescriptorParams<S extends object, T, P extends any[]> { |
|
61 | export interface ServiceDescriptorParams<S extends object, T, P extends any[]> { | |
| 62 | lifetime?: ILifetime; |
|
62 | lifetime?: ILifetime; | |
| 63 |
|
63 | |||
| 64 | params?: P; |
|
64 | params?: P; | |
| 65 |
|
65 | |||
| 66 | inject?: InjectionSpec<T>[]; |
|
66 | inject?: InjectionSpec<T>[]; | |
| 67 |
|
67 | |||
| 68 | services?: PartialServiceMap<S>; |
|
68 | services?: PartialServiceMap<S>; | |
| 69 |
|
69 | |||
| 70 | cleanup?: Cleaner<T>; |
|
70 | cleanup?: Cleaner<T>; | |
| 71 | } |
|
71 | } | |
| 72 |
|
72 | |||
| 73 | export class ServiceDescriptor<S extends object, T, P extends any[]> implements Descriptor<S, T> { |
|
73 | export class ServiceDescriptor<S extends object, T, P extends any[]> implements Descriptor<S, T> { | |
| 74 | _services: ServiceMap<S>; |
|
74 | _services: ServiceMap<S>; | |
| 75 |
|
75 | |||
| 76 | _params: P | undefined; |
|
76 | _params: P | undefined; | |
| 77 |
|
77 | |||
| 78 | _inject: InjectionSpec<T>[]; |
|
78 | _inject: InjectionSpec<T>[]; | |
| 79 |
|
79 | |||
| 80 | _cleanup: ((item: T) => void) | undefined; |
|
80 | _cleanup: ((item: T) => void) | undefined; | |
| 81 |
|
81 | |||
| 82 | _lifetime = LifetimeManager.empty(); |
|
82 | _lifetime = LifetimeManager.empty(); | |
| 83 |
|
83 | |||
| 84 | _objectLifetime: ILifetime | undefined; |
|
84 | _objectLifetime: ILifetime | undefined; | |
| 85 |
|
85 | |||
| 86 | constructor(opts: ServiceDescriptorParams<S, T, P>) { |
|
86 | constructor(opts: ServiceDescriptorParams<S, T, P>) { | |
| 87 |
|
87 | |||
| 88 | if (opts.lifetime) |
|
88 | if (opts.lifetime) | |
| 89 | this._lifetime = opts.lifetime; |
|
89 | this._lifetime = opts.lifetime; | |
| 90 |
|
90 | |||
| 91 | if (!isNull(opts.params)) |
|
91 | if (!isNull(opts.params)) | |
| 92 | this._params = opts.params; |
|
92 | this._params = opts.params; | |
| 93 |
|
93 | |||
| 94 | this._inject = opts.inject || []; |
|
94 | this._inject = opts.inject || []; | |
| 95 |
|
95 | |||
| 96 | this._services = (opts.services || {}) as ServiceMap<S>; |
|
96 | this._services = (opts.services || {}) as ServiceMap<S>; | |
| 97 |
|
97 | |||
| 98 | if (opts.cleanup) { |
|
98 | if (opts.cleanup) { | |
| 99 | if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function)) |
|
99 | if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function)) | |
| 100 | throw new Error( |
|
100 | throw new Error( | |
| 101 | "The cleanup parameter must be either a function or a function name"); |
|
101 | "The cleanup parameter must be either a function or a function name"); | |
| 102 |
|
102 | |||
| 103 | this._cleanup = makeCleanupCallback(opts.cleanup); |
|
103 | this._cleanup = makeCleanupCallback(opts.cleanup); | |
| 104 | } |
|
104 | } | |
| 105 | } |
|
105 | } | |
| 106 |
|
106 | |||
| 107 | activate(context: ActivationContext<S>) { |
|
107 | activate(context: ActivationContext<S>) { | |
| 108 | const lifetime = this._lifetime; |
|
108 | const lifetime = this._lifetime; | |
| 109 |
|
109 | |||
| 110 | if (lifetime.has()) { |
|
110 | if (lifetime.has()) { | |
| 111 | return lifetime.get(); |
|
111 | return lifetime.get(); | |
| 112 | } else { |
|
112 | } else { | |
| 113 | lifetime.initialize(context); |
|
113 | lifetime.initialize(context); | |
| 114 | const instance = this._create(context); |
|
114 | const instance = this._create(context); | |
| 115 | lifetime.store(instance, this._cleanup); |
|
115 | lifetime.store(instance, this._cleanup); | |
| 116 | return instance; |
|
116 | return instance; | |
| 117 | } |
|
117 | } | |
| 118 | } |
|
118 | } | |
| 119 |
|
119 | |||
| 120 | _factory(...params: any[]): T { |
|
120 | _factory(...params: any[]): T { | |
| 121 | throw Error("Not implemented"); |
|
121 | throw Error("Not implemented"); | |
| 122 | } |
|
122 | } | |
| 123 |
|
123 | |||
| 124 | _create(context: ActivationContext<S>) { |
|
124 | _create(context: ActivationContext<S>) { | |
| 125 | trace.debug(`constructing ${context._name}`); |
|
125 | trace.debug(`constructing ${context._name}`); | |
| 126 |
|
126 | |||
| 127 | if (this._services) { |
|
127 | if (this._services) { | |
| 128 | keys(this._services).forEach(p => context.register(p, this._services[p])); |
|
128 | keys(this._services).forEach(p => context.register(p, this._services[p])); | |
| 129 | } |
|
129 | } | |
| 130 |
|
130 | |||
| 131 | let instance: T; |
|
131 | let instance: T; | |
| 132 |
|
132 | |||
| 133 | if (this._params === undefined) { |
|
133 | if (this._params === undefined) { | |
| 134 | instance = this._factory(); |
|
134 | instance = this._factory(); | |
| 135 | } else if (this._params instanceof Array) { |
|
135 | } else if (this._params instanceof Array) { | |
| 136 | instance = this._factory.apply(this, _parse(this._params, context, "args")); |
|
136 | instance = this._factory.apply(this, _parse(this._params, context, "args")); | |
| 137 | } else { |
|
137 | } else { | |
| 138 | instance = this._factory(_parse(this._params, context, "args")); |
|
138 | instance = this._factory(_parse(this._params, context, "args")); | |
| 139 | } |
|
139 | } | |
| 140 |
|
140 | |||
| 141 | if (this._inject) { |
|
141 | if (this._inject) { | |
| 142 | this._inject.forEach(spec => { |
|
142 | this._inject.forEach(spec => { | |
| 143 | for (const m in spec) |
|
143 | for (const m in spec) | |
| 144 | injectMethod(instance, m, context, spec[m]); |
|
144 | injectMethod(instance, m, context, spec[m]); | |
| 145 | }); |
|
145 | }); | |
| 146 | } |
|
146 | } | |
| 147 | return instance; |
|
147 | return instance; | |
| 148 | } |
|
148 | } | |
| 149 |
|
149 | |||
| 150 | clone() { |
|
150 | clone() { | |
| 151 | return Object.create(this); |
|
151 | return Object.create(this); | |
| 152 | } |
|
152 | } | |
| 153 |
|
153 | |||
| 154 | } |
|
154 | } | |
| @@ -1,126 +1,126 | |||||
| 1 | export interface Constructor<T = {}> { |
|
1 | export interface Constructor<T = {}> { | |
| 2 | new(...args: any[]): T; |
|
2 | new(...args: any[]): T; | |
| 3 | prototype: T; |
|
3 | prototype: T; | |
| 4 | } |
|
4 | } | |
| 5 |
|
5 | |||
| 6 | export type PromiseOrValue<T> = T | PromiseLike<T>; |
|
6 | export type PromiseOrValue<T> = T | PromiseLike<T>; | |
| 7 |
|
7 | |||
| 8 | export type Factory<T = {}> = (...args: any[]) => T; |
|
8 | export type Factory<T = {}> = (...args: any[]) => T; | |
| 9 |
|
9 | |||
| 10 | export type Predicate<T = any> = (x: T) => boolean; |
|
10 | export type Predicate<T = any> = (x: T) => boolean; | |
| 11 |
|
11 | |||
| 12 |
export type MatchingMemberKeys<T, |
|
12 | export type MatchingMemberKeys<T, U> = { [K in keyof T]: T[K] extends U ? K : never}[keyof T]; | |
| 13 |
|
13 | |||
| 14 |
export type NotMatchingMemberKeys<T, |
|
14 | export type NotMatchingMemberKeys<T, U> = { [K in keyof T]: T[K] extends U ? never : K}[keyof T]; | |
| 15 |
|
15 | |||
| 16 |
export type ExtractMembers<T, |
|
16 | export type ExtractMembers<T, U> = Pick<T, MatchingMemberKeys<T, U>>; | |
| 17 |
|
17 | |||
| 18 |
export type ExcludeMembers<T, |
|
18 | export type ExcludeMembers<T, U> = Pick<T, NotMatchingMemberKeys<T, U>>; | |
| 19 |
|
19 | |||
| 20 | export interface MapOf<T> { |
|
20 | export interface MapOf<T> { | |
| 21 | [key: string]: T; |
|
21 | [key: string]: T; | |
| 22 | } |
|
22 | } | |
| 23 |
|
23 | |||
| 24 | export interface IDestroyable { |
|
24 | export interface IDestroyable { | |
| 25 | destroy(): void; |
|
25 | destroy(): void; | |
| 26 | } |
|
26 | } | |
| 27 |
|
27 | |||
| 28 | export interface IRemovable { |
|
28 | export interface IRemovable { | |
| 29 | remove(): void; |
|
29 | remove(): void; | |
| 30 | } |
|
30 | } | |
| 31 |
|
31 | |||
| 32 | export interface ICancellation { |
|
32 | export interface ICancellation { | |
| 33 | throwIfRequested(): void; |
|
33 | throwIfRequested(): void; | |
| 34 | isRequested(): boolean; |
|
34 | isRequested(): boolean; | |
| 35 | isSupported(): boolean; |
|
35 | isSupported(): boolean; | |
| 36 | register(cb: (e: any) => void): IDestroyable; |
|
36 | register(cb: (e: any) => void): IDestroyable; | |
| 37 | } |
|
37 | } | |
| 38 |
|
38 | |||
| 39 | /** |
|
39 | /** | |
| 40 | * ΠΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°ΡΡΠΈΠΉ Π°ΡΠΈΠ½Ρ ΡΠΎΠ½Π½ΡΡ Π°ΠΊΡΠΈΠ²Π°ΡΠΈΡ |
|
40 | * ΠΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°ΡΡΠΈΠΉ Π°ΡΠΈΠ½Ρ ΡΠΎΠ½Π½ΡΡ Π°ΠΊΡΠΈΠ²Π°ΡΠΈΡ | |
| 41 | */ |
|
41 | */ | |
| 42 | export interface IActivatable { |
|
42 | export interface IActivatable { | |
| 43 | /** |
|
43 | /** | |
| 44 | * @returns Boolean indicates the current state |
|
44 | * @returns Boolean indicates the current state | |
| 45 | */ |
|
45 | */ | |
| 46 | isActive(): boolean; |
|
46 | isActive(): boolean; | |
| 47 |
|
47 | |||
| 48 | /** |
|
48 | /** | |
| 49 | * Starts the component activation |
|
49 | * Starts the component activation | |
| 50 | * @param ct cancellation token for this operation |
|
50 | * @param ct cancellation token for this operation | |
| 51 | */ |
|
51 | */ | |
| 52 | activate(ct?: ICancellation): Promise<void>; |
|
52 | activate(ct?: ICancellation): Promise<void>; | |
| 53 |
|
53 | |||
| 54 | /** |
|
54 | /** | |
| 55 | * Starts the component deactivation |
|
55 | * Starts the component deactivation | |
| 56 | * @param ct cancellation token for this operation |
|
56 | * @param ct cancellation token for this operation | |
| 57 | */ |
|
57 | */ | |
| 58 | deactivate(ct?: ICancellation): Promise<void>; |
|
58 | deactivate(ct?: ICancellation): Promise<void>; | |
| 59 |
|
59 | |||
| 60 | /** |
|
60 | /** | |
| 61 | * Sets the activation controller for this component |
|
61 | * Sets the activation controller for this component | |
| 62 | * @param controller The activation controller |
|
62 | * @param controller The activation controller | |
| 63 | * |
|
63 | * | |
| 64 | * Activation controller checks whether this component |
|
64 | * Activation controller checks whether this component | |
| 65 | * can be activated and manages the active state of the |
|
65 | * can be activated and manages the active state of the | |
| 66 | * component |
|
66 | * component | |
| 67 | */ |
|
67 | */ | |
| 68 | setActivationController(controller: IActivationController): void; |
|
68 | setActivationController(controller: IActivationController): void; | |
| 69 |
|
69 | |||
| 70 | /** Indicates whether this component has an activation controller */ |
|
70 | /** Indicates whether this component has an activation controller */ | |
| 71 | hasActivationController(): boolean; |
|
71 | hasActivationController(): boolean; | |
| 72 |
|
72 | |||
| 73 | /** |
|
73 | /** | |
| 74 | * Gets the current activation controller for this component |
|
74 | * Gets the current activation controller for this component | |
| 75 | */ |
|
75 | */ | |
| 76 | getActivationController(): IActivationController; |
|
76 | getActivationController(): IActivationController; | |
| 77 | } |
|
77 | } | |
| 78 |
|
78 | |||
| 79 | export interface IActivationController { |
|
79 | export interface IActivationController { | |
| 80 | activating(component: IActivatable, ct?: ICancellation): Promise<void>; |
|
80 | activating(component: IActivatable, ct?: ICancellation): Promise<void>; | |
| 81 |
|
81 | |||
| 82 | activated(component: IActivatable, ct?: ICancellation): Promise<void>; |
|
82 | activated(component: IActivatable, ct?: ICancellation): Promise<void>; | |
| 83 |
|
83 | |||
| 84 | deactivating(component: IActivatable, ct?: ICancellation): Promise<void>; |
|
84 | deactivating(component: IActivatable, ct?: ICancellation): Promise<void>; | |
| 85 |
|
85 | |||
| 86 | deactivated(component: IActivatable, ct?: ICancellation): Promise<void>; |
|
86 | deactivated(component: IActivatable, ct?: ICancellation): Promise<void>; | |
| 87 |
|
87 | |||
| 88 | deactivate(ct?: ICancellation): Promise<void>; |
|
88 | deactivate(ct?: ICancellation): Promise<void>; | |
| 89 |
|
89 | |||
| 90 | activate(component: IActivatable, ct?: ICancellation): Promise<void>; |
|
90 | activate(component: IActivatable, ct?: ICancellation): Promise<void>; | |
| 91 |
|
91 | |||
| 92 | hasActive(): boolean; |
|
92 | hasActive(): boolean; | |
| 93 |
|
93 | |||
| 94 | getActive(): IActivatable; |
|
94 | getActive(): IActivatable; | |
| 95 | } |
|
95 | } | |
| 96 |
|
96 | |||
| 97 | export interface IAsyncComponent { |
|
97 | export interface IAsyncComponent { | |
| 98 | getCompletion(): Promise<void>; |
|
98 | getCompletion(): Promise<void>; | |
| 99 | } |
|
99 | } | |
| 100 |
|
100 | |||
| 101 | export interface ICancellable { |
|
101 | export interface ICancellable { | |
| 102 | cancel(reason?: any): void; |
|
102 | cancel(reason?: any): void; | |
| 103 | } |
|
103 | } | |
| 104 |
|
104 | |||
| 105 | export interface IObservable<T> { |
|
105 | export interface IObservable<T> { | |
| 106 | on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable; |
|
106 | on(next: (x: T) => void, error?: (e: any) => void, complete?: () => void): IDestroyable; | |
| 107 | next(ct?: ICancellation): Promise<T>; |
|
107 | next(ct?: ICancellation): Promise<T>; | |
| 108 | } |
|
108 | } | |
| 109 |
|
109 | |||
| 110 | export interface IObserver<T> { |
|
110 | export interface IObserver<T> { | |
| 111 | next(event: T): void; |
|
111 | next(event: T): void; | |
| 112 |
|
112 | |||
| 113 | error(e: any): void; |
|
113 | error(e: any): void; | |
| 114 |
|
114 | |||
| 115 | complete(): void; |
|
115 | complete(): void; | |
| 116 | } |
|
116 | } | |
| 117 |
|
117 | |||
| 118 | export interface TextWriter { |
|
118 | export interface TextWriter { | |
| 119 | write(obj: any): void; |
|
119 | write(obj: any): void; | |
| 120 | write(format: string, ...args: any[]): void; |
|
120 | write(format: string, ...args: any[]): void; | |
| 121 |
|
121 | |||
| 122 | writeLine(obj?: any): void; |
|
122 | writeLine(obj?: any): void; | |
| 123 | writeLine(format: string, ...args: any[]): void; |
|
123 | writeLine(format: string, ...args: any[]): void; | |
| 124 |
|
124 | |||
| 125 | writeValue(value: any, spec?: string): void; |
|
125 | writeValue(value: any, spec?: string): void; | |
| 126 | } |
|
126 | } | |
General Comments 0
You need to be logged in to leave comments.
Login now
