@onebun/core 0.1.10 → 0.1.12

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.
@@ -1,5 +1,8 @@
1
1
  import { Context } from 'effect';
2
2
 
3
+ import type { IConfig, OneBunAppConfig } from './config.interface';
4
+
5
+ import type { DeepPaths, DeepValue } from '@onebun/envs';
3
6
  import type { SyncLogger } from '@onebun/logger';
4
7
 
5
8
  import { BaseService, Service } from './service';
@@ -15,17 +18,16 @@ export const ConfigServiceTag = Context.GenericTag<ConfigServiceImpl>('ConfigSer
15
18
  */
16
19
  @Service(ConfigServiceTag)
17
20
  export class ConfigServiceImpl extends BaseService {
18
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- private configInstance: any = null;
21
+ private configInstance: IConfig<OneBunAppConfig> | null = null;
20
22
 
21
- constructor(logger?: SyncLogger, config?: unknown) {
23
+ constructor(logger?: SyncLogger, config?: IConfig<OneBunAppConfig>) {
22
24
  super();
23
25
  // If logger is provided directly (for backwards compatibility in tests),
24
26
  // initialize the service immediately
25
- if (logger) {
27
+ if (logger && config) {
26
28
  this.initializeService(logger, config);
27
29
  }
28
- this.configInstance = config;
30
+ this.configInstance = config ?? null;
29
31
  }
30
32
 
31
33
  /**
@@ -39,9 +41,24 @@ export class ConfigServiceImpl extends BaseService {
39
41
  }
40
42
 
41
43
  /**
42
- * Get configuration value by path
44
+ * Get configuration value by path with full type inference.
45
+ * Uses module augmentation of OneBunAppConfig for type-safe access.
46
+ *
47
+ * @example
48
+ * // With module augmentation:
49
+ * declare module '@onebun/core' {
50
+ * interface OneBunAppConfig {
51
+ * server: { port: number; host: string };
52
+ * }
53
+ * }
54
+ *
55
+ * const port = configService.get('server.port'); // number
56
+ * const host = configService.get('server.host'); // string
43
57
  */
44
- get<T = unknown>(path: string): T {
58
+ get<P extends DeepPaths<OneBunAppConfig>>(path: P): DeepValue<OneBunAppConfig, P>;
59
+ /** Fallback for dynamic paths */
60
+ get<T = unknown>(path: string): T;
61
+ get(path: string): unknown {
45
62
  if (!this.configInstance) {
46
63
  throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
47
64
  }
@@ -52,7 +69,7 @@ export class ConfigServiceImpl extends BaseService {
52
69
  /**
53
70
  * Get all configuration values
54
71
  */
55
- get values(): unknown {
72
+ get values(): OneBunAppConfig {
56
73
  if (!this.configInstance) {
57
74
  throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
58
75
  }
@@ -63,7 +80,7 @@ export class ConfigServiceImpl extends BaseService {
63
80
  /**
64
81
  * Get safe configuration for logging (sensitive data masked)
65
82
  */
66
- getSafeConfig(): unknown {
83
+ getSafeConfig(): OneBunAppConfig {
67
84
  if (!this.configInstance) {
68
85
  throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
69
86
  }
@@ -12,13 +12,17 @@ import {
12
12
  } from 'bun:test';
13
13
  import { Context } from 'effect';
14
14
 
15
+ import type { IConfig, OneBunAppConfig } from './config.interface';
16
+
15
17
  import type { SyncLogger } from '@onebun/logger';
16
18
 
19
+ import { createMockConfig } from '../testing/test-utils';
20
+
17
21
  import { Controller } from './controller';
18
22
 
19
23
  describe('Controller', () => {
20
24
  let mockLogger: SyncLogger;
21
- let mockConfig: unknown;
25
+ let mockConfig: IConfig<OneBunAppConfig>;
22
26
 
23
27
  beforeEach(() => {
24
28
  mockLogger = {
@@ -31,13 +35,11 @@ describe('Controller', () => {
31
35
  child: mock(() => mockLogger),
32
36
  };
33
37
 
34
- mockConfig = {
35
- test: 'value',
36
- database: {
37
- host: 'localhost',
38
- port: 5432,
39
- },
40
- };
38
+ mockConfig = createMockConfig({
39
+ 'test': 'value',
40
+ 'database.host': 'localhost',
41
+ 'database.port': 5432,
42
+ });
41
43
  });
42
44
 
43
45
  describe('Controller initialization', () => {
@@ -139,33 +141,28 @@ describe('Controller', () => {
139
141
  expect(mockLogger.debug).toHaveBeenCalledWith('Controller CustomNamedController initialized');
140
142
  });
141
143
 
142
- test('should work with different config types', () => {
144
+ test('should provide typed config access', () => {
143
145
  class TestController extends Controller {
144
- getConfigType() {
145
- return typeof this.config;
146
+ getConfigValue(path: string) {
147
+ return this.config.get(path);
148
+ }
149
+
150
+ checkConfigInitialized() {
151
+ return this.config.isInitialized;
146
152
  }
147
153
  }
148
154
 
149
155
  const controller = new TestController();
156
+ const typedConfig = createMockConfig({
157
+ 'server.port': 3000,
158
+ 'server.host': '0.0.0.0',
159
+ });
150
160
 
151
- // Test with object config
152
- controller.initializeController(mockLogger, { test: 'value' });
153
- expect(controller.getConfigType()).toBe('object');
161
+ controller.initializeController(mockLogger, typedConfig);
154
162
 
155
- // Test with string config
156
- const controller2 = new TestController();
157
- controller2.initializeController(mockLogger, 'string-config');
158
- expect(controller2.getConfigType()).toBe('string');
159
-
160
- // Test with null config
161
- const controller3 = new TestController();
162
- controller3.initializeController(mockLogger, null);
163
- expect(controller3.getConfigType()).toBe('object'); // null is typeof 'object'
164
-
165
- // Test with undefined config
166
- const controller4 = new TestController();
167
- controller4.initializeController(mockLogger, undefined);
168
- expect(controller4.getConfigType()).toBe('undefined');
163
+ expect(controller.getConfigValue('server.port')).toBe(3000);
164
+ expect(controller.getConfigValue('server.host')).toBe('0.0.0.0');
165
+ expect(controller.checkConfigInitialized()).toBe(true);
169
166
  });
170
167
  });
171
168
 
@@ -252,7 +249,8 @@ describe('Controller', () => {
252
249
  debug: mock(() => {}),
253
250
  };
254
251
 
255
- controller.initializeController(newMockLogger, { newConfig: true });
252
+ const newConfig = createMockConfig({ 'newConfig': true });
253
+ controller.initializeController(newMockLogger, newConfig);
256
254
  expect(newMockLogger.debug).toHaveBeenCalledWith('Controller TestController initialized');
257
255
  });
258
256
  });
@@ -1,3 +1,4 @@
1
+ import type { IConfig, OneBunAppConfig } from './config.interface';
1
2
  import type { Context } from 'effect';
2
3
 
3
4
  import type { SyncLogger } from '@onebun/logger';
@@ -14,13 +15,13 @@ export class Controller {
14
15
  // Logger instance with controller class name as context
15
16
  protected logger!: SyncLogger;
16
17
  // Configuration instance for accessing environment variables
17
- protected config!: unknown;
18
+ protected config!: IConfig<OneBunAppConfig>;
18
19
 
19
20
  /**
20
21
  * Initialize controller with logger and config (called by the framework)
21
22
  * @internal
22
23
  */
23
- initializeController(logger: SyncLogger, config: unknown): void {
24
+ initializeController(logger: SyncLogger, config: IConfig<OneBunAppConfig>): void {
24
25
  const className = this.constructor.name;
25
26
 
26
27
  if (logger) {
@@ -10,3 +10,4 @@ export * from './controller';
10
10
  export { Controller as BaseController } from './controller';
11
11
  export * from './service';
12
12
  export * from './config.service';
13
+ export * from './config.interface';
@@ -12,18 +12,17 @@ import {
12
12
  } from 'bun:test';
13
13
  import { Context } from 'effect';
14
14
 
15
- import { makeDevLogger } from '@onebun/logger';
16
-
17
15
  import { Module } from '../decorators/decorators';
16
+ import { makeMockLoggerLayer } from '../testing/test-utils';
18
17
 
19
18
  import { OneBunModule } from './module';
20
19
  import { Service } from './service';
21
20
 
22
21
  describe('OneBunModule', () => {
23
- let mockLogger: any;
22
+ let mockLoggerLayer: any;
24
23
 
25
24
  beforeEach(() => {
26
- mockLogger = makeDevLogger();
25
+ mockLoggerLayer = makeMockLoggerLayer();
27
26
  });
28
27
 
29
28
  describe('Module initialization', () => {
@@ -31,7 +30,7 @@ describe('OneBunModule', () => {
31
30
  @Module({})
32
31
  class TestModule {}
33
32
 
34
- const module = new OneBunModule(TestModule, mockLogger);
33
+ const module = new OneBunModule(TestModule, mockLoggerLayer);
35
34
  expect(module).toBeInstanceOf(OneBunModule);
36
35
  expect((module as any).logger).toBeDefined(); // Logger wrapper is created
37
36
  expect((module as any).moduleClass).toBe(TestModule);
@@ -49,7 +48,7 @@ describe('OneBunModule', () => {
49
48
  test('should handle module without metadata', () => {
50
49
  class TestModuleWithoutDecorator {}
51
50
 
52
- expect(() => new OneBunModule(TestModuleWithoutDecorator, mockLogger)).toThrow();
51
+ expect(() => new OneBunModule(TestModuleWithoutDecorator, mockLoggerLayer)).toThrow();
53
52
  });
54
53
  });
55
54
 
@@ -58,7 +57,7 @@ describe('OneBunModule', () => {
58
57
  @Module({})
59
58
  class EmptyModule {}
60
59
 
61
- const module = new OneBunModule(EmptyModule, mockLogger);
60
+ const module = new OneBunModule(EmptyModule, mockLoggerLayer);
62
61
  const layer = module.getLayer();
63
62
  expect(layer).toBeDefined();
64
63
  });
@@ -72,7 +71,7 @@ describe('OneBunModule', () => {
72
71
  })
73
72
  class ModuleWithProviders {}
74
73
 
75
- const module = new OneBunModule(ModuleWithProviders, mockLogger);
74
+ const module = new OneBunModule(ModuleWithProviders, mockLoggerLayer);
76
75
  const layer = module.getLayer();
77
76
  expect(layer).toBeDefined();
78
77
  });
@@ -85,7 +84,7 @@ describe('OneBunModule', () => {
85
84
  })
86
85
  class ModuleWithControllers {}
87
86
 
88
- const module = new OneBunModule(ModuleWithControllers, mockLogger);
87
+ const module = new OneBunModule(ModuleWithControllers, mockLoggerLayer);
89
88
  const layer = module.getLayer();
90
89
  expect(layer).toBeDefined();
91
90
  });
@@ -99,7 +98,7 @@ describe('OneBunModule', () => {
99
98
  })
100
99
  class ModuleWithImports {}
101
100
 
102
- const module = new OneBunModule(ModuleWithImports, mockLogger);
101
+ const module = new OneBunModule(ModuleWithImports, mockLoggerLayer);
103
102
  const layer = module.getLayer();
104
103
  expect(layer).toBeDefined();
105
104
  });
@@ -110,7 +109,7 @@ describe('OneBunModule', () => {
110
109
  @Module({})
111
110
  class TestModule {}
112
111
 
113
- const module = new OneBunModule(TestModule, mockLogger);
112
+ const module = new OneBunModule(TestModule, mockLoggerLayer);
114
113
  const layer = module.getLayer();
115
114
  expect(layer).toBeDefined();
116
115
  });
@@ -136,7 +135,7 @@ describe('OneBunModule', () => {
136
135
  })
137
136
  class ComplexModule {}
138
137
 
139
- const module = new OneBunModule(ComplexModule, mockLogger);
138
+ const module = new OneBunModule(ComplexModule, mockLoggerLayer);
140
139
  const layer = module.getLayer();
141
140
  expect(layer).toBeDefined();
142
141
  });
@@ -144,8 +143,8 @@ describe('OneBunModule', () => {
144
143
 
145
144
  describe('Error handling', () => {
146
145
  test('should handle invalid module class', () => {
147
- expect(() => new OneBunModule(null as any, mockLogger)).toThrow();
148
- expect(() => new OneBunModule(undefined as any, mockLogger)).toThrow();
146
+ expect(() => new OneBunModule(null as any, mockLoggerLayer)).toThrow();
147
+ expect(() => new OneBunModule(undefined as any, mockLoggerLayer)).toThrow();
149
148
  });
150
149
 
151
150
  test('should handle malformed metadata', () => {
@@ -153,7 +152,7 @@ describe('OneBunModule', () => {
153
152
  const TestClass = class {};
154
153
  (TestClass as any)[Symbol.for('module:metadata')] = 'invalid';
155
154
 
156
- expect(() => new OneBunModule(TestClass, mockLogger)).toThrow();
155
+ expect(() => new OneBunModule(TestClass, mockLoggerLayer)).toThrow();
157
156
  });
158
157
  });
159
158
 
@@ -162,7 +161,7 @@ describe('OneBunModule', () => {
162
161
  @Module({})
163
162
  class NamedModule {}
164
163
 
165
- const module = new OneBunModule(NamedModule, mockLogger);
164
+ const module = new OneBunModule(NamedModule, mockLoggerLayer);
166
165
  expect((module as any).moduleClass.name).toBe('NamedModule');
167
166
  });
168
167
 
@@ -170,7 +169,7 @@ describe('OneBunModule', () => {
170
169
  @Module({})
171
170
  class InitModule {}
172
171
 
173
- const module = new OneBunModule(InitModule, mockLogger);
172
+ const module = new OneBunModule(InitModule, mockLoggerLayer);
174
173
 
175
174
  // Test that initialization completes without error
176
175
  expect(() => module.getLayer()).not.toThrow();
@@ -191,7 +190,7 @@ describe('OneBunModule', () => {
191
190
  })
192
191
  class ServiceModule {}
193
192
 
194
- const module = new OneBunModule(ServiceModule, mockLogger);
193
+ const module = new OneBunModule(ServiceModule, mockLoggerLayer);
195
194
  const layer = module.getLayer();
196
195
  expect(layer).toBeDefined();
197
196
  });
@@ -208,7 +207,7 @@ describe('OneBunModule', () => {
208
207
  })
209
208
  class ControllerModule {}
210
209
 
211
- const module = new OneBunModule(ControllerModule, mockLogger);
210
+ const module = new OneBunModule(ControllerModule, mockLoggerLayer);
212
211
  const layer = module.getLayer();
213
212
  expect(layer).toBeDefined();
214
213
  });
@@ -227,7 +226,7 @@ describe('OneBunModule', () => {
227
226
  })
228
227
  class TestModule {}
229
228
 
230
- const module = new OneBunModule(TestModule, mockLogger);
229
+ const module = new OneBunModule(TestModule, mockLoggerLayer);
231
230
 
232
231
  // Access private method via type assertion
233
232
  const instances = (module as any).getControllerInstances();
@@ -248,7 +247,7 @@ describe('OneBunModule', () => {
248
247
  })
249
248
  class TestModule {}
250
249
 
251
- const module = new OneBunModule(TestModule, mockLogger);
250
+ const module = new OneBunModule(TestModule, mockLoggerLayer);
252
251
  module.getLayer(); // Initialize the module
253
252
 
254
253
  // Access private method via type assertion
@@ -262,7 +261,7 @@ describe('OneBunModule', () => {
262
261
  @Module({})
263
262
  class TestModule {}
264
263
 
265
- const module = new OneBunModule(TestModule, mockLogger);
264
+ const module = new OneBunModule(TestModule, mockLoggerLayer);
266
265
 
267
266
  // Access private deprecated method via type assertion
268
267
  const resolved = (module as any).resolveDependencyByName('SomeService');
@@ -295,7 +294,7 @@ describe('OneBunModule', () => {
295
294
  })
296
295
  class TagModule {}
297
296
 
298
- const module = new OneBunModule(TagModule, mockLogger);
297
+ const module = new OneBunModule(TagModule, mockLoggerLayer);
299
298
  const layer = module.getLayer();
300
299
 
301
300
  expect(layer).toBeDefined();
@@ -321,7 +320,7 @@ describe('OneBunModule', () => {
321
320
  })
322
321
  class TestModule {}
323
322
 
324
- const module = new OneBunModule(TestModule, mockLogger);
323
+ const module = new OneBunModule(TestModule, mockLoggerLayer);
325
324
  module.getLayer(); // Initialize the module
326
325
 
327
326
  // Try to resolve ServiceB which is not in the module
@@ -350,7 +349,7 @@ describe('OneBunModule', () => {
350
349
  })
351
350
  class TestModule {}
352
351
 
353
- const module = new OneBunModule(TestModule, mockLogger);
352
+ const module = new OneBunModule(TestModule, mockLoggerLayer);
354
353
  module.getLayer(); // Initialize the module
355
354
 
356
355
  // Try to resolve by base type
@@ -400,7 +399,7 @@ describe('OneBunModule', () => {
400
399
  })
401
400
  class RootModule {}
402
401
 
403
- const module = new OneBunModule(RootModule, mockLogger);
402
+ const module = new OneBunModule(RootModule, mockLoggerLayer);
404
403
  module.getLayer();
405
404
 
406
405
  // Check that global service is registered
@@ -452,7 +451,7 @@ describe('OneBunModule', () => {
452
451
  })
453
452
  class RootModule {}
454
453
 
455
- const module = new OneBunModule(RootModule, mockLogger);
454
+ const module = new OneBunModule(RootModule, mockLoggerLayer);
456
455
  module.getLayer();
457
456
 
458
457
  // GlobalDbService should be in the global registry
@@ -498,7 +497,7 @@ describe('OneBunModule', () => {
498
497
  })
499
498
  class RootModule {}
500
499
 
501
- const module = new OneBunModule(RootModule, mockLogger);
500
+ const module = new OneBunModule(RootModule, mockLoggerLayer);
502
501
  module.getLayer();
503
502
 
504
503
  // Should have only one SingletonService instance in global registry
@@ -532,7 +531,7 @@ describe('OneBunModule', () => {
532
531
  })
533
532
  class RootModule {}
534
533
 
535
- const module = new OneBunModule(RootModule, mockLogger);
534
+ const module = new OneBunModule(RootModule, mockLoggerLayer);
536
535
  module.getLayer();
537
536
 
538
537
  // LocalService should NOT be in the global registry
@@ -567,7 +566,7 @@ describe('OneBunModule', () => {
567
566
  })
568
567
  class RootModule {}
569
568
 
570
- const module = new OneBunModule(RootModule, mockLogger);
569
+ const module = new OneBunModule(RootModule, mockLoggerLayer);
571
570
  module.getLayer();
572
571
 
573
572
  // Should have service in registry
@@ -693,7 +692,7 @@ describe('OneBunModule', () => {
693
692
  class TestModule {}
694
693
 
695
694
  // Initialize module
696
- const module = new ModuleInstance(TestModule);
695
+ const module = new ModuleInstance(TestModule, mockLoggerLayer);
697
696
 
698
697
  // Verify services are created correctly
699
698
  const { getServiceTag } = require('./service');
@@ -752,7 +751,7 @@ describe('OneBunModule', () => {
752
751
  class ChainModule {}
753
752
 
754
753
  // Should not throw
755
- const module = new ModuleInstance(ChainModule);
754
+ const module = new ModuleInstance(ChainModule, mockLoggerLayer);
756
755
 
757
756
  const { getServiceTag } = require('./service');
758
757
  const apiServiceTag = getServiceTag(ApiService);
@@ -15,7 +15,6 @@ import {
15
15
  type SyncLogger,
16
16
  } from '@onebun/logger';
17
17
 
18
-
19
18
  import {
20
19
  autoDetectDependencies,
21
20
  getConstructorParamTypes,
@@ -25,6 +24,11 @@ import {
25
24
  } from '../decorators/decorators';
26
25
  import { isWebSocketGateway } from '../websocket/ws-decorators';
27
26
 
27
+ import {
28
+ NotInitializedConfig,
29
+ type IConfig,
30
+ type OneBunAppConfig,
31
+ } from './config.interface';
28
32
  import { getServiceMetadata, getServiceTag } from './service';
29
33
 
30
34
  /**
@@ -65,12 +69,12 @@ export class OneBunModule implements ModuleInstance {
65
69
  private controllerInstances: Map<Function, Controller> = new Map();
66
70
  private serviceInstances: Map<Context.Tag<unknown, unknown>, unknown> = new Map();
67
71
  private logger: SyncLogger;
68
- private config: unknown;
72
+ private config: IConfig<OneBunAppConfig>;
69
73
 
70
74
  constructor(
71
75
  private moduleClass: Function,
72
76
  private loggerLayer?: Layer.Layer<never, never, unknown>,
73
- config?: unknown,
77
+ config?: IConfig<OneBunAppConfig>,
74
78
  ) {
75
79
  // Initialize logger with module class name as context
76
80
  const effectLogger = Effect.runSync(
@@ -82,7 +86,7 @@ export class OneBunModule implements ModuleInstance {
82
86
  ) as Effect.Effect<Logger, never, never>,
83
87
  ) as Logger;
84
88
  this.logger = createSyncLogger(effectLogger);
85
- this.config = config;
89
+ this.config = config ?? new NotInitializedConfig();
86
90
 
87
91
  this.logger.debug(`Initializing OneBunModule for ${moduleClass.name}`);
88
92
  const { layer, controllers } = this.initModule();
@@ -520,7 +524,7 @@ export class OneBunModule implements ModuleInstance {
520
524
  static create(
521
525
  moduleClass: Function,
522
526
  loggerLayer?: Layer.Layer<never, never, unknown>,
523
- config?: unknown,
527
+ config?: IConfig<OneBunAppConfig>,
524
528
  ): ModuleInstance {
525
529
  // Using console.log here because we don't have access to the logger instance yet
526
530
  // The instance will create its own logger in the constructor
@@ -10,11 +10,18 @@ import {
10
10
  } from 'bun:test';
11
11
  import { Effect } from 'effect';
12
12
 
13
+ import { createMockConfig } from '../testing/test-utils';
14
+
15
+ import {
16
+ NotInitializedConfig,
17
+ type IConfig,
18
+ type OneBunAppConfig,
19
+ } from './config.interface';
13
20
  import { BaseService } from './service';
14
21
 
15
22
  describe('BaseService', () => {
16
23
  let mockLogger: any;
17
- let mockConfig: any;
24
+ let mockConfig: IConfig<OneBunAppConfig>;
18
25
 
19
26
  beforeEach(() => {
20
27
  mockLogger = {
@@ -25,19 +32,12 @@ describe('BaseService', () => {
25
32
  error: mock(),
26
33
  };
27
34
 
28
- mockConfig = {
29
- get: mock((path: string) => {
30
- if (path === 'database.host') {
31
- return 'localhost';
32
- }
33
- if (path === 'app.name') {
34
- return 'test-app';
35
- }
36
-
37
- return undefined;
38
- }),
39
- getSafeConfig: mock(() => ({ app: { name: 'test-app' } })),
40
- };
35
+ mockConfig = createMockConfig({
36
+ /* eslint-disable @typescript-eslint/naming-convention */
37
+ 'database.host': 'localhost',
38
+ 'app.name': 'test-app',
39
+ /* eslint-enable @typescript-eslint/naming-convention */
40
+ });
41
41
  });
42
42
 
43
43
  describe('Initialization via initializeService', () => {
@@ -56,15 +56,16 @@ describe('BaseService', () => {
56
56
  expect((service as any).config).toBe(mockConfig);
57
57
  });
58
58
 
59
- test('should initialize service with logger and undefined config', () => {
59
+ test('should initialize service with logger and NotInitializedConfig', () => {
60
60
  class TestService extends BaseService {}
61
61
 
62
62
  const service = new TestService();
63
- service.initializeService(mockLogger, undefined);
63
+ const notInitConfig = new NotInitializedConfig();
64
+ service.initializeService(mockLogger, notInitConfig);
64
65
 
65
66
  expect(service.isInitialized).toBe(true);
66
67
  expect((service as any).logger).toBeDefined();
67
- expect((service as any).config).toBeUndefined();
68
+ expect((service as any).config).toBeInstanceOf(NotInitializedConfig);
68
69
  });
69
70
 
70
71
  test('should throw error when logger is not provided to initializeService', () => {
@@ -90,7 +91,8 @@ describe('BaseService', () => {
90
91
  service.initializeService(mockLogger, mockConfig);
91
92
 
92
93
  const otherLogger = { ...mockLogger, child: mock(() => ({ ...mockLogger })) };
93
- service.initializeService(otherLogger, { other: 'config' });
94
+ const otherConfig = createMockConfig({ 'other': 'config' });
95
+ service.initializeService(otherLogger, otherConfig);
94
96
 
95
97
  // Should still have original logger (no reinit)
96
98
  expect((service as any).config).toBe(mockConfig);
@@ -174,7 +176,7 @@ describe('BaseService', () => {
174
176
  }
175
177
 
176
178
  const service = new TestService();
177
- service.initializeService(mockLogger, undefined);
179
+ service.initializeService(mockLogger, mockConfig);
178
180
  const result = await service.testComplexEffect();
179
181
  expect(result).toBe(84);
180
182
  });
@@ -190,7 +192,7 @@ describe('BaseService', () => {
190
192
  }
191
193
 
192
194
  const service = new TestService();
193
- service.initializeService(mockLogger, undefined);
195
+ service.initializeService(mockLogger, mockConfig);
194
196
  service.testStackTrace();
195
197
  });
196
198
  });
@@ -4,6 +4,8 @@ import {
4
4
  Layer,
5
5
  } from 'effect';
6
6
 
7
+ import type { IConfig, OneBunAppConfig } from './config.interface';
8
+
7
9
  import type { SyncLogger } from '@onebun/logger';
8
10
 
9
11
  /**
@@ -66,7 +68,7 @@ export class BaseService {
66
68
  // Logger instance with service class name as context
67
69
  protected logger!: SyncLogger;
68
70
  // Configuration instance for accessing environment variables
69
- protected config!: unknown;
71
+ protected config!: IConfig<OneBunAppConfig>;
70
72
  // Flag to track initialization status
71
73
  private _initialized = false;
72
74
 
@@ -74,7 +76,7 @@ export class BaseService {
74
76
  * Initialize service with logger and config (called by the framework)
75
77
  * @internal
76
78
  */
77
- initializeService(logger: SyncLogger, config: unknown): void {
79
+ initializeService(logger: SyncLogger, config: IConfig<OneBunAppConfig>): void {
78
80
  if (this._initialized) {
79
81
  return; // Already initialized
80
82
  }
@@ -143,7 +145,7 @@ export class BaseService {
143
145
  export function createServiceLayer<T>(
144
146
  serviceClass: new (...args: unknown[]) => T,
145
147
  logger?: SyncLogger,
146
- config?: unknown,
148
+ config?: IConfig<OneBunAppConfig>,
147
149
  ): Layer.Layer<never, never, unknown> {
148
150
  const metadata = getServiceMetadata(serviceClass);
149
151
  if (!metadata) {