@twin.org/api-service 0.0.3-next.44 → 0.0.3-next.45

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.
@@ -49,7 +49,7 @@ export class HealthService {
49
49
  /**
50
50
  * The component needs to be started when the node is initialized.
51
51
  * @param nodeLoggingComponentType The node logging component type.
52
- * @returns Nothing.
52
+ * @returns A promise that resolves when the initial health check timer has been scheduled.
53
53
  */
54
54
  async start(nodeLoggingComponentType) {
55
55
  const engineCore = EngineCoreFactory.getIfExists("engine");
@@ -63,7 +63,7 @@ export class HealthService {
63
63
  /**
64
64
  * The component needs to be stopped when the node is closed.
65
65
  * @param nodeLoggingComponentType The node logging component type.
66
- * @returns Nothing.
66
+ * @returns A promise that resolves when the health check timer has been cancelled.
67
67
  */
68
68
  async stop(nodeLoggingComponentType) {
69
69
  if (this._healthTimer) {
@@ -82,7 +82,7 @@ export class HealthService {
82
82
  * Check the health of all registered components and set the health info in the service.
83
83
  * @param engineCore The engine core to get the registered components from.
84
84
  * @param nodeLoggingComponentType The node logging component type to log any errors that occur during health checks.
85
- * @returns Nothing.
85
+ * @returns A promise that resolves when all component health checks are complete and the next check is scheduled.
86
86
  * @internal
87
87
  */
88
88
  async checkHealth(engineCore, nodeLoggingComponentType) {
@@ -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 * 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 * @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 A promise that resolves when the initial health check timer has been scheduled.\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 A promise that resolves when the health check timer has been cancelled.\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 A promise that resolves when all component health checks are complete and the next check is scheduled.\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 * @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"]}
@@ -57,7 +57,7 @@ export class InformationService {
57
57
  }
58
58
  /**
59
59
  * The service needs to be started when the application is initialized.
60
- * @returns Nothing.
60
+ * @returns A promise that resolves when the OpenAPI spec and favicon have been loaded from disk.
61
61
  */
62
62
  async start() {
63
63
  const openApiPath = this._openApiSpecPath;
@@ -100,7 +100,7 @@ export class InformationService {
100
100
  }
101
101
  /**
102
102
  * Is the server live.
103
- * @returns True if the server is live.
103
+ * @returns The liveness status of the server.
104
104
  */
105
105
  async livez() {
106
106
  return { status: "alive" };
@@ -1 +1 @@
1
- {"version":3,"file":"informationService.js","sourceRoot":"","sources":["../../src/informationService.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAI5D;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAC9B;;OAEG;IACI,MAAM,CAAU,UAAU,wBAAwC;IAEzE;;;OAGG;IACc,WAAW,CAAc;IAE1C;;;OAGG;IACc,YAAY,CAAU;IAEvC;;;OAGG;IACK,QAAQ,CAAc;IAE9B;;;OAGG;IACc,gBAAgB,CAAU;IAE3C;;;OAGG;IACK,YAAY,CAAU;IAE9B;;;OAGG;IACH,YAAY,OAA8C;QACzD,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QACvE,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,UAAU,oBAA0B,OAAO,CAAC,MAAM,CAAC,CAAC;QACrF,MAAM,CAAC,MAAM,CACZ,kBAAkB,CAAC,UAAU,+BAE7B,OAAO,CAAC,MAAM,CAAC,UAAU,CACzB,CAAC;QAEF,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC;QAC/C,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC;IACxD,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,kBAAkB,CAAC,UAAU,CAAC;IACtC,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,KAAK;QACjB,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC;QAC1C,IAAI,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC;YACjC,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YAC1D,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC;QACtC,IAAI,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC;YACjC,IAAI,CAAC,QAAQ,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC7C,CAAC;IACF,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,IAAI;QAChB,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;IACjE,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,IAAI;QAChB,OAAO,IAAI,CAAC,WAAW,CAAC;IACzB,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,OAAO;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,IAAI;QAChB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,KAAK;QAGjB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,MAAM;QAGlB,MAAM,MAAM,GAAG,iBAAiB,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAEvD,IAAI,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;YACzB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC5B,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAChC,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { readFile } from \"node:fs/promises\";\nimport type { IInformationComponent, IServerInfo } from \"@twin.org/api-models\";\nimport { Guards, Is } from \"@twin.org/core\";\nimport { EngineCoreFactory } from \"@twin.org/engine-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IInformationServiceConstructorOptions } from \"./models/IInformationServiceConstructorOptions.js\";\n\n/**\n * The information service for the server.\n */\nexport class InformationService implements IInformationComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<InformationService>();\n\n\t/**\n\t * The server information.\n\t * @internal\n\t */\n\tprivate readonly _serverInfo: IServerInfo;\n\n\t/**\n\t * The path to the favicon Spec.\n\t * @internal\n\t */\n\tprivate readonly _faviconPath?: string;\n\n\t/**\n\t * The favicon.\n\t * @internal\n\t */\n\tprivate _favicon?: Uint8Array;\n\n\t/**\n\t * The path to the OpenAPI Spec.\n\t * @internal\n\t */\n\tprivate readonly _openApiSpecPath?: string;\n\n\t/**\n\t * The OpenAPI spec.\n\t * @internal\n\t */\n\tprivate _openApiSpec?: string;\n\n\t/**\n\t * Create a new instance of InformationService.\n\t * @param options The options to create the service.\n\t */\n\tconstructor(options: IInformationServiceConstructorOptions) {\n\t\tGuards.object(InformationService.CLASS_NAME, nameof(options), options);\n\t\tGuards.object(InformationService.CLASS_NAME, nameof(options.config), options.config);\n\t\tGuards.object(\n\t\t\tInformationService.CLASS_NAME,\n\t\t\tnameof(options.config.serverInfo),\n\t\t\toptions.config.serverInfo\n\t\t);\n\n\t\tthis._serverInfo = options.config.serverInfo;\n\t\tthis._faviconPath = options.config.favIconPath;\n\t\tthis._openApiSpecPath = options.config.openApiSpecPath;\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 InformationService.CLASS_NAME;\n\t}\n\n\t/**\n\t * The service needs to be started when the application is initialized.\n\t * @returns Nothing.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tconst openApiPath = this._openApiSpecPath;\n\t\tif (Is.stringValue(openApiPath)) {\n\t\t\tconst contentBuffer = await readFile(openApiPath, \"utf8\");\n\t\t\tthis._openApiSpec = JSON.parse(contentBuffer);\n\t\t}\n\n\t\tconst favIconPath = this._faviconPath;\n\t\tif (Is.stringValue(favIconPath)) {\n\t\t\tthis._favicon = await readFile(favIconPath);\n\t\t}\n\t}\n\n\t/**\n\t * Get the root information.\n\t * @returns The root information.\n\t */\n\tpublic async root(): Promise<string> {\n\t\treturn `${this._serverInfo.name} - ${this._serverInfo.version}`;\n\t}\n\n\t/**\n\t * Get the server information.\n\t * @returns The service information.\n\t */\n\tpublic async info(): Promise<IServerInfo> {\n\t\treturn this._serverInfo;\n\t}\n\n\t/**\n\t * Get the favicon.\n\t * @returns The favicon.\n\t */\n\tpublic async favicon(): Promise<Uint8Array | undefined> {\n\t\treturn this._favicon;\n\t}\n\n\t/**\n\t * Get the OpenAPI spec.\n\t * @returns The OpenAPI spec.\n\t */\n\tpublic async spec(): Promise<unknown> {\n\t\treturn this._openApiSpec;\n\t}\n\n\t/**\n\t * Is the server live.\n\t * @returns True if the server is live.\n\t */\n\tpublic async livez(): Promise<{\n\t\tstatus: \"alive\" | \"dead\";\n\t}> {\n\t\treturn { status: \"alive\" };\n\t}\n\n\t/**\n\t * Is the server ready.\n\t * @returns The readyz status of the server.\n\t */\n\tpublic async readyz(): Promise<{\n\t\tstatus: \"ready\" | \"not ready\";\n\t}> {\n\t\tconst engine = EngineCoreFactory.getIfExists(\"engine\");\n\n\t\tif (engine?.isStarted()) {\n\t\t\treturn { status: \"ready\" };\n\t\t}\n\n\t\treturn { status: \"not ready\" };\n\t}\n}\n"]}
1
+ {"version":3,"file":"informationService.js","sourceRoot":"","sources":["../../src/informationService.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAI5D;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAC9B;;OAEG;IACI,MAAM,CAAU,UAAU,wBAAwC;IAEzE;;;OAGG;IACc,WAAW,CAAc;IAE1C;;;OAGG;IACc,YAAY,CAAU;IAEvC;;;OAGG;IACK,QAAQ,CAAc;IAE9B;;;OAGG;IACc,gBAAgB,CAAU;IAE3C;;;OAGG;IACK,YAAY,CAAU;IAE9B;;;OAGG;IACH,YAAY,OAA8C;QACzD,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QACvE,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,UAAU,oBAA0B,OAAO,CAAC,MAAM,CAAC,CAAC;QACrF,MAAM,CAAC,MAAM,CACZ,kBAAkB,CAAC,UAAU,+BAE7B,OAAO,CAAC,MAAM,CAAC,UAAU,CACzB,CAAC;QAEF,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC;QAC/C,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC;IACxD,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,kBAAkB,CAAC,UAAU,CAAC;IACtC,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,KAAK;QACjB,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC;QAC1C,IAAI,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC;YACjC,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YAC1D,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC;QACtC,IAAI,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,CAAC;YACjC,IAAI,CAAC,QAAQ,GAAG,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC7C,CAAC;IACF,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,IAAI;QAChB,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;IACjE,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,IAAI;QAChB,OAAO,IAAI,CAAC,WAAW,CAAC;IACzB,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,OAAO;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,IAAI;QAChB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,KAAK;QAGjB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,MAAM;QAGlB,MAAM,MAAM,GAAG,iBAAiB,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAEvD,IAAI,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC;YACzB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC5B,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;IAChC,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { readFile } from \"node:fs/promises\";\nimport type { IInformationComponent, IServerInfo } from \"@twin.org/api-models\";\nimport { Guards, Is } from \"@twin.org/core\";\nimport { EngineCoreFactory } from \"@twin.org/engine-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IInformationServiceConstructorOptions } from \"./models/IInformationServiceConstructorOptions.js\";\n\n/**\n * The information service for the server.\n */\nexport class InformationService implements IInformationComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<InformationService>();\n\n\t/**\n\t * The server information.\n\t * @internal\n\t */\n\tprivate readonly _serverInfo: IServerInfo;\n\n\t/**\n\t * The path to the favicon Spec.\n\t * @internal\n\t */\n\tprivate readonly _faviconPath?: string;\n\n\t/**\n\t * The favicon.\n\t * @internal\n\t */\n\tprivate _favicon?: Uint8Array;\n\n\t/**\n\t * The path to the OpenAPI Spec.\n\t * @internal\n\t */\n\tprivate readonly _openApiSpecPath?: string;\n\n\t/**\n\t * The OpenAPI spec.\n\t * @internal\n\t */\n\tprivate _openApiSpec?: string;\n\n\t/**\n\t * Create a new instance of InformationService.\n\t * @param options The options to create the service.\n\t */\n\tconstructor(options: IInformationServiceConstructorOptions) {\n\t\tGuards.object(InformationService.CLASS_NAME, nameof(options), options);\n\t\tGuards.object(InformationService.CLASS_NAME, nameof(options.config), options.config);\n\t\tGuards.object(\n\t\t\tInformationService.CLASS_NAME,\n\t\t\tnameof(options.config.serverInfo),\n\t\t\toptions.config.serverInfo\n\t\t);\n\n\t\tthis._serverInfo = options.config.serverInfo;\n\t\tthis._faviconPath = options.config.favIconPath;\n\t\tthis._openApiSpecPath = options.config.openApiSpecPath;\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 InformationService.CLASS_NAME;\n\t}\n\n\t/**\n\t * The service needs to be started when the application is initialized.\n\t * @returns A promise that resolves when the OpenAPI spec and favicon have been loaded from disk.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tconst openApiPath = this._openApiSpecPath;\n\t\tif (Is.stringValue(openApiPath)) {\n\t\t\tconst contentBuffer = await readFile(openApiPath, \"utf8\");\n\t\t\tthis._openApiSpec = JSON.parse(contentBuffer);\n\t\t}\n\n\t\tconst favIconPath = this._faviconPath;\n\t\tif (Is.stringValue(favIconPath)) {\n\t\t\tthis._favicon = await readFile(favIconPath);\n\t\t}\n\t}\n\n\t/**\n\t * Get the root information.\n\t * @returns The root information.\n\t */\n\tpublic async root(): Promise<string> {\n\t\treturn `${this._serverInfo.name} - ${this._serverInfo.version}`;\n\t}\n\n\t/**\n\t * Get the server information.\n\t * @returns The service information.\n\t */\n\tpublic async info(): Promise<IServerInfo> {\n\t\treturn this._serverInfo;\n\t}\n\n\t/**\n\t * Get the favicon.\n\t * @returns The favicon.\n\t */\n\tpublic async favicon(): Promise<Uint8Array | undefined> {\n\t\treturn this._favicon;\n\t}\n\n\t/**\n\t * Get the OpenAPI spec.\n\t * @returns The OpenAPI spec.\n\t */\n\tpublic async spec(): Promise<unknown> {\n\t\treturn this._openApiSpec;\n\t}\n\n\t/**\n\t * Is the server live.\n\t * @returns The liveness status of the server.\n\t */\n\tpublic async livez(): Promise<{\n\t\tstatus: \"alive\" | \"dead\";\n\t}> {\n\t\treturn { status: \"alive\" };\n\t}\n\n\t/**\n\t * Is the server ready.\n\t * @returns The readyz status of the server.\n\t */\n\tpublic async readyz(): Promise<{\n\t\tstatus: \"ready\" | \"not ready\";\n\t}> {\n\t\tconst engine = EngineCoreFactory.getIfExists(\"engine\");\n\n\t\tif (engine?.isStarted()) {\n\t\t\treturn { status: \"ready\" };\n\t\t}\n\n\t\treturn { status: \"not ready\" };\n\t}\n}\n"]}
@@ -49,7 +49,7 @@ export class PlatformService {
49
49
  /**
50
50
  * Execute a method, if single tenant will run once, if multi-tenant will run for each tenant.
51
51
  * @param method The method to run for each tenant.
52
- * @returns Nothing.
52
+ * @returns A promise that resolves when the method has been executed for all applicable tenants.
53
53
  */
54
54
  async execute(method) {
55
55
  if (this._isMultiTenant) {
@@ -1 +1 @@
1
- {"version":3,"file":"platformService.js","sourceRoot":"","sources":["../../src/platformService.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACpC,OAAO,EACN,6BAA6B,EAE7B,MAAM,iCAAiC,CAAC;AAIzC;;GAEG;AACH,MAAM,OAAO,eAAe;IAC3B;;OAEG;IACI,MAAM,CAAU,UAAU,qBAAqC;IAEtE;;;OAGG;IACc,2BAA2B,CAAS;IAErD;;;OAGG;IACK,uBAAuB,CAAoC;IAEnE;;;OAGG;IACc,cAAc,CAAU;IAEzC;;;OAGG;IACH,YAAY,OAA4C;QACvD,IAAI,CAAC,2BAA2B,GAAG,OAAO,EAAE,uBAAuB,IAAI,QAAQ,CAAC;QAChF,IAAI,CAAC,cAAc,GAAG,OAAO,EAAE,MAAM,EAAE,aAAa,IAAI,KAAK,CAAC;IAC/D,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,eAAe,CAAC,UAAU,CAAC;IACnC,CAAC;IAED;;;OAGG;IACI,aAAa;QACnB,OAAO,IAAI,CAAC,cAAc,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,OAAO,CAAC,MAA2B;QAC/C,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,EAAE,CAAC;gBAC5C,IAAI,CAAC,uBAAuB,GAAG,6BAA6B,CAAC,GAAG,CAC/D,IAAI,CAAC,2BAA2B,CAChC,CAAC;YACH,CAAC;YAED,IAAI,MAA0B,CAAC;YAE/B,MAAM,cAAc,GAAG,CAAC,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,CAAC;YAEpE,GAAG,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,KAAK,CACtD,SAAS,EACT,SAAS,EACT,CAAC,IAAI,CAAC,EACN,MAAM,CACN,CAAC;gBAEF,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;oBACtC,MAAM,cAAc,CAAC,GAAG,CACvB,EAAE,GAAG,cAAc,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,EAAE,EACxD,KAAK,IAAI,EAAE;wBACV,MAAM,MAAM,EAAE,CAAC;oBAChB,CAAC,CACD,CAAC;gBACH,CAAC;gBAED,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YACxB,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE;QAClC,CAAC;aAAM,CAAC;YACP,MAAM,MAAM,EAAE,CAAC;QAChB,CAAC;IACF,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IPlatformComponent, ITenant } from \"@twin.org/api-models\";\nimport { ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport { Is } from \"@twin.org/core\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IPlatformServiceConstructorOptions } from \"./models/IPlatformServiceConstructorOptions.js\";\n\n/**\n * Service for performing platform operations.\n */\nexport class PlatformService implements IPlatformComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<PlatformService>();\n\n\t/**\n\t * The type of entity storage connector to use for tenant lookups, if multi-tenant.\n\t * @internal\n\t */\n\tprivate readonly _entityStorageConnectorType: string;\n\n\t/**\n\t * Entity storage connector used by the service.\n\t * @internal\n\t */\n\tprivate _entityStorageConnector?: IEntityStorageConnector<ITenant>;\n\n\t/**\n\t * Indicates whether the service is running in a multi-tenant environment.\n\t * @internal\n\t */\n\tprivate readonly _isMultiTenant: boolean;\n\n\t/**\n\t * Create a new instance of PlatformService.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: IPlatformServiceConstructorOptions) {\n\t\tthis._entityStorageConnectorType = options?.tenantEntityStorageType ?? \"tenant\";\n\t\tthis._isMultiTenant = options?.config?.isMultiTenant ?? false;\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 PlatformService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Indicates whether the component is running in a multi-tenant environment.\n\t * @returns True if the component is running in a multi-tenant environment, false otherwise.\n\t */\n\tpublic isMultiTenant(): boolean {\n\t\treturn this._isMultiTenant;\n\t}\n\n\t/**\n\t * Execute a method, if single tenant will run once, if multi-tenant will run for each tenant.\n\t * @param method The method to run for each tenant.\n\t * @returns Nothing.\n\t */\n\tpublic async execute(method: () => Promise<void>): Promise<void> {\n\t\tif (this._isMultiTenant) {\n\t\t\tif (Is.empty(this._entityStorageConnector)) {\n\t\t\t\tthis._entityStorageConnector = EntityStorageConnectorFactory.get(\n\t\t\t\t\tthis._entityStorageConnectorType\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet cursor: string | undefined;\n\n\t\t\tconst baseContextIds = (await ContextIdStore.getContextIds()) ?? {};\n\n\t\t\tdo {\n\t\t\t\tconst result = await this._entityStorageConnector.query(\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\t[\"id\"],\n\t\t\t\t\tcursor\n\t\t\t\t);\n\n\t\t\t\tfor (const tenant of result.entities) {\n\t\t\t\t\tawait ContextIdStore.run(\n\t\t\t\t\t\t{ ...baseContextIds, [ContextIdKeys.Tenant]: tenant.id },\n\t\t\t\t\t\tasync () => {\n\t\t\t\t\t\t\tawait method();\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tcursor = result.cursor;\n\t\t\t} while (Is.stringValue(cursor));\n\t\t} else {\n\t\t\tawait method();\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"platformService.js","sourceRoot":"","sources":["../../src/platformService.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AACpC,OAAO,EACN,6BAA6B,EAE7B,MAAM,iCAAiC,CAAC;AAIzC;;GAEG;AACH,MAAM,OAAO,eAAe;IAC3B;;OAEG;IACI,MAAM,CAAU,UAAU,qBAAqC;IAEtE;;;OAGG;IACc,2BAA2B,CAAS;IAErD;;;OAGG;IACK,uBAAuB,CAAoC;IAEnE;;;OAGG;IACc,cAAc,CAAU;IAEzC;;;OAGG;IACH,YAAY,OAA4C;QACvD,IAAI,CAAC,2BAA2B,GAAG,OAAO,EAAE,uBAAuB,IAAI,QAAQ,CAAC;QAChF,IAAI,CAAC,cAAc,GAAG,OAAO,EAAE,MAAM,EAAE,aAAa,IAAI,KAAK,CAAC;IAC/D,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,eAAe,CAAC,UAAU,CAAC;IACnC,CAAC;IAED;;;OAGG;IACI,aAAa;QACnB,OAAO,IAAI,CAAC,cAAc,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,OAAO,CAAC,MAA2B;QAC/C,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,EAAE,CAAC;gBAC5C,IAAI,CAAC,uBAAuB,GAAG,6BAA6B,CAAC,GAAG,CAC/D,IAAI,CAAC,2BAA2B,CAChC,CAAC;YACH,CAAC;YAED,IAAI,MAA0B,CAAC;YAE/B,MAAM,cAAc,GAAG,CAAC,MAAM,cAAc,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,CAAC;YAEpE,GAAG,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,uBAAuB,CAAC,KAAK,CACtD,SAAS,EACT,SAAS,EACT,CAAC,IAAI,CAAC,EACN,MAAM,CACN,CAAC;gBAEF,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;oBACtC,MAAM,cAAc,CAAC,GAAG,CACvB,EAAE,GAAG,cAAc,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,EAAE,EACxD,KAAK,IAAI,EAAE;wBACV,MAAM,MAAM,EAAE,CAAC;oBAChB,CAAC,CACD,CAAC;gBACH,CAAC;gBAED,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YACxB,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE;QAClC,CAAC;aAAM,CAAC;YACP,MAAM,MAAM,EAAE,CAAC;QAChB,CAAC;IACF,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IPlatformComponent, ITenant } from \"@twin.org/api-models\";\nimport { ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport { Is } from \"@twin.org/core\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@twin.org/entity-storage-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IPlatformServiceConstructorOptions } from \"./models/IPlatformServiceConstructorOptions.js\";\n\n/**\n * Service for performing platform operations.\n */\nexport class PlatformService implements IPlatformComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<PlatformService>();\n\n\t/**\n\t * The type of entity storage connector to use for tenant lookups, if multi-tenant.\n\t * @internal\n\t */\n\tprivate readonly _entityStorageConnectorType: string;\n\n\t/**\n\t * Entity storage connector used by the service.\n\t * @internal\n\t */\n\tprivate _entityStorageConnector?: IEntityStorageConnector<ITenant>;\n\n\t/**\n\t * Indicates whether the service is running in a multi-tenant environment.\n\t * @internal\n\t */\n\tprivate readonly _isMultiTenant: boolean;\n\n\t/**\n\t * Create a new instance of PlatformService.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: IPlatformServiceConstructorOptions) {\n\t\tthis._entityStorageConnectorType = options?.tenantEntityStorageType ?? \"tenant\";\n\t\tthis._isMultiTenant = options?.config?.isMultiTenant ?? false;\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 PlatformService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Indicates whether the component is running in a multi-tenant environment.\n\t * @returns True if the component is running in a multi-tenant environment, false otherwise.\n\t */\n\tpublic isMultiTenant(): boolean {\n\t\treturn this._isMultiTenant;\n\t}\n\n\t/**\n\t * Execute a method, if single tenant will run once, if multi-tenant will run for each tenant.\n\t * @param method The method to run for each tenant.\n\t * @returns A promise that resolves when the method has been executed for all applicable tenants.\n\t */\n\tpublic async execute(method: () => Promise<void>): Promise<void> {\n\t\tif (this._isMultiTenant) {\n\t\t\tif (Is.empty(this._entityStorageConnector)) {\n\t\t\t\tthis._entityStorageConnector = EntityStorageConnectorFactory.get(\n\t\t\t\t\tthis._entityStorageConnectorType\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tlet cursor: string | undefined;\n\n\t\t\tconst baseContextIds = (await ContextIdStore.getContextIds()) ?? {};\n\n\t\t\tdo {\n\t\t\t\tconst result = await this._entityStorageConnector.query(\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\t[\"id\"],\n\t\t\t\t\tcursor\n\t\t\t\t);\n\n\t\t\t\tfor (const tenant of result.entities) {\n\t\t\t\t\tawait ContextIdStore.run(\n\t\t\t\t\t\t{ ...baseContextIds, [ContextIdKeys.Tenant]: tenant.id },\n\t\t\t\t\t\tasync () => {\n\t\t\t\t\t\t\tawait method();\n\t\t\t\t\t\t}\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tcursor = result.cursor;\n\t\t\t} while (Is.stringValue(cursor));\n\t\t} else {\n\t\t\tawait method();\n\t\t}\n\t}\n}\n"]}
@@ -1,5 +1,8 @@
1
1
  import { generateRestRoutesHealth, tagsHealth } from "./healthRoutes.js";
2
2
  import { generateRestRoutesInformation, tagsInformation } from "./informationRoutes.js";
3
+ /**
4
+ * REST entry points for the information and health services.
5
+ */
3
6
  export const restEntryPoints = [
4
7
  {
5
8
  name: "information",
@@ -1 +1 @@
1
- {"version":3,"file":"restEntryPoints.js","sourceRoot":"","sources":["../../src/restEntryPoints.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,wBAAwB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAExF,MAAM,CAAC,MAAM,eAAe,GAA2B;IACtD;QACC,IAAI,EAAE,aAAa;QACnB,gBAAgB,EAAE,EAAE;QACpB,IAAI,EAAE,eAAe;QACrB,cAAc,EAAE,6BAA6B;KAC7C;IACD;QACC,IAAI,EAAE,QAAQ;QACd,gBAAgB,EAAE,EAAE;QACpB,IAAI,EAAE,UAAU;QAChB,cAAc,EAAE,wBAAwB;KACxC;CACD,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IRestRouteEntryPoint } from \"@twin.org/api-models\";\nimport { generateRestRoutesHealth, tagsHealth } from \"./healthRoutes.js\";\nimport { generateRestRoutesInformation, tagsInformation } from \"./informationRoutes.js\";\n\nexport const restEntryPoints: IRestRouteEntryPoint[] = [\n\t{\n\t\tname: \"information\",\n\t\tdefaultBaseRoute: \"\",\n\t\ttags: tagsInformation,\n\t\tgenerateRoutes: generateRestRoutesInformation\n\t},\n\t{\n\t\tname: \"health\",\n\t\tdefaultBaseRoute: \"\",\n\t\ttags: tagsHealth,\n\t\tgenerateRoutes: generateRestRoutesHealth\n\t}\n];\n"]}
1
+ {"version":3,"file":"restEntryPoints.js","sourceRoot":"","sources":["../../src/restEntryPoints.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,wBAAwB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAExF;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAA2B;IACtD;QACC,IAAI,EAAE,aAAa;QACnB,gBAAgB,EAAE,EAAE;QACpB,IAAI,EAAE,eAAe;QACrB,cAAc,EAAE,6BAA6B;KAC7C;IACD;QACC,IAAI,EAAE,QAAQ;QACd,gBAAgB,EAAE,EAAE;QACpB,IAAI,EAAE,UAAU;QAChB,cAAc,EAAE,wBAAwB;KACxC;CACD,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IRestRouteEntryPoint } from \"@twin.org/api-models\";\nimport { generateRestRoutesHealth, tagsHealth } from \"./healthRoutes.js\";\nimport { generateRestRoutesInformation, tagsInformation } from \"./informationRoutes.js\";\n\n/**\n * REST entry points for the information and health services.\n */\nexport const restEntryPoints: IRestRouteEntryPoint[] = [\n\t{\n\t\tname: \"information\",\n\t\tdefaultBaseRoute: \"\",\n\t\ttags: tagsInformation,\n\t\tgenerateRoutes: generateRestRoutesInformation\n\t},\n\t{\n\t\tname: \"health\",\n\t\tdefaultBaseRoute: \"\",\n\t\ttags: tagsHealth,\n\t\tgenerateRoutes: generateRestRoutesHealth\n\t}\n];\n"]}
@@ -22,13 +22,13 @@ export declare class HealthService implements IHealthComponent {
22
22
  /**
23
23
  * The component needs to be started when the node is initialized.
24
24
  * @param nodeLoggingComponentType The node logging component type.
25
- * @returns Nothing.
25
+ * @returns A promise that resolves when the initial health check timer has been scheduled.
26
26
  */
27
27
  start(nodeLoggingComponentType?: string): Promise<void>;
28
28
  /**
29
29
  * The component needs to be stopped when the node is closed.
30
30
  * @param nodeLoggingComponentType The node logging component type.
31
- * @returns Nothing.
31
+ * @returns A promise that resolves when the health check timer has been cancelled.
32
32
  */
33
33
  stop(nodeLoggingComponentType?: string): Promise<void>;
34
34
  /**
@@ -20,7 +20,7 @@ export declare class InformationService implements IInformationComponent {
20
20
  className(): string;
21
21
  /**
22
22
  * The service needs to be started when the application is initialized.
23
- * @returns Nothing.
23
+ * @returns A promise that resolves when the OpenAPI spec and favicon have been loaded from disk.
24
24
  */
25
25
  start(): Promise<void>;
26
26
  /**
@@ -45,7 +45,7 @@ export declare class InformationService implements IInformationComponent {
45
45
  spec(): Promise<unknown>;
46
46
  /**
47
47
  * Is the server live.
48
- * @returns True if the server is live.
48
+ * @returns The liveness status of the server.
49
49
  */
50
50
  livez(): Promise<{
51
51
  status: "alive" | "dead";
@@ -26,7 +26,7 @@ export declare class PlatformService implements IPlatformComponent {
26
26
  /**
27
27
  * Execute a method, if single tenant will run once, if multi-tenant will run for each tenant.
28
28
  * @param method The method to run for each tenant.
29
- * @returns Nothing.
29
+ * @returns A promise that resolves when the method has been executed for all applicable tenants.
30
30
  */
31
31
  execute(method: () => Promise<void>): Promise<void>;
32
32
  }
@@ -1,2 +1,5 @@
1
1
  import type { IRestRouteEntryPoint } from "@twin.org/api-models";
2
+ /**
3
+ * REST entry points for the information and health services.
4
+ */
2
5
  export declare const restEntryPoints: IRestRouteEntryPoint[];
package/docs/changelog.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.3-next.45](https://github.com/iotaledger/twin-api/compare/api-service-v0.0.3-next.44...api-service-v0.0.3-next.45) (2026-06-15)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * throw AlreadyExistsError on duplicate tenant id in create() ([#164](https://github.com/iotaledger/twin-api/issues/164)) ([b69f408](https://github.com/iotaledger/twin-api/commit/b69f408fc06a0599723b048fa4a5053305a06b78))
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.44 to 0.0.3-next.45
16
+
3
17
  ## [0.0.3-next.44](https://github.com/iotaledger/twin-api/compare/api-service-v0.0.3-next.43...api-service-v0.0.3-next.44) (2026-06-11)
4
18
 
5
19
 
@@ -72,7 +72,7 @@ The node logging component type.
72
72
 
73
73
  `Promise`\<`void`\>
74
74
 
75
- Nothing.
75
+ A promise that resolves when the initial health check timer has been scheduled.
76
76
 
77
77
  #### Implementation of
78
78
 
@@ -98,7 +98,7 @@ The node logging component type.
98
98
 
99
99
  `Promise`\<`void`\>
100
100
 
101
- Nothing.
101
+ A promise that resolves when the health check timer has been cancelled.
102
102
 
103
103
  #### Implementation of
104
104
 
@@ -64,7 +64,7 @@ The service needs to be started when the application is initialized.
64
64
 
65
65
  `Promise`\<`void`\>
66
66
 
67
- Nothing.
67
+ A promise that resolves when the OpenAPI spec and favicon have been loaded from disk.
68
68
 
69
69
  #### Implementation of
70
70
 
@@ -154,7 +154,7 @@ Is the server live.
154
154
 
155
155
  `Promise`\<\{ `status`: `"alive"` \| `"dead"`; \}\>
156
156
 
157
- True if the server is live.
157
+ The liveness status of the server.
158
158
 
159
159
  #### Implementation of
160
160
 
@@ -90,7 +90,7 @@ The method to run for each tenant.
90
90
 
91
91
  `Promise`\<`void`\>
92
92
 
93
- Nothing.
93
+ A promise that resolves when the method has been executed for all applicable tenants.
94
94
 
95
95
  #### Implementation of
96
96
 
@@ -1,3 +1,5 @@
1
1
  # Variable: restEntryPoints
2
2
 
3
3
  > `const` **restEntryPoints**: `IRestRouteEntryPoint`[]
4
+
5
+ REST entry points for the information and health services.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/api-service",
3
- "version": "0.0.3-next.44",
3
+ "version": "0.0.3-next.45",
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.44",
17
+ "@twin.org/api-models": "0.0.3-next.45",
18
18
  "@twin.org/context": "next",
19
19
  "@twin.org/core": "next",
20
20
  "@twin.org/engine-models": "next",