@btravstack/observability 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _btravstack_config = require("@btravstack/config");
3
3
  let _btravstack_core = require("@btravstack/core");
4
- let unthrown = require("unthrown");
5
4
  let _btravstack_di = require("@btravstack/di");
6
5
  //#region src/config.ts
6
+ const isLevel = (value) => _btravstack_core.LEVELS.includes(value);
7
7
  /**
8
8
  * `LOG_LEVEL`, as a `ConfigField` of the six levels and nothing else.
9
9
  *
@@ -18,7 +18,7 @@ const logLevel = (options = {}) => {
18
18
  const text = _btravstack_config.Config.string("LOG_LEVEL", { default: options.default ?? "info" });
19
19
  return {
20
20
  variable: text.variable,
21
- parse: (raw) => text.parse(raw).flatMap((value) => _btravstack_core.LEVELS.includes(value) ? (0, unthrown.Ok)(value) : (0, unthrown.Err)(new _btravstack_config.ConfigFieldInvalid({ reason: `must be one of ${_btravstack_core.LEVELS.join(", ")}, got ${JSON.stringify(value)}` })))
21
+ parse: (raw) => text.parse(raw).ensure(isLevel, (value) => new _btravstack_config.ConfigFieldInvalid({ reason: `must be one of ${_btravstack_core.LEVELS.join(", ")}, got ${JSON.stringify(value)}` }))
22
22
  };
23
23
  };
24
24
  /** The schema `observability()` binds `LoggerConfig` through — one field today, and the place a second one lands. */
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Config, ConfigFieldInvalid, Env } from "@btravstack/config";
2
2
  import { LEVELS, Logger, currentUnit } from "@btravstack/core";
3
- import { Err, Ok } from "unthrown";
4
3
  import { Module, Port, Provider } from "@btravstack/di";
5
4
  //#region src/config.ts
5
+ const isLevel = (value) => LEVELS.includes(value);
6
6
  /**
7
7
  * `LOG_LEVEL`, as a `ConfigField` of the six levels and nothing else.
8
8
  *
@@ -17,7 +17,7 @@ const logLevel = (options = {}) => {
17
17
  const text = Config.string("LOG_LEVEL", { default: options.default ?? "info" });
18
18
  return {
19
19
  variable: text.variable,
20
- parse: (raw) => text.parse(raw).flatMap((value) => LEVELS.includes(value) ? Ok(value) : Err(new ConfigFieldInvalid({ reason: `must be one of ${LEVELS.join(", ")}, got ${JSON.stringify(value)}` })))
20
+ parse: (raw) => text.parse(raw).ensure(isLevel, (value) => new ConfigFieldInvalid({ reason: `must be one of ${LEVELS.join(", ")}, got ${JSON.stringify(value)}` }))
21
21
  };
22
22
  };
23
23
  /** The schema `observability()` binds `LoggerConfig` through — one field today, and the place a second one lands. */
package/dist/otel.cjs CHANGED
@@ -1,24 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let _btravstack_core = require("@btravstack/core");
3
- let unthrown = require("unthrown");
4
3
  let _btravstack_di = require("@btravstack/di");
5
4
  let _opentelemetry_api = require("@opentelemetry/api");
6
5
  let _opentelemetry_sdk_node = require("@opentelemetry/sdk-node");
6
+ let unthrown = require("unthrown");
7
7
  //#region src/otel.ts
8
- /**
9
- * The tracing half of observability — the ADAPTER, not the contract. `Tracer`
10
- * and `Meter` are the kernel's ports, and OTel's presence stops at this subpath,
11
- * behind an optional peer exactly like `pino`.
12
- *
13
- * The ports are narrowings of OTel's own shapes, so what the SDK hands back
14
- * satisfies them with no translation in between.
15
- */
16
- /**
17
- * The SDK itself, module-private: providing it is what starts it, and `release`
18
- * is what flushes it. It rides the graph as a RESOURCE so the kernel closes it
19
- * on every exit path — a span lost in `shutdown()` becomes a `teardownError` and
20
- * exit `2`, never silence.
21
- */
22
8
  var OtelSdk = class extends (0, _btravstack_di.Port)("OtelSdk") {};
23
9
  /**
24
10
  * The OTel starter: a module providing `Tracer` and `Meter` over a `NodeSDK`
@@ -33,15 +19,32 @@ var OtelSdk = class extends (0, _btravstack_di.Port)("OtelSdk") {};
33
19
  * **Auto-instrumentation cannot live here**: the register hook must be
34
20
  * preloaded before the instrumented libraries are imported, which no DI
35
21
  * provider can promise. Preloading is the deployment's line.
22
+ *
23
+ * **What CAN live here is a starter's own instrumentation.** Every package
24
+ * that contributed to `Instrumentations` is loaded and handed to the SDK, so
25
+ * composing a starter is what declares the instrumentation and composing this
26
+ * is what turns it on. A contribution whose optional peer is not installed
27
+ * loads as `undefined` and is dropped — the contributor says so at `debug`,
28
+ * since it is the one that knows why.
29
+ *
30
+ * The module contributes one member of its own, which loads nothing. That is
31
+ * what makes `Instrumentations` a port the graph always has — a collector
32
+ * depending on a set port NOTHING provides is an unmet dependency, both at
33
+ * plan time and in `Needs`. Guice's `newSetBinder` declares the empty set for
34
+ * the same reason.
36
35
  */
