@getstrata/bootstrap 2.2.9 → 2.2.10

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @getstrata/bootstrap changelog
2
2
 
3
+ ## 2.2.10
4
+
5
+ - Add bounded OpenTelemetry SDK health observations and selected native transaction-acquisition histograms through explicit metrics options.
6
+ - Add bounded Postgres failed-job counts using ORM ID projections, with schema guards, native acquisition deadlines and read-only collector draining.
7
+ - Observe Streams retry promotion lateness using Redis time; cancellation cleanup sentinels omit unavailable age instead of reporting age since epoch.
8
+ - Generate typed runtimeMetrics opt-in and provider-owned SQL collector draining, including startup cleanup; HTTP-only defaults and borrowed tracing ownership remain intact.
9
+ - Keep additional SQL observation dialects, sustained operational qualification and encryption rotation explicit follow-up work.
10
+
3
11
  ## 2.2.9
4
12
 
5
13
  - Support native cancellable transaction acquisition
@@ -1,5 +1,7 @@
1
+ import { type TransactionAcquisitionMetrics } from "@getstrata/core/database/transaction";
1
2
  import { type OutboxMetricsCollector } from "@getstrata/core/events/outbox";
2
- import { type RedisQueueSnapshotOptions } from "@getstrata/core/queue/queueMetrics";
3
+ import { type FailedJobMetricsCollector, type RedisQueueSnapshotOptions } from "@getstrata/core/queue/queueMetrics";
4
+ import { type TracingRuntime } from "@getstrata/core/tracing/tracingMiddleware";
3
5
  interface MetricsRoutesOptions {
4
6
  /** Opt in to shared Redis queue observations; no database or failed-job scan. */
5
7
  queue?: RedisQueueSnapshotOptions & {
@@ -7,9 +9,17 @@ interface MetricsRoutesOptions {
7
9
  };
8
10
  /** Explicit platform outbox observation; caller owns collector shutdown. */
9
11
  outbox?: Pick<OutboxMetricsCollector, "collect">;
12
+ /** Read an existing runtime; scraping never initializes tracing or flushes spans. */
13
+ tracing?: Pick<TracingRuntime, "metrics">;
14
+ /** Fixed process-local observations; scrape does not reserve a database connection. */
15
+ transactionAcquisition?: Pick<TransactionAcquisitionMetrics, "snapshot">;
16
+ /** Explicit global failed-job observation; caller owns collector shutdown. */
17
+ failedJobs?: Pick<FailedJobMetricsCollector, "collect">;
10
18
  }
11
19
  declare function createMetricsRoutes(options?: MetricsRoutesOptions): {
12
20
  "/metrics": (request: Request) => Promise<Response>;
13
21
  };
22
+ export type { MetricsRuntime, MetricsRuntimeOptions } from "./metricsRuntime";
23
+ export { createMetricsRuntime } from "./metricsRuntime";
14
24
  export type { MetricsRoutesOptions };
15
25
  export { createMetricsRoutes };
@@ -0,0 +1,21 @@
1
+ import { type OutboxMetricsOptions } from "@getstrata/core/events/outbox";
2
+ import { type FailedJobMetricsOptions } from "@getstrata/core/queue/queueMetrics";
3
+ import type { MetricsRoutesOptions } from "./metricsRoutes";
4
+ interface MetricsRuntimeOptions {
5
+ queue?: MetricsRoutesOptions["queue"];
6
+ outbox?: OutboxMetricsOptions;
7
+ failedJobs?: FailedJobMetricsOptions;
8
+ /** Borrowed: the existing tracing provider retains flush/shutdown ownership. */
9
+ tracing?: MetricsRoutesOptions["tracing"];
10
+ /** Borrowed: callers choose which transactions to instrument. */
11
+ transactionAcquisition?: MetricsRoutesOptions["transactionAcquisition"];
12
+ }
13
+ interface MetricsRuntime {
14
+ readonly options: Readonly<MetricsRoutesOptions>;
15
+ /** Drain owned SQL observations after HTTP admission stops and requests drain, before pool closure. */
16
+ close(): Promise<void>;
17
+ }
18
+ /** Lazy, explicit observations only: no schema provisioning, worker or tracing initialization. */
19
+ declare function createMetricsRuntime(options?: MetricsRuntimeOptions): MetricsRuntime;
20
+ export type { MetricsRuntime, MetricsRuntimeOptions };
21
+ export { createMetricsRuntime };
@@ -28,6 +28,8 @@ export { checkDatabase, checkRedis, createHealthRoutes, pingDatabase } from "./h
28
28
  export { type RouteModelAuthorization, securedBindRouteModel, securedBindRouteModelByKey, } from "./http/securedRouteModelBinding.ts";
29
29
  export { createHttpKernel, type HttpKernel, type MiddlewareGroupName } from "./httpKernel.ts";
30
30
  export { resolveMembershipService } from "./membershipService.ts";
31
+ export type { MetricsRoutesOptions, MetricsRuntime, MetricsRuntimeOptions, } from "./metricsRoutes.ts";
32
+ export { createMetricsRoutes, createMetricsRuntime } from "./metricsRoutes.ts";
31
33
  export { prefixRouteMap } from "./prefixRouteMap.ts";
32
34
  export { coreProviders } from "./providers/index.ts";
33
35
  export { default as tracingProvider } from "./providers/tracing.ts";
@@ -1,13 +1,50 @@
1
1
  // @bun
2
2
  // ../../src/bootstrap/metricsRoutes.ts
3
3
  import { timingSafeEqual } from "crypto";
4
+ import {
5
+ renderTransactionAcquisitionMetrics
6
+ } from "@getstrata/core/database/transaction";
4
7
  import { renderOutboxMetrics } from "@getstrata/core/events/outbox";
5
8
  import { prometheusRegistry } from "@getstrata/core/metrics/prometheus";
6
9
  import {
7
10
  readRedisQueueSnapshot,
11
+ renderFailedJobMetrics,
8
12
  renderRedisQueueMetrics
9
13
  } from "@getstrata/core/queue/queueMetrics";
10
14
  import { isProductionEnv } from "@getstrata/core/runtime/appEnv";
15
+ import {
16
+ renderTracingMetrics
17
+ } from "@getstrata/core/tracing/tracingMiddleware";
18
+
19
+ // ../../src/bootstrap/metricsRuntime.ts
20
+ import {
21
+ createOutboxMetricsCollector
22
+ } from "@getstrata/core/events/outbox";
23
+ import {
24
+ createFailedJobMetricsCollector
25
+ } from "@getstrata/core/queue/queueMetrics";
26
+ function createMetricsRuntime(options = {}) {
27
+ const outbox = options.outbox ? createOutboxMetricsCollector(options.outbox) : undefined;
28
+ const failedJobs = options.failedJobs ? createFailedJobMetricsCollector(options.failedJobs) : undefined;
29
+ let closing;
30
+ return {
31
+ options: Object.freeze({
32
+ queue: options.queue ? { ...options.queue } : undefined,
33
+ outbox,
34
+ failedJobs,
35
+ tracing: options.tracing,
36
+ transactionAcquisition: options.transactionAcquisition
37
+ }),
38
+ close() {
39
+ closing ??= Promise.all([outbox?.close(), failedJobs?.close()]).then(() => {
40
+ return;
41
+ });
42
+ return closing;
43
+ }
44
+ };
45
+ }
46
+
47
+ // ../../src/bootstrap/metricsRoutes.ts
11
48
  function tokensMatch(left, right) {
12
49
  const leftBuffer = Buffer.from(left);
13
50
  const rightBuffer = Buffer.from(right);
@@ -31,7 +68,7 @@ function authorizeMetrics(request) {
31
68
  function createMetricsRoutes(options = {}) {
32
69
  let collection;
33
70
  const collect = () => {
34
- if (!options.queue && !options.outbox)
71
+ if (!options.queue && !options.outbox && !options.tracing && !options.transactionAcquisition && !options.failedJobs)
35
72
  return Promise.resolve("");
36
73
  const failure = (name) => `# HELP strata_${name}_collector_success Whether the configured ${name} collection succeeded.
37
74
  # TYPE strata_${name}_collector_success gauge
@@ -39,7 +76,10 @@ strata_${name}_collector_success 0
39
76
  `;
40
77
  collection ??= Promise.all([
41
78
  options.queue ? readRedisQueueSnapshot(options.queue.redisUrl, options.queue).then(renderRedisQueueMetrics).catch(() => failure("queue")) : Promise.resolve(""),
42
- options.outbox ? Promise.resolve().then(() => options.outbox?.collect()).then((snapshot) => snapshot ? renderOutboxMetrics(snapshot) : "").catch(() => failure("outbox")) : Promise.resolve("")
79
+ options.outbox ? Promise.resolve().then(() => options.outbox?.collect()).then((snapshot) => snapshot ? renderOutboxMetrics(snapshot) : "").catch(() => failure("outbox")) : Promise.resolve(""),
80
+ options.tracing ? Promise.resolve().then(() => options.tracing ? renderTracingMetrics(options.tracing.metrics()) : "").catch(() => failure("tracing")) : Promise.resolve(""),
81
+ options.transactionAcquisition ? Promise.resolve().then(() => options.transactionAcquisition ? renderTransactionAcquisitionMetrics(options.transactionAcquisition.snapshot()) : "").catch(() => failure("database_acquisition")) : Promise.resolve(""),
82
+ options.failedJobs ? Promise.resolve().then(() => options.failedJobs?.collect()).then((snapshot) => snapshot ? renderFailedJobMetrics(snapshot) : "").catch(() => failure("queue_failed_job")) : Promise.resolve("")
43
83
  ]).then((parts) => parts.join("")).finally(() => {
44
84
  collection = undefined;
45
85
  });
@@ -61,5 +101,6 @@ strata_${name}_collector_success 0
61
101
  };
62
102
  }
63
103
  export {
64
- createMetricsRoutes
104
+ createMetricsRoutes,
105
+ createMetricsRuntime
65
106
  };
package/dist/index.js CHANGED
@@ -1648,9 +1648,110 @@ import {
1648
1648
  } from "@getstrata/core/http/securedRouteModelBinding";
1649
1649
  // ../../src/bootstrap/membershipService.ts
1650
1650
  import { resolveMembershipService } from "@getstrata/core/auth/membershipService";
1651
+ // ../../src/bootstrap/metricsRoutes.ts
1652
+ import { timingSafeEqual } from "crypto";
1653
+ import {
1654
+ renderTransactionAcquisitionMetrics
1655
+ } from "@getstrata/core/database/transaction";
1656
+ import { renderOutboxMetrics } from "@getstrata/core/events/outbox";
1657
+ import { prometheusRegistry } from "@getstrata/core/metrics/prometheus";
1658
+ import {
1659
+ readRedisQueueSnapshot,
1660
+ renderFailedJobMetrics,
1661
+ renderRedisQueueMetrics
1662
+ } from "@getstrata/core/queue/queueMetrics";
1663
+ import { isProductionEnv } from "@getstrata/core/runtime/appEnv";
1664
+ import {
1665
+ renderTracingMetrics
1666
+ } from "@getstrata/core/tracing/tracingMiddleware";
1667
+
1668
+ // ../../src/bootstrap/metricsRuntime.ts
1669
+ import {
1670
+ createOutboxMetricsCollector
1671
+ } from "@getstrata/core/events/outbox";
1672
+ import {
1673
+ createFailedJobMetricsCollector
1674
+ } from "@getstrata/core/queue/queueMetrics";
1675
+ function createMetricsRuntime(options = {}) {
1676
+ const outbox = options.outbox ? createOutboxMetricsCollector(options.outbox) : undefined;
1677
+ const failedJobs = options.failedJobs ? createFailedJobMetricsCollector(options.failedJobs) : undefined;
1678
+ let closing;
1679
+ return {
1680
+ options: Object.freeze({
1681
+ queue: options.queue ? { ...options.queue } : undefined,
1682
+ outbox,
1683
+ failedJobs,
1684
+ tracing: options.tracing,
1685
+ transactionAcquisition: options.transactionAcquisition
1686
+ }),
1687
+ close() {
1688
+ closing ??= Promise.all([outbox?.close(), failedJobs?.close()]).then(() => {
1689
+ return;
1690
+ });
1691
+ return closing;
1692
+ }
1693
+ };
1694
+ }
1695
+
1696
+ // ../../src/bootstrap/metricsRoutes.ts
1697
+ function tokensMatch(left, right) {
1698
+ const leftBuffer = Buffer.from(left);
1699
+ const rightBuffer = Buffer.from(right);
1700
+ if (leftBuffer.length !== rightBuffer.length) {
1701
+ return false;
1702
+ }
1703
+ return timingSafeEqual(leftBuffer, rightBuffer);
1704
+ }
1705
+ function authorizeMetrics(request) {
1706
+ const expected = process.env.METRICS_TOKEN?.trim();
1707
+ const authorization = request.headers.get("authorization") ?? "";
1708
+ const presented = authorization.startsWith("Bearer ") ? authorization.slice("Bearer ".length) : "";
1709
+ if (expected) {
1710
+ return presented.length > 0 && tokensMatch(presented, expected);
1711
+ }
1712
+ if (isProductionEnv()) {
1713
+ return false;
1714
+ }
1715
+ return true;
1716
+ }
1717
+ function createMetricsRoutes(options = {}) {
1718
+ let collection;
1719
+ const collect = () => {
1720
+ if (!options.queue && !options.outbox && !options.tracing && !options.transactionAcquisition && !options.failedJobs)
1721
+ return Promise.resolve("");
1722
+ const failure = (name) => `# HELP strata_${name}_collector_success Whether the configured ${name} collection succeeded.
1723
+ # TYPE strata_${name}_collector_success gauge
1724
+ strata_${name}_collector_success 0
1725
+ `;
1726
+ collection ??= Promise.all([
1727
+ options.queue ? readRedisQueueSnapshot(options.queue.redisUrl, options.queue).then(renderRedisQueueMetrics).catch(() => failure("queue")) : Promise.resolve(""),
1728
+ options.outbox ? Promise.resolve().then(() => options.outbox?.collect()).then((snapshot) => snapshot ? renderOutboxMetrics(snapshot) : "").catch(() => failure("outbox")) : Promise.resolve(""),
1729
+ options.tracing ? Promise.resolve().then(() => options.tracing ? renderTracingMetrics(options.tracing.metrics()) : "").catch(() => failure("tracing")) : Promise.resolve(""),
1730
+ options.transactionAcquisition ? Promise.resolve().then(() => options.transactionAcquisition ? renderTransactionAcquisitionMetrics(options.transactionAcquisition.snapshot()) : "").catch(() => failure("database_acquisition")) : Promise.resolve(""),
1731
+ options.failedJobs ? Promise.resolve().then(() => options.failedJobs?.collect()).then((snapshot) => snapshot ? renderFailedJobMetrics(snapshot) : "").catch(() => failure("queue_failed_job")) : Promise.resolve("")
1732
+ ]).then((parts) => parts.join("")).finally(() => {
1733
+ collection = undefined;
1734
+ });
1735
+ return collection;
1736
+ };
1737
+ return {
1738
+ "/metrics": async (request) => {
1739
+ if (!authorizeMetrics(request)) {
1740
+ return new Response("Not Found", { status: 404 });
1741
+ }
1742
+ return new Response(prometheusRegistry.renderMetrics() + await collect(), {
1743
+ status: 200,
1744
+ headers: {
1745
+ "content-type": "text/plain; version=0.0.4; charset=utf-8",
1746
+ "cache-control": "no-store"
1747
+ }
1748
+ });
1749
+ }
1750
+ };
1751
+ }
1651
1752
  // ../../src/bootstrap/secretsGuard.ts
1652
1753
  import { getDefaultDatabasePool as getDefaultDatabasePool2 } from "@getstrata/core/database/defaultConnection";
1653
- import { envFlagEnabled as envFlagEnabled3, isProductionEnv } from "@getstrata/core/runtime/appEnv";
1754
+ import { envFlagEnabled as envFlagEnabled3, isProductionEnv as isProductionEnv2 } from "@getstrata/core/runtime/appEnv";
1654
1755
  import { isViewsMode, parseFrontendMode } from "@getstrata/core/runtime/frontendMode";
1655
1756
  import {
1656
1757
  assertPostgresRoleCannotBypassRls,
@@ -1829,7 +1930,7 @@ function assertPublicAppUrl(env) {
1829
1930
  }
1830
1931
  }
1831
1932
  function assertProductionSecrets(env = process.env) {
1832
- if (!isProductionEnv(env)) {
1933
+ if (!isProductionEnv2(env)) {
1833
1934
  return;
1834
1935
  }
1835
1936
  assertNoPlaceholderSecrets(env);
@@ -2087,7 +2188,7 @@ import {
2087
2188
  import { currentSqlDialect, sqlTimestamp } from "@getstrata/core/database/dialect";
2088
2189
  import { readRequestCookie } from "@getstrata/core/http/cookies";
2089
2190
  import { currentRequestMeta as currentRequestMeta3 } from "@getstrata/core/http/requestMetaContext";
2090
- import { isProductionEnv as isProductionEnv2 } from "@getstrata/core/runtime/appEnv";
2191
+ import { isProductionEnv as isProductionEnv3 } from "@getstrata/core/runtime/appEnv";
2091
2192
  import { timingSafeCompareString } from "@getstrata/core/security/timingSafeCompare";
2092
2193
  import { runWithMigrationBypassForIdentifier } from "@getstrata/core/tenant/databaseTenantContext";
2093
2194
  function sqlPlaceholder(index) {
@@ -2217,7 +2318,7 @@ class CookieSessionStore {
2217
2318
  return sessionId;
2218
2319
  }
2219
2320
  withSecureFlag(header) {
2220
- if (!isProductionEnv2()) {
2321
+ if (!isProductionEnv3()) {
2221
2322
  return header;
2222
2323
  }
2223
2324
  return header.includes("Secure") ? header : `${header}; Secure`;
@@ -2371,6 +2472,8 @@ export {
2371
2472
  createCsrfProtection,
2372
2473
  createHealthRoutes,
2373
2474
  createHttpKernel,
2475
+ createMetricsRoutes,
2476
+ createMetricsRuntime,
2374
2477
  createRouteKernel,
2375
2478
  createWebRoutes,
2376
2479
  createWebServer,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/bootstrap",
3
- "version": "2.2.9",
3
+ "version": "2.2.10",
4
4
  "description": "Strata application bootstrap: HttpKernel, DI, web session helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -190,7 +190,7 @@
190
190
  "access": "public"
191
191
  },
192
192
  "peerDependencies": {
193
- "@getstrata/core": "^2.2.9",
193
+ "@getstrata/core": "^2.2.10",
194
194
  "typescript": "^5.9.0 || ^6.0.0 || ^7.0.0"
195
195
  },
196
196
  "engines": {