@btravstack/core 0.5.0 → 0.6.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,59 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- let unthrown = require("unthrown");
3
2
  let _btravstack_di = require("@btravstack/di");
3
+ let unthrown = require("unthrown");
4
4
  let _btravstack_config = require("@btravstack/config");
5
5
  let node_http = require("node:http");
6
6
  let node_async_hooks = require("node:async_hooks");
7
+ //#region src/instrumentation.ts
8
+ /**
9
+ * The set port a package contributes its instrumentation to.
10
+ *
11
+ * Nothing collects it unless an OTel SDK is composed, which is the point: a
12
+ * starter DECLARES what it can instrument, and composing
13
+ * `@btravstack/observability/otel` is what turns the declarations on. A graph
14
+ * without it registers nothing and pays nothing — the Spring Boot starter
15
+ * shape, in one port.
16
+ */
17
+ var Instrumentations = class extends _btravstack_di.Port.many("Instrumentations") {};
18
+ //#endregion
19
+ //#region src/health.ts
20
+ /**
21
+ * A component could not answer for itself. Modeled rather than thrown, because
22
+ * a health check that throws is a bug in the check; one that fails is the news
23
+ * `/healthz` exists to carry.
24
+ */
25
+ var HealthCheckFailed = class extends (0, unthrown.TaggedError)("HealthCheckFailed") {};
26
+ /**
27
+ * The set port every starter contributes to and the kernel reads whole.
28
+ *
29
+ * A set port rather than a registry the kernel hands out: a starter DECLARES
30
+ * its check the same way it declares anything else, and a starter an
31
+ * application never composed contributes nothing — no registration call to
32
+ * forget, and no order to get wrong.
33
+ */
34
+ var HealthChecks = class extends _btravstack_di.Port.many("HealthChecks") {};
35
+ /**
36
+ * Runs every check and folds the results into one report — the whole app is
37
+ * unhealthy if any single component is.
38
+ *
39
+ * Each check's failure is recovered into a component line BEFORE `allAsync`
40
+ * sees it, so a failing dependency cannot short-circuit the others: a report
41
+ * naming one component is worth less than one naming all of them. What
42
+ * survives to `allAsync`'s error channel is a `Defect` alone — a check that
43
+ * threw rather than answered, which is a bug in the check.
44
+ */
45
+ const runHealthChecks = (checks) => (0, unthrown.allAsync)(checks.map((health) => health.check().map(() => ({
46
+ name: health.name,
47
+ status: "healthy"
48
+ })).recoverErrCases((matcher) => matcher.with(unthrown.P.tag("HealthCheckFailed"), (error) => ({
49
+ name: health.name,
50
+ status: "unhealthy",
51
+ reason: error.reason
52
+ }))))).map((components) => ({
53
+ status: components.every((c) => c.status === "healthy") ? "healthy" : "unhealthy",
54
+ components
55
+ }));
56
+ //#endregion
7
57
  //#region src/clock.ts
