@twin.org/telemetry-connector-opentelemetry 0.0.3-next.2 → 0.0.3-next.6

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.
Files changed (25) hide show
  1. package/dist/es/index.js +2 -0
  2. package/dist/es/index.js.map +1 -1
  3. package/dist/es/models/IOpenTelemetryPrometheusReaderConfig.js +0 -2
  4. package/dist/es/models/IOpenTelemetryPrometheusReaderConfig.js.map +1 -1
  5. package/dist/es/models/IOpenTelemetryTelemetryConnectorConfig.js +2 -0
  6. package/dist/es/models/IOpenTelemetryTelemetryConnectorConfig.js.map +1 -0
  7. package/dist/es/models/IOpenTelemetryTelemetryConnectorConstructorOptions.js.map +1 -1
  8. package/dist/es/models/openTelemetryReaderTypes.js +13 -0
  9. package/dist/es/models/openTelemetryReaderTypes.js.map +1 -0
  10. package/dist/es/openTelemetryTelemetryConnector.js +6 -6
  11. package/dist/es/openTelemetryTelemetryConnector.js.map +1 -1
  12. package/dist/types/index.d.ts +2 -0
  13. package/dist/types/models/IOpenTelemetryPrometheusReaderConfig.d.ts +3 -2
  14. package/dist/types/models/IOpenTelemetryTelemetryConnectorConfig.d.ts +24 -0
  15. package/dist/types/models/IOpenTelemetryTelemetryConnectorConstructorOptions.d.ts +5 -20
  16. package/dist/types/models/openTelemetryReaderTypes.d.ts +13 -0
  17. package/docs/changelog.md +39 -0
  18. package/docs/examples.md +6 -4
  19. package/docs/reference/index.md +6 -0
  20. package/docs/reference/interfaces/IOpenTelemetryPrometheusReaderConfig.md +1 -1
  21. package/docs/reference/interfaces/IOpenTelemetryTelemetryConnectorConfig.md +45 -0
  22. package/docs/reference/interfaces/IOpenTelemetryTelemetryConnectorConstructorOptions.md +8 -43
  23. package/docs/reference/type-aliases/OpenTelemetryReaderTypes.md +5 -0
  24. package/docs/reference/variables/OpenTelemetryReaderTypes.md +13 -0
  25. package/package.json +5 -5
package/dist/es/index.js CHANGED
@@ -2,6 +2,8 @@
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  export * from "./models/IOpenTelemetryPrometheusReaderConfig.js";
4
4
  export * from "./models/IOpenTelemetryReaderConfig.js";
5
+ export * from "./models/IOpenTelemetryTelemetryConnectorConfig.js";
5
6
  export * from "./models/IOpenTelemetryTelemetryConnectorConstructorOptions.js";
7
+ export * from "./models/openTelemetryReaderTypes.js";
6
8
  export * from "./openTelemetryTelemetryConnector.js";
7
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,kDAAkD,CAAC;AACjE,cAAc,wCAAwC,CAAC;AACvD,cAAc,gEAAgE,CAAC;AAC/E,cAAc,sCAAsC,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./models/IOpenTelemetryPrometheusReaderConfig.js\";\nexport * from \"./models/IOpenTelemetryReaderConfig.js\";\nexport * from \"./models/IOpenTelemetryTelemetryConnectorConstructorOptions.js\";\nexport * from \"./openTelemetryTelemetryConnector.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,kDAAkD,CAAC;AACjE,cAAc,wCAAwC,CAAC;AACvD,cAAc,oDAAoD,CAAC;AACnE,cAAc,gEAAgE,CAAC;AAC/E,cAAc,sCAAsC,CAAC;AACrD,cAAc,sCAAsC,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./models/IOpenTelemetryPrometheusReaderConfig.js\";\nexport * from \"./models/IOpenTelemetryReaderConfig.js\";\nexport * from \"./models/IOpenTelemetryTelemetryConnectorConfig.js\";\nexport * from \"./models/IOpenTelemetryTelemetryConnectorConstructorOptions.js\";\nexport * from \"./models/openTelemetryReaderTypes.js\";\nexport * from \"./openTelemetryTelemetryConnector.js\";\n"]}
@@ -1,4 +1,2 @@
1
- // Copyright 2026 IOTA Stiftung.
2
- // SPDX-License-Identifier: Apache-2.0.
3
1
  export {};
4
2
  //# sourceMappingURL=IOpenTelemetryPrometheusReaderConfig.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"IOpenTelemetryPrometheusReaderConfig.js","sourceRoot":"","sources":["../../../src/models/IOpenTelemetryPrometheusReaderConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for a Prometheus scrape-endpoint reader.\n * The connector instantiates a PrometheusExporter from these options in start().\n */\nexport interface IOpenTelemetryPrometheusReaderConfig {\n\t/**\n\t * Discriminator — must be \"prometheus\".\n\t */\n\ttype: \"prometheus\";\n\n\t/**\n\t * TCP port the Prometheus HTTP server listens on.\n\t * @default 9464\n\t */\n\tport?: number;\n\n\t/**\n\t * HTTP path that Prometheus scrapes.\n\t * @default /metrics\n\t */\n\tendpoint?: string;\n\n\t/**\n\t * Whether to start the built-in HTTP server automatically.\n\t * Set to false if you manage the server externally.\n\t * @default true\n\t */\n\tstartServer?: boolean;\n\n\t/**\n\t * Optional string prepended to every exported metric name.\n\t */\n\tprefix?: string;\n}\n"]}
