@getstrata/core 2.2.8 → 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.
@@ -129,6 +129,9 @@ function unwrapTransactionRollback(error, depth = 0) {
129
129
  }
130
130
  return unwrapTransactionRollback(error.cause, depth + 1);
131
131
  }
132
+ function hasActiveTransaction() {
133
+ return hasActiveDatabaseConnection() && (transactionScopes.getStore()?.rollback.length ?? 0) > 0;
134
+ }
132
135
  function requestTransactionRollback() {
133
136
  const state = transactionScopes.getStore();
134
137
  const index = state ? state.rollback.length - 1 : -1;
@@ -344,6 +347,55 @@ var init_deferredModelEvents = __esm(() => {
344
347
  deferredModelEvents = createAsyncContextStore("@getstrata/deferredModelEvents");
345
348
  });
346
349
 
350
+ // ../../src/core/database/acquisitionMetrics.ts
351
+ async function observeTransactionAcquisition(metrics, acquire, signal) {
352
+ const state = states.get(metrics);
353
+ if (!state)
354
+ throw new TypeError("Transaction acquisition metrics must be created by the framework factory.");
355
+ const started = performance.now();
356
+ state.inflight++;
357
+ let outcome = "acquired";
358
+ try {
359
+ return await acquire();
360
+ } catch (error) {
361
+ outcome = signal?.aborted ? "aborted" : "failed";
362
+ throw error;
363
+ } finally {
364
+ state.inflight--;
365
+ const elapsed = Math.max(0, performance.now() - started) / 1000;
366
+ const histogram = state.outcomes[outcomes.indexOf(outcome)];
367
+ if (histogram) {
368
+ histogram.count++;
369
+ histogram.sumSeconds += elapsed;
370
+ for (let index = 0;index < ACQUISITION_BUCKETS_SECONDS.length; index++) {
371
+ const bound = ACQUISITION_BUCKETS_SECONDS[index];
372
+ if (bound !== undefined && elapsed <= bound)
373
+ histogram.buckets[index] = (histogram.buckets[index] ?? 0) + 1;
374
+ }
375
+ }
376
+ }
377
+ }
378
+ var ACQUISITION_BUCKETS_SECONDS, outcomes, metricsBrand, states;
379
+ var init_acquisitionMetrics = __esm(() => {
380
+ ACQUISITION_BUCKETS_SECONDS = [
381
+ 0.001,
382
+ 0.005,
383
+ 0.01,
384
+ 0.025,
385
+ 0.05,
386
+ 0.1,
387
+ 0.25,
388
+ 0.5,
389
+ 1,
390
+ 2.5,
391
+ 5,
392
+ 10
393
+ ];
394
+ outcomes = ["acquired", "failed", "aborted"];
395
+ metricsBrand = Symbol("transaction acquisition metrics");
396
+ states = new WeakMap;
397
+ });
398
+
347
399
  // ../../src/core/database/connection.ts
348
400
  function createDatabaseConnection(source) {
349
401
  return {
@@ -697,28 +749,51 @@ async function runInSavepoint(connection, operation) {
697
749
  function supportsTransactions(connection) {
698
750
  return typeof connection.begin === "function";
699
751
  }
700
- async function runInTransaction(operation) {
752
+ async function runInTransaction(operation, options = {}) {
753
+ const signal = options.acquisitionSignal;
754
+ signal?.throwIfAborted();
755
+ const admittedOperation = (connection) => {
756
+ signal?.throwIfAborted();
757
+ return operation(connection);
758
+ };
701
759
  const pool = resolveRepositoryConnection();
702
760
  if (hasActiveDatabaseConnection()) {
703
- return await runInSavepoint(getActiveDatabaseConnection(pool), operation);
761
+ return await runInSavepoint(getActiveDatabaseConnection(pool), admittedOperation);
704
762
  }
705
763
  if (!supportsTransactions(pool)) {
706
764
  throw new Error("Active database connection does not support transactions. Bind a client with begin() via bindDatabaseConnection.");
707
765
  }
708
766
  return await settleTransaction(() => runWithDeferredModelEvents(async () => {
709
- return await pool.begin(async (transaction) => {
710
- return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await operation(createDatabaseConnection(transaction))))));
711
- });
767
+ let reserved;
768
+ if (signal || options.acquisitionMetrics) {
769
+ if (!pool.reserve)
770
+ throw new Error(signal ? "Active database connection does not support cancellable transaction acquisition." : "Active database connection does not support observable transaction acquisition.");
771
+ const reserve = pool.reserve.bind(pool);
772
+ reserved = options.acquisitionMetrics ? await observeTransactionAcquisition(options.acquisitionMetrics, () => reserve({ signal }), signal) : await reserve({ signal });
773
+ }
774
+ const connection = reserved ?? pool;
775
+ try {
776
+ signal?.throwIfAborted();
777
+ if (!supportsTransactions(connection))
778
+ throw new Error("Reserved database connection does not support transactions.");
779
+ return await connection.begin(async (transaction) => {
780
+ return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
781
+ });
782
+ } finally {
783
+ await reserved?.release();
784
+ }
712
785
  }));
713
786
  }
714
787
  var transactionFrame, outerFrames;
715
788
  var init_transaction = __esm(() => {
716
789
  init_deferredModelEvents();
717
790
  init_asyncContextStore();
791
+ init_acquisitionMetrics();
718
792
  init_connection();
719
793
  init_connectionContext();
720
794
  init_repositoryConnection();
721
795
  init_transactionControl();
796
+ init_acquisitionMetrics();
722
797
  transactionFrame = createAsyncContextStore("@getstrata/transactionFrame");
723
798
  outerFrames = new WeakMap;
724
799
  });
@@ -52,6 +52,9 @@ function unwrapTransactionRollback(error, depth = 0) {
52
52
  }
53
53
  return unwrapTransactionRollback(error.cause, depth + 1);
54
54
  }
