@twin.org/telemetry-service 0.0.3-next.9 → 0.9.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.
@@ -1,5 +1,3 @@
1
- // Copyright 2026 IOTA Stiftung.
2
- // SPDX-License-Identifier: Apache-2.0.
3
1
  import { BaseError, ComponentFactory } from "@twin.org/core";
4
2
  import { MetricsProducerFactory } from "@twin.org/telemetry-models";
5
3
  /**
@@ -12,8 +10,14 @@ export class MetricsCollectorService {
12
10
  static CLASS_NAME = "MetricsCollectorService";
13
11
  /**
14
12
  * Resolved logging component for internal use.
13
+ * @internal
15
14
  */
16
15
  _loggingComponent;
16
+ /**
17
+ * Platform component.
18
+ * @internal
19
+ */
20
+ _platformComponent;
17
21
  /**
18
22
  * Polling interval in milliseconds.
19
23
  * @internal
@@ -32,9 +36,11 @@ export class MetricsCollectorService {
32
36
  /**
33
37
  * Create a new instance of MetricsCollectorService.
34
38
  * @param options The options for the service.
39
+ * @throws {RangeError} If intervalMs is not a finite positive number.
35
40
  */
36
41
  constructor(options) {
37
- this._loggingComponent = ComponentFactory.getIfExists(options?.loggingComponentType ?? "logging");
42
+ this._loggingComponent = ComponentFactory.getIfExists(options?.loggingComponentType);
43
+ this._platformComponent = ComponentFactory.get(options?.platformComponentType ?? "platform");
38
44
  const intervalMs = options?.config?.intervalMs ?? 60_000;
39
45
  if (!Number.isFinite(intervalMs) || intervalMs <= 0) {
40
46
  throw new RangeError(`intervalMs must be a finite positive number, got ${intervalMs}`);
@@ -51,6 +57,7 @@ export class MetricsCollectorService {
51
57
  }
52
58
  /**
53
59
  * Start the service: register all producers and begin the polling cycle.
60
+ * @returns A promise that resolves after the first collection tick has completed.
54
61
  */
55
62
  async start() {
56
63
  if (this._running) {
@@ -60,12 +67,13 @@ export class MetricsCollectorService {
60
67
  const names = MetricsProducerFactory.names();
61
68
  const producers = names.map(name => MetricsProducerFactory.get(name));
62
69
  for (const producer of producers) {
63
- await producer.register();
70
+ await this._platformComponent.execute(async () => producer.register());
64
71
  }
65
72
  await this.tick();
66
73
  }
67
74
  /**
68
75
  * Stop the service and cancel the pending timer.
76
+ * @returns A promise that resolves when the service has stopped.
69
77
  */
70
78
  async stop() {
71
79
  this._running = false;
@@ -77,13 +85,14 @@ export class MetricsCollectorService {
77
85
  /**
78
86
  * One collection cycle across all registered producers.
79
87
  * Public so tests can drive it deterministically.
88
+ * @returns A promise that resolves when all producers have been polled and the next tick scheduled.
80
89
  */
81
90
  async tick() {
82
91
  const names = MetricsProducerFactory.names();
83
92
  const producers = names.map(name => MetricsProducerFactory.get(name));
84
93
  for (const producer of producers) {
85
94
  try {
86
- await producer.collect();
95
+ await this._platformComponent.execute(async () => producer.collect());
87
96
  }
88
97
  catch (err) {
89
98
  await this._loggingComponent?.log({
@@ -100,7 +109,7 @@ export class MetricsCollectorService {
100
109
  this.scheduleNext();
101
110
  }
102
111
  /**
103
- * Schedule the next tick. Re-schedules itself after each tick completes.
112
+ * Schedule the next tick after the configured interval.
104
113
  * @internal
105
114
  */
106
115
  scheduleNext() {
@@ -1 +1 @@
1
- {"version":3,"file":"metricsCollectorService.js","sourceRoot":"","sources":["../../src/metricsCollectorService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { BaseError, ComponentFactory } from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\ttype IMetricsCollectorComponent,\n\tMetricsProducerFactory\n} from \"@twin.org/telemetry-models\";\nimport type { IMetricsCollectorServiceConstructorOptions } from \"./models/IMetricsCollectorServiceConstructorOptions.js\";\n\n/**\n * Collects metrics from registered producers at a fixed interval.\n */\nexport class MetricsCollectorService implements IMetricsCollectorComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MetricsCollectorService>();\n\n\t/**\n\t * Resolved logging component for internal use.\n\t */\n\tprivate readonly _loggingComponent?: ILoggingComponent;\n\n\t/**\n\t * Polling interval in milliseconds.\n\t * @internal\n\t */\n\tprivate readonly _intervalMs: number;\n\n\t/**\n\t * Pending scheduled tick handle.\n\t * @internal\n\t */\n\tprivate _timer: ReturnType<typeof globalThis.setTimeout> | undefined;\n\n\t/**\n\t * Whether the service is currently running.\n\t * @internal\n\t */\n\tprivate _running: boolean;\n\n\t/**\n\t * Create a new instance of MetricsCollectorService.\n\t * @param options The options for the service.\n\t */\n\tconstructor(options?: IMetricsCollectorServiceConstructorOptions) {\n\t\tthis._loggingComponent = ComponentFactory.getIfExists<ILoggingComponent>(\n\t\t\toptions?.loggingComponentType ?? \"logging\"\n\t\t);\n\n\t\tconst intervalMs = options?.config?.intervalMs ?? 60_000;\n\t\tif (!Number.isFinite(intervalMs) || intervalMs <= 0) {\n\t\t\tthrow new RangeError(`intervalMs must be a finite positive number, got ${intervalMs}`);\n\t\t}\n\t\tthis._intervalMs = intervalMs;\n\t\tthis._running = 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 MetricsCollectorService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Start the service: register all producers and begin the polling cycle.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tif (this._running) {\n\t\t\treturn;\n\t\t}\n\t\tthis._running = true;\n\n\t\tconst names = MetricsProducerFactory.names();\n\t\tconst producers = names.map(name => MetricsProducerFactory.get(name));\n\n\t\tfor (const producer of producers) {\n\t\t\tawait producer.register();\n\t\t}\n\n\t\tawait this.tick();\n\t}\n\n\t/**\n\t * Stop the service and cancel the pending timer.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis._running = false;\n\t\tif (this._timer !== undefined) {\n\t\t\tglobalThis.clearTimeout(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * One collection cycle across all registered producers.\n\t * Public so tests can drive it deterministically.\n\t */\n\tpublic async tick(): Promise<void> {\n\t\tconst names = MetricsProducerFactory.names();\n\t\tconst producers = names.map(name => MetricsProducerFactory.get(name));\n\n\t\tfor (const producer of producers) {\n\t\t\ttry {\n\t\t\t\tawait producer.collect();\n\t\t\t} catch (err) {\n\t\t\t\tawait this._loggingComponent?.log({\n\t\t\t\t\tsource: MetricsCollectorService.CLASS_NAME,\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tmessage: \"producerCollectionFailed\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tproducer: producer.className()\n\t\t\t\t\t},\n\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis.scheduleNext();\n\t}\n\n\t/**\n\t * Schedule the next tick. Re-schedules itself after each tick completes.\n\t * @internal\n\t */\n\tprivate scheduleNext(): void {\n\t\tif (this._running) {\n\t\t\tthis._timer = globalThis.setTimeout(async () => {\n\t\t\t\tawait this.tick();\n\t\t\t}, this._intervalMs);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"metricsCollectorService.js","sourceRoot":"","sources":["../../src/metricsCollectorService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IPlatformComponent } from \"@twin.org/api-models\";\nimport { BaseError, ComponentFactory } from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\ttype IMetricsCollectorComponent,\n\tMetricsProducerFactory\n} from \"@twin.org/telemetry-models\";\nimport type { IMetricsCollectorServiceConstructorOptions } from \"./models/IMetricsCollectorServiceConstructorOptions.js\";\n\n/**\n * Collects metrics from registered producers at a fixed interval.\n */\nexport class MetricsCollectorService implements IMetricsCollectorComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MetricsCollectorService>();\n\n\t/**\n\t * Resolved logging component for internal use.\n\t * @internal\n\t */\n\tprivate readonly _loggingComponent?: ILoggingComponent;\n\n\t/**\n\t * Platform component.\n\t * @internal\n\t */\n\tprivate readonly _platformComponent: IPlatformComponent;\n\n\t/**\n\t * Polling interval in milliseconds.\n\t * @internal\n\t */\n\tprivate readonly _intervalMs: number;\n\n\t/**\n\t * Pending scheduled tick handle.\n\t * @internal\n\t */\n\tprivate _timer: ReturnType<typeof globalThis.setTimeout> | undefined;\n\n\t/**\n\t * Whether the service is currently running.\n\t * @internal\n\t */\n\tprivate _running: boolean;\n\n\t/**\n\t * Create a new instance of MetricsCollectorService.\n\t * @param options The options for the service.\n\t * @throws {RangeError} If intervalMs is not a finite positive number.\n\t */\n\tconstructor(options?: IMetricsCollectorServiceConstructorOptions) {\n\t\tthis._loggingComponent = ComponentFactory.getIfExists<ILoggingComponent>(\n\t\t\toptions?.loggingComponentType\n\t\t);\n\t\tthis._platformComponent = ComponentFactory.get<IPlatformComponent>(\n\t\t\toptions?.platformComponentType ?? \"platform\"\n\t\t);\n\n\t\tconst intervalMs = options?.config?.intervalMs ?? 60_000;\n\t\tif (!Number.isFinite(intervalMs) || intervalMs <= 0) {\n\t\t\tthrow new RangeError(`intervalMs must be a finite positive number, got ${intervalMs}`);\n\t\t}\n\t\tthis._intervalMs = intervalMs;\n\t\tthis._running = 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 MetricsCollectorService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Start the service: register all producers and begin the polling cycle.\n\t * @returns A promise that resolves after the first collection tick has completed.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tif (this._running) {\n\t\t\treturn;\n\t\t}\n\t\tthis._running = true;\n\n\t\tconst names = MetricsProducerFactory.names();\n\t\tconst producers = names.map(name => MetricsProducerFactory.get(name));\n\n\t\tfor (const producer of producers) {\n\t\t\tawait this._platformComponent.execute(async () => producer.register());\n\t\t}\n\n\t\tawait this.tick();\n\t}\n\n\t/**\n\t * Stop the service and cancel the pending timer.\n\t * @returns A promise that resolves when the service has stopped.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis._running = false;\n\t\tif (this._timer !== undefined) {\n\t\t\tglobalThis.clearTimeout(this._timer);\n\t\t\tthis._timer = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * One collection cycle across all registered producers.\n\t * Public so tests can drive it deterministically.\n\t * @returns A promise that resolves when all producers have been polled and the next tick scheduled.\n\t */\n\tpublic async tick(): Promise<void> {\n\t\tconst names = MetricsProducerFactory.names();\n\t\tconst producers = names.map(name => MetricsProducerFactory.get(name));\n\n\t\tfor (const producer of producers) {\n\t\t\ttry {\n\t\t\t\tawait this._platformComponent.execute(async () => producer.collect());\n\t\t\t} catch (err) {\n\t\t\t\tawait this._loggingComponent?.log({\n\t\t\t\t\tsource: MetricsCollectorService.CLASS_NAME,\n\t\t\t\t\tlevel: \"error\",\n\t\t\t\t\tmessage: \"producerCollectionFailed\",\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tproducer: producer.className()\n\t\t\t\t\t},\n\t\t\t\t\terror: BaseError.fromError(err)\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis.scheduleNext();\n\t}\n\n\t/**\n\t * Schedule the next tick after the configured interval.\n\t * @internal\n\t */\n\tprivate scheduleNext(): void {\n\t\tif (this._running) {\n\t\t\tthis._timer = globalThis.setTimeout(async () => {\n\t\t\t\tawait this.tick();\n\t\t\t}, this._intervalMs);\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IMetricsCollectorServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IMetricsCollectorServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IMetricsCollectorServiceConfig } from \"./IMetricsCollectorServiceConfig.js\";\n\n/**\n * The options for the metrics collector service constructor.\n */\nexport interface IMetricsCollectorServiceConstructorOptions {\n\t/**\n\t * The type of the logging component to use.\n\t * @default logging\n\t */\n\tloggingComponentType?: string;\n\n\t/**\n\t * The configuration options for the metrics collector service.\n\t */\n\tconfig?: IMetricsCollectorServiceConfig;\n}\n"]}
1
+ {"version":3,"file":"IMetricsCollectorServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IMetricsCollectorServiceConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IMetricsCollectorServiceConfig } from \"./IMetricsCollectorServiceConfig.js\";\n\n/**\n * The options for the metrics collector service constructor.\n */\nexport interface IMetricsCollectorServiceConstructorOptions {\n\t/**\n\t * The type of the logging component to use, can be undefined for no logging.\n\t */\n\tloggingComponentType?: string;\n\n\t/**\n\t * The type of the platform component to use for partitioned producers.\n\t * @default platform\n\t */\n\tplatformComponentType?: string;\n\n\t/**\n\t * The configuration options for the metrics collector service.\n\t */\n\tconfig?: IMetricsCollectorServiceConfig;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ITelemetryServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/ITelemetryServiceConstructorOptions.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The options for the telemetry service constructor.\n */\nexport interface ITelemetryServiceConstructorOptions {\n\t/**\n\t * The type of the telemetry connector to use.\n\t * @default telemetry\n\t */\n\ttelemetryConnectorType?: string;\n}\n"]}
1
+ {"version":3,"file":"ITelemetryServiceConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/ITelemetryServiceConstructorOptions.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The options for the telemetry service constructor.\n */\nexport interface ITelemetryServiceConstructorOptions {\n\t/**\n\t * The type of the telemetry connector to use.\n\t * @default telemetry\n\t */\n\ttelemetryConnectorType?: string;\n\n\t/**\n\t * The type of the platform component to use for partition iteration.\n\t * @default platform\n\t */\n\tplatformComponentType?: string;\n}\n"]}
@@ -1,6 +1,5 @@
1
- // Copyright 2024 IOTA Stiftung.
2
- // SPDX-License-Identifier: Apache-2.0.
3
- import { Guards } from "@twin.org/core";
1
+ import { ContextIdKeys, ContextIdStore } from "@twin.org/context";
2
+ import { ComponentFactory, Guards, Is } from "@twin.org/core";
4
3
  import { TelemetryConnectorFactory } from "@twin.org/telemetry-models";
5
4
  /**
6
5
  * Service for performing telemetry operations to a connector.
@@ -15,12 +14,18 @@ export class TelemetryService {
15
14
  * @internal
16
15
  */
17
16
  _telemetryConnector;
17
+ /**
18
+ * Platform component.
19
+ * @internal
20
+ */
21
+ _platformComponent;
18
22
  /**
19
23
  * Create a new instance of TelemetryService.
20
24
  * @param options The options for the connector.
21
25
  */
22
26
  constructor(options) {
23
27
  this._telemetryConnector = TelemetryConnectorFactory.get(options?.telemetryConnectorType ?? "telemetry");
28
+ this._platformComponent = ComponentFactory.get(options?.platformComponentType ?? "platform");
24
29
  }
25
30
  /**
26
31
  * Returns the class name of the component.
@@ -32,11 +37,21 @@ export class TelemetryService {
32
37
  /**
33
38
  * Create a new metric.
34
39
  * @param metric The metric details.
35
- * @returns Nothing.
40
+ * @returns A promise that resolves when the metric has been created.
36
41
  */
37
42
  async createMetric(metric) {
38
43
  Guards.object(TelemetryService.CLASS_NAME, "metric", metric);
39
- await this._telemetryConnector.createMetric(metric);
44
+ // If we don't have a tenant context ID and the tenant component is multi-tenant,
45
+ // we consider the entry as per-tenant and run it in all the tenant context to ensure
46
+ // the metric is created for each tenant.
47
+ const contextIds = (await ContextIdStore.getContextIds()) ?? {};
48
+ const perTenant = !Is.stringValue(contextIds[ContextIdKeys.Tenant]) && this._platformComponent.isMultiTenant();
49
+ if (perTenant) {
50
+ await this._platformComponent.execute(async () => this._telemetryConnector.createMetric(metric));
51
+ }
52
+ else {
53
+ await this._telemetryConnector.createMetric(metric);
54
+ }
40
55
  }
41
56
  /**
42
57
  * Get the metric details and it's most recent value.
@@ -50,7 +65,7 @@ export class TelemetryService {
50
65
  /**
51
66
  * Update metric.
52
67
  * @param metric The metric details.
53
- * @returns Nothing.
68
+ * @returns A promise that resolves when the metric has been updated.
54
69
  */
55
70
  async updateMetric(metric) {
56
71
  Guards.object(TelemetryService.CLASS_NAME, "metric", metric);
@@ -72,7 +87,7 @@ export class TelemetryService {
72
87
  /**
73
88
  * Remove metric.
74
89
  * @param id The id of the metric.
75
- * @returns Nothing.
90
+ * @returns A promise that resolves when the metric and all its values have been removed.
76
91
  */
77
92
  async removeMetric(id) {
78
93
  Guards.stringValue(TelemetryService.CLASS_NAME, "id", id);
@@ -1 +1 @@
1
- {"version":3,"file":"telemetryService.js","sourceRoot":"","sources":["../../src/telemetryService.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,OAAO,EACN,yBAAyB,EAOzB,MAAM,4BAA4B,CAAC;AAGpC;;GAEG;AACH,MAAM,OAAO,gBAAgB;IAC5B;;OAEG;IACI,MAAM,CAAU,UAAU,sBAAsC;IAEvE;;;OAGG;IACc,mBAAmB,CAAsB;IAE1D;;;OAGG;IACH,YAAY,OAA6C;QACxD,IAAI,CAAC,mBAAmB,GAAG,yBAAyB,CAAC,GAAG,CACvD,OAAO,EAAE,sBAAsB,IAAI,WAAW,CAC9C,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,gBAAgB,CAAC,UAAU,CAAC;IACpC,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,YAAY,CAAC,MAAwB;QACjD,MAAM,CAAC,MAAM,CAAmB,gBAAgB,CAAC,UAAU,YAAkB,MAAM,CAAC,CAAC;QAErF,MAAM,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACrD,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,SAAS,CAAC,EAAU;QAIhC,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,UAAU,QAAc,EAAE,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,YAAY,CAAC,MAAsC;QAC/D,MAAM,CAAC,MAAM,CAAmB,gBAAgB,CAAC,UAAU,YAAkB,MAAM,CAAC,CAAC;QACrF,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,UAAU,eAAqB,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9E,OAAO,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACtD,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,cAAc,CAC1B,EAAU,EACV,KAAsC,EACtC,UAAuC;QAEvC,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,UAAU,QAAc,EAAE,CAAC,CAAC;QAChE,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,UAAU,WAAiB,KAAK,CAAC,CAAC;QAClE,OAAO,IAAI,CAAC,mBAAmB,CAAC,cAAc,CAAC,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;IACvE,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,YAAY,CAAC,EAAU;QACnC,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,UAAU,QAAc,EAAE,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAClD,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,KAAK,CACjB,IAAiB,EACjB,MAAe,EACf,KAAc;QAYd,OAAO,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC5D,CAAC;IAED;;;;;;;;;;OAUG;IACI,KAAK,CAAC,WAAW,CACvB,EAAU,EACV,SAAkB,EAClB,OAAgB,EAChB,MAAe,EACf,KAAc;QAed,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,UAAU,QAAc,EAAE,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IACpF,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Guards } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tTelemetryConnectorFactory,\n\ttype ITelemetryComponent,\n\ttype ITelemetryConnector,\n\ttype ITelemetryMetric,\n\ttype ITelemetryMetricValue,\n\ttype MetricCounterOperation,\n\ttype MetricType\n} from \"@twin.org/telemetry-models\";\nimport type { ITelemetryServiceConstructorOptions } from \"./models/ITelemetryServiceConstructorOptions.js\";\n\n/**\n * Service for performing telemetry operations to a connector.\n */\nexport class TelemetryService implements ITelemetryComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TelemetryService>();\n\n\t/**\n\t * Telemetry connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _telemetryConnector: ITelemetryConnector;\n\n\t/**\n\t * Create a new instance of TelemetryService.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: ITelemetryServiceConstructorOptions) {\n\t\tthis._telemetryConnector = TelemetryConnectorFactory.get(\n\t\t\toptions?.telemetryConnectorType ?? \"telemetry\"\n\t\t);\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 TelemetryService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Create a new metric.\n\t * @param metric The metric details.\n\t * @returns Nothing.\n\t */\n\tpublic async createMetric(metric: ITelemetryMetric): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(TelemetryService.CLASS_NAME, nameof(metric), metric);\n\n\t\tawait this._telemetryConnector.createMetric(metric);\n\t}\n\n\t/**\n\t * Get the metric details and it's most recent value.\n\t * @param id The metric id.\n\t * @returns The metric details and it's most recent value.\n\t */\n\tpublic async getMetric(id: string): Promise<{\n\t\tmetric: ITelemetryMetric;\n\t\tvalue: ITelemetryMetricValue;\n\t}> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.getMetric(id);\n\t}\n\n\t/**\n\t * Update metric.\n\t * @param metric The metric details.\n\t * @returns Nothing.\n\t */\n\tpublic async updateMetric(metric: Omit<ITelemetryMetric, \"type\">): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(TelemetryService.CLASS_NAME, nameof(metric), metric);\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(metric.id), metric.id);\n\t\treturn this._telemetryConnector.updateMetric(metric);\n\t}\n\n\t/**\n\t * Add a metric value.\n\t * @param id The id of the metric.\n\t * @param value The value for the add operation.\n\t * @param customData The custom data for the add operation.\n\t * @returns The created metric value id.\n\t */\n\tpublic async addMetricValue(\n\t\tid: string,\n\t\tvalue: MetricCounterOperation | number,\n\t\tcustomData?: { [key: string]: unknown }\n\t): Promise<string> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\tGuards.defined(TelemetryService.CLASS_NAME, nameof(value), value);\n\t\treturn this._telemetryConnector.addMetricValue(id, value, customData);\n\t}\n\n\t/**\n\t * Remove metric.\n\t * @param id The id of the metric.\n\t * @returns Nothing.\n\t */\n\tpublic async removeMetric(id: string): Promise<void> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.removeMetric(id);\n\t}\n\n\t/**\n\t * Query the metrics.\n\t * @param type The type of the metric.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t * @throws NotImplementedError if the implementation does not support retrieval.\n\t */\n\tpublic async query(\n\t\ttype?: MetricType,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The metrics.\n\t\t */\n\t\tentities: ITelemetryMetric[];\n\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\treturn this._telemetryConnector.query(type, cursor, limit);\n\t}\n\n\t/**\n\t * Query the metric values.\n\t * @param id The id of the metric.\n\t * @param timeStart The inclusive time as the start of the metric entries.\n\t * @param timeEnd The inclusive time as the end of the metric entries.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t * @throws NotImplementedError if the implementation does not support retrieval.\n\t */\n\tpublic async queryValues(\n\t\tid: string,\n\t\ttimeStart?: number,\n\t\ttimeEnd?: number,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The metric details.\n\t\t */\n\t\tmetric: ITelemetryMetric;\n\t\t/**\n\t\t * The values for the metric.\n\t\t */\n\t\tentities: ITelemetryMetricValue[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.queryValues(id, timeStart, timeEnd, cursor, limit);\n\t}\n}\n"]}
1
+ {"version":3,"file":"telemetryService.js","sourceRoot":"","sources":["../../src/telemetryService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IPlatformComponent } from \"@twin.org/api-models\";\nimport { ContextIdKeys, ContextIdStore } from \"@twin.org/context\";\nimport { ComponentFactory, Guards, Is } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport {\n\tTelemetryConnectorFactory,\n\ttype ITelemetryComponent,\n\ttype ITelemetryConnector,\n\ttype ITelemetryMetric,\n\ttype ITelemetryMetricValue,\n\ttype MetricCounterOperation,\n\ttype MetricType\n} from \"@twin.org/telemetry-models\";\nimport type { ITelemetryServiceConstructorOptions } from \"./models/ITelemetryServiceConstructorOptions.js\";\n\n/**\n * Service for performing telemetry operations to a connector.\n */\nexport class TelemetryService implements ITelemetryComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TelemetryService>();\n\n\t/**\n\t * Telemetry connector used by the service.\n\t * @internal\n\t */\n\tprivate readonly _telemetryConnector: ITelemetryConnector;\n\n\t/**\n\t * Platform component.\n\t * @internal\n\t */\n\tprivate readonly _platformComponent: IPlatformComponent;\n\n\t/**\n\t * Create a new instance of TelemetryService.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: ITelemetryServiceConstructorOptions) {\n\t\tthis._telemetryConnector = TelemetryConnectorFactory.get(\n\t\t\toptions?.telemetryConnectorType ?? \"telemetry\"\n\t\t);\n\t\tthis._platformComponent = ComponentFactory.get<IPlatformComponent>(\n\t\t\toptions?.platformComponentType ?? \"platform\"\n\t\t);\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 TelemetryService.CLASS_NAME;\n\t}\n\n\t/**\n\t * Create a new metric.\n\t * @param metric The metric details.\n\t * @returns A promise that resolves when the metric has been created.\n\t */\n\tpublic async createMetric(metric: ITelemetryMetric): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(TelemetryService.CLASS_NAME, nameof(metric), metric);\n\n\t\t// If we don't have a tenant context ID and the tenant component is multi-tenant,\n\t\t// we consider the entry as per-tenant and run it in all the tenant context to ensure\n\t\t// the metric is created for each tenant.\n\t\tconst contextIds = (await ContextIdStore.getContextIds()) ?? {};\n\t\tconst perTenant =\n\t\t\t!Is.stringValue(contextIds[ContextIdKeys.Tenant]) && this._platformComponent.isMultiTenant();\n\n\t\tif (perTenant) {\n\t\t\tawait this._platformComponent.execute(async () =>\n\t\t\t\tthis._telemetryConnector.createMetric(metric)\n\t\t\t);\n\t\t} else {\n\t\t\tawait this._telemetryConnector.createMetric(metric);\n\t\t}\n\t}\n\n\t/**\n\t * Get the metric details and it's most recent value.\n\t * @param id The metric id.\n\t * @returns The metric details and it's most recent value.\n\t */\n\tpublic async getMetric(id: string): Promise<{\n\t\tmetric: ITelemetryMetric;\n\t\tvalue: ITelemetryMetricValue;\n\t}> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.getMetric(id);\n\t}\n\n\t/**\n\t * Update metric.\n\t * @param metric The metric details.\n\t * @returns A promise that resolves when the metric has been updated.\n\t */\n\tpublic async updateMetric(metric: Omit<ITelemetryMetric, \"type\">): Promise<void> {\n\t\tGuards.object<ITelemetryMetric>(TelemetryService.CLASS_NAME, nameof(metric), metric);\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(metric.id), metric.id);\n\t\treturn this._telemetryConnector.updateMetric(metric);\n\t}\n\n\t/**\n\t * Add a metric value.\n\t * @param id The id of the metric.\n\t * @param value The value for the add operation.\n\t * @param customData The custom data for the add operation.\n\t * @returns The created metric value id.\n\t */\n\tpublic async addMetricValue(\n\t\tid: string,\n\t\tvalue: MetricCounterOperation | number,\n\t\tcustomData?: { [key: string]: unknown }\n\t): Promise<string> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\tGuards.defined(TelemetryService.CLASS_NAME, nameof(value), value);\n\t\treturn this._telemetryConnector.addMetricValue(id, value, customData);\n\t}\n\n\t/**\n\t * Remove metric.\n\t * @param id The id of the metric.\n\t * @returns A promise that resolves when the metric and all its values have been removed.\n\t */\n\tpublic async removeMetric(id: string): Promise<void> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.removeMetric(id);\n\t}\n\n\t/**\n\t * Query the metrics.\n\t * @param type The type of the metric.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t * @throws NotImplementedError if the implementation does not support retrieval.\n\t */\n\tpublic async query(\n\t\ttype?: MetricType,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The metrics.\n\t\t */\n\t\tentities: ITelemetryMetric[];\n\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\treturn this._telemetryConnector.query(type, cursor, limit);\n\t}\n\n\t/**\n\t * Query the metric values.\n\t * @param id The id of the metric.\n\t * @param timeStart The inclusive time as the start of the metric entries.\n\t * @param timeEnd The inclusive time as the end of the metric entries.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit Limit the number of entities to return.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t * @throws NotImplementedError if the implementation does not support retrieval.\n\t */\n\tpublic async queryValues(\n\t\tid: string,\n\t\ttimeStart?: number,\n\t\ttimeEnd?: number,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The metric details.\n\t\t */\n\t\tmetric: ITelemetryMetric;\n\t\t/**\n\t\t * The values for the metric.\n\t\t */\n\t\tentities: ITelemetryMetricValue[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more values.\n\t\t */\n\t\tcursor?: string;\n\t}> {\n\t\tGuards.stringValue(TelemetryService.CLASS_NAME, nameof(id), id);\n\t\treturn this._telemetryConnector.queryValues(id, timeStart, timeEnd, cursor, limit);\n\t}\n}\n"]}
@@ -8,13 +8,10 @@ export declare class MetricsCollectorService implements IMetricsCollectorCompone
8
8
  * Runtime name for the class.
9
9
  */
10
10
  static readonly CLASS_NAME: string;
11
- /**
12
- * Resolved logging component for internal use.
13
- */
14
- private readonly _loggingComponent?;
15
11
  /**
16
12
  * Create a new instance of MetricsCollectorService.
17
13
  * @param options The options for the service.
14
+ * @throws {RangeError} If intervalMs is not a finite positive number.
18
15
  */
19
16
  constructor(options?: IMetricsCollectorServiceConstructorOptions);
20
17
  /**
@@ -24,15 +21,18 @@ export declare class MetricsCollectorService implements IMetricsCollectorCompone
24
21
  className(): string;
25
22
  /**
26
23
  * Start the service: register all producers and begin the polling cycle.
24
+ * @returns A promise that resolves after the first collection tick has completed.
27
25
  */
28
26
  start(): Promise<void>;
29
27
  /**
30
28
  * Stop the service and cancel the pending timer.
29
+ * @returns A promise that resolves when the service has stopped.
31
30
  */
32
31
  stop(): Promise<void>;
33
32
  /**
34
33
  * One collection cycle across all registered producers.
35
34
  * Public so tests can drive it deterministically.
35
+ * @returns A promise that resolves when all producers have been polled and the next tick scheduled.
36
36
  */
37
37
  tick(): Promise<void>;
38
38
  }
@@ -4,10 +4,14 @@ import type { IMetricsCollectorServiceConfig } from "./IMetricsCollectorServiceC
4
4
  */
5
5
  export interface IMetricsCollectorServiceConstructorOptions {
6
6
  /**
7
- * The type of the logging component to use.
8
- * @default logging
7
+ * The type of the logging component to use, can be undefined for no logging.
9
8
  */
10
9
  loggingComponentType?: string;
10
+ /**
11
+ * The type of the platform component to use for partitioned producers.
12
+ * @default platform
13
+ */
14
+ platformComponentType?: string;
11
15
  /**
12
16
  * The configuration options for the metrics collector service.
13
17
  */
@@ -7,4 +7,9 @@ export interface ITelemetryServiceConstructorOptions {
7
7
  * @default telemetry
8
8
  */
9
9
  telemetryConnectorType?: string;
10
+ /**
11
+ * The type of the platform component to use for partition iteration.
12
+ * @default platform
13
+ */
14
+ platformComponentType?: string;
10
15
  }
@@ -21,7 +21,7 @@ export declare class TelemetryService implements ITelemetryComponent {
21
21
  /**
22
22
  * Create a new metric.
23
23
  * @param metric The metric details.
24
- * @returns Nothing.
24
+ * @returns A promise that resolves when the metric has been created.
25
25
  */
26
26
  createMetric(metric: ITelemetryMetric): Promise<void>;
27
27
  /**
@@ -36,7 +36,7 @@ export declare class TelemetryService implements ITelemetryComponent {
36
36
  /**
37
37
  * Update metric.
38
38
  * @param metric The metric details.
39
- * @returns Nothing.
39
+ * @returns A promise that resolves when the metric has been updated.
40
40
  */
41
41
  updateMetric(metric: Omit<ITelemetryMetric, "type">): Promise<void>;
42
42
  /**
@@ -52,7 +52,7 @@ export declare class TelemetryService implements ITelemetryComponent {
52
52
  /**
53
53
  * Remove metric.
54
54
  * @param id The id of the metric.
55
- * @returns Nothing.
55
+ * @returns A promise that resolves when the metric and all its values have been removed.
56
56
  */
57
57
  removeMetric(id: string): Promise<void>;
58
58
  /**
package/docs/changelog.md CHANGED
@@ -1,5 +1,103 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.0](https://github.com/iotaledger/twin-telemetry/compare/telemetry-service-v0.9.0...telemetry-service-v0.9.0) (2026-06-24)
4
+
5
+
6
+ ### Features
7
+
8
+ * release to production ([df2151d](https://github.com/iotaledger/twin-telemetry/commit/df2151d24844fd2c3e6092ce3a6f888ac16219a0))
9
+ * release to production ([#50](https://github.com/iotaledger/twin-telemetry/issues/50)) ([192bfe3](https://github.com/iotaledger/twin-telemetry/commit/192bfe3bea060163f2887e51fc6c2ceb74a7683f))
10
+
11
+ ## [0.9.0-next.1](https://github.com/iotaledger/twin-telemetry/compare/telemetry-service-v0.9.0-next.0...telemetry-service-v0.9.0-next.1) (2026-06-23)
12
+
13
+
14
+ ### Features
15
+
16
+ * add context id features ([#19](https://github.com/iotaledger/twin-telemetry/issues/19)) ([5942442](https://github.com/iotaledger/twin-telemetry/commit/5942442991fa7ad7afa9d8b7c4583326a8c4c9ae))
17
+ * add helper and command types ([7166013](https://github.com/iotaledger/twin-telemetry/commit/7166013f8a0693a1d92101cb600c37a1aba66417))
18
+ * add metrics producer infrastructure ([#30](https://github.com/iotaledger/twin-telemetry/issues/30)) ([8990b99](https://github.com/iotaledger/twin-telemetry/commit/8990b990d22f331d44562c2781c9b6ddf846db88))
19
+ * add validate-locales ([24ed804](https://github.com/iotaledger/twin-telemetry/commit/24ed804f7ebc47036f323722bc18452729860c92))
20
+ * eslint migration to flat config ([69ac91a](https://github.com/iotaledger/twin-telemetry/commit/69ac91a89d0b5bc63374a0bd314f770b557dabb4))
21
+ * organization identifiers ([#42](https://github.com/iotaledger/twin-telemetry/issues/42)) ([1cea68c](https://github.com/iotaledger/twin-telemetry/commit/1cea68c858deea67ab3364c93db56928e0064ec3))
22
+ * remove unused namespace ([54939fa](https://github.com/iotaledger/twin-telemetry/commit/54939faac3790dc1b51aa96a54005a03c99f98da))
23
+ * remove unused namespace ([02dcabb](https://github.com/iotaledger/twin-telemetry/commit/02dcabbe00f5b03d5e4b2c12e655efac50409acf))
24
+ * system metrics partitioning ([#38](https://github.com/iotaledger/twin-telemetry/issues/38)) ([3914183](https://github.com/iotaledger/twin-telemetry/commit/3914183bc17d0fd0adc8e30b7bb7aaee5d75e986))
25
+ * typescript 6 update ([0acecc2](https://github.com/iotaledger/twin-telemetry/commit/0acecc2692b174fbaec6a8a89bd9277fa3530b5f))
26
+ * update dependencies ([0e55c48](https://github.com/iotaledger/twin-telemetry/commit/0e55c48de4139c6fe66b823101ca17973e60847c))
27
+ * update framework core ([4b10bcd](https://github.com/iotaledger/twin-telemetry/commit/4b10bcd4d3101151671bdcf9aef7c54f5937fc2a))
28
+ * use shared store mechanism ([#7](https://github.com/iotaledger/twin-telemetry/issues/7)) ([806fe2c](https://github.com/iotaledger/twin-telemetry/commit/806fe2c2b7653d6b949c27ebf57bd13c3e040242))
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * api data types ([2edc5ac](https://github.com/iotaledger/twin-telemetry/commit/2edc5ac665b996a1b29173cac3b389fa3ceafa34))
34
+ * query params force coercion ([ec7999e](https://github.com/iotaledger/twin-telemetry/commit/ec7999eadb66c65585efa19f3ce4cabb50eed761))
35
+ * test mocks ([4a8c19d](https://github.com/iotaledger/twin-telemetry/commit/4a8c19d7e4b5e3082dd7933a34bf1bda79e6b86a))
36
+ * Update metric endpoint paths by removing trailing slashes and query type as string ([#3](https://github.com/iotaledger/twin-telemetry/issues/3)) ([b386ca5](https://github.com/iotaledger/twin-telemetry/commit/b386ca55404aa933ad8917f82a7f0e588593fcc8))
37
+
38
+
39
+ ### Dependencies
40
+
41
+ * The following workspace dependencies were updated
42
+ * dependencies
43
+ * @twin.org/telemetry-models bumped from 0.9.0-next.0 to 0.9.0-next.1
44
+
45
+ ## [0.0.3-next.13](https://github.com/iotaledger/twin-telemetry/compare/telemetry-service-v0.0.3-next.12...telemetry-service-v0.0.3-next.13) (2026-06-11)
46
+
47
+
48
+ ### Features
49
+
50
+ * organization identifiers ([#42](https://github.com/iotaledger/twin-telemetry/issues/42)) ([1cea68c](https://github.com/iotaledger/twin-telemetry/commit/1cea68c858deea67ab3364c93db56928e0064ec3))
51
+
52
+
53
+ ### Dependencies
54
+
55
+ * The following workspace dependencies were updated
56
+ * dependencies
57
+ * @twin.org/telemetry-models bumped from 0.0.3-next.12 to 0.0.3-next.13
58
+
59
+ ## [0.0.3-next.12](https://github.com/iotaledger/twin-telemetry/compare/telemetry-service-v0.0.3-next.11...telemetry-service-v0.0.3-next.12) (2026-06-08)
60
+
61
+
62
+ ### Miscellaneous Chores
63
+
64
+ * **telemetry-service:** Synchronize repo versions
65
+
66
+
67
+ ### Dependencies
68
+
69
+ * The following workspace dependencies were updated
70
+ * dependencies
71
+ * @twin.org/telemetry-models bumped from 0.0.3-next.11 to 0.0.3-next.12
72
+
73
+ ## [0.0.3-next.11](https://github.com/iotaledger/twin-telemetry/compare/telemetry-service-v0.0.3-next.10...telemetry-service-v0.0.3-next.11) (2026-06-04)
74
+
75
+
76
+ ### Features
77
+
78
+ * system metrics partitioning ([#38](https://github.com/iotaledger/twin-telemetry/issues/38)) ([3914183](https://github.com/iotaledger/twin-telemetry/commit/3914183bc17d0fd0adc8e30b7bb7aaee5d75e986))
79
+
80
+
81
+ ### Dependencies
82
+
83
+ * The following workspace dependencies were updated
84
+ * dependencies
85
+ * @twin.org/telemetry-models bumped from 0.0.3-next.10 to 0.0.3-next.11
86
+
87
+ ## [0.0.3-next.10](https://github.com/iotaledger/twin-telemetry/compare/telemetry-service-v0.0.3-next.9...telemetry-service-v0.0.3-next.10) (2026-05-20)
88
+
89
+
90
+ ### Miscellaneous Chores
91
+
92
+ * **telemetry-service:** Synchronize repo versions
93
+
94
+
95
+ ### Dependencies
96
+
97
+ * The following workspace dependencies were updated
98
+ * dependencies
99
+ * @twin.org/telemetry-models bumped from 0.0.3-next.9 to 0.0.3-next.10
100
+
3
101
  ## [0.0.3-next.9](https://github.com/iotaledger/twin-telemetry/compare/telemetry-service-v0.0.3-next.8...telemetry-service-v0.0.3-next.9) (2026-05-20)
4
102
 
5
103
 
@@ -26,6 +26,10 @@ The options for the service.
26
26
 
27
27
  `MetricsCollectorService`
28
28
 
29
+ #### Throws
30
+
31
+ If intervalMs is not a finite positive number.
32
+
29
33
  ## Properties
30
34
 
31
35
  ### CLASS\_NAME {#class_name}
@@ -64,6 +68,8 @@ Start the service: register all producers and begin the polling cycle.
64
68
 
65
69
  `Promise`\<`void`\>
66
70
 
71
+ A promise that resolves after the first collection tick has completed.
72
+
67
73
  #### Implementation of
68
74
 
69
75
  `IMetricsCollectorComponent.start`
@@ -80,6 +86,8 @@ Stop the service and cancel the pending timer.
80
86
 
81
87
  `Promise`\<`void`\>
82
88
 
89
+ A promise that resolves when the service has stopped.
90
+
83
91
  #### Implementation of
84
92
 
85
93
  `IMetricsCollectorComponent.stop`
@@ -96,3 +104,5 @@ Public so tests can drive it deterministically.
96
104
  #### Returns
97
105
 
98
106
  `Promise`\<`void`\>
107
+
108
+ A promise that resolves when all producers have been polled and the next tick scheduled.
@@ -72,7 +72,7 @@ The metric details.
72
72
 
73
73
  `Promise`\<`void`\>
74
74
 
75
- Nothing.
75
+ A promise that resolves when the metric has been created.
76
76
 
77
77
  #### Implementation of
78
78
 
@@ -124,7 +124,7 @@ The metric details.
124
124
 
125
125
  `Promise`\<`void`\>
126
126
 
127
- Nothing.
127
+ A promise that resolves when the metric has been updated.
128
128
 
129
129
  #### Implementation of
130
130
 
@@ -186,7 +186,7 @@ The id of the metric.
186
186
 
187
187
  `Promise`\<`void`\>
188
188
 
189
- Nothing.
189
+ A promise that resolves when the metric and all its values have been removed.
190
190
 
191
191
  #### Implementation of
192
192
 
@@ -8,12 +8,20 @@ The options for the metrics collector service constructor.
8
8
 
9
9
  > `optional` **loggingComponentType?**: `string`
10
10
 
11
- The type of the logging component to use.
11
+ The type of the logging component to use, can be undefined for no logging.
12
+
13
+ ***
14
+
15
+ ### platformComponentType? {#platformcomponenttype}
16
+
17
+ > `optional` **platformComponentType?**: `string`
18
+
19
+ The type of the platform component to use for partitioned producers.
12
20
 
13
21
  #### Default
14
22
 
15
23
  ```ts
16
- logging
24
+ platform
17
25
  ```
18
26
 
19
27
  ***
@@ -15,3 +15,17 @@ The type of the telemetry connector to use.
15
15
  ```ts
16
16
  telemetry
17
17
  ```
18
+
19
+ ***
20
+
21
+ ### platformComponentType? {#platformcomponenttype}
22
+
23
+ > `optional` **platformComponentType?**: `string`
24
+
25
+ The type of the platform component to use for partition iteration.
26
+
27
+ #### Default
28
+
29
+ ```ts
30
+ platform
31
+ ```
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@twin.org/telemetry-service",
3
- "version": "0.0.3-next.9",
3
+ "version": "0.9.0",
4
4
  "description": "Telemetry service implementation with REST entry points.",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "git+https://github.com/iotaledger/telemetry.git",
7
+ "url": "git+https://github.com/iotaledger/twin-telemetry.git",
8
8
  "directory": "packages/telemetry-service"
9
9
  },
10
10
  "author": "martyn.janes@iota.org",
@@ -14,13 +14,14 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@twin.org/api-models": "next",
18
- "@twin.org/core": "next",
19
- "@twin.org/entity": "next",
20
- "@twin.org/logging-models": "next",
21
- "@twin.org/nameof": "next",
22
- "@twin.org/telemetry-models": "0.0.3-next.9",
23
- "@twin.org/web": "next"
17
+ "@twin.org/api-models": "^0.9.0",
18
+ "@twin.org/context": "^0.9.0",
19
+ "@twin.org/core": "^0.9.0",
20
+ "@twin.org/entity": "^0.9.0",
21
+ "@twin.org/logging-models": "^0.9.0",
22
+ "@twin.org/nameof": "^0.9.0",
23
+ "@twin.org/telemetry-models": "^0.9.0",
24
+ "@twin.org/web": "^0.9.0"
24
25
  },
25
26
  "main": "./dist/es/index.js",
26
27
  "types": "./dist/types/index.d.ts",
@@ -50,7 +51,7 @@
50
51
  "business-logic"
51
52
  ],
52
53
  "bugs": {
53
- "url": "git+https://github.com/iotaledger/telemetry/issues"
54
+ "url": "git+https://github.com/iotaledger/twin-telemetry/issues"
54
55
  },
55
56
  "homepage": "https://twindev.org"
56
57
  }