@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2024-01-30T21:37:19Z
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: 539 bytes
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.state),
156
- deps: ["logger", "state"],
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
- // Calls stop() & if successful followed by start()
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 construction,
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
- // "state"[label="state"];
218
+ // "cache"[label="cache"];
217
219
  // "dummy"[label="dummy"];
218
220
  // "db" -> "logger";
219
- // "db" -> "state";
220
- // "state" -> "logger";
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
- export interface ILifecycle {
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
- * Component initialization function.
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": "2.2.1",
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": "a86521dfac00768f7e51a607209325eb72004730\n"
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 map
7
+ * @param specs
8
8
  */
9
- export declare const defSystem: <T extends SystemMap<T>>(map: SystemSpecs<T>) => System<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(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,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
  }