@thi.ng/system 2.2.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -1
- package/README.md +18 -16
- package/api.d.ts +14 -5
- package/package.json +2 -2
- package/system.d.ts +11 -4
- package/system.js +18 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2024-
|
|
3
|
+
- **Last updated**: 2024-02-01T13:52:02Z
|
|
4
4
|
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file.
|
|
@@ -9,6 +9,24 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
|
|
|
9
9
|
**Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
|
|
10
10
|
and/or version bumps of transitive dependencies.
|
|
11
11
|
|
|
12
|
+
# [3.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/system@3.0.0) (2024-02-01)
|
|
13
|
+
|
|
14
|
+
#### 🛑 Breaking changes
|
|
15
|
+
|
|
16
|
+
- Async component factories ([998409d](https://github.com/thi-ng/umbrella/commit/998409d))
|
|
17
|
+
- BREAKING CHANGE: Component factory functions are async now
|
|
18
|
+
- add async System.init()
|
|
19
|
+
- update System ctor
|
|
20
|
+
- add/update docs
|
|
21
|
+
- update tests
|
|
22
|
+
|
|
23
|
+
## [2.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/system@2.3.0) (2024-01-31)
|
|
24
|
+
|
|
25
|
+
#### 🚀 Features
|
|
26
|
+
|
|
27
|
+
- update ILifecycle, add system arg ([89d341a](https://github.com/thi-ng/umbrella/commit/89d341a))
|
|
28
|
+
- add/update tests
|
|
29
|
+
|
|
12
30
|
## [2.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/system@2.2.0) (2024-01-30)
|
|
13
31
|
|
|
14
32
|
#### 🚀 Features
|
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ For Node.js REPL:
|
|
|
62
62
|
const system = await import("@thi.ng/system");
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
Package sizes (brotli'd, pre-treeshake): ESM:
|
|
65
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 570 bytes
|
|
66
66
|
|
|
67
67
|
## Dependencies
|
|
68
68
|
|
|
@@ -78,7 +78,7 @@ TODO
|
|
|
78
78
|
|
|
79
79
|
### Example system
|
|
80
80
|
|
|
81
|
-
```ts
|
|
81
|
+
```ts tangle:export/readme.ts
|
|
82
82
|
import { defSystem, ILifecycle } from "@thi.ng/system";
|
|
83
83
|
|
|
84
84
|
// Step 1: Define the structure / components of your system
|
|
@@ -152,16 +152,16 @@ class Cache implements ILifecycle {
|
|
|
152
152
|
|
|
153
153
|
const FOO = defSystem<FooSys>({
|
|
154
154
|
db: {
|
|
155
|
-
factory: (deps) => new DB(deps.logger, deps.
|
|
156
|
-
deps: ["logger", "
|
|
155
|
+
factory: async (deps) => new DB(deps.logger, deps.cache),
|
|
156
|
+
deps: ["logger", "cache"],
|
|
157
157
|
},
|
|
158
|
-
logger: { factory: () => new Logger() },
|
|
158
|
+
logger: { factory: async () => new Logger() },
|
|
159
159
|
cache: {
|
|
160
|
-
factory: ({ logger }) => new Cache(logger),
|
|
160
|
+
factory: async ({ logger }) => new Cache(logger),
|
|
161
161
|
deps: ["logger"],
|
|
162
162
|
},
|
|
163
163
|
dummy: {
|
|
164
|
-
factory: ({ logger }) => ({
|
|
164
|
+
factory: async ({ logger }) => ({
|
|
165
165
|
async start() {
|
|
166
166
|
logger.info("start dummy");
|
|
167
167
|
return true;
|
|
@@ -176,21 +176,21 @@ const FOO = defSystem<FooSys>({
|
|
|
176
176
|
});
|
|
177
177
|
|
|
178
178
|
// Step 4: Asynchronously start all components in dependency order
|
|
179
|
-
FOO.start();
|
|
179
|
+
await FOO.start();
|
|
180
180
|
// [info] start logger
|
|
181
181
|
// [info] start cache
|
|
182
182
|
// [info] start dummy
|
|
183
183
|
// [info] start db
|
|
184
184
|
|
|
185
185
|
// Step 5 (optional): Async shutdown all (in reverse order)
|
|
186
|
-
FOO.stop();
|
|
186
|
+
await FOO.stop();
|
|
187
187
|
// [info] stop db
|
|
188
188
|
// [info] stop dummy
|
|
189
189
|
// [info] stop cache
|
|
190
190
|
// [info] stop logger
|
|
191
191
|
|
|
192
|
-
//
|
|
193
|
-
FOO.reset();
|
|
192
|
+
// Alternatively, calls stop() & if successful followed by start()
|
|
193
|
+
await FOO.reset();
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
### System visualization
|
|
@@ -198,7 +198,9 @@ FOO.reset();
|
|
|
198
198
|
For a `System` to initialize its components in the correct order, an internal
|
|
199
199
|
[dependency
|
|
200
200
|
graph](https://github.com/thi-ng/umbrella/tree/develop/packages/dgraph) is
|
|
201
|
-
constructed. This graph is not required any further after system
|
|
201
|
+
constructed. This graph is not required any further after system initialization
|
|
202
|
+
(see
|
|
203
|
+
[System.init()](https://docs.thi.ng/umbrella/system/classes/System.html#init)),
|
|
202
204
|
though can be useful for debugging and documentation purposes.
|
|
203
205
|
|
|
204
206
|
For example, we can utilize the
|
|
@@ -206,18 +208,18 @@ For example, we can utilize the
|
|
|
206
208
|
package to generate a [Graphviz](https://graphviz.org) source file to visualize
|
|
207
209
|
the dependencies between the system's components.
|
|
208
210
|
|
|
209
|
-
```ts
|
|
211
|
+
```ts tangle:export/readme.ts
|
|
210
212
|
import { toDot } from "@thi.ng/dgraph-dot";
|
|
211
213
|
|
|
212
214
|
console.log(toDot(FOO.graph, { id: (node) => node }));
|
|
213
215
|
// digraph g {
|
|
214
216
|
// "db"[label="db"];
|
|
215
217
|
// "logger"[label="logger"];
|
|
216
|
-
// "
|
|
218
|
+
// "cache"[label="cache"];
|
|
217
219
|
// "dummy"[label="dummy"];
|
|
218
220
|
// "db" -> "logger";
|
|
219
|
-
// "db" -> "
|
|
220
|
-
// "
|
|
221
|
+
// "db" -> "cache";
|
|
222
|
+
// "cache" -> "logger";
|
|
221
223
|
// "dummy" -> "logger";
|
|
222
224
|
// }
|
|
223
225
|
```
|
package/api.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Fn, Keys } from "@thi.ng/api";
|
|
2
2
|
import type { ILogger } from "@thi.ng/logger";
|
|
3
|
-
|
|
3
|
+
import type { System } from "./system.js";
|
|
4
|
+
export interface ILifecycle<T extends SystemMap<T> = any> {
|
|
4
5
|
/**
|
|
5
6
|
* Starts component. Defined as async method to simplify internal use of
|
|
6
7
|
* `await` for starting any child/sub-components. Usually called by
|
|
@@ -15,7 +16,7 @@ export interface ILifecycle {
|
|
|
15
16
|
* components will be stopped (see {@link ILifecycle.stop}) in reverse
|
|
16
17
|
* order.
|
|
17
18
|
*/
|
|
18
|
-
start?(): Promise<boolean>;
|
|
19
|
+
start?(sys: System<T>): Promise<boolean>;
|
|
19
20
|
/**
|
|
20
21
|
* Similar to {@link ILifecycle.start} but for stopping components.
|
|
21
22
|
*
|
|
@@ -23,7 +24,7 @@ export interface ILifecycle {
|
|
|
23
24
|
* {@link ILifecycle.start}, returning false will **not** stop the shutdown
|
|
24
25
|
* of other components.
|
|
25
26
|
*/
|
|
26
|
-
stop?(): Promise<boolean>;
|
|
27
|
+
stop?(sys: System<T>): Promise<boolean>;
|
|
27
28
|
[id: string]: any;
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
@@ -32,11 +33,19 @@ export interface ILifecycle {
|
|
|
32
33
|
*/
|
|
33
34
|
export type SystemMap<T> = Record<Keys<T>, ILifecycle>;
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
+
* Async system component initialization function.
|
|
36
37
|
*/
|
|
37
|
-
export type ComponentFactory<T extends SystemMap<T>> = Fn<T, ILifecycle
|
|
38
|
+
export type ComponentFactory<T extends SystemMap<T>> = Fn<T, Promise<ILifecycle>>;
|
|
38
39
|
export interface SystemSpec<T extends SystemMap<T>> {
|
|
40
|
+
/**
|
|
41
|
+
* Async system component initialization function.
|
|
42
|
+
*/
|
|
39
43
|
factory: ComponentFactory<T>;
|
|
44
|
+
/**
|
|
45
|
+
* Component IDs which this component directly depends on. Used to construct
|
|
46
|
+
* the system's dependency graph and to initialize (and later start)
|
|
47
|
+
* components in the correct order.
|
|
48
|
+
*/
|
|
40
49
|
deps?: Keys<T>[];
|
|
41
50
|
}
|
|
42
51
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/system",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Minimal and explicit dependency-injection & lifecycle container for stateful app components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"thi.ng": {
|
|
85
85
|
"year": 2020
|
|
86
86
|
},
|
|
87
|
-
"gitHead": "
|
|
87
|
+
"gitHead": "2137e8a24ecea66997b53032fec96e73fa0d0e02\n"
|
|
88
88
|
}
|
package/system.d.ts
CHANGED
|
@@ -4,14 +4,21 @@ import { type ILifecycle, type SystemMap, type SystemSpecs } from "./api.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Syntax sugar for {@link System} constructor.
|
|
6
6
|
*
|
|
7
|
-
* @param
|
|
7
|
+
* @param specs
|
|
8
8
|
*/
|
|
9
|
-
export declare const defSystem: <T extends SystemMap<T>>(
|
|
10
|
-
export declare class System<T extends SystemMap<T>> implements ILifecycle {
|
|
9
|
+
export declare const defSystem: <T extends SystemMap<T>>(specs: SystemSpecs<T>) => System<T>;
|
|
10
|
+
export declare class System<T extends SystemMap<T>> implements ILifecycle<T> {
|
|
11
|
+
protected specs: SystemSpecs<T>;
|
|
11
12
|
components: T;
|
|
12
13
|
topology: Keys<T>[];
|
|
13
14
|
graph: DGraph<Keys<T>>;
|
|
14
|
-
constructor(
|
|
15
|
+
constructor(specs: SystemSpecs<T>);
|
|
16
|
+
/**
|
|
17
|
+
* Builds the system component dependency graph and initializes all
|
|
18
|
+
* components using their async {@link SystemSpec.factory} functions.
|
|
19
|
+
* Returns the initialized system.
|
|
20
|
+
*/
|
|
21
|
+
init(): Promise<this>;
|
|
15
22
|
/**
|
|
16
23
|
* Initializes all system components in dependency order. If any component's
|
|
17
24
|
* `start()` method returns false, system start up will be stopped, any
|
package/system.js
CHANGED
|
@@ -2,22 +2,31 @@ import { DGraph } from "@thi.ng/dgraph";
|
|
|
2
2
|
import {
|
|
3
3
|
LOGGER
|
|
4
4
|
} from "./api.js";
|
|
5
|
-
const defSystem = (
|
|
5
|
+
const defSystem = (specs) => new System(specs);
|
|
6
6
|
class System {
|
|
7
|
+
constructor(specs) {
|
|
8
|
+
this.specs = specs;
|
|
9
|
+
}
|
|
7
10
|
components;
|
|
8
11
|
topology;
|
|
9
12
|
graph;
|
|
10
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Builds the system component dependency graph and initializes all
|
|
15
|
+
* components using their async {@link SystemSpec.factory} functions.
|
|
16
|
+
* Returns the initialized system.
|
|
17
|
+
*/
|
|
18
|
+
async init() {
|
|
11
19
|
this.graph = new DGraph();
|
|
12
|
-
for (let id in
|
|
13
|
-
const deps =
|
|
20
|
+
for (let id in this.specs) {
|
|
21
|
+
const deps = this.specs[id].deps;
|
|
14
22
|
deps ? this.graph.addDependencies(id, deps) : this.graph.addNode(id);
|
|
15
23
|
}
|
|
16
24
|
this.topology = this.graph.sort();
|
|
17
25
|
this.components = {};
|
|
18
26
|
for (let id of this.topology) {
|
|
19
|
-
this.components[id] =
|
|
27
|
+
this.components[id] = await this.specs[id].factory(this.components);
|
|
20
28
|
}
|
|
29
|
+
return this;
|
|
21
30
|
}
|
|
22
31
|
/**
|
|
23
32
|
* Initializes all system components in dependency order. If any component's
|
|
@@ -30,11 +39,13 @@ class System {
|
|
|
30
39
|
* **not** be intercepted.
|
|
31
40
|
*/
|
|
32
41
|
async start() {
|
|
42
|
+
if (!this.graph)
|
|
43
|
+
await this.init();
|
|
33
44
|
const topo = this.topology;
|
|
34
45
|
for (let i = 0; i < topo.length; i++) {
|
|
35
46
|
const id = topo[i];
|
|
36
47
|
const comp = this.components[id];
|
|
37
|
-
if (comp.start && !await comp.start()) {
|
|
48
|
+
if (comp.start && !await comp.start(this)) {
|
|
38
49
|
LOGGER.warn(`error starting component: ${String(id)}`);
|
|
39
50
|
await this.__stop(topo, i);
|
|
40
51
|
return false;
|
|
@@ -73,7 +84,7 @@ class System {
|
|
|
73
84
|
for (let i = n; i-- > 0; ) {
|
|
74
85
|
const id = topo[i];
|
|
75
86
|
const comp = this.components[id];
|
|
76
|
-
if (comp.stop && !await comp.stop()) {
|
|
87
|
+
if (comp.stop && !await comp.stop(this)) {
|
|
77
88
|
LOGGER.warn(`error stopping component: ${String(id)}`);
|
|
78
89
|
result = false;
|
|
79
90
|
}
|