@onebun/core 0.2.16 → 0.2.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onebun/core",
3
- "version": "0.2.16",
3
+ "version": "0.2.17",
4
4
  "description": "Core package for OneBun framework - decorators, DI, modules, controllers",
5
5
  "license": "LGPL-3.0",
6
6
  "author": "RemRyahirev",
@@ -43,13 +43,14 @@
43
43
  "test": "bun test"
44
44
  },
45
45
  "dependencies": {
46
- "effect": "^3.13.10",
47
- "arktype": "^2.0.0",
48
- "@onebun/logger": "^0.2.2",
49
46
  "@onebun/envs": "^0.2.2",
47
+ "@onebun/logger": "^0.2.2",
50
48
  "@onebun/metrics": "^0.2.2",
51
49
  "@onebun/requests": "^0.2.1",
52
- "@onebun/trace": "^0.2.2"
50
+ "@onebun/trace": "^0.2.3",
51
+ "@opentelemetry/api": "^1.9.1",
52
+ "arktype": "^2.0.0",
53
+ "effect": "^3.13.10"
53
54
  },
54
55
  "devDependencies": {
55
56
  "bun-types": "^1.3.8",
@@ -521,7 +521,12 @@ export class OneBunApplication<QA extends import('../queue/types').QueueAdapterC
521
521
  // Create the root module AFTER config is initialized and QueueService proxy is registered,
522
522
  // so services can safely use this.config.get() in their constructors
523
523
  // and inject QueueService in any module depth.
524
- this.rootModule = OneBunModule.create(this.moduleClass, this.loggerLayer, this.config);
524
+ this.rootModule = OneBunModule.create(
525
+ this.moduleClass, this.loggerLayer, this.config,
526
+ this.options.tracing?.traceAll
527
+ ? { traceAll: true, traceFilter: this.options.tracing.traceFilter }
528
+ : undefined,
529
+ );
525
530
 
526
531
  // Register test provider overrides (must happen before setup() so controllers receive mocks)
527
532
  if (this.options._testProviders) {
@@ -1,9 +1,12 @@
1
+ import { trace } from '@opentelemetry/api';
2
+
1
3
  import type { IConfig, OneBunAppConfig } from './config.interface';
2
4
  import type {
3
5
  OneBunRequest,
4
6
  SseEvent,
5
7
  SseOptions,
6
8
  } from '../types';
9
+ import type { Span } from '@opentelemetry/api';
7
10
  import type { Context } from 'effect';
8
11
 
9
12
  import type { SyncLogger } from '@onebun/logger';
@@ -134,6 +137,14 @@ export class Controller {
134
137
  this.logger.debug(`Controller ${className} initialized`);
135
138
  }
136
139
 
140
+ /**
141
+ * Get the currently active OpenTelemetry span.
142
+ * Returns undefined when no span is active.
143
+ */
144
+ protected get span(): Span | undefined {
145
+ return trace.getActiveSpan();
146
+ }
147
+
137
148
  /**
138
149
  * Get a service instance by tag
139
150
  * @param tag - The service tag
@@ -14,6 +14,11 @@ import {
14
14
  makeLogger,
15
15
  type SyncLogger,
16
16
  } from '@onebun/logger';
17
+ import {
18
+ applyAutoTrace,
19
+ shouldAutoTrace,
20
+ type TraceFilterOptions,
21
+ } from '@onebun/trace';
17
22
 
18
23
  import {
19
24
  autoDetectDependencies,
@@ -48,6 +53,7 @@ import {
48
53
  getServiceTag,
49
54
  } from './service';
50
55
 
56
+
51
57
  /**
52
58
  * Global services registry
53
59
  * Stores services from modules marked with @Global() decorator
@@ -127,11 +133,17 @@ export class OneBunModule implements ModuleInstance {
127
133
  */
128
134
  private resolvedAncestorMiddleware: Function[] = [];
129
135
 
136
+ /**
137
+ * Tracing options for auto-trace (traceAll + filters)
138
+ */
139
+ private readonly tracingOptions?: { traceAll?: boolean; traceFilter?: TraceFilterOptions };
140
+
130
141
  constructor(
131
142
  private moduleClass: Function,
132
143
  private loggerLayer?: Layer.Layer<never, never, unknown>,
133
144
  config?: IConfig<OneBunAppConfig>,
134
145
  ancestorMiddleware?: Function[],
146
+ tracingOptions?: { traceAll?: boolean; traceFilter?: TraceFilterOptions },
135
147
  ) {
136
148
  // Initialize logger with module class name as context
137
149
  const effectLogger = Effect.runSync(
@@ -144,6 +156,7 @@ export class OneBunModule implements ModuleInstance {
144
156
  ) as Logger;
145
157
  this.logger = createSyncLogger(effectLogger);
146
158
  this.config = config ?? new NotInitializedConfig();
159
+ this.tracingOptions = tracingOptions;
147
160
  this.ancestorMiddlewareClasses = ancestorMiddleware ?? [];
148
161
 
149
162
  // Read module-level middleware from OnModuleConfigure interface
@@ -230,7 +243,10 @@ export class OneBunModule implements ModuleInstance {
230
243
 
231
244
  // Pass the logger layer, config, and accumulated middleware class refs to child modules
232
245
  const accumulatedMiddleware = [...this.ancestorMiddlewareClasses, ...this.ownMiddlewareClasses];
233
- const childModule = new OneBunModule(importModule, this.loggerLayer, this.config, accumulatedMiddleware);
246
+ const childModule = new OneBunModule(
247
+ importModule, this.loggerLayer, this.config,
248
+ accumulatedMiddleware, this.tracingOptions,
249
+ );
234
250
  this.childModules.push(childModule);
235
251
 
236
252
  // Merge layers
@@ -426,6 +442,14 @@ export class OneBunModule implements ModuleInstance {
426
442
  });
427
443
  }
428
444
 
445
+ // Apply auto-tracing if enabled
446
+ if (
447
+ this.tracingOptions &&
448
+ shouldAutoTrace(provider, provider.name, !!this.tracingOptions.traceAll, this.tracingOptions.traceFilter)
449
+ ) {
450
+ applyAutoTrace(serviceInstance, provider.name, this.tracingOptions.traceFilter);
451
+ }
452
+
429
453
  this.serviceInstances.set(serviceMetadata.tag, serviceInstance);
430
454
  createdServices.add(provider.name);
431
455
  this.logger.debug(
@@ -633,6 +657,17 @@ export class OneBunModule implements ModuleInstance {
633
657
  controller.initializeController(this.logger, this.config);
634
658
  }
635
659
 
660
+ // Apply auto-tracing if enabled
661
+ if (
662
+ this.tracingOptions &&
663
+ shouldAutoTrace(
664
+ controllerClass, controllerClass.name,
665
+ !!this.tracingOptions.traceAll, this.tracingOptions.traceFilter,
666
+ )
667
+ ) {
668
+ applyAutoTrace(controller, controllerClass.name, this.tracingOptions.traceFilter);
669
+ }
670
+
636
671
  this.controllerInstances.set(controllerClass, controller);
637
672
 
638
673
  // Inject all services into controller (for legacy compatibility)
@@ -1145,9 +1180,10 @@ export class OneBunModule implements ModuleInstance {
1145
1180
  moduleClass: Function,
1146
1181
  loggerLayer?: Layer.Layer<never, never, unknown>,
1147
1182
  config?: IConfig<OneBunAppConfig>,
1183
+ tracingOptions?: { traceAll?: boolean; traceFilter?: TraceFilterOptions },
1148
1184
  ): ModuleInstance {
1149
1185
  // Using console.log here because we don't have access to the logger instance yet
1150
1186
  // The instance will create its own logger in the constructor
1151
- return new OneBunModule(moduleClass, loggerLayer, config);
1187
+ return new OneBunModule(moduleClass, loggerLayer, config, undefined, tracingOptions);
1152
1188
  }
1153
1189
  }
@@ -1,3 +1,4 @@
1
+ import { trace } from '@opentelemetry/api';
1
2
  import {
2
3
  Context,
3
4
  Effect,
@@ -5,6 +6,7 @@ import {
5
6
  } from 'effect';
6
7
 
7
8
  import type { IConfig, OneBunAppConfig } from './config.interface';
9
+ import type { Span } from '@opentelemetry/api';
8
10
 
9
11
  import type { SyncLogger } from '@onebun/logger';
10
12
 
@@ -22,7 +24,7 @@ const META_SERVICES = new Map<
22
24
  * Services extending BaseService will have logger and config available
23
25
  * immediately after super() in the constructor (via ambient init context),
24
26
  * as well as through the initializeService fallback method.
25
- *
27
+ *
26
28
  * @param tag - Optional Effect Context tag for the service
27
29
  */
28
30
  export function Service<T>(tag?: Context.Tag<T, T>) {
@@ -170,6 +172,21 @@ export class BaseService {
170
172
  return this._initialized;
171
173
  }
172
174
 
175
+ /**
176
+ * Get the currently active OpenTelemetry span.
177
+ * Returns undefined when no span is active (e.g. outside of @Traced context).
178
+ *
179
+ * The returned Span has a fully synchronous API:
180
+ * - `span.setAttribute(key, value)`
181
+ * - `span.setAttributes({ key: value })`
182
+ * - `span.addEvent(name, attributes?)`
183
+ * - `span.recordException(error)`
184
+ * - `span.setStatus({ code, message })`
185
+ */
186
+ protected get span(): Span | undefined {
187
+ return trace.getActiveSpan();
188
+ }
189
+
173
190
  /**
174
191
  * Run an effect with error handling
175
192
  * @param effect - The effect to run
package/src/types.ts CHANGED
@@ -385,6 +385,47 @@ export interface ApplicationOptions<QA extends QueueAdapterConstructor<any> = Qu
385
385
  */
386
386
  traceDatabaseQueries?: boolean;
387
387
 
388
+ /**
389
+ * Auto-trace all async methods on services and controllers.
390
+ * When enabled, all async methods are wrapped in spans without
391
+ * requiring @Traced() on each method.
392
+ *
393
+ * Priority: method-level decorators > class-level > this setting.
394
+ * Use @NoTrace() to opt out, @TraceAll() on a class to opt in
395
+ * when traceAll is false.
396
+ *
397
+ * @defaultValue false
398
+ */
399
+ traceAll?: boolean;
400
+
401
+ /**
402
+ * Filter options for traceAll. Only effective when traceAll is true.
403
+ */
404
+ traceFilter?: {
405
+ /**
406
+ * Only trace async methods. Sync methods are skipped.
407
+ * @defaultValue true
408
+ */
409
+ asyncOnly?: boolean;
410
+
411
+ /**
412
+ * Additional method names to exclude from auto-tracing
413
+ * (on top of built-in exclusions like lifecycle hooks and base class methods).
414
+ */
415
+ excludeMethods?: string[];
416
+
417
+ /**
418
+ * Class name glob patterns to include (e.g. ['*Service', '*Repository']).
419
+ * When set, only matching classes are auto-traced.
420
+ */
421
+ includeClasses?: string[];
422
+
423
+ /**
424
+ * Class name glob patterns to exclude (e.g. ['HealthController']).
425
+ */
426
+ excludeClasses?: string[];
427
+ };
428
+
388
429
  /**
389
430
  * Custom attributes to add to all spans
390
431
  */