@twin.org/api-service 0.0.3-next.35 → 0.0.3-next.37
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/dist/es/healthService.js +8 -1
- package/dist/es/healthService.js.map +1 -1
- package/dist/es/models/IHealthServiceConfig.js.map +1 -1
- package/dist/types/models/IHealthServiceConfig.d.ts +6 -0
- package/docs/changelog.md +28 -0
- package/docs/reference/interfaces/IHealthServiceConfig.md +15 -0
- package/package.json +2 -2
package/dist/es/healthService.js
CHANGED
|
@@ -19,6 +19,12 @@ export class HealthService {
|
|
|
19
19
|
* @internal
|
|
20
20
|
*/
|
|
21
21
|
_healthCheckInterval;
|
|
22
|
+
/**
|
|
23
|
+
* The initial interval for checking the health of the components and setting it in the health service.
|
|
24
|
+
* This is used to check the health of the components immediately after the service is started.
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
_initialInterval;
|
|
22
28
|
/**
|
|
23
29
|
* Interval for checking the health of the components and setting it in the health service.
|
|
24
30
|
* @internal
|
|
@@ -31,6 +37,7 @@ export class HealthService {
|
|
|
31
37
|
constructor(options) {
|
|
32
38
|
this._healthInfo = { status: HealthStatus.Ok, components: [] };
|
|
33
39
|
this._healthCheckInterval = options?.config?.healthCheckInterval ?? 60000;
|
|
40
|
+
this._initialInterval = options?.config?.initialInterval ?? 2000;
|
|
34
41
|
}
|
|
35
42
|
/**
|
|
36
43
|
* Returns the class name of the component.
|
|
@@ -50,7 +57,7 @@ export class HealthService {
|
|
|
50
57
|
// Immediately check health after a startup settling period
|
|
51
58
|
// the interval for the next checks are trigger on success of the current
|
|
52
59
|
// check to prevent overlapping checks in case of long running health checks
|
|
53
|
-
this._healthTimer = globalThis.setTimeout(async () => this.checkHealth(engineCore, nodeLoggingComponentType),
|
|
60
|
+
this._healthTimer = globalThis.setTimeout(async () => this.checkHealth(engineCore, nodeLoggingComponentType), this._initialInterval);
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"healthService.js","sourceRoot":"","sources":["../../src/healthService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IHealthComponent } from \"@twin.org/api-models\";\nimport { ContextIdStore } from \"@twin.org/context\";\nimport { BaseError, ComponentFactory, HealthStatus, type IHealth, Is } from \"@twin.org/core\";\nimport { EngineCoreFactory, type IEngineCore } from \"@twin.org/engine-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IHealthServiceConstructorOptions } from \"./models/IHealthServiceConstructorOptions.js\";\n\n/**\n * The health service for the server.\n */\nexport class HealthService implements IHealthComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<HealthService>();\n\n\t/**\n\t * The server health.\n\t * @internal\n\t */\n\tprivate _healthInfo: {\n\t\tstatus: HealthStatus;\n\t\tcomponents: IHealth[];\n\t};\n\n\t/**\n\t * The interval for checking the health of the components and setting it in the health service.\n\t * @internal\n\t */\n\tprivate readonly _healthCheckInterval: number;\n\n\t/**\n\t * Interval for checking the health of the components and setting it in the health service.\n\t * @internal\n\t */\n\tprivate _healthTimer: NodeJS.Timeout | undefined;\n\n\t/**\n\t * Create a new instance of HealthService.\n\t * @param options The constructor options.\n\t */\n\tconstructor(options?: IHealthServiceConstructorOptions) {\n\t\tthis._healthInfo = { status: HealthStatus.Ok, components: [] };\n\t\tthis._healthCheckInterval = options?.config?.healthCheckInterval ?? 60000;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn HealthService.CLASS_NAME;\n\t}\n\n\t/**\n\t * The component needs to be started when the node is initialized.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tconst engineCore = EngineCoreFactory.getIfExists(\"engine\");\n\n\t\tif (!Is.empty(engineCore) && Is.empty(this._healthTimer)) {\n\t\t\t// Immediately check health after a startup settling period\n\t\t\t// the interval for the next checks are trigger on success of the current\n\t\t\t// check to prevent overlapping checks in case of long running health checks\n\t\t\tthis._healthTimer = globalThis.setTimeout(\n\t\t\t\tasync () => this.checkHealth(engineCore, nodeLoggingComponentType),\n\t\t\t\t5000\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * The component needs to be stopped when the node is closed.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async stop(nodeLoggingComponentType?: string): Promise<void> {\n\t\tif (this._healthTimer) {\n\t\t\tclearTimeout(this._healthTimer);\n\t\t\tthis._healthTimer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Get the server health.\n\t * @returns The service health.\n\t */\n\tpublic async healthStatus(): Promise<{ status: HealthStatus; components: IHealth[] }> {\n\t\treturn this._healthInfo;\n\t}\n\n\t/**\n\t * Check the health of all registered components and set the health info in the service.\n\t * @param engineCore The engine core to get the registered components from.\n\t * @param nodeLoggingComponentType The node logging component type to log any errors that occur during health checks.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async checkHealth(\n\t\tengineCore: IEngineCore,\n\t\tnodeLoggingComponentType?: string\n\t): Promise<void> {\n\t\tawait ContextIdStore.run(engineCore.getContextIds() ?? {}, async () => {\n\t\t\tconst allHealth: IHealth[] = [];\n\n\t\t\tconst registeredInstances = await engineCore.getRegisteredComponents();\n\t\t\tfor (const registeredInstance of registeredInstances) {\n\t\t\t\tconst healthMethod = registeredInstance.component.health?.bind(\n\t\t\t\t\tregisteredInstance.component\n\t\t\t\t);\n\t\t\t\tif (Is.function(healthMethod)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tallHealth.push(...(await healthMethod()));\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst nodeLogging =\n\t\t\t\t\t\t\tComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\t\t\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\t\tsource: HealthService.CLASS_NAME,\n\t\t\t\t\t\t\tmessage: \"componentHealthCheckFailed\",\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\tclassName: registeredInstance.component.className()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\terror: BaseError.fromError(error)\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.groupHealthByName(allHealth);\n\t\t});\n\n\t\t// Queue the next health check.\n\t\tthis._healthTimer = globalThis.setTimeout(\n\t\t\tasync () => this.checkHealth(engineCore, nodeLoggingComponentType),\n\t\t\tthis._healthCheckInterval\n\t\t);\n\t}\n\n\t/**\n\t * Group raw health entries by name, collapsing duplicates into a parent with a grouped array.\n\t * @param entries The flat list of health entries from all components.\n\t * @returns The grouped list.\n\t * @internal\n\t */\n\tprivate groupHealthByName(entries: IHealth[]): void {\n\t\tconst bySource = new Map<string, IHealth[]>();\n\t\tfor (const entry of entries) {\n\t\t\tconst existing = bySource.get(entry.source) ?? [];\n\t\t\texisting.push(entry);\n\t\t\tbySource.set(entry.source, existing);\n\t\t}\n\n\t\tconst result: IHealth[] = [];\n\t\tfor (const [source, group] of bySource) {\n\t\t\tif (group.length > 1) {\n\t\t\t\tlet parentStatus: HealthStatus = HealthStatus.Ok;\n\n\t\t\t\tif (group.some(e => e.status === HealthStatus.Error)) {\n\t\t\t\t\tparentStatus = HealthStatus.Error;\n\t\t\t\t} else if (group.some(e => e.status === HealthStatus.Warning)) {\n\t\t\t\t\tparentStatus = HealthStatus.Warning;\n\t\t\t\t}\n\n\t\t\t\tresult.push({ source, status: parentStatus, grouped: group });\n\t\t\t} else {\n\t\t\t\tresult.push(group[0]);\n\t\t\t}\n\t\t}\n\n\t\tlet finalStatus: HealthStatus = HealthStatus.Ok;\n\t\tif (result.some(e => e.status === HealthStatus.Error)) {\n\t\t\tfinalStatus = HealthStatus.Error;\n\t\t} else if (result.some(e => e.status === HealthStatus.Warning)) {\n\t\t\tfinalStatus = HealthStatus.Warning;\n\t\t}\n\n\t\tthis._healthInfo = { status: finalStatus, components: result };\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"healthService.js","sourceRoot":"","sources":["../../src/healthService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IHealthComponent } from \"@twin.org/api-models\";\nimport { ContextIdStore } from \"@twin.org/context\";\nimport { BaseError, ComponentFactory, HealthStatus, type IHealth, Is } from \"@twin.org/core\";\nimport { EngineCoreFactory, type IEngineCore } from \"@twin.org/engine-models\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IHealthServiceConstructorOptions } from \"./models/IHealthServiceConstructorOptions.js\";\n\n/**\n * The health service for the server.\n */\nexport class HealthService implements IHealthComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<HealthService>();\n\n\t/**\n\t * The server health.\n\t * @internal\n\t */\n\tprivate _healthInfo: {\n\t\tstatus: HealthStatus;\n\t\tcomponents: IHealth[];\n\t};\n\n\t/**\n\t * The interval for checking the health of the components and setting it in the health service.\n\t * @internal\n\t */\n\tprivate readonly _healthCheckInterval: number;\n\n\t/**\n\t * The initial interval for checking the health of the components and setting it in the health service.\n\t * This is used to check the health of the components immediately after the service is started.\n\t * @internal\n\t */\n\tprivate readonly _initialInterval: number;\n\n\t/**\n\t * Interval for checking the health of the components and setting it in the health service.\n\t * @internal\n\t */\n\tprivate _healthTimer: NodeJS.Timeout | undefined;\n\n\t/**\n\t * Create a new instance of HealthService.\n\t * @param options The constructor options.\n\t */\n\tconstructor(options?: IHealthServiceConstructorOptions) {\n\t\tthis._healthInfo = { status: HealthStatus.Ok, components: [] };\n\t\tthis._healthCheckInterval = options?.config?.healthCheckInterval ?? 60000;\n\t\tthis._initialInterval = options?.config?.initialInterval ?? 2000;\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn HealthService.CLASS_NAME;\n\t}\n\n\t/**\n\t * The component needs to be started when the node is initialized.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tconst engineCore = EngineCoreFactory.getIfExists(\"engine\");\n\n\t\tif (!Is.empty(engineCore) && Is.empty(this._healthTimer)) {\n\t\t\t// Immediately check health after a startup settling period\n\t\t\t// the interval for the next checks are trigger on success of the current\n\t\t\t// check to prevent overlapping checks in case of long running health checks\n\t\t\tthis._healthTimer = globalThis.setTimeout(\n\t\t\t\tasync () => this.checkHealth(engineCore, nodeLoggingComponentType),\n\t\t\t\tthis._initialInterval\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * The component needs to be stopped when the node is closed.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns Nothing.\n\t */\n\tpublic async stop(nodeLoggingComponentType?: string): Promise<void> {\n\t\tif (this._healthTimer) {\n\t\t\tclearTimeout(this._healthTimer);\n\t\t\tthis._healthTimer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Get the server health.\n\t * @returns The service health.\n\t */\n\tpublic async healthStatus(): Promise<{ status: HealthStatus; components: IHealth[] }> {\n\t\treturn this._healthInfo;\n\t}\n\n\t/**\n\t * Check the health of all registered components and set the health info in the service.\n\t * @param engineCore The engine core to get the registered components from.\n\t * @param nodeLoggingComponentType The node logging component type to log any errors that occur during health checks.\n\t * @returns Nothing.\n\t * @internal\n\t */\n\tprivate async checkHealth(\n\t\tengineCore: IEngineCore,\n\t\tnodeLoggingComponentType?: string\n\t): Promise<void> {\n\t\tawait ContextIdStore.run(engineCore.getContextIds() ?? {}, async () => {\n\t\t\tconst allHealth: IHealth[] = [];\n\n\t\t\tconst registeredInstances = await engineCore.getRegisteredComponents();\n\t\t\tfor (const registeredInstance of registeredInstances) {\n\t\t\t\tconst healthMethod = registeredInstance.component.health?.bind(\n\t\t\t\t\tregisteredInstance.component\n\t\t\t\t);\n\t\t\t\tif (Is.function(healthMethod)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tallHealth.push(...(await healthMethod()));\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst nodeLogging =\n\t\t\t\t\t\t\tComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\t\t\t\t\t\tawait nodeLogging?.log({\n\t\t\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\t\t\tsource: HealthService.CLASS_NAME,\n\t\t\t\t\t\t\tmessage: \"componentHealthCheckFailed\",\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\tclassName: registeredInstance.component.className()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\terror: BaseError.fromError(error)\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.groupHealthByName(allHealth);\n\t\t});\n\n\t\t// Queue the next health check.\n\t\tthis._healthTimer = globalThis.setTimeout(\n\t\t\tasync () => this.checkHealth(engineCore, nodeLoggingComponentType),\n\t\t\tthis._healthCheckInterval\n\t\t);\n\t}\n\n\t/**\n\t * Group raw health entries by name, collapsing duplicates into a parent with a grouped array.\n\t * @param entries The flat list of health entries from all components.\n\t * @returns The grouped list.\n\t * @internal\n\t */\n\tprivate groupHealthByName(entries: IHealth[]): void {\n\t\tconst bySource = new Map<string, IHealth[]>();\n\t\tfor (const entry of entries) {\n\t\t\tconst existing = bySource.get(entry.source) ?? [];\n\t\t\texisting.push(entry);\n\t\t\tbySource.set(entry.source, existing);\n\t\t}\n\n\t\tconst result: IHealth[] = [];\n\t\tfor (const [source, group] of bySource) {\n\t\t\tif (group.length > 1) {\n\t\t\t\tlet parentStatus: HealthStatus = HealthStatus.Ok;\n\n\t\t\t\tif (group.some(e => e.status === HealthStatus.Error)) {\n\t\t\t\t\tparentStatus = HealthStatus.Error;\n\t\t\t\t} else if (group.some(e => e.status === HealthStatus.Warning)) {\n\t\t\t\t\tparentStatus = HealthStatus.Warning;\n\t\t\t\t}\n\n\t\t\t\tresult.push({ source, status: parentStatus, grouped: group });\n\t\t\t} else {\n\t\t\t\tresult.push(group[0]);\n\t\t\t}\n\t\t}\n\n\t\tlet finalStatus: HealthStatus = HealthStatus.Ok;\n\t\tif (result.some(e => e.status === HealthStatus.Error)) {\n\t\t\tfinalStatus = HealthStatus.Error;\n\t\t} else if (result.some(e => e.status === HealthStatus.Warning)) {\n\t\t\tfinalStatus = HealthStatus.Warning;\n\t\t}\n\n\t\tthis._healthInfo = { status: finalStatus, components: result };\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IHealthServiceConfig.js","sourceRoot":"","sources":["../../../src/models/IHealthServiceConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the health service.\n */\nexport interface IHealthServiceConfig {\n\t/**\n\t * The interval for checking the health of the components and setting it in the health service.\n\t * @default 60000\n\t */\n\thealthCheckInterval?: number;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"IHealthServiceConfig.js","sourceRoot":"","sources":["../../../src/models/IHealthServiceConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the health service.\n */\nexport interface IHealthServiceConfig {\n\t/**\n\t * The interval for checking the health of the components and setting it in the health service.\n\t * @default 60000\n\t */\n\thealthCheckInterval?: number;\n\n\t/**\n\t * The initial interval for checking the health of the components and setting it in the health service.\n\t * This is used to check the health of the components immediately after the service is started.\n\t * @default 2000\n\t */\n\tinitialInterval?: number;\n}\n"]}
|
|
@@ -7,4 +7,10 @@ export interface IHealthServiceConfig {
|
|
|
7
7
|
* @default 60000
|
|
8
8
|
*/
|
|
9
9
|
healthCheckInterval?: number;
|
|
10
|
+
/**
|
|
11
|
+
* The initial interval for checking the health of the components and setting it in the health service.
|
|
12
|
+
* This is used to check the health of the components immediately after the service is started.
|
|
13
|
+
* @default 2000
|
|
14
|
+
*/
|
|
15
|
+
initialInterval?: number;
|
|
10
16
|
}
|
package/docs/changelog.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.0.3-next.37](https://github.com/iotaledger/twin-api/compare/api-service-v0.0.3-next.36...api-service-v0.0.3-next.37) (2026-05-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add initial interval config for health service ([daf0412](https://github.com/iotaledger/twin-api/commit/daf0412cb24d856bd80f2a6eda02f7294677baae))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Dependencies
|
|
12
|
+
|
|
13
|
+
* The following workspace dependencies were updated
|
|
14
|
+
* dependencies
|
|
15
|
+
* @twin.org/api-models bumped from 0.0.3-next.36 to 0.0.3-next.37
|
|
16
|
+
|
|
17
|
+
## [0.0.3-next.36](https://github.com/iotaledger/twin-api/compare/api-service-v0.0.3-next.35...api-service-v0.0.3-next.36) (2026-05-22)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Miscellaneous Chores
|
|
21
|
+
|
|
22
|
+
* **api-service:** Synchronize repo versions
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Dependencies
|
|
26
|
+
|
|
27
|
+
* The following workspace dependencies were updated
|
|
28
|
+
* dependencies
|
|
29
|
+
* @twin.org/api-models bumped from 0.0.3-next.35 to 0.0.3-next.36
|
|
30
|
+
|
|
3
31
|
## [0.0.3-next.35](https://github.com/iotaledger/twin-api/compare/api-service-v0.0.3-next.34...api-service-v0.0.3-next.35) (2026-05-21)
|
|
4
32
|
|
|
5
33
|
|
|
@@ -15,3 +15,18 @@ The interval for checking the health of the components and setting it in the hea
|
|
|
15
15
|
```ts
|
|
16
16
|
60000
|
|
17
17
|
```
|
|
18
|
+
|
|
19
|
+
***
|
|
20
|
+
|
|
21
|
+
### initialInterval? {#initialinterval}
|
|
22
|
+
|
|
23
|
+
> `optional` **initialInterval?**: `number`
|
|
24
|
+
|
|
25
|
+
The initial interval for checking the health of the components and setting it in the health service.
|
|
26
|
+
This is used to check the health of the components immediately after the service is started.
|
|
27
|
+
|
|
28
|
+
#### Default
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
2000
|
|
32
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twin.org/api-service",
|
|
3
|
-
"version": "0.0.3-next.
|
|
3
|
+
"version": "0.0.3-next.37",
|
|
4
4
|
"description": "Information and hosting service implementations with generated REST route handlers.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"node": ">=20.0.0"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@twin.org/api-models": "0.0.3-next.
|
|
17
|
+
"@twin.org/api-models": "0.0.3-next.37",
|
|
18
18
|
"@twin.org/context": "next",
|
|
19
19
|
"@twin.org/core": "next",
|
|
20
20
|
"@twin.org/engine-models": "next",
|