-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathindex.d.ts
195 lines (175 loc) · 7.13 KB
/
index.d.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
type RequiredKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];
type NameToDestination<TOption> = TOption extends {
component: infer Component;
destination?: infer Destination;
}
? unknown extends Destination
? Component
: Destination
: TOption extends string | number | symbol
? TOption
: never;
type MissingDependencies<TDependencies extends Record<string, unknown>, TNames extends unknown[]> = TNames extends [
infer Name,
...infer Rest
]
? NameToDestination<Name> extends keyof TDependencies
? MissingDependencies<Omit<TDependencies, NameToDestination<Name>>, Rest>
: MissingDependencies<TDependencies, Rest>
: TDependencies;
/**
* Systemic component that can be added to the systemic system.
* @template TComponent The type of the component that will be exposed by the systemic system
* @template TDependencies The type of the dependencies this component depends on
*/
export type Component<TComponent, TDependencies extends Record<string, unknown> = {}> = {
/**
* Starts this component
* @param {TDependencies} dependencies The dependencies of this component
* @returns A started component
*/
start: (dependencies: TDependencies) => Promise<TComponent>;
/**
* Stops this component
*/
stop?: () => Promise<void>;
};
/**
* Systemic component that can be added to the systemic system.
* @template TComponent The type of the component that will be exposed by the systemic system
* @template TDependencies The type of the dependencies this component depends on
*/
export type CallbackComponent<TComponent, TDependencies extends Record<string, unknown> = {}> = {
/**
* Starts this component
* @param {TDependencies} dependencies The dependencies of this component
* @param callback Callback receives the component after it has been built
*/
start: (dependencies: TDependencies, callback: (err: any, component: TComponent) => void) => void;
/**
* Stops this component
* @param callback Callback is called when the component has been stopped
*/
stop?: (callback: (err?: any) => void) => void;
};
type SimpleDependsOnOption<TSystemic> = keyof TSystemic;
type MappingDependsOnOption<TDependencyKeys, TSystemic> = TDependencyKeys extends keyof TSystemic
? {
component: keyof TSystemic;
destination?: TDependencyKeys;
optional?: boolean;
source?: string;
}
: {
component: keyof TSystemic;
destination: TDependencyKeys;
optional?: boolean;
source?: string;
};
type DependsOnOption<TDependencyKeys, TSystemic> =
| SimpleDependsOnOption<TSystemic>
| MappingDependsOnOption<TDependencyKeys, TSystemic>;
type DependsOn<TSystemic extends Record<string, unknown>, TDependencies extends Record<string, unknown>> = {
/**
* Specifies which other components the last added components depends on.
* When name and type of the dependencies match those available in the system, the dependency can be added by name.
* When a dependency is named differently in the system or only part of a component is required as a dependency, a MappingDependsOnOption can be used.
*/
dependsOn: <TNames extends DependsOnOption<keyof TDependencies, TSystemic>[]>(
...names: TNames
) => SystemicBuild<TSystemic, MissingDependencies<TDependencies, TNames>>;
};
type SystemicBuild<TSystemic extends Record<string, unknown>, TDependencies extends Record<string, unknown>> = [
RequiredKeys<TDependencies>
] extends [never]
? Systemic<TSystemic> & DependsOn<TSystemic, TDependencies>
: DependsOn<TSystemic, TDependencies>;
/**
* Systemic system.
*/
export type Systemic<T extends Record<string, unknown>> = {
/**
* The name of the system
*/
name: string;
/**
* Adds a component to the system
* @param {string} name the name under which the component will be registered in the system
* @param {Component} component the component to be added
* @param options registration options
*/
add: <S extends string, TComponent, TDependencies extends Record<string, unknown> = {}>(
name: S extends keyof T ? never : S, // We don't allow duplicate names
component?: Component<TComponent, TDependencies> | CallbackComponent<TComponent, TDependencies> | TComponent,
options?: { scoped?: boolean }
) => SystemicBuild<
{
[G in keyof T | S]: G extends keyof T ? T[G] : TComponent;
},
TDependencies
>;
/**
* Attempting to add the same component twice will result in an error, but sometimes you need to replace existing components with test doubles. Under such circumstances use set instead of add.
* @param {string} name the name under which the component will be registered in the system
* @param {Component} component the component to be added
* @param options registration options
*/
set: <S extends string, TComponent, TDependencies extends Record<string, unknown> = {}>(
name: S,
component: Component<TComponent, TDependencies> | CallbackComponent<TComponent, TDependencies> | TComponent,
options?: { scoped?: boolean }
) => SystemicBuild<
{
[G in keyof T | S]: G extends keyof T ? T[G] : TComponent;
},
TDependencies
>;
/**
* Adds a configuration to the system, which will be available as a scoped dependency named 'config'
*/
configure: <TComponent, TDependencies extends Record<string, unknown> = {}>(
component: Component<TComponent, TDependencies> | CallbackComponent<TComponent, TDependencies> | TComponent
) => SystemicBuild<T & { config: TComponent }, TDependencies>;
/**
* Removes a component from the system.
* Removing components during tests can decrease startup time.
*/
remove: <S extends string>(name: S) => Systemic<Omit<T, S>>;
/**
* Includes a subsystem into this systemic system
*/
merge: <TSubSystem extends Record<string, unknown>>(subSystem: Systemic<TSubSystem>) => Systemic<T & TSubSystem>;
/**
* Includes a subsystem into this systemic system
*/
include: <TSubSystem extends Record<string, unknown>>(subSystem: Systemic<TSubSystem>) => Systemic<T & TSubSystem>;
/**
* Starts the system and all of its components
*/
start(callback: (error: Error | null, result?: T) => void): void;
start(): Promise<T>;
/**
* Stops the system and all of its components
*/
stop(callback: (error: Error | null) => void): void;
stop(): Promise<void>;
/**
* Restarts the system and all of its components
*/
restart(callback: (error: Error | null, result?: T) => void): void;
restart(): Promise<T>;
/**
* The dependency graph for a medium size project can grow quickly leading to a large system definition.
* To simplify this you can bootstrap components from a specified directory, where each folder in the directory includes an index.js which defines a sub system. e.g.
* See documentation for more details.
*/
bootstrap: <TSystem extends Record<string, unknown> = Record<string, unknown>>(path: string) => Systemic<TSystem>;
};
/**
* Creates a system to which components for dependency injection can be added
* @returns An empty systemic system
*/
declare function Systemic<TMaster extends Record<string, unknown> = {}>(options?: { name?: string }): Systemic<TMaster>;
export default Systemic;