@thi.ng/system 2.1.95 → 2.2.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-29T21:04:46Z
3
+ - **Last updated**: 2024-01-30T15:21:30Z
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
+ ## [2.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/system@2.2.0) (2024-01-30)
13
+
14
+ #### 🚀 Features
15
+
16
+ - update start/stop logic ([71f2dc5](https://github.com/thi-ng/umbrella/commit/71f2dc5))
17
+ - update System.start() to stop already started components if current
18
+ component startup failed
19
+ - update System.stop() to only return true if _all_ components did shutdown
20
+ - update docs
21
+ - add tests
22
+
12
23
  ### [2.1.80](https://github.com/thi-ng/umbrella/tree/@thi.ng/system@2.1.80) (2023-11-09)
13
24
 
14
25
  #### ♻️ Refactoring
package/README.md CHANGED
@@ -33,6 +33,9 @@ Inspired by Stuart Sierra's
33
33
  [component](https://github.com/stuartsierra/component) framework for
34
34
  Clojure/ClojureScript.
35
35
 
36
+ Uses a declarative approach to define system components with a simple [lifecycle
37
+ API](https://docs.thi.ng/umbrella/system/interfaces/ILifecycle.html).
38
+
36
39
  ## Status
37
40
 
38
41
  **STABLE** - used in production
@@ -59,7 +62,7 @@ For Node.js REPL:
59
62
  const system = await import("@thi.ng/system");
60
63
  ```
61
64
 
62
- Package sizes (brotli'd, pre-treeshake): ESM: 490 bytes
65
+ Package sizes (brotli'd, pre-treeshake): ESM: 539 bytes
63
66
 
64
67
  ## Dependencies
65
68
 
package/api.d.ts CHANGED
@@ -2,22 +2,26 @@ import type { Fn, Keys } from "@thi.ng/api";
2
2
  import type { ILogger } from "@thi.ng/logger";
3
3
  export interface ILifecycle {
4
4
  /**
5
- * Starts component. Defined as async method to simplify internal
6
- * use of `await` for starting any child/sub-components. Usually
7
- * called by {@link System.start} which synchronously starts all of
8
- * its components in dependency order.
5
+ * Starts component. Defined as async method to simplify internal use of
6
+ * `await` for starting any child/sub-components. Usually called by
7
+ * {@link System.start} which synchronously starts all of its components in
8
+ * dependency order.
9
9
  *
10
- * Returns false to indicate component startup failed and to cancel
11
- * initialization of dependent components. Alternatively, an error
12
- * can be thrown, but it's the user's responsibility to catch it.
10
+ * Returns false to indicate component startup failed and to cancel startup
11
+ * of any further components. Alternatively, an error can be thrown, but
12
+ * it's the user's responsibility to catch it.
13
+ *
14
+ * If a component's start method returns false, any already started
15
+ * components will be stopped (see {@link ILifecycle.stop}) in reverse
16
+ * order.
13
17
  */
14
18
  start?(): Promise<boolean>;
15
19
  /**
16
20
  * Similar to {@link ILifecycle.start} but for stopping components.
17
21
  *
18
- * Returns false to indicate component startup failed and log a
19
- * warning message to the console. Unlike with `start()`, returning
20
- * false will NOT stop decommision other components.
22
+ * Returns false to indicate component shutdown failed. Unlike with
23
+ * {@link ILifecycle.start}, returning false will **not** stop the shutdown
24
+ * of other components.
21
25
  */
22
26
  stop?(): Promise<boolean>;
23
27
  [id: string]: any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/system",
3
- "version": "2.1.95",
3
+ "version": "2.2.0",
4
4
  "description": "Minimal and explicit dependency-injection & lifecycle container for stateful app components",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -38,9 +38,9 @@
38
38
  "test": "bun test"
39
39
  },
40
40
  "dependencies": {
41
- "@thi.ng/api": "^8.9.20",
42
- "@thi.ng/dgraph": "^2.1.91",
43
- "@thi.ng/logger": "^2.1.6"
41
+ "@thi.ng/api": "^8.9.21",
42
+ "@thi.ng/dgraph": "^2.1.92",
43
+ "@thi.ng/logger": "^2.1.7"
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": "3fa30b2dc2de762f24fd64b881b68d4da33d4c38\n"
87
+ "gitHead": "ded7c5e87ebc7f6279c24f183c24ff348db8f304\n"
88
88
  }
package/system.d.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import type { Keys } from "@thi.ng/api";
2
2
  import { DGraph } from "@thi.ng/dgraph";
3
3
  import { type ILifecycle, type SystemMap, type SystemSpecs } from "./api.js";
4
+ /**
5
+ * Syntax sugar for {@link System} constructor.
6
+ *
7
+ * @param map
8
+ */
4
9
  export declare const defSystem: <T extends SystemMap<T>>(map: SystemSpecs<T>) => System<T>;
5
10
  export declare class System<T extends SystemMap<T>> implements ILifecycle {
6
11
  components: T;
@@ -8,12 +13,14 @@ export declare class System<T extends SystemMap<T>> implements ILifecycle {
8
13
  graph: DGraph<Keys<T>>;
9
14
  constructor(map: SystemSpecs<T>);
10
15
  /**
11
- * Initializes all system components in dependency order. If any
12
- * component's `start()` method returns false, system start up will
13
- * be stopped and this method returns false itself.
16
+ * Initializes all system components in dependency order. If any component's
17
+ * `start()` method returns false, system start up will be stopped, any
18
+ * already started components will be stopped (in reverse order) and this
19
+ * method returns false itself.
14
20
  *
15
- * Also any errors thrown during child component startup will not be
16
- * intercepted.
21
+ * @remarks
22
+ * Any errors thrown during child component startup (or shutdown) will
23
+ * **not** be intercepted.
17
24
  */
18
25
  start(): Promise<boolean>;
19
26
  /**
@@ -27,8 +34,17 @@ export declare class System<T extends SystemMap<T>> implements ILifecycle {
27
34
  */
28
35
  stop(): Promise<boolean>;
29
36
  /**
30
- * Syntax sugar for `stop() && start()` sequence.
37
+ * Syntax sugar for `stop() && start()` sequence. The restart phase will
38
+ * only be executed if **all** components could be stopped successfully.
31
39
  */
32
40
  reset(): Promise<boolean>;
41
+ /**
42
+ * Calls {@link ILifecycle.stop} on the `n` first items in given topo array
43
+ * (in reverse order).
44
+ *
45
+ * @param topo
46
+ * @param n
47
+ */
48
+ protected __stop(topo: Keys<T>[], n?: number): Promise<boolean>;
33
49
  }
34
50
  //# sourceMappingURL=system.d.ts.map
package/system.js CHANGED
@@ -20,18 +20,23 @@ class System {
20
20
  }
21
21
  }
22
22
  /**
23
- * Initializes all system components in dependency order. If any
24
- * component's `start()` method returns false, system start up will
25
- * be stopped and this method returns false itself.
23
+ * Initializes all system components in dependency order. If any component's
24
+ * `start()` method returns false, system start up will be stopped, any
25
+ * already started components will be stopped (in reverse order) and this
26
+ * method returns false itself.
26
27
  *
27
- * Also any errors thrown during child component startup will not be
28
- * intercepted.
28
+ * @remarks
29
+ * Any errors thrown during child component startup (or shutdown) will
30
+ * **not** be intercepted.
29
31
  */
30
32
  async start() {
31
- for (let id of this.topology) {
33
+ const topo = this.topology;
34
+ for (let i = 0; i < topo.length; i++) {
35
+ const id = topo[i];
32
36
  const comp = this.components[id];
33
37
  if (comp.start && !await comp.start()) {
34
38
  LOGGER.warn(`error starting component: ${String(id)}`);
39
+ await this.__stop(topo, i);
35
40
  return false;
36
41
  }
37
42
  }
@@ -47,21 +52,33 @@ class System {
47
52
  * intercepted.
48
53
  */
49
54
  async stop() {
50
- const topo = this.topology;
51
- for (let i = topo.length; i-- > 0; ) {
55
+ return this.__stop(this.topology);
56
+ }
57
+ /**
58
+ * Syntax sugar for `stop() && start()` sequence. The restart phase will
59
+ * only be executed if **all** components could be stopped successfully.
60
+ */
61
+ async reset() {
62
+ return await this.stop() && await this.start();
63
+ }
64
+ /**
65
+ * Calls {@link ILifecycle.stop} on the `n` first items in given topo array
66
+ * (in reverse order).
67
+ *
68
+ * @param topo
69
+ * @param n
70
+ */
71
+ async __stop(topo, n = topo.length) {
72
+ let result = true;
73
+ for (let i = n; i-- > 0; ) {
52
74
  const id = topo[i];
53
75
  const comp = this.components[id];
54
76
  if (comp.stop && !await comp.stop()) {
55
77
  LOGGER.warn(`error stopping component: ${String(id)}`);
78
+ result = false;
56
79
  }
57
80
  }
58
- return true;
59
- }
60
- /**
61
- * Syntax sugar for `stop() && start()` sequence.
62
- */
63
- async reset() {
64
- return await this.stop() && await this.start();
81
+ return result;
65
82
  }
66
83
  }
67
84
  export {