@thi.ng/system 2.3.0 → 3.0.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2024-01-31T12:41:36Z
3
+ - **Last updated**: 2024-02-06T23:18:11Z
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,17 @@ 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
+
12
23
  ## [2.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/system@2.3.0) (2024-01-31)
13
24
 
14
25
  #### 🚀 Features
package/README.md CHANGED
@@ -19,6 +19,7 @@
19
19
  - [Status](#status)
20
20
  - [Installation](#installation)
21
21
  - [Dependencies](#dependencies)
22
+ - [Usage examples](#usage-examples)
22
23
  - [API](#api)
23
24
  - [Example system](#example-system)
24
25
  - [System visualization](#system-visualization)
@@ -62,7 +63,7 @@ For Node.js REPL:
62
63
  const system = await import("@thi.ng/system");
63
64
  ```
64
65
 
65
- Package sizes (brotli'd, pre-treeshake): ESM: 539 bytes
66
+ Package sizes (brotli'd, pre-treeshake): ESM: 570 bytes
66
67
 
67
68
  ## Dependencies
68
69
 
@@ -70,6 +71,16 @@ Package sizes (brotli'd, pre-treeshake): ESM: 539 bytes
70
71
  - [@thi.ng/dgraph](https://github.com/thi-ng/umbrella/tree/develop/packages/dgraph)
71
72
  - [@thi.ng/logger](https://github.com/thi-ng/umbrella/tree/develop/packages/logger)
72
73
 
74
+ ## Usage examples
75
+
76
+ One project in this repo's
77
+ [/examples](https://github.com/thi-ng/umbrella/tree/develop/examples)
78
+ directory is using this package:
79
+
80
+ | Screenshot | Description | Live demo | Source |
81
+ |:--------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------|:---------------------------------------------------------|:--------------------------------------------------------------------------------------|
82
+ | <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/rstream-system-bus.png" width="240"/> | Declarative component-based system with central rstream-based pubsub event bus | [Demo](https://demo.thi.ng/umbrella/rstream-system-bus/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/rstream-system-bus) |
83
+
73
84
  ## API
74
85
 
75
86
  [Generated API docs](https://docs.thi.ng/umbrella/system/)
@@ -78,7 +89,7 @@ TODO
78
89
 
79
90
  ### Example system
80
91
 
81
- ```ts
92
+ ```ts tangle:export/readme.ts
82
93
  import { defSystem, ILifecycle } from "@thi.ng/system";
83
94
 
84
95
  // Step 1: Define the structure / components of your system
@@ -152,16 +163,16 @@ class Cache implements ILifecycle {
152
163
 
153
164
  const FOO = defSystem<FooSys>({
154
165
  db: {
155
- factory: (deps) => new DB(deps.logger, deps.state),
156
- deps: ["logger", "state"],
166
+ factory: async (deps) => new DB(deps.logger, deps.cache),
167
+ deps: ["logger", "cache"],
157
168
  },
158
- logger: { factory: () => new Logger() },
169
+ logger: { factory: async () => new Logger() },
159
170
  cache: {
160
- factory: ({ logger }) => new Cache(logger),
171
+ factory: async ({ logger }) => new Cache(logger),
161
172
  deps: ["logger"],
162
173
  },
163
174
  dummy: {
164
- factory: ({ logger }) => ({
175
+ factory: async ({ logger }) => ({
165
176
  async start() {
166
177
  logger.info("start dummy");
167
178
  return true;
@@ -176,21 +187,21 @@ const FOO = defSystem<FooSys>({
176
187
  });
177
188
 
178
189
  // Step 4: Asynchronously start all components in dependency order
179
- FOO.start();
190
+ await FOO.start();
180
191
  // [info] start logger
181
192
  // [info] start cache
182
193
  // [info] start dummy
183
194
  // [info] start db
184
195
 
185
196
  // Step 5 (optional): Async shutdown all (in reverse order)
186
- FOO.stop();
197
+ await FOO.stop();
187
198
  // [info] stop db
188
199
  // [info] stop dummy
189
200
  // [info] stop cache
190
201
  // [info] stop logger
191
202
 
192
- // Calls stop() & if successful followed by start()
193
- FOO.reset();
203
+ // Alternatively, calls stop() & if successful followed by start()
204
+ await FOO.reset();
194
205
  ```
195
206
 
196
207
  ### System visualization
@@ -198,7 +209,9 @@ FOO.reset();
198
209
  For a `System` to initialize its components in the correct order, an internal
199
210
  [dependency
200
211
  graph](https://github.com/thi-ng/umbrella/tree/develop/packages/dgraph) is
201
- constructed. This graph is not required any further after system construction,
212
+ constructed. This graph is not required any further after system initialization
213
+ (see
214
+ [System.init()](https://docs.thi.ng/umbrella/system/classes/System.html#init)),
202
215
  though can be useful for debugging and documentation purposes.
203
216
 
204
217
  For example, we can utilize the
@@ -206,18 +219,18 @@ For example, we can utilize the
206
219
  package to generate a [Graphviz](https://graphviz.org) source file to visualize
207
220
  the dependencies between the system's components.
208
221
 
209
- ```ts
222
+ ```ts tangle:export/readme.ts
210
223
  import { toDot } from "@thi.ng/dgraph-dot";
211
224
 
212
225
  console.log(toDot(FOO.graph, { id: (node) => node }));
213
226
  // digraph g {
214
227
  // "db"[label="db"];
215
228
  // "logger"[label="logger"];
216
- // "state"[label="state"];
229
+ // "cache"[label="cache"];
217
230
  // "dummy"[label="dummy"];
218
231
  // "db" -> "logger";
219
- // "db" -> "state";
220
- // "state" -> "logger";
232
+ // "db" -> "cache";
233
+ // "cache" -> "logger";
221
234
  // "dummy" -> "logger";
222
235
  // }
223
236
  ```
package/api.d.ts CHANGED
@@ -33,11 +33,19 @@ export interface ILifecycle<T extends SystemMap<T> = any> {
33
33
  */
34
34
  export type SystemMap<T> = Record<Keys<T>, ILifecycle>;
35
35
  /**
36
- * Component initialization function.
36
+ * Async system component initialization function.
37
37
  */
38
- export type ComponentFactory<T extends SystemMap<T>> = Fn<T, ILifecycle>;
38
+ export type ComponentFactory<T extends SystemMap<T>> = Fn<T, Promise<ILifecycle>>;
39
39
  export interface SystemSpec<T extends SystemMap<T>> {
40
+ /**
41
+ * Async system component initialization function.
42
+ */
40
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
+ */
41
49
  deps?: Keys<T>[];
42
50
  }
43
51
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/system",
3
- "version": "2.3.0",
3
+ "version": "3.0.1",
4
4
  "description": "Minimal and explicit dependency-injection & lifecycle container for stateful app components",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -39,8 +39,8 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@thi.ng/api": "^8.9.22",
42
- "@thi.ng/dgraph": "^2.1.93",
43
- "@thi.ng/logger": "^2.1.8"
42
+ "@thi.ng/dgraph": "^2.1.94",
43
+ "@thi.ng/logger": "^2.1.9"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@microsoft/api-extractor": "^7.39.0",
@@ -84,5 +84,5 @@
84
84
  "thi.ng": {
85
85
  "year": 2020
86
86
  },
87
- "gitHead": "0edcb19135b9d56983e44541b949a057ee11b683\n"
87
+ "gitHead": "ce8202c237a367c4038d41919a8acf75e1122507\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 map
7
+ * @param specs
8
8
  */
9
- export declare const defSystem: <T extends SystemMap<T>>(map: SystemSpecs<T>) => System<T>;
9
+ export declare const defSystem: <T extends SystemMap<T>>(specs: SystemSpecs<T>) => System<T>;
10
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(map: SystemSpecs<T>);
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 = (map) => new System(map);
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
- constructor(map) {
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 map) {
13
- const deps = map[id].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] = map[id].factory(this.components);
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,6 +39,8 @@ 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];