8
58
  const systemClock = {
9
59
  now: () => Date.now(),
@@ -185,6 +235,12 @@ const startProbeServer = (args) => (0, unthrown.fromSafePromise)(new Promise((re
185
235
  respond(response, args.ready(), "ready");
186
236
  return;
187
237
  }
238
+ if (request.url === "/healthz") {
239
+ args.health().map((report) => {
240
+ response.writeHead(report.status === "healthy" ? 200 : 503, { "content-type": "application/json" }).end(JSON.stringify(report));
241
+ });
242
+ return;
243
+ }
188
244
  response.writeHead(404).end();
189
245
  });
190
246
  const onBindError = (cause) => {
@@ -359,6 +415,8 @@ const start = (module, options = {}) => {
359
415
  requestShutdown("uncaught");
360
416
  });
361
417
  let disposeProbes = () => {};
418
+ let healthChecks = [];
419
+ const health = () => runHealthChecks(healthChecks);
362
420
  const probeBound = Promise.withResolvers();
363
421
  const runtimePublished = Promise.withResolvers();
364
422
  emit({ type: "building" });
@@ -376,7 +434,8 @@ const start = (module, options = {}) => {
376
434
  const probesStarted = probesOptions.toAsync().flatMap((probes) => probes === false ? (0, unthrown.OkAsync)() : startProbeServer({
377
435
  port: probes.port,
378
436
  live,
379
- ready
437
+ ready,
438
+ health
380
439
  }).tap((server) => {
381
440
  probeBoundPort = server.port;
382
441
  probeBound.resolve(server.port);
@@ -444,6 +503,7 @@ const start = (module, options = {}) => {
444
503
  tracker.advanceTo("starting");
445
504
  const runtime = ctx.get(RuntimePort);
446
505
  runtimeName = runtime.name;
506
+ healthChecks = ctx.get(HealthChecks);
447
507
  const runtimeCtx = ctx;
448
508
  const unit = options.unit;
449
509
  const run = (meta, work) => registry.run(meta, (signal) => {
@@ -554,6 +614,9 @@ const runMain = async (module, options = {}, exit = (code) => {
554
614
  await awaitExit(start(module, options), exit);
555
615
  };
556
616
  //#endregion
617
+ exports.HealthCheckFailed = HealthCheckFailed;
618
+ exports.HealthChecks = HealthChecks;
619
+ exports.Instrumentations = Instrumentations;
557
620
  exports.LEVELS = LEVELS;
558
621
  exports.Logger = Logger;
559
622
  exports.Meter = Meter;
@@ -563,6 +626,7 @@ exports.SPAN_STATUS = SPAN_STATUS;
563
626
  exports.Tracer = Tracer;
564
627
  exports.currentUnit = currentUnit;
565
628
  exports.releasedBy = releasedBy;
629
+ exports.runHealthChecks = runHealthChecks;
566
630
  exports.runMain = runMain;
567
631
  exports.start = start;
568
632
  exports.stderrSink = stderrSink;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,85 @@
1
1
  import { AsyncResult, Result } from "unthrown";
2
2
  import { AnyPort, Context, Module, PortClass, Scope, ServiceOf } from "@btravstack/di";
3
3
  import { Env, Environment } from "@btravstack/config";
4
+ //#region src/instrumentation.d.ts
5
+ /**
6
+ * One package's offer of an OpenTelemetry instrumentation.
7
+ *
8
+ * **Async, and answers `undefined` rather than failing, because the package
9
+ * supplying the instrumentation is an OPTIONAL peer.** A starter cannot know at
10
+ * composition time whether the consumer installed it, so it contributes the
11
+ * ATTEMPT and the collector drops what did not load — which is what lets
12
+ * `@btravstack/prisma` declare engine tracing without making every consumer
13
+ * install `@prisma/instrumentation`. A contributor that wants to say why its
14
+ * load answered nothing logs that itself; it is the one that knows.
15
+ *
16
+ * The instrumentation is `unknown` on purpose. Naming OpenTelemetry's
17
+ * `Instrumentation` would put the vendor in the package every other one peers
18
+ * on — the same reason `Tracer` and `Meter` are declared as narrowings rather
19
+ * than as OTel's own types. The collector
20
+ * (`@btravstack/observability/otel`) is where the vendor is already a
21
+ * dependency, so that is where the cast belongs.
22
+ */
23
+ type InstrumentationLoader = () => Promise<unknown>;
24
+ declare const Instrumentations_base: import("@btravstack/di").ManyPortClass<"Instrumentations">;
25
+ /**
26
+ * The set port a package contributes its instrumentation to.
27
+ *
28
+ * Nothing collects it unless an OTel SDK is composed, which is the point: a
29
+ * starter DECLARES what it can instrument, and composing
30
+ * `@btravstack/observability/otel` is what turns the declarations on. A graph
31
+ * without it registers nothing and pays nothing — the Spring Boot starter
32
+ * shape, in one port.
33
+ */
34
+ declare class Instrumentations extends Instrumentations_base<InstrumentationLoader> {}
35
+ //#endregion
36
+ //#region src/health.d.ts
37
+ declare const HealthCheckFailed_base: import("unthrown").TaggedErrorConstructor<"HealthCheckFailed">;
38
+ /**
39
+ * A component could not answer for itself. Modeled rather than thrown, because
40
+ * a health check that throws is a bug in the check; one that fails is the news
41
+ * `/healthz` exists to carry.
42
+ */
43
+ declare class HealthCheckFailed extends HealthCheckFailed_base<{
44
+ readonly reason: string;
45
+ }> {}
46
+ /** One contribution: what to call the component, and how to ask it. */
47
+ type HealthCheck = {
48
+ readonly name: string;
49
+ readonly check: () => AsyncResult<void, HealthCheckFailed>;
50
+ };
51
+ declare const HealthChecks_base: import("@btravstack/di").ManyPortClass<"HealthChecks">;
52
+ /**
53
+ * The set port every starter contributes to and the kernel reads whole.
54
+ *
55
+ * A set port rather than a registry the kernel hands out: a starter DECLARES
56
+ * its check the same way it declares anything else, and a starter an
57
+ * application never composed contributes nothing — no registration call to
58
+ * forget, and no order to get wrong.
59
+ */
60
+ declare class HealthChecks extends HealthChecks_base<HealthCheck> {}
61
+ type ComponentHealth = {
62
+ readonly name: string;
63
+ readonly status: "healthy" | "unhealthy";
64
+ /** Present only when the component is unhealthy. */
65
+ readonly reason?: string;
66
+ };
67
+ type HealthReport = {
68
+ readonly status: "healthy" | "unhealthy";
69
+ readonly components: readonly ComponentHealth[];
70
+ };
71
+ /**
72
+ * Runs every check and folds the results into one report — the whole app is
73
+ * unhealthy if any single component is.
74
+ *
75
+ * Each check's failure is recovered into a component line BEFORE `allAsync`
76
+ * sees it, so a failing dependency cannot short-circuit the others: a report
77
+ * naming one component is worth less than one naming all of them. What
78
+ * survives to `allAsync`'s error channel is a `Defect` alone — a check that
79
+ * threw rather than answered, which is a bug in the check.
80
+ */
81
+ declare const runHealthChecks: (checks: readonly HealthCheck[]) => AsyncResult<HealthReport, never>;
82
+ //#endregion
4
83
  //#region src/clock.d.ts
5
84
  type Clock = {
6
85
  readonly now: () => number;
@@ -455,4 +534,4 @@ declare const start: <X, E, UnitX = never, UnitNeeds = never>(module: Module<X,
455
534
  */
456
535
  declare const runMain: <X, E, UnitX = never, UnitNeeds = never>(module: Module<X, E, Scope | Env> & StartGate<X, UnitNeeds>, options?: StartOptions<UnitX, UnitNeeds>, exit?: (code: number) => void) => Promise<void>;
457
536
  //#endregion
458
- export { type Attributes, type Clock, type Counter, type DrainReport, type EventSink, type ExitReport, type Histogram, type KernelEvent, LEVELS, type Level, Logger, type LoggerService, Meter, type MeterService, type Phase, type RunUnit, type RunningApp, type Runtime, type RuntimeHost, type RuntimeInfoOf, RuntimePort, RuntimeStartFailed, SPAN_STATUS, type Serving, type Span, type SpanStatusCode, type StartGate, type StartOptions, type TeardownError, Tracer, type TracerService, type UnitMeta, type UnitRecord, type UnitRegistry, type UnitWork, currentUnit, releasedBy, runMain, start, stderrSink, systemClock };
537
+ export { type Attributes, type Clock, type ComponentHealth, type Counter, type DrainReport, type EventSink, type ExitReport, type HealthCheck, HealthCheckFailed, HealthChecks, type HealthReport, type Histogram, type InstrumentationLoader, Instrumentations, type KernelEvent, LEVELS, type Level, Logger, type LoggerService, Meter, type MeterService, type Phase, type RunUnit, type RunningApp, type Runtime, type RuntimeHost, type RuntimeInfoOf, RuntimePort, RuntimeStartFailed, SPAN_STATUS, type Serving, type Span, type SpanStatusCode, type StartGate, type StartOptions, type TeardownError, Tracer, type TracerService, type UnitMeta, type UnitRecord, type UnitRegistry, type UnitWork, currentUnit, releasedBy, runHealthChecks, runMain, start, stderrSink, systemClock };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,85 @@
1
- import { AsyncResult, Result } from "unthrown";
2
1
  import { AnyPort, Context, Module, PortClass, Scope, ServiceOf } from "@btravstack/di";
2
+ import { AsyncResult, Result } from "unthrown";
3
3
  import { Env, Environment } from "@btravstack/config";
4
+ //#region src/instrumentation.d.ts
5
+ /**
6
+ * One package's offer of an OpenTelemetry instrumentation.
7
+ *
8
+ * **Async, and answers `undefined` rather than failing, because the package
9
+ * supplying the instrumentation is an OPTIONAL peer.** A starter cannot know at
10
+ * composition time whether the consumer installed it, so it contributes the
11
+ * ATTEMPT and the collector drops what did not load — which is what lets
12
+ * `@btravstack/prisma` declare engine tracing without making every consumer
13
+ * install `@prisma/instrumentation`. A contributor that wants to say why its
14
+ * load answered nothing logs that itself; it is the one that knows.
15
+ *
16
+ * The instrumentation is `unknown` on purpose. Naming OpenTelemetry's
17
+ * `Instrumentation` would put the vendor in the package every other one peers
18
+ * on — the same reason `Tracer` and `Meter` are declared as narrowings rather
19
+ * than as OTel's own types. The collector
20
+ * (`@btravstack/observability/otel`) is where the vendor is already a
21
+ * dependency, so that is where the cast belongs.
22
+ */
23
+ type InstrumentationLoader = () => Promise<unknown>;
24
+ declare const Instrumentations_base: import("@btravstack/di").ManyPortClass<"Instrumentations">;
25
+ /**
26
+ * The set port a package contributes its instrumentation to.
27
+ *
28
+ * Nothing collects it unless an OTel SDK is composed, which is the point: a
29
+ * starter DECLARES what it can instrument, and composing
30
+ * `@btravstack/observability/otel` is what turns the declarations on. A graph
31
+ * without it registers nothing and pays nothing — the Spring Boot starter
32
+ * shape, in one port.
33
+ */
34
+ declare class Instrumentations extends Instrumentations_base<InstrumentationLoader> {}
35
+ //#endregion
36
+ //#region src/health.d.ts
37
+ declare const HealthCheckFailed_base: import("unthrown").TaggedErrorConstructor<"HealthCheckFailed">;
38
+ /**
39
+ * A component could not answer for itself. Modeled rather than thrown, because
40
+ * a health check that throws is a bug in the check; one that fails is the news
41
+ * `/healthz` exists to carry.
42
+ */
43
+ declare class HealthCheckFailed extends HealthCheckFailed_base<{
44
+ readonly reason: string;
45
+ }> {}
46
+ /** One contribution: what to call the component, and how to ask it. */
47
+ type HealthCheck = {
48
+ readonly name: string;
49
+ readonly check: () => AsyncResult<void, HealthCheckFailed>;
50
+ };
51
+ declare const HealthChecks_base: import("@btravstack/di").ManyPortClass<"HealthChecks">;
52
+ /**
53
+ * The set port every starter contributes to and the kernel reads whole.
54
+ *
55
+ * A set port rather than a registry the kernel hands out: a starter DECLARES
56
+ * its check the same way it declares anything else, and a starter an
57
+ * application never composed contributes nothing — no registration call to
58
+ * forget, and no order to get wrong.
59
+ */
60
+ declare class HealthChecks extends HealthChecks_base<HealthCheck> {}
61
+ type ComponentHealth = {
62
+ readonly name: string;
63
+ readonly status: "healthy" | "unhealthy";
64
+ /** Present only when the component is unhealthy. */
65
+ readonly reason?: string;
66
+ };
67
+ type HealthReport = {
68
+ readonly status: "healthy" | "unhealthy";
69
+ readonly components: readonly ComponentHealth[];
70
+ };
71
+ /**
72
+ * Runs every check and folds the results into one report — the whole app is
73
+ * unhealthy if any single component is.
74
+ *
75
+ * Each check's failure is recovered into a component line BEFORE `allAsync`
76
+ * sees it, so a failing dependency cannot short-circuit the others: a report
77
+ * naming one component is worth less than one naming all of them. What
78
+ * survives to `allAsync`'s error channel is a `Defect` alone — a check that
79
+ * threw rather than answered, which is a bug in the check.
80
+ */
81
+ declare const runHealthChecks: (checks: readonly HealthCheck[]) => AsyncResult<HealthReport, never>;
82
+ //#endregion
4
83
  //#region src/clock.d.ts
5
84
  type Clock = {
6
85
  readonly now: () => number;
@@ -455,4 +534,4 @@ declare const start: <X, E, UnitX = never, UnitNeeds = never>(module: Module<X,
455
534
  */
456
535
  declare const runMain: <X, E, UnitX = never, UnitNeeds = never>(module: Module<X, E, Scope | Env> & StartGate<X, UnitNeeds>, options?: StartOptions<UnitX, UnitNeeds>, exit?: (code: number) => void) => Promise<void>;
457
536
  //#endregion
458
- export { type Attributes, type Clock, type Counter, type DrainReport, type EventSink, type ExitReport, type Histogram, type KernelEvent, LEVELS, type Level, Logger, type LoggerService, Meter, type MeterService, type Phase, type RunUnit, type RunningApp, type Runtime, type RuntimeHost, type RuntimeInfoOf, RuntimePort, RuntimeStartFailed, SPAN_STATUS, type Serving, type Span, type SpanStatusCode, type StartGate, type StartOptions, type TeardownError, Tracer, type TracerService, type UnitMeta, type UnitRecord, type UnitRegistry, type UnitWork, currentUnit, releasedBy, runMain, start, stderrSink, systemClock };
537
+ export { type Attributes, type Clock, type ComponentHealth, type Counter, type DrainReport, type EventSink, type ExitReport, type HealthCheck, HealthCheckFailed, HealthChecks, type HealthReport, type Histogram, type InstrumentationLoader, Instrumentations, type KernelEvent, LEVELS, type Level, Logger, type LoggerService, Meter, type MeterService, type Phase, type RunUnit, type RunningApp, type Runtime, type RuntimeHost, type RuntimeInfoOf, RuntimePort, RuntimeStartFailed, SPAN_STATUS, type Serving, type Span, type SpanStatusCode, type StartGate, type StartOptions, type TeardownError, Tracer, type TracerService, type UnitMeta, type UnitRecord, type UnitRegistry, type UnitWork, currentUnit, releasedBy, runHealthChecks, runMain, start, stderrSink, systemClock };
package/dist/index.mjs CHANGED
@@ -1,8 +1,58 @@
1
- import { Err, Ok, OkAsync, P, TaggedError, fromSafePromise } from "unthrown";
2
1
  import { Module, Port, Provider } from "@btravstack/di";
2
+ import { Err, Ok, OkAsync, P, TaggedError, allAsync, fromSafePromise } from "unthrown";
3
3
  import { Config, ConfigInvalid, Env } from "@btravstack/config";
4
4
  import { createServer } from "node:http";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
+ //#region src/instrumentation.ts
7
+ /**
8
+ * The set port a package contributes its instrumentation to.
9
+ *
10
+ * Nothing collects it unless an OTel SDK is composed, which is the point: a
11
+ * starter DECLARES what it can instrument, and composing
12
+ * `@btravstack/observability/otel` is what turns the declarations on. A graph
13
+ * without it registers nothing and pays nothing — the Spring Boot starter
14
+ * shape, in one port.
15
+ */
16
+ var Instrumentations = class extends Port.many("Instrumentations") {};
17
+ //#endregion
18
+ //#region src/health.ts
19
+ /**
20
+ * A component could not answer for itself. Modeled rather than thrown, because
21
+ * a health check that throws is a bug in the check; one that fails is the news
22
+ * `/healthz` exists to carry.
23
+ */
24
+ var HealthCheckFailed = class extends TaggedError("HealthCheckFailed") {};
25
+ /**
26
+ * The set port every starter contributes to and the kernel reads whole.
27
+ *
28
+ * A set port rather than a registry the kernel hands out: a starter DECLARES
29
+ * its check the same way it declares anything else, and a starter an
30
+ * application never composed contributes nothing — no registration call to
31
+ * forget, and no order to get wrong.
32
+ */
33
+ var HealthChecks = class extends Port.many("HealthChecks") {};
34
+ /**
35
+ * Runs every check and folds the results into one report — the whole app is
36
+ * unhealthy if any single component is.
37
+ *
38
+ * Each check's failure is recovered into a component line BEFORE `allAsync`
39
+ * sees it, so a failing dependency cannot short-circuit the others: a report
40
+ * naming one component is worth less than one naming all of them. What
41
+ * survives to `allAsync`'s error channel is a `Defect` alone — a check that
42
+ * threw rather than answered, which is a bug in the check.
43
+ */
44
+ const runHealthChecks = (checks) => allAsync(checks.map((health) => health.check().map(() => ({
45
+ name: health.name,
46
+ status: "healthy"
47
+ })).recoverErrCases((matcher) => matcher.with(P.tag("HealthCheckFailed"), (error) => ({
48
+ name: health.name,
49
+ status: "unhealthy",
50
+ reason: error.reason
51
+ }))))).map((components) => ({
52
+ status: components.every((c) => c.status === "healthy") ? "healthy" : "unhealthy",
53
+ components
54
+ }));
55
+ //#endregion
6
56
  //#region src/clock.ts
7
57
  const systemClock = {
8
58
  now: () => Date.now(),
@@ -184,6 +234,12 @@ const startProbeServer = (args) => fromSafePromise(new Promise((resolve) => {
184
234
  respond(response, args.ready(), "ready");
185
235
  return;
186
236
  }
237
+ if (request.url === "/healthz") {
238
+ args.health().map((report) => {
239
+ response.writeHead(report.status === "healthy" ? 200 : 503, { "content-type": "application/json" }).end(JSON.stringify(report));
240
+ });
241
+ return;
242
+ }
187
243
  response.writeHead(404).end();
188
244
  });
189
245
  const onBindError = (cause) => {
@@ -358,6 +414,8 @@ const start = (module, options = {}) => {
358
414
  requestShutdown("uncaught");
359
415
  });
360
416
  let disposeProbes = () => {};
417
+ let healthChecks = [];
418
+ const health = () => runHealthChecks(healthChecks);
361
419
  const probeBound = Promise.withResolvers();
362
420
  const runtimePublished = Promise.withResolvers();
363
421
  emit({ type: "building" });
@@ -375,7 +433,8 @@ const start = (module, options = {}) => {
375
433
  const probesStarted = probesOptions.toAsync().flatMap((probes) => probes === false ? OkAsync() : startProbeServer({
376
434
  port: probes.port,
377
435
  live,
378
- ready
436
+ ready,
437
+ health
379
438
  }).tap((server) => {
380
439
  probeBoundPort = server.port;
381
440
  probeBound.resolve(server.port);
@@ -443,6 +502,7 @@ const start = (module, options = {}) => {
443
502
  tracker.advanceTo("starting");
444
503
  const runtime = ctx.get(RuntimePort);
445
504
  runtimeName = runtime.name;
505
+ healthChecks = ctx.get(HealthChecks);
446
506
  const runtimeCtx = ctx;
447
507
  const unit = options.unit;
448
508
  const run = (meta, work) => registry.run(meta, (signal) => {
@@ -553,4 +613,4 @@ const runMain = async (module, options = {}, exit = (code) => {
553
613
  await awaitExit(start(module, options), exit);
554
614
  };
555
615
  //#endregion
556
- export { LEVELS, Logger, Meter, RuntimePort, RuntimeStartFailed, SPAN_STATUS, Tracer, currentUnit, releasedBy, runMain, start, stderrSink, systemClock };
616
+ export { HealthCheckFailed, HealthChecks, Instrumentations, LEVELS, Logger, Meter, RuntimePort, RuntimeStartFailed, SPAN_STATUS, Tracer, currentUnit, releasedBy, runHealthChecks, runMain, start, stderrSink, systemClock };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btravstack/core",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "The application kernel: boot a di module into a running process, and stop it without losing work",
5
5
  "keywords": [
6
6
  "application",
@@ -53,13 +53,13 @@
53
53
  "typescript": "7.0.2",
54
54
  "unthrown": "5.7.0",
55
55
  "vitest": "4.1.11",
56
- "@btravstack/config": "0.5.0",
57
- "@btravstack/di": "0.5.0"
56
+ "@btravstack/config": "0.6.0",
57
+ "@btravstack/di": "0.6.0"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "unthrown": "^5.0.0",
61
- "@btravstack/config": "^0.5.0",
62
- "@btravstack/di": "^0.5.0"
61
+ "@btravstack/config": "^0.6.0",
62
+ "@btravstack/di": "^0.6.0"
63
63
  },
64
64
  "engines": {
65
65
  "node": ">=22"