55
+ function hasActiveTransaction() {
56
+ return hasActiveDatabaseConnection() && (transactionScopes.getStore()?.rollback.length ?? 0) > 0;
57
+ }
55
58
  function requestTransactionRollback() {
56
59
  const state = transactionScopes.getStore();
57
60
  const index = state ? state.rollback.length - 1 : -1;
@@ -278,6 +281,55 @@ var init_deferredModelEvents = __esm(() => {
278
281
  deferredModelEvents = createAsyncContextStore("@getstrata/deferredModelEvents");
279
282
  });
280
283
 
284
+ // ../../src/core/database/acquisitionMetrics.ts
285
+ async function observeTransactionAcquisition(metrics, acquire, signal) {
286
+ const state = states.get(metrics);
287
+ if (!state)
288
+ throw new TypeError("Transaction acquisition metrics must be created by the framework factory.");
289
+ const started = performance.now();
290
+ state.inflight++;
291
+ let outcome = "acquired";
292
+ try {
293
+ return await acquire();
294
+ } catch (error) {
295
+ outcome = signal?.aborted ? "aborted" : "failed";
296
+ throw error;
297
+ } finally {
298
+ state.inflight--;
299
+ const elapsed = Math.max(0, performance.now() - started) / 1000;
300
+ const histogram = state.outcomes[outcomes.indexOf(outcome)];
301
+ if (histogram) {
302
+ histogram.count++;
303
+ histogram.sumSeconds += elapsed;
304
+ for (let index = 0;index < ACQUISITION_BUCKETS_SECONDS.length; index++) {
305
+ const bound = ACQUISITION_BUCKETS_SECONDS[index];
306
+ if (bound !== undefined && elapsed <= bound)
307
+ histogram.buckets[index] = (histogram.buckets[index] ?? 0) + 1;
308
+ }
309
+ }
310
+ }
311
+ }
312
+ var ACQUISITION_BUCKETS_SECONDS, outcomes, metricsBrand, states;
313
+ var init_acquisitionMetrics = __esm(() => {
314
+ ACQUISITION_BUCKETS_SECONDS = [
315
+ 0.001,
316
+ 0.005,
317
+ 0.01,
318
+ 0.025,
319
+ 0.05,
320
+ 0.1,
321
+ 0.25,
322
+ 0.5,
323
+ 1,
324
+ 2.5,
325
+ 5,
326
+ 10
327
+ ];
328
+ outcomes = ["acquired", "failed", "aborted"];
329
+ metricsBrand = Symbol("transaction acquisition metrics");
330
+ states = new WeakMap;
331
+ });
332
+
281
333
  // ../../src/core/database/connection.ts
282
334
  function createDatabaseConnection(source) {
283
335
  return {
@@ -631,28 +683,51 @@ async function runInSavepoint(connection, operation) {
631
683
  function supportsTransactions(connection) {
632
684
  return typeof connection.begin === "function";
633
685
  }
634
- async function runInTransaction(operation) {
686
+ async function runInTransaction(operation, options = {}) {
687
+ const signal = options.acquisitionSignal;
688
+ signal?.throwIfAborted();
689
+ const admittedOperation = (connection) => {
690
+ signal?.throwIfAborted();
691
+ return operation(connection);
692
+ };
635
693
  const pool = resolveRepositoryConnection();
636
694
  if (hasActiveDatabaseConnection()) {
637
- return await runInSavepoint(getActiveDatabaseConnection(pool), operation);
695
+ return await runInSavepoint(getActiveDatabaseConnection(pool), admittedOperation);
638
696
  }
639
697
  if (!supportsTransactions(pool)) {
640
698
  throw new Error("Active database connection does not support transactions. Bind a client with begin() via bindDatabaseConnection.");
641
699
  }
642
700
  return await settleTransaction(() => runWithDeferredModelEvents(async () => {
643
- return await pool.begin(async (transaction) => {
644
- return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await operation(createDatabaseConnection(transaction))))));
645
- });
701
+ let reserved;
702
+ if (signal || options.acquisitionMetrics) {
703
+ if (!pool.reserve)
704
+ throw new Error(signal ? "Active database connection does not support cancellable transaction acquisition." : "Active database connection does not support observable transaction acquisition.");
705
+ const reserve = pool.reserve.bind(pool);
706
+ reserved = options.acquisitionMetrics ? await observeTransactionAcquisition(options.acquisitionMetrics, () => reserve({ signal }), signal) : await reserve({ signal });
707
+ }
708
+ const connection = reserved ?? pool;
709
+ try {
710
+ signal?.throwIfAborted();
711
+ if (!supportsTransactions(connection))
712
+ throw new Error("Reserved database connection does not support transactions.");
713
+ return await connection.begin(async (transaction) => {
714
+ return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
715
+ });
716
+ } finally {
717
+ await reserved?.release();
718
+ }
646
719
  }));
647
720
  }
648
721
  var transactionFrame, outerFrames;
649
722
  var init_transaction = __esm(() => {
650
723
  init_deferredModelEvents();
651
724
  init_asyncContextStore();
725
+ init_acquisitionMetrics();
652
726
  init_connection();
653
727
  init_connectionContext();
654
728
  init_repositoryConnection();
655
729
  init_transactionControl();
730
+ init_acquisitionMetrics();
656
731
  transactionFrame = createAsyncContextStore("@getstrata/transactionFrame");
657
732
  outerFrames = new WeakMap;
658
733
  });
@@ -48,10 +48,10 @@ import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-ho
48
48
  import { W3CTraceContextPropagator } from "@opentelemetry/core";
49
49
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
50
50
  import { resourceFromAttributes } from "@opentelemetry/resources";
51
+ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace";
51
52
  import {
52
53
  AlwaysOffSampler,
53
54
  AlwaysOnSampler,
54
- BatchSpanProcessor,
55
55
  ParentBasedSampler,
56
56
  TraceIdRatioBasedSampler
57
57
  } from "@opentelemetry/sdk-trace-base";
@@ -143,6 +143,172 @@ function sdkClientClassName() {
143
143
  return fromName ? `${fromName}Client` : "AppClient";
144
144
  }
145
145
 
146
+ // ../../src/core/tracing/health.ts
147
+ import {
148
+ createNoopMeter
149
+ } from "@opentelemetry/api";
150
+ import { ExportResultCode } from "@opentelemetry/core";
151
+ function observeExportDeadline(state, timeoutMillis) {
152
+ const deadline = setTimeout(() => {
153
+ state.exportDeadlineExceededBatches++;
154
+ }, timeoutMillis);
155
+ deadline.unref();
156
+ return deadline;
157
+ }
158
+ function createTracingHealth(enabled, exporterConfigured, timeoutMillis) {
159
+ const state = {
160
+ enabled,
161
+ exporterConfigured,
162
+ active: true,
163
+ queueSize: null,
164
+ queueCapacity: null,
165
+ droppedSpans: 0,
166
+ exportedBatches: 0,
167
+ failedBatches: 0,
168
+ exportedSpans: 0,
169
+ failedSpans: 0,
170
+ exportDeadlineExceededBatches: 0,
171
+ flushFailures: 0,
172
+ shutdownFailures: 0
173
+ };
174
+ let exportQueueFullSpans = 0;
175
+ let queueCallback;
176
+ const noop = createNoopMeter();
177
+ const meter = {
178
+ createGauge: noop.createGauge.bind(noop),
179
+ createHistogram: noop.createHistogram.bind(noop),
180
+ createObservableGauge: noop.createObservableGauge.bind(noop),
181
+ createObservableCounter: noop.createObservableCounter.bind(noop),
182
+ addBatchObservableCallback: noop.addBatchObservableCallback.bind(noop),
183
+ removeBatchObservableCallback: noop.removeBatchObservableCallback.bind(noop),
184
+ createCounter(name, options) {
185
+ if (name !== "otel.sdk.processor.span.processed")
186
+ return noop.createCounter(name, options);
187
+ return {
188
+ add(value, attributes) {
189
+ if (attributes?.["error.type"] === "queue_full")
190
+ state.droppedSpans += value;
191
+ }
192
+ };
193
+ },
194
+ createUpDownCounter(name, options) {
195
+ if (name !== "otel.sdk.processor.span.queue.capacity")
196
+ return noop.createUpDownCounter(name, options);
197
+ return {
198
+ add(value) {
199
+ state.queueCapacity = (state.queueCapacity ?? 0) + value;
200
+ }
201
+ };
202
+ },
203
+ createObservableUpDownCounter(name, options) {
204
+ if (name !== "otel.sdk.processor.span.queue.size")
205
+ return noop.createObservableUpDownCounter(name, options);
206
+ return {
207
+ addCallback(callback) {
208
+ queueCallback = callback;
209
+ },
210
+ removeCallback(callback) {
211
+ if (queueCallback === callback)
212
+ queueCallback = undefined;
213
+ }
214
+ };
215
+ }
216
+ };
217
+ const meterProvider = { getMeter: () => meter };
218
+ return {
219
+ state,
220
+ meterProvider,
221
+ snapshot() {
222
+ let queueSize = null;
223
+ queueCallback?.({
224
+ observe(value) {
225
+ queueSize = value;
226
+ }
227
+ });
228
+ return {
229
+ ...state,
230
+ queueSize,
231
+ droppedSpans: Math.max(0, state.droppedSpans - exportQueueFullSpans)
232
+ };
233
+ },
234
+ wrap(exporter) {
235
+ return {
236
+ export(spans, callback) {
237
+ const count = spans.length;
238
+ let completed = false;
239
+ const deadline = observeExportDeadline(state, timeoutMillis);
240
+ const finish = (success) => {
241
+ completed = true;
242
+ clearTimeout(deadline);
243
+ if (success) {
244
+ state.exportedBatches++;
245
+ state.exportedSpans += count;
246
+ } else {
247
+ state.failedBatches++;
248
+ state.failedSpans += count;
249
+ }
250
+ };
251
+ try {
252
+ exporter.export(spans, (result) => {
253
+ if (completed)
254
+ return;
255
+ finish(result.code === ExportResultCode.SUCCESS);
256
+ if (result.error?.name === "queue_full")
257
+ exportQueueFullSpans += count;
258
+ callback(result);
259
+ });
260
+ } catch (error) {
261
+ if (!completed)
262
+ finish(false);
263
+ throw error;
264
+ }
265
+ },
266
+ shutdown: () => exporter.shutdown(),
267
+ ...exporter.forceFlush ? { forceFlush: exporter.forceFlush.bind(exporter) } : {}
268
+ };
269
+ }
270
+ };
271
+ }
272
+ function renderTracingMetrics(snapshot) {
273
+ for (const key of ["enabled", "exporterConfigured", "active"])
274
+ if (typeof snapshot[key] !== "boolean")
275
+ throw new TypeError("Invalid tracing metrics.");
276
+ const values = {
277
+ strata_tracing_enabled: Number(snapshot.enabled),
278
+ strata_tracing_exporter_configured: Number(snapshot.exporterConfigured),
279
+ strata_tracing_active: Number(snapshot.active),
280
+ strata_tracing_queue_size: snapshot.queueSize,
281
+ strata_tracing_queue_capacity: snapshot.queueCapacity,
282
+ strata_tracing_dropped_spans_total: snapshot.droppedSpans,
283
+ strata_tracing_exported_batches_total: snapshot.exportedBatches,
284
+ strata_tracing_failed_batches_total: snapshot.failedBatches,
285
+ strata_tracing_exported_spans_total: snapshot.exportedSpans,
286
+ strata_tracing_failed_spans_total: snapshot.failedSpans,
287
+ strata_tracing_export_deadline_exceeded_batches_total: snapshot.exportDeadlineExceededBatches,
288
+ strata_tracing_flush_failures_total: snapshot.flushFailures,
289
+ strata_tracing_shutdown_failures_total: snapshot.shutdownFailures
290
+ };
291
+ let output = `# HELP strata_tracing_collector_success Whether tracing observation succeeded.
292
+ # TYPE strata_tracing_collector_success gauge
293
+ strata_tracing_collector_success 1
294
+ `;
295
+ for (const [name, value] of Object.entries(values)) {
296
+ if (value === null) {
297
+ if (name === "strata_tracing_queue_size" || name === "strata_tracing_queue_capacity")
298
+ continue;
299
+ throw new TypeError("Invalid tracing metrics.");
300
+ }
301
+ if (!Number.isSafeInteger(value) || value < 0)
302
+ throw new TypeError("Invalid tracing metrics.");
303
+ const type = name.endsWith("_total") ? "counter" : "gauge";
304
+ output += `# HELP ${name} Process-local tracing ${name.slice(15).replaceAll("_", " ")}.
305
+ # TYPE ${name} ${type}
306
+ ${name} ${value}
307
+ `;
308
+ }
309
+ return output;
310
+ }
311
+
146
312
  // ../../src/core/tracing/otel.ts
147
313
  function numberOption(value, fallback, name, max = 1e6) {
148
314
  const result = value ?? fallback;
@@ -193,11 +359,14 @@ function createTracingRuntime(options = {}) {
193
359
  throw new TypeError("Invalid tracing collector endpoint.");
194
360
  }
195
361
  const exporter = disabled ? undefined : options.exporter ?? (endpoint ? new OTLPTraceExporter({ url: endpoint, timeoutMillis: timeout, concurrencyLimit: 1 }) : undefined);
362
+ const health = createTracingHealth(!disabled, exporter !== undefined, timeout);
196
363
  const provider = new NodeTracerProvider({
197
364
  resource: resourceFromAttributes({ "service.name": options.serviceName ?? otelServiceName() }),
198
365
  sampler,
199
366
  spanProcessors: exporter ? [
200
- new BatchSpanProcessor(exporter, {
367
+ new BatchSpanProcessor({
368
+ exporter: health.wrap(exporter),
369
+ selfObsMeterProvider: health.meterProvider,
201
370
  maxQueueSize: queueSize,
202
371
  maxExportBatchSize: batchSize,
203
372
  scheduledDelayMillis: delay,
@@ -217,8 +386,21 @@ function createTracingRuntime(options = {}) {
217
386
  tracer: provider.getTracer("@getstrata/http"),
218
387
  propagator: new W3CTraceContextPropagator,
219
388
  run: (parent, callback) => context.with(parent, callback),
220
- forceFlush: () => provider.forceFlush(),
221
- shutdown: () => shutdown ??= provider.shutdown()
389
+ metrics: () => health.snapshot(),
390
+ async forceFlush() {
391
+ try {
392
+ await provider.forceFlush();
393
+ } catch (error) {
394
+ health.state.flushFailures++;
395
+ throw error;
396
+ }
397
+ },
398
+ shutdown: () => shutdown ??= provider.shutdown().catch((error) => {
399
+ health.state.shutdownFailures++;
400
+ throw error;
401
+ }).finally(() => {
402
+ health.state.active = false;
403
+ })
222
404
  };
223
405
  }
224
406
  var runtimeKey = Symbol.for("@getstrata/otel-runtime");
@@ -319,5 +501,6 @@ export {
319
501
  acquireTracingRuntime,
320
502
  createTracingMiddleware,
321
503
  createTracingRuntime,
322
- getTracingRuntime
504
+ getTracingRuntime,
505
+ renderTracingMetrics
323
506
  };
@@ -32,6 +32,8 @@ export { CACHE_TAGS } from "../core/cache/tags.ts";
32
32
  export { ConfigStore, createServiceToken, ServiceContainer, type ServiceToken, } from "../core/contracts/container.ts";
33
33
  export type { ServiceProvider } from "../core/contracts/di.ts";
34
34
  export { resolveService } from "../core/contracts/di.ts";
35
+ export type { TransactionAcquisitionMetrics, TransactionAcquisitionSnapshot, } from "../core/database/acquisitionMetrics.ts";
36
+ export { createTransactionAcquisitionMetrics, renderTransactionAcquisitionMetrics, } from "../core/database/acquisitionMetrics.ts";
35
37
  export type { DatabaseConnection } from "../core/database/baseRepository.ts";
36
38
  export { default as BaseRepository } from "../core/database/baseRepository.ts";
37
39
  export { bindDatabaseConnection, getBoundDatabaseConnection, resetBoundDatabaseConnection, } from "../core/database/bindConnection.ts";
@@ -64,6 +66,7 @@ export { loadSeedersFromDirectory, runSeedersFromDirectory, } from "../core/data
64
66
  export type { Seeder, SeederDatabase } from "../core/database/seeders/types.ts";
65
67
  export { createSqliteConnection } from "../core/database/sqliteConnection.ts";
66
68
  export { defineTable } from "../core/database/table.ts";
69
+ export type { TransactionOptions } from "../core/database/transaction.ts";
67
70
  export { hasActiveTransaction, requestTransactionRollback, runInTransaction, } from "../core/database/transaction.ts";
68
71
  export type { ModelWhere, ModelWriteValues, QueryJoin, QueryJoinOn, QueryOptions, QueryOrder, QuerySelectItem, QueryWhere, } from "../core/database/types.ts";
69
72
  export type { WhereNode } from "../core/database/whereBuilder.ts";
@@ -127,6 +130,8 @@ export { PrometheusRegistry, prometheusRegistry } from "../core/metrics/promethe
127
130
  export type { DatabaseNotificationPayload, DatabaseNotificationStore, MailNotificationMessage, Notifiable, NotificationChannelName, } from "../core/notifications/index.ts";
128
131
  export { createNotificationDispatcher, Notification, NotificationDispatcher, } from "../core/notifications/index.ts";
129
132
  export type { CursorPaginatedResult, KeysetColumn, KeysetCursor, KeysetOptions, PaginatedResult, PaginationMeta, } from "../core/pagination/index.ts";
133
+ export type { FailedJobMetricsCollector, FailedJobMetricsOptions, FailedJobMetricsSnapshot, } from "../core/queue/failedJobMetrics.ts";
134
+ export { createFailedJobMetricsCollector, renderFailedJobMetrics, } from "../core/queue/failedJobMetrics.ts";
130
135
  export type { Queue, QueuePriority } from "../core/queue/index.ts";
131
136
  export { AsyncQueue, createQueue, Job, SyncQueue } from "../core/queue/index.ts";
132
137
  export { JobRegistry } from "../core/queue/jobRegistry.ts";
@@ -154,6 +159,8 @@ export { currentTenant, currentTenantId, runWithTenant, tenantContext, } from ".
154
159
  export { isInsideTenantDatabaseScope, runWithTenantDatabase, } from "../core/tenant/tenantDatabaseScope.ts";
155
160
  export type { PublicTenancyOptions, TenantMiddlewareOptions, TenantResolver, } from "../core/tenant/tenantMiddleware.ts";
156
161
  export { auditChecksum, createTenantMiddleware, DEFAULT_TENANT, normalizeTenantHostname, resolveUserTenantId, } from "../core/tenant/tenantMiddleware.ts";
162
+ export type { TracingMetricsSnapshot } from "../core/tracing/health.ts";
163
+ export { renderTracingMetrics } from "../core/tracing/health.ts";
157
164
  export type { TraceContext } from "../core/tracing/traceContext.ts";
158
165
  export { currentTraceId, runWithTraceContext, traceContextStorage, } from "../core/tracing/traceContext.ts";
159
166
  export type { TracingOptions, TracingRuntime } from "../core/tracing/tracingMiddleware.ts";