1
+ {"version":3,"file":"IOpenTelemetryPrometheusReaderConfig.js","sourceRoot":"","sources":["../../../src/models/IOpenTelemetryPrometheusReaderConfig.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { OpenTelemetryReaderTypes } from \"./openTelemetryReaderTypes.js\";\n\n/**\n * Configuration for a Prometheus scrape-endpoint reader.\n * The connector instantiates a PrometheusExporter from these options in start().\n */\nexport interface IOpenTelemetryPrometheusReaderConfig {\n\t/**\n\t * Type.\n\t */\n\ttype: typeof OpenTelemetryReaderTypes.Prometheus;\n\n\t/**\n\t * TCP port the Prometheus HTTP server listens on.\n\t * @default 9464\n\t */\n\tport?: number;\n\n\t/**\n\t * HTTP path that Prometheus scrapes.\n\t * @default /metrics\n\t */\n\tendpoint?: string;\n\n\t/**\n\t * Whether to start the built-in HTTP server automatically.\n\t * Set to false if you manage the server externally.\n\t * @default true\n\t */\n\tstartServer?: boolean;\n\n\t/**\n\t * Optional string prepended to every exported metric name.\n\t */\n\tprefix?: string;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IOpenTelemetryTelemetryConnectorConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IOpenTelemetryTelemetryConnectorConfig.js","sourceRoot":"","sources":["../../../src/models/IOpenTelemetryTelemetryConnectorConfig.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IOpenTelemetryReaderConfig } from \"./IOpenTelemetryReaderConfig.js\";\n\n/**\n * The options for the OpenTelemetry telemetry connector constructor.\n */\nexport interface IOpenTelemetryTelemetryConnectorConfig {\n\t/**\n\t * The name of the OpenTelemetry meter used to create instruments.\n\t * @default twin-telemetry\n\t */\n\tmeterName?: string;\n\n\t/**\n\t * The version reported by the OpenTelemetry meter.\n\t * @default 1.0.0\n\t */\n\tmeterVersion?: string;\n\n\t/**\n\t * Named metric-reader configurations keyed by an arbitrary id.\n\t * Each entry's `type` field determines which exporter the connector instantiates\n\t * in start(). Omit or pass an empty object for a no-op provider (useful for tests).\n\t */\n\treaders?: { [id: string]: IOpenTelemetryReaderConfig };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IOpenTelemetryTelemetryConnectorConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IOpenTelemetryTelemetryConnectorConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IOpenTelemetryReaderConfig } from \"./IOpenTelemetryReaderConfig.js\";\n\n/**\n * The options for the OpenTelemetry telemetry connector constructor.\n */\nexport interface IOpenTelemetryTelemetryConnectorConstructorOptions {\n\t/**\n\t * The name of the OpenTelemetry meter used to create instruments.\n\t * @default twin-telemetry\n\t */\n\tmeterName?: string;\n\n\t/**\n\t * The version reported by the OpenTelemetry meter.\n\t * @default 0.0.1\n\t */\n\tmeterVersion?: string;\n\n\t/**\n\t * Named metric-reader configurations keyed by an arbitrary id.\n\t * Each entry's `type` field determines which exporter the connector instantiates\n\t * in start(). Omit or pass an empty object for a no-op provider (useful for tests).\n\t * Example: { \"main\": { type: \"prometheus\", port: 9464 } }\n\t */\n\treaders?: { [id: string]: IOpenTelemetryReaderConfig };\n\n\t/**\n\t * The component type to use for logging inside the connector and the inner\n\t * entity-storage connector. When omitted logging is disabled.\n\t */\n\tloggingComponentType?: string;\n\n\t/**\n\t * The entity storage connector type to use for storing metric definitions.\n\t * Must be registered in `EntityStorageConnectorFactory` before calling `start()`.\n\t * @default telemetry-metric\n\t */\n\ttelemetryMetricStorageConnectorType?: string;\n\n\t/**\n\t * The entity storage connector type to use for storing metric values.\n\t * Must be registered in `EntityStorageConnectorFactory` before calling `start()`.\n\t * @default telemetry-metric-value\n\t */\n\ttelemetryMetricValueStorageConnectorType?: string;\n}\n"]}
1
+ {"version":3,"file":"IOpenTelemetryTelemetryConnectorConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IOpenTelemetryTelemetryConnectorConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IOpenTelemetryTelemetryConnectorConfig } from \"./IOpenTelemetryTelemetryConnectorConfig.js\";\n\n/**\n * The options for the OpenTelemetry telemetry connector constructor.\n */\nexport interface IOpenTelemetryTelemetryConnectorConstructorOptions {\n\t/**\n\t * The component type to use for logging inside the connector and the inner\n\t * entity-storage connector. When omitted logging is disabled.\n\t */\n\tloggingComponentType?: string;\n\n\t/**\n\t * The entity storage connector type to use for storing metric definitions.\n\t * Must be registered in `EntityStorageConnectorFactory` before calling `start()`.\n\t * @default telemetry-metric\n\t */\n\ttelemetryMetricStorageConnectorType?: string;\n\n\t/**\n\t * The entity storage connector type to use for storing metric values.\n\t * Must be registered in `EntityStorageConnectorFactory` before calling `start()`.\n\t * @default telemetry-metric-value\n\t */\n\ttelemetryMetricValueStorageConnectorType?: string;\n\n\t/**\n\t * The config for the telemetry connector.\n\t */\n\tconfig?: IOpenTelemetryTelemetryConnectorConfig;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ /**
4
+ * The types of readers.
5
+ */
6
+ // eslint-disable-next-line @typescript-eslint/naming-convention
7
+ export const OpenTelemetryReaderTypes = {
8
+ /**
9
+ * Prometheus.
10
+ */
11
+ Prometheus: "prometheus"
12
+ };
13
+ //# sourceMappingURL=openTelemetryReaderTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openTelemetryReaderTypes.js","sourceRoot":"","sources":["../../../src/models/openTelemetryReaderTypes.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AAEvC;;GAEG;AACH,gEAAgE;AAChE,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACvC;;OAEG;IACH,UAAU,EAAE,YAAY;CACf,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The types of readers.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const OpenTelemetryReaderTypes = {\n\t/**\n\t * Prometheus.\n\t */\n\tPrometheus: \"prometheus\"\n} as const;\n\n/**\n * The types of readers.\n */\nexport type OpenTelemetryReaderTypes =\n\t(typeof OpenTelemetryReaderTypes)[keyof typeof OpenTelemetryReaderTypes];\n"]}
@@ -20,10 +20,10 @@ export class OpenTelemetryTelemetryConnector {
20
20
  */
21
21
  static CLASS_NAME = "OpenTelemetryTelemetryConnector";
22
22
  /**
23
- * Constructor options, stored so start() can initialise the MeterProvider.
23
+ * Config options, stored so start() can initialise the MeterProvider.
24
24
  * @internal
25
25
  */
26
- _options;
26
+ _config;
27
27
  /**
28
28
  * Internal entity-storage connector that owns all metric metadata and value history.
29
29
  * Created at construction time — fails fast if entity storage is not set up.
@@ -54,7 +54,7 @@ export class OpenTelemetryTelemetryConnector {
54
54
  * @param options The options for the connector.
55
55
  */
56
56
  constructor(options) {
57
- this._options = options ?? {};
57
+ this._config = options?.config ?? {};
58
58
  this._inner = new EntityStorageTelemetryConnector({
59
59
  loggingComponentType: options?.loggingComponentType,
60
60
  telemetryMetricStorageConnectorType: options?.telemetryMetricStorageConnectorType,
@@ -80,7 +80,7 @@ export class OpenTelemetryTelemetryConnector {
80
80
  return;
81
81
  }
82
82
  const readers = [];
83
- for (const [, config] of Object.entries(this._options.readers ?? {})) {
83
+ for (const [, config] of Object.entries(this._config.readers ?? {})) {
84
84
  if (config.type === "prometheus") {
85
85
  // Dynamic import so @opentelemetry/exporter-prometheus is an optional peer dep;
86
86
  // consumers only need to install it if they actually configure a Prometheus reader.
@@ -101,13 +101,13 @@ export class OpenTelemetryTelemetryConnector {
101
101
  }
102
102
  }
103
103
  this._meterProvider = new MeterProvider({ readers });
104
- this._meter = this._meterProvider.getMeter(this._options.meterName ?? "twin-telemetry", this._options.meterVersion ?? "0.0.1");
104
+ this._meter = this._meterProvider.getMeter(this._config.meterName ?? "twin-telemetry", this._config.meterVersion ?? "1.0.0");
105
105
  const nodeLogging = ComponentFactory.getIfExists(nodeLoggingComponentType);
106
106
  await nodeLogging?.log({
107
107
  source: OpenTelemetryTelemetryConnector.CLASS_NAME,
108
108
  message: "connectorStarted",
109
109
  level: "info",
110
- data: { readerCount: Object.keys(this._options.readers ?? {}).length }
110
+ data: { readerCount: Object.keys(this._config.readers ?? {}).length }
111
111
  });
112
112
  }
113
113
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"openTelemetryTelemetryConnector.js","sourceRoot":"","sources":["../../src/openTelemetryTelemetryConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { Attributes, Counter, Gauge, Meter, UpDownCounter } from \"@opentelemetry/api\";\nimport { MeterProvider, type MetricReader } from \"@opentelemetry/sdk-metrics\";\nimport { ComponentFactory, GeneralError, Is } from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { EntityStorageTelemetryConnector } from \"@twin.org/telemetry-connector-entity-storage\";\nimport {\n\ttype ITelemetryConnector,\n\ttype ITelemetryMetric,\n\ttype ITelemetryMetricValue,\n\tMetricType\n} from \"@twin.org/telemetry-models\";\nimport type { IOpenTelemetryTelemetryConnectorConstructorOptions } from \"./models/IOpenTelemetryTelemetryConnectorConstructorOptions.js\";\n\n/**\n * Class for performing telemetry operations using OpenTelemetry instruments.\n * Metric definitions and value history are persisted via an internal\n * EntityStorageTelemetryConnector instance created at construction time.\n * Call `start()` to initialise the MeterProvider and exporters; metrics can be\n * created and queried before start() — OTEL forwarding is simply skipped until\n * the MeterProvider is running.\n */\nexport class OpenTelemetryTelemetryConnector implements ITelemetryConnector {\n\t/**\n\t * The namespace supported by the telemetry connector.\n\t */\n\tpublic static readonly NAMESPACE: string = \"opentelemetry\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<OpenTelemetryTelemetryConnector>();\n\n\t/**\n\t * Constructor options, stored so start() can initialise the MeterProvider.\n\t * @internal\n\t */\n\tprivate readonly _options: IOpenTelemetryTelemetryConnectorConstructorOptions;\n\n\t/**\n\t * Internal entity-storage connector that owns all metric metadata and value history.\n\t * Created at construction time — fails fast if entity storage is not set up.\n\t * @internal\n\t */\n\tprivate readonly _inner: EntityStorageTelemetryConnector;\n\n\t/**\n\t * Live OTEL instrument handles keyed by metric id, paired with the metric type\n\t * so addMetricValue can dispatch without querying storage.\n\t * These are runtime objects and cannot be persisted.\n\t * @internal\n\t */\n\tprivate readonly _instruments: Map<\n\t\tstring,\n\t\t{ metricType: MetricType; instrument: Counter | UpDownCounter | Gauge }\n\t>;\n\n\t/**\n\t * The MeterProvider that owns all instruments. Set by start(), cleared by stop().\n\t * @internal\n\t */\n\tprivate _meterProvider: MeterProvider | undefined;\n\n\t/**\n\t * The Meter used to create instruments. Set by start(), cleared by stop().\n\t * @internal\n\t */\n\tprivate _meter: Meter | undefined;\n\n\t/**\n\t * Create a new instance of OpenTelemetryTelemetryConnector.\n\t * Eagerly constructs the inner EntityStorageTelemetryConnector — if the required\n\t * entity storage types are not registered this constructor will throw (fail fast).\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: IOpenTelemetryTelemetryConnectorConstructorOptions) {\n\t\tthis._options = options ?? {};\n\t\tthis._inner = new EntityStorageTelemetryConnector({\n\t\t\tloggingComponentType: options?.loggingComponentType,\n\t\t\ttelemetryMetricStorageConnectorType: options?.telemetryMetricStorageConnectorType,\n\t\t\ttelemetryMetricValueStorageConnectorType: options?.telemetryMetricValueStorageConnectorType\n\t\t});\n\t\tthis._instruments = new Map();\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 OpenTelemetryTelemetryConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Initialise the MeterProvider and configured exporters.\n\t * Calling start() on an already-started connector is a no-op.\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\tif (!Is.undefined(this._meterProvider)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst readers: MetricReader[] = [];\n\t\tfor (const [, config] of Object.entries(this._options.readers ?? {})) {\n\t\t\tif (config.type === \"prometheus\") {\n\t\t\t\t// Dynamic import so @opentelemetry/exporter-prometheus is an optional peer dep;\n\t\t\t\t// consumers only need to install it if they actually configure a Prometheus reader.\n\t\t\t\tconst { PrometheusExporter: prometheusExporterClass } =\n\t\t\t\t\tawait import(\"@opentelemetry/exporter-prometheus\");\n\t\t\t\treaders.push(\n\t\t\t\t\tnew prometheusExporterClass({\n\t\t\t\t\t\tport: config.port,\n\t\t\t\t\t\tendpoint: config.endpoint,\n\t\t\t\t\t\t// PrometheusExporter uses preventServerStart (inverted); our config\n\t\t\t\t\t\t// exposes the more intuitive startServer (defaults to true).\n\t\t\t\t\t\tpreventServerStart: !(config.startServer ?? true),\n\t\t\t\t\t\tprefix: config.prefix\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrow new GeneralError(OpenTelemetryTelemetryConnector.CLASS_NAME, \"unknownReaderType\", {\n\t\t\t\t\ttype: config.type\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis._meterProvider = new MeterProvider({ readers });\n\t\tthis._meter = this._meterProvider.getMeter(\n\t\t\tthis._options.meterName ?? \"twin-telemetry\",\n\t\t\tthis._options.meterVersion ?? \"0.0.1\"\n\t\t);\n\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\t\tawait nodeLogging?.log({\n\t\t\tsource: OpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"connectorStarted\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { readerCount: Object.keys(this._options.readers ?? {}).length }\n\t\t});\n\t}\n\n\t/**\n\t * Shut down the MeterProvider and release resources.\n\t * Calling stop() on a connector that has not been started is a no-op.\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 (Is.undefined(this._meterProvider)) {\n\t\t\treturn;\n\t\t}\n\n\t\tawait this._meterProvider.shutdown();\n\t\tthis._meterProvider = undefined;\n\t\tthis._meter = undefined;\n\t\tthis._instruments.clear();\n\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\t\tawait nodeLogging?.log({\n\t\t\tsource: OpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"connectorStopped\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: {}\n\t\t});\n\t}\n\n\t/**\n\t * Create a new metric.\n\t * The definition is always persisted via the inner entity-storage connector.\n\t * If the MeterProvider is running the corresponding OTEL instrument is also registered.\n\t * @param metric The metric details.\n\t * @returns Nothing.\n\t */\n\tpublic async createMetric(metric: ITelemetryMetric): Promise<void> {\n\t\t// Entity storage performs all validation and throws AlreadyExistsError on duplicates.\n\t\tawait this._inner.createMetric(metric);\n\n\t\t// Register an OTEL instrument only when the MeterProvider is running.\n\t\tconst meter = this._meter;\n\t\tif (meter !== undefined) {\n\t\t\tconst instrumentOptions = {\n\t\t\t\tdescription: metric.description,\n\t\t\t\tunit: metric.unit\n\t\t\t};\n\t\t\tlet instrument: Counter | UpDownCounter | Gauge;\n\t\t\tif (metric.type === MetricType.Counter) {\n\t\t\t\tinstrument = meter.createCounter(metric.id, instrumentOptions);\n\t\t\t} else if (metric.type === MetricType.IncDecCounter) {\n\t\t\t\tinstrument = meter.createUpDownCounter(metric.id, instrumentOptions);\n\t\t\t} else {\n\t\t\t\tinstrument = meter.createGauge(metric.id, instrumentOptions);\n\t\t\t}\n\t\t\tthis._instruments.set(metric.id, { metricType: metric.type, instrument });\n\t\t}\n\t}\n\n\t/**\n\t * Get the metric details and its most recent value.\n\t * @param id The metric id.\n\t * @returns The metric details and its most recent value.\n\t */\n\tpublic async getMetric(id: string): Promise<{\n\t\tmetric: ITelemetryMetric;\n\t\tvalue: ITelemetryMetricValue;\n\t}> {\n\t\tconst result = await this._inner.getMetric(id);\n\n\t\t// When the metric has no recorded values yet the inner connector returns\n\t\t// entities[0] = undefined via queryValues. Return a placeholder so callers\n\t\t// are not surprised by a null value field — treat id=\"\" as \"no measurements yet\".\n\t\tconst value = result.value ?? { id: \"\", ts: 0, value: 0 };\n\t\treturn { metric: result.metric, value };\n\t}\n\n\t/**\n\t * Update the metric metadata.\n\t * Note: OpenTelemetry instrument descriptors are immutable once created.\n\t * This method updates the persisted metadata mirror; the description/unit changes\n\t * are NOT propagated to the registered MeterProvider and will not appear at the\n\t * OTEL backend (Prometheus, OTLP, etc.).\n\t * @param metric The metric details (type cannot be changed).\n\t * @returns Nothing.\n\t */\n\tpublic async updateMetric(metric: Omit<ITelemetryMetric, \"type\">): Promise<void> {\n\t\treturn this._inner.updateMetric(metric);\n\t}\n\n\t/**\n\t * Record a metric value.\n\t * Entity storage always receives the value first and performs all validation.\n\t * If the MeterProvider is running the measurement is also forwarded to the OTEL instrument.\n\t * Counter accepts positive integers or \"inc\".\n\t * UpDownCounter accepts integers (positive or negative) or \"inc\"/\"dec\".\n\t * Gauge accepts any number.\n\t * @param id The id of the metric.\n\t * @param value The value for the operation.\n\t * @param customData Optional custom data forwarded as OTEL attributes.\n\t * @returns The id of the new metric value entry.\n\t */\n\tpublic async addMetricValue(\n\t\tid: string,\n\t\tvalue: \"inc\" | \"dec\" | number,\n\t\tcustomData?: { [key: string]: unknown }\n\t): Promise<string> {\n\t\t// Entity storage validates and persists first; throws NotFoundError if metric not found.\n\t\tconst valueId = await this._inner.addMetricValue(id, value, customData);\n\n\t\t// Forward to the OTEL instrument only when the MeterProvider is running.\n\t\tif (this._meter !== undefined) {\n\t\t\tconst entry = this._instruments.get(id);\n\t\t\tif (entry !== undefined) {\n\t\t\t\tconst attributes = this.toAttributes(customData);\n\t\t\t\tconst { metricType, instrument } = entry;\n\n\t\t\t\t// Value already validated by inner connector — dispatch unconditionally.\n\t\t\t\tif (metricType === MetricType.Counter) {\n\t\t\t\t\t(instrument as Counter).add(value === \"inc\" ? 1 : (value as number), attributes);\n\t\t\t\t} else if (metricType === MetricType.IncDecCounter) {\n\t\t\t\t\tlet delta: number;\n\t\t\t\t\tif (value === \"inc\") {\n\t\t\t\t\t\tdelta = 1;\n\t\t\t\t\t} else if (value === \"dec\") {\n\t\t\t\t\t\tdelta = -1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdelta = value;\n\t\t\t\t\t}\n\t\t\t\t\t(instrument as UpDownCounter).add(delta, attributes);\n\t\t\t\t} else {\n\t\t\t\t\t(instrument as Gauge).record(value as number, attributes);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn valueId;\n\t}\n\n\t/**\n\t * Remove a metric and its persisted value history.\n\t * Note: OpenTelemetry exposes no API to deregister an instrument from a Meter,\n\t * so the underlying Counter/UpDownCounter/Gauge remains resident for the lifetime\n\t * of the process. Re-creating a metric with the same id but a different MetricType\n\t * is therefore not safe.\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\tthis._instruments.delete(id);\n\t\t// The inner connector cascades and removes all associated metric values.\n\t\treturn this._inner.removeMetric(id);\n\t}\n\n\t/**\n\t * Query the registered metrics, optionally filtered by type.\n\t * @param type The type of the metric.\n\t * @param cursor The cursor to request the next page.\n\t * @param limit Limit the number of entities to return.\n\t * @returns The matching metrics and an optional cursor for the next page.\n\t */\n\tpublic async query(\n\t\ttype?: MetricType,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\tentities: ITelemetryMetric[];\n\t\tcursor?: string;\n\t}> {\n\t\treturn this._inner.query(type, cursor, limit);\n\t}\n\n\t/**\n\t * Query the recorded values for a metric, ordered by most recent first.\n\t * @param id The id of the metric.\n\t * @param timeStart The inclusive start time (epoch ms).\n\t * @param timeEnd The inclusive end time (epoch ms).\n\t * @param cursor The cursor returned by the previous call.\n\t * @param limit Limit the number of values to return.\n\t * @returns The metric details, matching values, and an optional cursor for the next page.\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\tmetric: ITelemetryMetric;\n\t\tentities: ITelemetryMetricValue[];\n\t\tcursor?: string;\n\t}> {\n\t\treturn this._inner.queryValues(id, timeStart, timeEnd, cursor, limit);\n\t}\n\n\t/**\n\t * Convert customData to OTEL-compatible Attributes.\n\t * Scalar values (string, number, boolean) and uniform primitive arrays\n\t * (string[], number[], boolean[]) are forwarded; other values are silently dropped.\n\t * @param customData The raw custom data map.\n\t * @returns An OTEL Attributes object.\n\t * @internal\n\t */\n\tprivate toAttributes(customData?: { [key: string]: unknown }): Attributes {\n\t\tif (Is.empty(customData)) {\n\t\t\treturn {};\n\t\t}\n\t\tconst attrs: Attributes = {};\n\t\tfor (const [key, val] of Object.entries(customData)) {\n\t\t\tif (Is.string(val) || Is.number(val) || Is.boolean(val)) {\n\t\t\t\tattrs[key] = val;\n\t\t\t} else if (Is.arrayValue(val)) {\n\t\t\t\tif (\n\t\t\t\t\t(Is.string(val[0]) || Is.number(val[0]) || Is.boolean(val[0])) &&\n\t\t\t\t\tval.every(el => typeof el === typeof val[0])\n\t\t\t\t) {\n\t\t\t\t\tattrs[key] = val as string[] | number[] | boolean[];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn attrs;\n\t}\n}\n"]}
1
+ {"version":3,"file":"openTelemetryTelemetryConnector.js","sourceRoot":"","sources":["../../src/openTelemetryTelemetryConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { Attributes, Counter, Gauge, Meter, UpDownCounter } from \"@opentelemetry/api\";\nimport { MeterProvider, type MetricReader } from \"@opentelemetry/sdk-metrics\";\nimport { ComponentFactory, GeneralError, Is } from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { EntityStorageTelemetryConnector } from \"@twin.org/telemetry-connector-entity-storage\";\nimport {\n\ttype ITelemetryConnector,\n\ttype ITelemetryMetric,\n\ttype ITelemetryMetricValue,\n\tMetricType\n} from \"@twin.org/telemetry-models\";\nimport type { IOpenTelemetryTelemetryConnectorConfig } from \"./models/IOpenTelemetryTelemetryConnectorConfig.js\";\nimport type { IOpenTelemetryTelemetryConnectorConstructorOptions } from \"./models/IOpenTelemetryTelemetryConnectorConstructorOptions.js\";\n\n/**\n * Class for performing telemetry operations using OpenTelemetry instruments.\n * Metric definitions and value history are persisted via an internal\n * EntityStorageTelemetryConnector instance created at construction time.\n * Call `start()` to initialise the MeterProvider and exporters; metrics can be\n * created and queried before start() — OTEL forwarding is simply skipped until\n * the MeterProvider is running.\n */\nexport class OpenTelemetryTelemetryConnector implements ITelemetryConnector {\n\t/**\n\t * The namespace supported by the telemetry connector.\n\t */\n\tpublic static readonly NAMESPACE: string = \"opentelemetry\";\n\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<OpenTelemetryTelemetryConnector>();\n\n\t/**\n\t * Config options, stored so start() can initialise the MeterProvider.\n\t * @internal\n\t */\n\tprivate readonly _config: IOpenTelemetryTelemetryConnectorConfig;\n\n\t/**\n\t * Internal entity-storage connector that owns all metric metadata and value history.\n\t * Created at construction time — fails fast if entity storage is not set up.\n\t * @internal\n\t */\n\tprivate readonly _inner: EntityStorageTelemetryConnector;\n\n\t/**\n\t * Live OTEL instrument handles keyed by metric id, paired with the metric type\n\t * so addMetricValue can dispatch without querying storage.\n\t * These are runtime objects and cannot be persisted.\n\t * @internal\n\t */\n\tprivate readonly _instruments: Map<\n\t\tstring,\n\t\t{ metricType: MetricType; instrument: Counter | UpDownCounter | Gauge }\n\t>;\n\n\t/**\n\t * The MeterProvider that owns all instruments. Set by start(), cleared by stop().\n\t * @internal\n\t */\n\tprivate _meterProvider: MeterProvider | undefined;\n\n\t/**\n\t * The Meter used to create instruments. Set by start(), cleared by stop().\n\t * @internal\n\t */\n\tprivate _meter: Meter | undefined;\n\n\t/**\n\t * Create a new instance of OpenTelemetryTelemetryConnector.\n\t * Eagerly constructs the inner EntityStorageTelemetryConnector — if the required\n\t * entity storage types are not registered this constructor will throw (fail fast).\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options?: IOpenTelemetryTelemetryConnectorConstructorOptions) {\n\t\tthis._config = options?.config ?? {};\n\t\tthis._inner = new EntityStorageTelemetryConnector({\n\t\t\tloggingComponentType: options?.loggingComponentType,\n\t\t\ttelemetryMetricStorageConnectorType: options?.telemetryMetricStorageConnectorType,\n\t\t\ttelemetryMetricValueStorageConnectorType: options?.telemetryMetricValueStorageConnectorType\n\t\t});\n\t\tthis._instruments = new Map();\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 OpenTelemetryTelemetryConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Initialise the MeterProvider and configured exporters.\n\t * Calling start() on an already-started connector is a no-op.\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\tif (!Is.undefined(this._meterProvider)) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst readers: MetricReader[] = [];\n\t\tfor (const [, config] of Object.entries(this._config.readers ?? {})) {\n\t\t\tif (config.type === \"prometheus\") {\n\t\t\t\t// Dynamic import so @opentelemetry/exporter-prometheus is an optional peer dep;\n\t\t\t\t// consumers only need to install it if they actually configure a Prometheus reader.\n\t\t\t\tconst { PrometheusExporter: prometheusExporterClass } =\n\t\t\t\t\tawait import(\"@opentelemetry/exporter-prometheus\");\n\t\t\t\treaders.push(\n\t\t\t\t\tnew prometheusExporterClass({\n\t\t\t\t\t\tport: config.port,\n\t\t\t\t\t\tendpoint: config.endpoint,\n\t\t\t\t\t\t// PrometheusExporter uses preventServerStart (inverted); our config\n\t\t\t\t\t\t// exposes the more intuitive startServer (defaults to true).\n\t\t\t\t\t\tpreventServerStart: !(config.startServer ?? true),\n\t\t\t\t\t\tprefix: config.prefix\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tthrow new GeneralError(OpenTelemetryTelemetryConnector.CLASS_NAME, \"unknownReaderType\", {\n\t\t\t\t\ttype: config.type\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis._meterProvider = new MeterProvider({ readers });\n\t\tthis._meter = this._meterProvider.getMeter(\n\t\t\tthis._config.meterName ?? \"twin-telemetry\",\n\t\t\tthis._config.meterVersion ?? \"1.0.0\"\n\t\t);\n\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\t\tawait nodeLogging?.log({\n\t\t\tsource: OpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"connectorStarted\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: { readerCount: Object.keys(this._config.readers ?? {}).length }\n\t\t});\n\t}\n\n\t/**\n\t * Shut down the MeterProvider and release resources.\n\t * Calling stop() on a connector that has not been started is a no-op.\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 (Is.undefined(this._meterProvider)) {\n\t\t\treturn;\n\t\t}\n\n\t\tawait this._meterProvider.shutdown();\n\t\tthis._meterProvider = undefined;\n\t\tthis._meter = undefined;\n\t\tthis._instruments.clear();\n\n\t\tconst nodeLogging = ComponentFactory.getIfExists<ILoggingComponent>(nodeLoggingComponentType);\n\t\tawait nodeLogging?.log({\n\t\t\tsource: OpenTelemetryTelemetryConnector.CLASS_NAME,\n\t\t\tmessage: \"connectorStopped\",\n\t\t\tlevel: \"info\",\n\t\t\tdata: {}\n\t\t});\n\t}\n\n\t/**\n\t * Create a new metric.\n\t * The definition is always persisted via the inner entity-storage connector.\n\t * If the MeterProvider is running the corresponding OTEL instrument is also registered.\n\t * @param metric The metric details.\n\t * @returns Nothing.\n\t */\n\tpublic async createMetric(metric: ITelemetryMetric): Promise<void> {\n\t\t// Entity storage performs all validation and throws AlreadyExistsError on duplicates.\n\t\tawait this._inner.createMetric(metric);\n\n\t\t// Register an OTEL instrument only when the MeterProvider is running.\n\t\tconst meter = this._meter;\n\t\tif (meter !== undefined) {\n\t\t\tconst instrumentOptions = {\n\t\t\t\tdescription: metric.description,\n\t\t\t\tunit: metric.unit\n\t\t\t};\n\t\t\tlet instrument: Counter | UpDownCounter | Gauge;\n\t\t\tif (metric.type === MetricType.Counter) {\n\t\t\t\tinstrument = meter.createCounter(metric.id, instrumentOptions);\n\t\t\t} else if (metric.type === MetricType.IncDecCounter) {\n\t\t\t\tinstrument = meter.createUpDownCounter(metric.id, instrumentOptions);\n\t\t\t} else {\n\t\t\t\tinstrument = meter.createGauge(metric.id, instrumentOptions);\n\t\t\t}\n\t\t\tthis._instruments.set(metric.id, { metricType: metric.type, instrument });\n\t\t}\n\t}\n\n\t/**\n\t * Get the metric details and its most recent value.\n\t * @param id The metric id.\n\t * @returns The metric details and its most recent value.\n\t */\n\tpublic async getMetric(id: string): Promise<{\n\t\tmetric: ITelemetryMetric;\n\t\tvalue: ITelemetryMetricValue;\n\t}> {\n\t\tconst result = await this._inner.getMetric(id);\n\n\t\t// When the metric has no recorded values yet the inner connector returns\n\t\t// entities[0] = undefined via queryValues. Return a placeholder so callers\n\t\t// are not surprised by a null value field — treat id=\"\" as \"no measurements yet\".\n\t\tconst value = result.value ?? { id: \"\", ts: 0, value: 0 };\n\t\treturn { metric: result.metric, value };\n\t}\n\n\t/**\n\t * Update the metric metadata.\n\t * Note: OpenTelemetry instrument descriptors are immutable once created.\n\t * This method updates the persisted metadata mirror; the description/unit changes\n\t * are NOT propagated to the registered MeterProvider and will not appear at the\n\t * OTEL backend (Prometheus, OTLP, etc.).\n\t * @param metric The metric details (type cannot be changed).\n\t * @returns Nothing.\n\t */\n\tpublic async updateMetric(metric: Omit<ITelemetryMetric, \"type\">): Promise<void> {\n\t\treturn this._inner.updateMetric(metric);\n\t}\n\n\t/**\n\t * Record a metric value.\n\t * Entity storage always receives the value first and performs all validation.\n\t * If the MeterProvider is running the measurement is also forwarded to the OTEL instrument.\n\t * Counter accepts positive integers or \"inc\".\n\t * UpDownCounter accepts integers (positive or negative) or \"inc\"/\"dec\".\n\t * Gauge accepts any number.\n\t * @param id The id of the metric.\n\t * @param value The value for the operation.\n\t * @param customData Optional custom data forwarded as OTEL attributes.\n\t * @returns The id of the new metric value entry.\n\t */\n\tpublic async addMetricValue(\n\t\tid: string,\n\t\tvalue: \"inc\" | \"dec\" | number,\n\t\tcustomData?: { [key: string]: unknown }\n\t): Promise<string> {\n\t\t// Entity storage validates and persists first; throws NotFoundError if metric not found.\n\t\tconst valueId = await this._inner.addMetricValue(id, value, customData);\n\n\t\t// Forward to the OTEL instrument only when the MeterProvider is running.\n\t\tif (this._meter !== undefined) {\n\t\t\tconst entry = this._instruments.get(id);\n\t\t\tif (entry !== undefined) {\n\t\t\t\tconst attributes = this.toAttributes(customData);\n\t\t\t\tconst { metricType, instrument } = entry;\n\n\t\t\t\t// Value already validated by inner connector — dispatch unconditionally.\n\t\t\t\tif (metricType === MetricType.Counter) {\n\t\t\t\t\t(instrument as Counter).add(value === \"inc\" ? 1 : (value as number), attributes);\n\t\t\t\t} else if (metricType === MetricType.IncDecCounter) {\n\t\t\t\t\tlet delta: number;\n\t\t\t\t\tif (value === \"inc\") {\n\t\t\t\t\t\tdelta = 1;\n\t\t\t\t\t} else if (value === \"dec\") {\n\t\t\t\t\t\tdelta = -1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdelta = value;\n\t\t\t\t\t}\n\t\t\t\t\t(instrument as UpDownCounter).add(delta, attributes);\n\t\t\t\t} else {\n\t\t\t\t\t(instrument as Gauge).record(value as number, attributes);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn valueId;\n\t}\n\n\t/**\n\t * Remove a metric and its persisted value history.\n\t * Note: OpenTelemetry exposes no API to deregister an instrument from a Meter,\n\t * so the underlying Counter/UpDownCounter/Gauge remains resident for the lifetime\n\t * of the process. Re-creating a metric with the same id but a different MetricType\n\t * is therefore not safe.\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\tthis._instruments.delete(id);\n\t\t// The inner connector cascades and removes all associated metric values.\n\t\treturn this._inner.removeMetric(id);\n\t}\n\n\t/**\n\t * Query the registered metrics, optionally filtered by type.\n\t * @param type The type of the metric.\n\t * @param cursor The cursor to request the next page.\n\t * @param limit Limit the number of entities to return.\n\t * @returns The matching metrics and an optional cursor for the next page.\n\t */\n\tpublic async query(\n\t\ttype?: MetricType,\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\tentities: ITelemetryMetric[];\n\t\tcursor?: string;\n\t}> {\n\t\treturn this._inner.query(type, cursor, limit);\n\t}\n\n\t/**\n\t * Query the recorded values for a metric, ordered by most recent first.\n\t * @param id The id of the metric.\n\t * @param timeStart The inclusive start time (epoch ms).\n\t * @param timeEnd The inclusive end time (epoch ms).\n\t * @param cursor The cursor returned by the previous call.\n\t * @param limit Limit the number of values to return.\n\t * @returns The metric details, matching values, and an optional cursor for the next page.\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\tmetric: ITelemetryMetric;\n\t\tentities: ITelemetryMetricValue[];\n\t\tcursor?: string;\n\t}> {\n\t\treturn this._inner.queryValues(id, timeStart, timeEnd, cursor, limit);\n\t}\n\n\t/**\n\t * Convert customData to OTEL-compatible Attributes.\n\t * Scalar values (string, number, boolean) and uniform primitive arrays\n\t * (string[], number[], boolean[]) are forwarded; other values are silently dropped.\n\t * @param customData The raw custom data map.\n\t * @returns An OTEL Attributes object.\n\t * @internal\n\t */\n\tprivate toAttributes(customData?: { [key: string]: unknown }): Attributes {\n\t\tif (Is.empty(customData)) {\n\t\t\treturn {};\n\t\t}\n\t\tconst attrs: Attributes = {};\n\t\tfor (const [key, val] of Object.entries(customData)) {\n\t\t\tif (Is.string(val) || Is.number(val) || Is.boolean(val)) {\n\t\t\t\tattrs[key] = val;\n\t\t\t} else if (Is.arrayValue(val)) {\n\t\t\t\tif (\n\t\t\t\t\t(Is.string(val[0]) || Is.number(val[0]) || Is.boolean(val[0])) &&\n\t\t\t\t\tval.every(el => typeof el === typeof val[0])\n\t\t\t\t) {\n\t\t\t\t\tattrs[key] = val as string[] | number[] | boolean[];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn attrs;\n\t}\n}\n"]}
@@ -1,4 +1,6 @@
1
1
  export * from "./models/IOpenTelemetryPrometheusReaderConfig.js";
2
2
  export * from "./models/IOpenTelemetryReaderConfig.js";
3
+ export * from "./models/IOpenTelemetryTelemetryConnectorConfig.js";
3
4
  export * from "./models/IOpenTelemetryTelemetryConnectorConstructorOptions.js";
5
+ export * from "./models/openTelemetryReaderTypes.js";
4
6
  export * from "./openTelemetryTelemetryConnector.js";
@@ -1,12 +1,13 @@
1
+ import type { OpenTelemetryReaderTypes } from "./openTelemetryReaderTypes.js";
1
2
  /**
2
3
  * Configuration for a Prometheus scrape-endpoint reader.
3
4
  * The connector instantiates a PrometheusExporter from these options in start().
4
5
  */
5
6
  export interface IOpenTelemetryPrometheusReaderConfig {
6
7
  /**
7
- * Discriminator — must be "prometheus".
8
+ * Type.
8
9
  */
9
- type: "prometheus";
10
+ type: typeof OpenTelemetryReaderTypes.Prometheus;
10
11
  /**
11
12
  * TCP port the Prometheus HTTP server listens on.
12
13
  * @default 9464
@@ -0,0 +1,24 @@
1
+ import type { IOpenTelemetryReaderConfig } from "./IOpenTelemetryReaderConfig.js";
2
+ /**
3
+ * The options for the OpenTelemetry telemetry connector constructor.
4
+ */
5
+ export interface IOpenTelemetryTelemetryConnectorConfig {
6
+ /**
7
+ * The name of the OpenTelemetry meter used to create instruments.
8
+ * @default twin-telemetry
9
+ */
10
+ meterName?: string;
11
+ /**
12
+ * The version reported by the OpenTelemetry meter.
13
+ * @default 1.0.0
14
+ */
15
+ meterVersion?: string;
16
+ /**
17
+ * Named metric-reader configurations keyed by an arbitrary id.
18
+ * Each entry's `type` field determines which exporter the connector instantiates
19
+ * in start(). Omit or pass an empty object for a no-op provider (useful for tests).
20
+ */
21
+ readers?: {
22
+ [id: string]: IOpenTelemetryReaderConfig;
23
+ };
24
+ }
@@ -1,27 +1,8 @@
1
- import type { IOpenTelemetryReaderConfig } from "./IOpenTelemetryReaderConfig.js";
1
+ import type { IOpenTelemetryTelemetryConnectorConfig } from "./IOpenTelemetryTelemetryConnectorConfig.js";
2
2
  /**
3
3
  * The options for the OpenTelemetry telemetry connector constructor.
4
4
  */
5
5
  export interface IOpenTelemetryTelemetryConnectorConstructorOptions {
6
- /**
7
- * The name of the OpenTelemetry meter used to create instruments.
8
- * @default twin-telemetry
9
- */
10
- meterName?: string;
11
- /**
12
- * The version reported by the OpenTelemetry meter.
13
- * @default 0.0.1
14
- */
15
- meterVersion?: string;
16
- /**
17
- * Named metric-reader configurations keyed by an arbitrary id.
18
- * Each entry's `type` field determines which exporter the connector instantiates
19
- * in start(). Omit or pass an empty object for a no-op provider (useful for tests).
20
- * Example: { "main": { type: "prometheus", port: 9464 } }
21
- */
22
- readers?: {
23
- [id: string]: IOpenTelemetryReaderConfig;
24
- };
25
6
  /**
26
7
  * The component type to use for logging inside the connector and the inner
27
8
  * entity-storage connector. When omitted logging is disabled.
@@ -39,4 +20,8 @@ export interface IOpenTelemetryTelemetryConnectorConstructorOptions {
39
20
  * @default telemetry-metric-value
40
21
  */
41
22
  telemetryMetricValueStorageConnectorType?: string;
23
+ /**
24
+ * The config for the telemetry connector.
25
+ */
26
+ config?: IOpenTelemetryTelemetryConnectorConfig;
42
27
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The types of readers.
3
+ */
4
+ export declare const OpenTelemetryReaderTypes: {
5
+ /**
6
+ * Prometheus.
7
+ */
8
+ readonly Prometheus: "prometheus";
9
+ };
10
+ /**
11
+ * The types of readers.
12
+ */
13
+ export type OpenTelemetryReaderTypes = (typeof OpenTelemetryReaderTypes)[keyof typeof OpenTelemetryReaderTypes];
package/docs/changelog.md CHANGED
@@ -1 +1,40 @@
1
1
  # Changelog
2
+
3
+ ## [0.0.3-next.6](https://github.com/iotaledger/twin-telemetry/compare/telemetry-connector-opentelemetry-v0.0.3-next.5...telemetry-connector-opentelemetry-v0.0.3-next.6) (2026-05-11)
4
+
5
+
6
+ ### Features
7
+
8
+ * improve open telemetry config ([8803201](https://github.com/iotaledger/twin-telemetry/commit/8803201611b1799a00cd657e0675f85596c4edce))
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/telemetry-connector-entity-storage bumped from 0.0.3-next.5 to 0.0.3-next.6
16
+ * @twin.org/telemetry-models bumped from 0.0.3-next.5 to 0.0.3-next.6
17
+
18
+ ## [0.0.3-next.5](https://github.com/iotaledger/twin-telemetry/compare/telemetry-connector-opentelemetry-v0.0.3-next.4...telemetry-connector-opentelemetry-v0.0.3-next.5) (2026-05-11)
19
+
20
+
21
+ ### Features
22
+
23
+ * add OpenTelemetry connector, tests and a graphana example ([#22](https://github.com/iotaledger/twin-telemetry/issues/22)) ([ae3e08f](https://github.com/iotaledger/twin-telemetry/commit/ae3e08f459cb20ff5d2a149982676de8c2f90ae1))
24
+ * typescript 6 update ([0acecc2](https://github.com/iotaledger/twin-telemetry/commit/0acecc2692b174fbaec6a8a89bd9277fa3530b5f))
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * docs ([acc23a6](https://github.com/iotaledger/twin-telemetry/commit/acc23a674e75752e3817e2717d6b029d0b1404ff))
30
+ * open-telemetry config options ([c35ee76](https://github.com/iotaledger/twin-telemetry/commit/c35ee764ccad9168985fcac6efc3461422884ce4))
31
+
32
+
33
+ ### Dependencies
34
+
35
+ * The following workspace dependencies were updated
36
+ * dependencies
37
+ * @twin.org/telemetry-connector-entity-storage bumped from 0.0.3-next.2 to 0.0.3-next.5
38
+ * @twin.org/telemetry-models bumped from 0.0.3-next.2 to 0.0.3-next.5
39
+
40
+ ## Changelog
package/docs/examples.md CHANGED
@@ -51,10 +51,12 @@ EntityStorageConnectorFactory.register(
51
51
  );
52
52
 
53
53
  const connector = new OpenTelemetryTelemetryConnector({
54
- meterName: 'my-service',
55
- meterVersion: '1.0.0',
56
- readers: {
57
- prometheus: { type: 'prometheus', port: 9464 }
54
+ config: {
55
+ meterName: 'my-service',
56
+ meterVersion: '1.0.0',
57
+ readers: {
58
+ prometheus: { type: 'prometheus', port: 9464 }
59
+ }
58
60
  }
59
61
  });
60
62
 
@@ -7,8 +7,14 @@
7
7
  ## Interfaces
8
8
 
9
9
  - [IOpenTelemetryPrometheusReaderConfig](interfaces/IOpenTelemetryPrometheusReaderConfig.md)
10
+ - [IOpenTelemetryTelemetryConnectorConfig](interfaces/IOpenTelemetryTelemetryConnectorConfig.md)
10
11
  - [IOpenTelemetryTelemetryConnectorConstructorOptions](interfaces/IOpenTelemetryTelemetryConnectorConstructorOptions.md)
11
12
 
12
13
  ## Type Aliases
13
14
 
14
15
  - [IOpenTelemetryReaderConfig](type-aliases/IOpenTelemetryReaderConfig.md)
16
+ - [OpenTelemetryReaderTypes](type-aliases/OpenTelemetryReaderTypes.md)
17
+
18
+ ## Variables
19
+
20
+ - [OpenTelemetryReaderTypes](variables/OpenTelemetryReaderTypes.md)
@@ -9,7 +9,7 @@ The connector instantiates a PrometheusExporter from these options in start().
9
9
 
10
10
  > **type**: `"prometheus"`
11
11
 
12
- Discriminator — must be "prometheus".
12
+ Type.
13
13
 
14
14
  ***
15
15
 
@@ -0,0 +1,45 @@
1
+ # Interface: IOpenTelemetryTelemetryConnectorConfig
2
+
3
+ The options for the OpenTelemetry telemetry connector constructor.
4
+
5
+ ## Properties
6
+
7
+ ### meterName? {#metername}
8
+
9
+ > `optional` **meterName?**: `string`
10
+
11
+ The name of the OpenTelemetry meter used to create instruments.
12
+
13
+ #### Default
14
+
15
+ ```ts
16
+ twin-telemetry
17
+ ```
18
+
19
+ ***
20
+
21
+ ### meterVersion? {#meterversion}
22
+
23
+ > `optional` **meterVersion?**: `string`
24
+
25
+ The version reported by the OpenTelemetry meter.
26
+
27
+ #### Default
28
+
29
+ ```ts
30
+ 1.0.0
31
+ ```
32
+
33
+ ***
34
+
35
+ ### readers? {#readers}
36
+
37
+ > `optional` **readers?**: `object`
38
+
39
+ Named metric-reader configurations keyed by an arbitrary id.
40
+ Each entry's `type` field determines which exporter the connector instantiates
41
+ in start(). Omit or pass an empty object for a no-op provider (useful for tests).
42
+
43
+ #### Index Signature
44
+
45
+ \[`id`: `string`\]: [`IOpenTelemetryPrometheusReaderConfig`](IOpenTelemetryPrometheusReaderConfig.md)
@@ -4,49 +4,6 @@ The options for the OpenTelemetry telemetry connector constructor.
4
4
 
5
5
  ## Properties
6
6
 
7
- ### meterName? {#metername}
8
-
9
- > `optional` **meterName?**: `string`
10
-
11
- The name of the OpenTelemetry meter used to create instruments.
12
-
13
- #### Default
14
-
15
- ```ts
16
- twin-telemetry
17
- ```
18
-
19
- ***
20
-
21
- ### meterVersion? {#meterversion}
22
-
23
- > `optional` **meterVersion?**: `string`
24
-
25
- The version reported by the OpenTelemetry meter.
26
-
27
- #### Default
28
-
29
- ```ts
30
- 0.0.1
31
- ```
32
-
33
- ***
34
-
35
- ### readers? {#readers}
36
-
37
- > `optional` **readers?**: `object`
38
-
39
- Named metric-reader configurations keyed by an arbitrary id.
40
- Each entry's `type` field determines which exporter the connector instantiates
41
- in start(). Omit or pass an empty object for a no-op provider (useful for tests).
42
- Example: { "main": { type: "prometheus", port: 9464 } }
43
-
44
- #### Index Signature
45
-
46
- \[`id`: `string`\]: [`IOpenTelemetryPrometheusReaderConfig`](IOpenTelemetryPrometheusReaderConfig.md)
47
-
48
- ***
49
-
50
7
  ### loggingComponentType? {#loggingcomponenttype}
51
8
 
52
9
  > `optional` **loggingComponentType?**: `string`
@@ -83,3 +40,11 @@ Must be registered in `EntityStorageConnectorFactory` before calling `start()`.
83
40
  ```ts
84
41
  telemetry-metric-value
85
42
  ```
43
+
44
+ ***
45
+
46
+ ### config? {#config}
47
+
48
+ > `optional` **config?**: [`IOpenTelemetryTelemetryConnectorConfig`](IOpenTelemetryTelemetryConnectorConfig.md)
49
+
50
+ The config for the telemetry connector.
@@ -0,0 +1,5 @@
1
+ # Type Alias: OpenTelemetryReaderTypes
2
+
3
+ > **OpenTelemetryReaderTypes** = *typeof* [`OpenTelemetryReaderTypes`](../variables/OpenTelemetryReaderTypes.md)\[keyof *typeof* [`OpenTelemetryReaderTypes`](../variables/OpenTelemetryReaderTypes.md)\]
4
+
5
+ The types of readers.
@@ -0,0 +1,13 @@
1
+ # Variable: OpenTelemetryReaderTypes
2
+
3
+ > `const` **OpenTelemetryReaderTypes**: `object`
4
+
5
+ The types of readers.
6
+
7
+ ## Type Declaration
8
+
9
+ ### Prometheus {#prometheus}
10
+
11
+ > `readonly` **Prometheus**: `"prometheus"` = `"prometheus"`
12
+
13
+ Prometheus.
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@twin.org/telemetry-connector-opentelemetry",
3
- "version": "0.0.3-next.2",
3
+ "version": "0.0.3-next.6",
4
4
  "description": "OpenTelemetry connector for pushing telemetry metrics to OTEL-compatible backends.",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "git+https://github.com/twinfoundation/telemetry.git",
7
+ "url": "git+https://github.com/iotaledger/telemetry.git",
8
8
  "directory": "packages/telemetry-connector-opentelemetry"
9
9
  },
10
10
  "author": "adrian.sanchez.sequeira@iota.org",
@@ -20,8 +20,8 @@
20
20
  "@twin.org/entity-storage-models": "next",
21
21
  "@twin.org/logging-models": "next",
22
22
  "@twin.org/nameof": "next",
23
- "@twin.org/telemetry-connector-entity-storage": "0.0.3-next.2",
24
- "@twin.org/telemetry-models": "0.0.3-next.2"
23
+ "@twin.org/telemetry-connector-entity-storage": "0.0.3-next.6",
24
+ "@twin.org/telemetry-models": "0.0.3-next.6"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "@opentelemetry/exporter-prometheus": ">=0.200.0"
@@ -64,7 +64,7 @@
64
64
  "integration"
65
65
  ],
66
66
  "bugs": {
67
- "url": "git+https://github.com/twinfoundation/telemetry/issues"
67
+ "url": "git+https://github.com/iotaledger/telemetry/issues"
68
68
  },
69
69
  "homepage": "https://twindev.org"
70
70
  }