37
36
  const otel = (options) => (0, _btravstack_di.Module)("Otel")({
38
37
  provides: [
39
- (0, _btravstack_di.Provider)(OtelSdk)({
40
- acquire: () => {
41
- const sdk = new _opentelemetry_sdk_node.NodeSDK(options);
38
+ _btravstack_di.Provider.member(_btravstack_core.Instrumentations)({ value: () => Promise.resolve(void 0) }),
39
+ (0, _btravstack_di.Provider)(OtelSdk)({ offered: _btravstack_core.Instrumentations }, {
40
+ acquire: ({ offered }) => (0, unthrown.fromSafePromise)(Promise.all(offered.map((load) => load())).then((loaded) => {
41
+ const sdk = new _opentelemetry_sdk_node.NodeSDK({
42
+ ...options,
43
+ instrumentations: [...options?.instrumentations ?? [], ...loaded.filter((one) => one !== void 0)]
44
+ });
42
45
  sdk.start();
43
- return (0, unthrown.Ok)(sdk);
44
- },
46
+ return sdk;
47
+ })),
45
48
  release: (sdk) => sdk.shutdown()
46
49
  }),
47
50
  (0, _btravstack_di.Provider)(_btravstack_core.Tracer)({ sdk: OtelSdk }, { sync: () => {
package/dist/otel.d.cts CHANGED
@@ -15,6 +15,19 @@ import { NodeSDKConfiguration } from "@opentelemetry/sdk-node";
15
15
  * **Auto-instrumentation cannot live here**: the register hook must be
16
16
  * preloaded before the instrumented libraries are imported, which no DI
17
17
  * provider can promise. Preloading is the deployment's line.
18
+ *
19
+ * **What CAN live here is a starter's own instrumentation.** Every package
20
+ * that contributed to `Instrumentations` is loaded and handed to the SDK, so
21
+ * composing a starter is what declares the instrumentation and composing this
22
+ * is what turns it on. A contribution whose optional peer is not installed
23
+ * loads as `undefined` and is dropped — the contributor says so at `debug`,
24
+ * since it is the one that knows why.
25
+ *
26
+ * The module contributes one member of its own, which loads nothing. That is
27
+ * what makes `Instrumentations` a port the graph always has — a collector
28
+ * depending on a set port NOTHING provides is an unmet dependency, both at
29
+ * plan time and in `Needs`. Guice's `newSetBinder` declares the empty set for
30
+ * the same reason.
18
31
  */
19
32
  declare const otel: (options?: Partial<NodeSDKConfiguration>) => Module<Tracer | Meter, never, Scope>;
20
33
  declare const UnitSpan_base: import("@btravstack/di").PortClass<"UnitSpan">;
package/dist/otel.d.mts CHANGED
@@ -15,6 +15,19 @@ import { NodeSDKConfiguration } from "@opentelemetry/sdk-node";
15
15
  * **Auto-instrumentation cannot live here**: the register hook must be
16
16
  * preloaded before the instrumented libraries are imported, which no DI
17
17
  * provider can promise. Preloading is the deployment's line.
18
+ *
19
+ * **What CAN live here is a starter's own instrumentation.** Every package
20
+ * that contributed to `Instrumentations` is loaded and handed to the SDK, so
21
+ * composing a starter is what declares the instrumentation and composing this
22
+ * is what turns it on. A contribution whose optional peer is not installed
23
+ * loads as `undefined` and is dropped — the contributor says so at `debug`,
24
+ * since it is the one that knows why.
25
+ *
26
+ * The module contributes one member of its own, which loads nothing. That is
27
+ * what makes `Instrumentations` a port the graph always has — a collector
28
+ * depending on a set port NOTHING provides is an unmet dependency, both at
29
+ * plan time and in `Needs`. Guice's `newSetBinder` declares the empty set for
30
+ * the same reason.
18
31
  */
19
32
  declare const otel: (options?: Partial<NodeSDKConfiguration>) => Module<Tracer | Meter, never, Scope>;
20
33
  declare const UnitSpan_base: import("@btravstack/di").PortClass<"UnitSpan">;
package/dist/otel.mjs CHANGED
@@ -1,23 +1,9 @@
1
- import { Meter, Tracer, currentUnit } from "@btravstack/core";
2
- import { Ok } from "unthrown";
1
+ import { Instrumentations, Meter, Tracer, currentUnit } from "@btravstack/core";
3
2
  import { Module, Port, Provider } from "@btravstack/di";
4
3
  import { metrics, trace } from "@opentelemetry/api";
5
4
  import { NodeSDK } from "@opentelemetry/sdk-node";
5
+ import { fromSafePromise } from "unthrown";
6
6
  //#region src/otel.ts
7
- /**
8
- * The tracing half of observability — the ADAPTER, not the contract. `Tracer`
9
- * and `Meter` are the kernel's ports, and OTel's presence stops at this subpath,
10
- * behind an optional peer exactly like `pino`.
11
- *
12
- * The ports are narrowings of OTel's own shapes, so what the SDK hands back
13
- * satisfies them with no translation in between.
14
- */
15
- /**
16
- * The SDK itself, module-private: providing it is what starts it, and `release`
17
- * is what flushes it. It rides the graph as a RESOURCE so the kernel closes it
18
- * on every exit path — a span lost in `shutdown()` becomes a `teardownError` and
19
- * exit `2`, never silence.
20
- */
21
7
  var OtelSdk = class extends Port("OtelSdk") {};
22
8
  /**
23
9
  * The OTel starter: a module providing `Tracer` and `Meter` over a `NodeSDK`
@@ -32,15 +18,32 @@ var OtelSdk = class extends Port("OtelSdk") {};
32
18
  * **Auto-instrumentation cannot live here**: the register hook must be
33
19
  * preloaded before the instrumented libraries are imported, which no DI
34
20
  * provider can promise. Preloading is the deployment's line.
21
+ *
22
+ * **What CAN live here is a starter's own instrumentation.** Every package
23
+ * that contributed to `Instrumentations` is loaded and handed to the SDK, so
24
+ * composing a starter is what declares the instrumentation and composing this
25
+ * is what turns it on. A contribution whose optional peer is not installed
26
+ * loads as `undefined` and is dropped — the contributor says so at `debug`,
27
+ * since it is the one that knows why.
28
+ *
29
+ * The module contributes one member of its own, which loads nothing. That is
30
+ * what makes `Instrumentations` a port the graph always has — a collector
31
+ * depending on a set port NOTHING provides is an unmet dependency, both at
32
+ * plan time and in `Needs`. Guice's `newSetBinder` declares the empty set for
33
+ * the same reason.
35
34
  */
36
35
  const otel = (options) => Module("Otel")({
37
36
  provides: [
38
- Provider(OtelSdk)({
39
- acquire: () => {
40
- const sdk = new NodeSDK(options);
37
+ Provider.member(Instrumentations)({ value: () => Promise.resolve(void 0) }),
38
+ Provider(OtelSdk)({ offered: Instrumentations }, {
39
+ acquire: ({ offered }) => fromSafePromise(Promise.all(offered.map((load) => load())).then((loaded) => {
40
+ const sdk = new NodeSDK({
41
+ ...options,
42
+ instrumentations: [...options?.instrumentations ?? [], ...loaded.filter((one) => one !== void 0)]
43
+ });
41
44
  sdk.start();
42
- return Ok(sdk);
43
- },
45
+ return sdk;
46
+ })),
44
47
  release: (sdk) => sdk.shutdown()
45
48
  }),
46
49
  Provider(Tracer)({ sdk: OtelSdk }, { sync: () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btravstack/observability",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Observability for @btravstack/core: a strict Logger port correlated with the kernel's units, a dependency-free JSON sink, and the lifecycle events as log lines",
5
5
  "keywords": [
6
6
  "dependency-injection",
@@ -77,19 +77,19 @@
77
77
  "typescript": "7.0.2",
78
78
  "unthrown": "5.7.0",
79
79
  "vitest": "4.1.11",
80
- "@btravstack/core": "0.5.0",
81
- "@btravstack/config": "0.5.0",
82
- "@btravstack/di": "0.5.0",
83
- "@btravstack/testing": "0.5.0"
80
+ "@btravstack/config": "0.7.0",
81
+ "@btravstack/core": "0.7.0",
82
+ "@btravstack/di": "0.7.0",
83
+ "@btravstack/testing": "0.7.0"
84
84
  },
85
85
  "peerDependencies": {
86
86
  "@opentelemetry/api": "^1.9.0",
87
87
  "@opentelemetry/sdk-node": "^0.221.0",
88
88
  "pino": "^10",
89
89
  "unthrown": "^5.0.0",
90
- "@btravstack/config": "^0.5.0",
91
- "@btravstack/core": "^0.5.0",
92
- "@btravstack/di": "^0.5.0"
90
+ "@btravstack/config": "^0.7.0",
91
+ "@btravstack/core": "^0.7.0",
92
+ "@btravstack/di": "^0.7.0"
93
93
  },
94
94
  "peerDependenciesMeta": {
95
95
  "@opentelemetry/api": {