@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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onebun/core",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Core package for OneBun framework - decorators, DI, modules, controllers",
5
5
  "license": "LGPL-3.0",
6
6
  "author": "RemRyahirev",
@@ -41,14 +41,15 @@
41
41
  "dependencies": {
42
42
  "effect": "^3.13.10",
43
43
  "arktype": "^2.0.0",
44
- "@onebun/logger": "^0.1.4",
45
- "@onebun/envs": "^0.1.3",
46
- "@onebun/metrics": "^0.1.5",
44
+ "@onebun/logger": "^0.1.5",
45
+ "@onebun/envs": "^0.1.4",
46
+ "@onebun/metrics": "^0.1.6",
47
47
  "@onebun/requests": "^0.1.3",
48
- "@onebun/trace": "^0.1.3"
48
+ "@onebun/trace": "^0.1.4"
49
49
  },
50
50
  "devDependencies": {
51
- "bun-types": "1.2.2"
51
+ "bun-types": "1.2.2",
52
+ "testcontainers": "^11.7.1"
52
53
  },
53
54
  "engines": {
54
55
  "bun": "1.2.2"
@@ -164,6 +164,98 @@ describe('OneBunApplication', () => {
164
164
  // The actual value access might need the config to be fully initialized
165
165
  // which happens during runtime, not during construction
166
166
  });
167
+
168
+ test('should provide typed access to config values via getConfig()', () => {
169
+ @Module({})
170
+ class TestModule {}
171
+
172
+ // Mock config that simulates typed IConfig<OneBunAppConfig>
173
+ const mockConfigValues = {
174
+ server: { port: 9991, host: 'localhost' },
175
+ };
176
+ const mockConfig = {
177
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
178
+ initialize: mock(async () => {}),
179
+ get: mock((path: string) => {
180
+ const parts = path.split('.');
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
+ let value: any = mockConfigValues;
183
+ for (const part of parts) {
184
+ value = value?.[part];
185
+ }
186
+
187
+ return value;
188
+ }),
189
+ values: mockConfigValues,
190
+ getSafeConfig: mock(() => mockConfigValues),
191
+ isInitialized: true,
192
+ };
193
+
194
+ const app = createTestApp(TestModule);
195
+ // Inject mock config
196
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
197
+ (app as any).config = mockConfig;
198
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
199
+ (app as any).configService = {
200
+ get: mockConfig.get, values: mockConfig.values, getSafeConfig: mockConfig.getSafeConfig, isInitialized: true,
201
+ };
202
+
203
+ // getConfig() returns IConfig<OneBunAppConfig> which provides typed .get() method
204
+ const config = app.getConfig();
205
+ expect(config).toBeDefined();
206
+
207
+ // Access values through the typed interface
208
+ // TypeScript will infer the correct types based on module augmentation
209
+ const port = config.get('server.port');
210
+ const host = config.get('server.host');
211
+
212
+ expect(port).toBe(9991);
213
+ expect(host).toBe('localhost');
214
+ });
215
+
216
+ test('should provide typed access via getConfigValue() convenience method', () => {
217
+ @Module({})
218
+ class TestModule {}
219
+
220
+ // Mock config that simulates typed IConfig<OneBunAppConfig>
221
+ const mockConfigValues = {
222
+ app: { name: 'test-app', debug: true },
223
+ };
224
+ const mockConfig = {
225
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
226
+ initialize: mock(async () => {}),
227
+ get: mock((path: string) => {
228
+ const parts = path.split('.');
229
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
+ let value: any = mockConfigValues;
231
+ for (const part of parts) {
232
+ value = value?.[part];
233
+ }
234
+
235
+ return value;
236
+ }),
237
+ values: mockConfigValues,
238
+ getSafeConfig: mock(() => mockConfigValues),
239
+ isInitialized: true,
240
+ };
241
+
242
+ const app = createTestApp(TestModule);
243
+ // Inject mock config
244
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
245
+ (app as any).config = mockConfig;
246
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
247
+ (app as any).configService = {
248
+ get: mockConfig.get, values: mockConfig.values, getSafeConfig: mockConfig.getSafeConfig, isInitialized: true,
249
+ };
250
+
251
+ // getConfigValue() is a convenience method that delegates to getConfig().get()
252
+ // It also provides typed access based on OneBunAppConfig module augmentation
253
+ const appName = app.getConfigValue('app.name');
254
+ const debug = app.getConfigValue('app.debug');
255
+
256
+ expect(appName).toBe('test-app');
257
+ expect(debug).toBe(true);
258
+ });
167
259
  });
168
260
 
169
261
  describe('Layer methods', () => {
@@ -3,7 +3,11 @@ import { Effect, type Layer } from 'effect';
3
3
  import type { Controller } from '../module/controller';
4
4
  import type { WsClientData } from '../websocket/ws.types';
5
5
 
6
- import { TypedEnv } from '@onebun/envs';
6
+ import {
7
+ type DeepPaths,
8
+ type DeepValue,
9
+ TypedEnv,
10
+ } from '@onebun/envs';
7
11
  import {
8
12
  createSyncLogger,
9
13
  type Logger,
@@ -21,6 +25,11 @@ import {
21
25
  import { makeTraceService, TraceService } from '@onebun/trace';
22
26
 
23
27
  import { getControllerMetadata } from '../decorators/decorators';
28
+ import {
29
+ NotInitializedConfig,
30
+ type IConfig,
31
+ type OneBunAppConfig,
32
+ } from '../module/config.interface';
24
33
  import { ConfigServiceImpl } from '../module/config.service';
25
34
  import { OneBunModule } from '../module/module';
26
35
  import { QueueService, type QueueAdapter } from '../queue';
@@ -110,8 +119,7 @@ export class OneBunApplication {
110
119
  development: process.env.NODE_ENV !== 'production',
111
120
  };
112
121
  private logger: SyncLogger;
113
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
- private config: any = null;
122
+ private config: IConfig<OneBunAppConfig>;
115
123
  private configService: ConfigServiceImpl | null = null;
116
124
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
125
  private metricsService: any = null;
@@ -135,10 +143,12 @@ export class OneBunApplication {
135
143
  this.options = { ...this.options, ...options };
136
144
  }
137
145
 
138
- // Initialize configuration if schema is provided
146
+ // Initialize configuration - TypedEnv if schema provided, otherwise NotInitializedConfig
139
147
  if (this.options.envSchema) {
140
148
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
149
  this.config = TypedEnv.create(this.options.envSchema as any, this.options.envOptions);
150
+ } else {
151
+ this.config = new NotInitializedConfig();
142
152
  }
143
153
 
144
154
  // Use provided logger layer or create a default one
@@ -155,8 +165,8 @@ export class OneBunApplication {
155
165
  ) as Logger;
156
166
  this.logger = createSyncLogger(effectLogger);
157
167
 
158
- // Create configuration service if config is available
159
- if (this.config) {
168
+ // Create configuration service if config is initialized
169
+ if (this.config.isInitialized || !(this.config instanceof NotInitializedConfig)) {
160
170
  this.configService = new ConfigServiceImpl(this.logger, this.config);
161
171
  }
162
172
 
@@ -224,9 +234,21 @@ export class OneBunApplication {
224
234
  }
225
235
 
226
236
  /**
227
- * Get configuration service
237
+ * Get configuration service with full type inference.
238
+ * Uses module augmentation of OneBunAppConfig for type-safe access.
239
+ *
240
+ * @example
241
+ * // With module augmentation:
242
+ * declare module '@onebun/core' {
243
+ * interface OneBunAppConfig {
244
+ * server: { port: number; host: string };
245
+ * }
246
+ * }
247
+ *
248
+ * const config = app.getConfig();
249
+ * const port = config.get('server.port'); // number
228
250
  */
229
- getConfig(): ConfigServiceImpl {
251
+ getConfig(): IConfig<OneBunAppConfig> {
230
252
  if (!this.configService) {
231
253
  throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
232
254
  }
@@ -235,10 +257,25 @@ export class OneBunApplication {
235
257
  }
236
258
 
237
259
  /**
238
- * Get configuration value by path (convenience method)
260
+ * Get configuration value by path (convenience method) with full type inference.
261
+ * Uses module augmentation of OneBunAppConfig for type-safe access.
262
+ *
263
+ * @example
264
+ * // With module augmentation:
265
+ * declare module '@onebun/core' {
266
+ * interface OneBunAppConfig {
267
+ * server: { port: number; host: string };
268
+ * }
269
+ * }
270
+ *
271
+ * const port = app.getConfigValue('server.port'); // number
272
+ * const host = app.getConfigValue('server.host'); // string
239
273
  */
240
- getConfigValue<T = unknown>(path: string): T {
241
- return this.getConfig().get<T>(path);
274
+ getConfigValue<P extends DeepPaths<OneBunAppConfig>>(path: P): DeepValue<OneBunAppConfig, P>;
275
+ /** Fallback for dynamic paths */
276
+ getConfigValue<T = unknown>(path: string): T;
277
+ getConfigValue(path: string): unknown {
278
+ return this.getConfig().get(path);
242
279
  }
243
280
 
244
281
  /**
@@ -289,8 +326,8 @@ export class OneBunApplication {
289
326
  */
290
327
  async start(): Promise<void> {
291
328
  try {
292
- // Initialize configuration if provided
293
- if (this.config) {
329
+ // Initialize configuration if schema was provided
330
+ if (!(this.config instanceof NotInitializedConfig)) {
294
331
  await this.config.initialize();
295
332
  this.logger.info('Application configuration initialized');
296
333
  }
@@ -80,6 +80,7 @@ import {
80
80
  createWsServiceDefinition,
81
81
  createWsClient,
82
82
  matchPattern,
83
+ makeMockLoggerLayer,
83
84
  } from './';
84
85
 
85
86
  /**
@@ -157,6 +158,7 @@ describe('Minimal Working Example (docs/index.md)', () => {
157
158
  const app = new OneBunApplication(AppModule, {
158
159
  port: 3000,
159
160
  envSchema,
161
+ loggerLayer: makeMockLoggerLayer(),
160
162
  metrics: { enabled: true },
161
163
  tracing: { enabled: true },
162
164
  });
@@ -1302,6 +1304,7 @@ describe('OneBunApplication (docs/api/core.md)', () => {
1302
1304
 
1303
1305
  // From docs: OneBunApplication constructor
1304
1306
  const app = new OneBunApplication(AppModule, {
1307
+ loggerLayer: makeMockLoggerLayer(),
1305
1308
  port: 3000,
1306
1309
  basePath: '/api/v1',
1307
1310
  });
@@ -1330,6 +1333,7 @@ describe('OneBunApplication (docs/api/core.md)', () => {
1330
1333
  basePath: '/api/v1',
1331
1334
  routePrefix: 'myservice',
1332
1335
  development: true,
1336
+ loggerLayer: makeMockLoggerLayer(),
1333
1337
  metrics: {
1334
1338
  enabled: true,
1335
1339
  path: '/metrics',
@@ -1369,7 +1373,7 @@ describe('OneBunApplication (docs/api/core.md)', () => {
1369
1373
  httpDurationBuckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
1370
1374
  };
1371
1375
 
1372
- const app = new OneBunApplication(AppModule, { metrics: metricsOptions });
1376
+ const app = new OneBunApplication(AppModule, { metrics: metricsOptions, loggerLayer: makeMockLoggerLayer() });
1373
1377
  expect(app).toBeDefined();
1374
1378
  });
1375
1379
 
@@ -1400,7 +1404,7 @@ describe('OneBunApplication (docs/api/core.md)', () => {
1400
1404
  };
1401
1405
  /* eslint-enable @typescript-eslint/naming-convention */
1402
1406
 
1403
- const app = new OneBunApplication(AppModule, { tracing: tracingOptions });
1407
+ const app = new OneBunApplication(AppModule, { tracing: tracingOptions, loggerLayer: makeMockLoggerLayer() });
1404
1408
  expect(app).toBeDefined();
1405
1409
  });
1406
1410
  });
@@ -2182,6 +2186,7 @@ describe('Getting Started Documentation (docs/getting-started.md)', () => {
2182
2186
  loadDotEnv: true,
2183
2187
  envFilePath: '.env',
2184
2188
  },
2189
+ loggerLayer: makeMockLoggerLayer(),
2185
2190
  metrics: {
2186
2191
  enabled: true,
2187
2192
  path: '/metrics',
@@ -2589,6 +2594,7 @@ describe('WebSocket Gateway API Documentation (docs/api/websocket.md)', () => {
2589
2594
  pingTimeout: 20000,
2590
2595
  maxPayload: 1048576,
2591
2596
  },
2597
+ loggerLayer: makeMockLoggerLayer(),
2592
2598
  });
2593
2599
 
2594
2600
  expect(app).toBeDefined();
@@ -2865,6 +2871,7 @@ describe('WebSocket Chat Example (docs/examples/websocket-chat.md)', () => {
2865
2871
  pingInterval: 25000,
2866
2872
  pingTimeout: 20000,
2867
2873
  },
2874
+ loggerLayer: makeMockLoggerLayer(),
2868
2875
  });
2869
2876
 
2870
2877
  expect(app).toBeDefined();
package/src/index.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  // Re-export from external packages
2
- export { Env, type EnvSchema, EnvValidationError } from '@onebun/envs';
2
+ export {
3
+ Env,
4
+ type EnvSchema,
5
+ EnvValidationError,
6
+ type InferConfigType,
7
+ type EnvVariableConfig,
8
+ } from '@onebun/envs';
3
9
  export type { SyncLogger } from '@onebun/logger';
4
10
  export {
5
11
  createHttpClient,
@@ -56,6 +62,10 @@ export {
56
62
  // Global modules support
57
63
  clearGlobalServicesRegistry,
58
64
  getGlobalServicesRegistry,
65
+ // Config interface types
66
+ type IConfig,
67
+ type OneBunAppConfig,
68
+ NotInitializedConfig,
59
69
  } from './module';
60
70
 
61
71
  // Application
@@ -0,0 +1,60 @@
1
+ import type { DeepPaths, DeepValue } from '@onebun/envs';
2
+
3
+ /**
4
+ * Empty interface for module augmentation.
5
+ * Users extend this to provide typed config access.
6
+ *
7
+ * @example
8
+ * declare module '@onebun/core' {
9
+ * interface OneBunAppConfig {
10
+ * server: { port: number; host: string };
11
+ * }
12
+ * }
13
+ */
14
+
15
+ export interface OneBunAppConfig {}
16
+
17
+ /**
18
+ * Generic typed configuration interface.
19
+ * Provides type-safe access to configuration values.
20
+ */
21
+ export interface IConfig<T = OneBunAppConfig> {
22
+ /** Get configuration value by dot-notation path with full type inference */
23
+ get<P extends DeepPaths<T>>(path: P): DeepValue<T, P>;
24
+ /** Fallback for dynamic paths */
25
+ get(path: string): unknown;
26
+ /** Get all configuration values */
27
+ readonly values: T;
28
+ /** Get safe configuration for logging (sensitive data masked) */
29
+ getSafeConfig(): T;
30
+ /** Check if configuration is initialized */
31
+ readonly isInitialized: boolean;
32
+ /** Initialize configuration (async) */
33
+ initialize(): Promise<void>;
34
+ }
35
+
36
+ /**
37
+ * Config stub that throws on any access.
38
+ * Used when envSchema is not provided.
39
+ */
40
+ export class NotInitializedConfig implements IConfig<never> {
41
+ get(_path: string): never {
42
+ throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
43
+ }
44
+
45
+ get values(): never {
46
+ throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
47
+ }
48
+
49
+ getSafeConfig(): never {
50
+ throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
51
+ }
52
+
53
+ get isInitialized(): boolean {
54
+ return false;
55
+ }
56
+
57
+ async initialize(): Promise<void> {
58
+ throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
59
+ }
60
+ }
@@ -11,6 +11,8 @@ import {
11
11
  mock,
12
12
  } from 'bun:test';
13
13
 
14
+ import type { IConfig, OneBunAppConfig } from './config.interface';
15
+
14
16
  import type { SyncLogger } from '@onebun/logger';
15
17
 
16
18
  import {
@@ -22,7 +24,7 @@ import {
22
24
  describe('ConfigService', () => {
23
25
  let mockLogger: SyncLogger;
24
26
 
25
- let mockConfig: any;
27
+ let mockConfig: IConfig<OneBunAppConfig>;
26
28
 
27
29
  beforeEach(() => {
28
30
  mockLogger = {
@@ -38,8 +40,8 @@ describe('ConfigService', () => {
38
40
  mockConfig = {
39
41
  initialize: mock(async () => {}),
40
42
  get: mock((path: string): any => `value-for-${path}`),
41
- values: { test: 'value' },
42
- getSafeConfig: mock(() => ({ test: '***' })),
43
+ values: { test: 'value' } as unknown as OneBunAppConfig,
44
+ getSafeConfig: mock(() => ({ test: '***' })) as unknown as () => OneBunAppConfig,
43
45
  isInitialized: true,
44
46
  };
45
47
  });
@@ -63,7 +65,7 @@ describe('ConfigService', () => {
63
65
  const service = new ConfigServiceImpl(mockLogger);
64
66
 
65
67
  expect(service).toBeInstanceOf(ConfigServiceImpl);
66
- expect(service.instance).toBeUndefined();
68
+ expect(service.instance).toBeNull();
67
69
  });
68
70
 
69
71
  test('should create instance with config but without logger (uninitialized)', () => {
@@ -83,8 +85,8 @@ describe('ConfigService', () => {
83
85
  expect(mockLogger.info).toHaveBeenCalledWith('Configuration initialized successfully');
84
86
  });
85
87
 
86
- test('should not throw when config instance is null', async () => {
87
- const service = new ConfigServiceImpl(mockLogger, null);
88
+ test('should not throw when config instance is undefined', async () => {
89
+ const service = new ConfigServiceImpl(mockLogger, undefined);
88
90
 
89
91
  await expect(service.initialize()).resolves.toBeUndefined();
90
92
  expect(mockLogger.info).not.toHaveBeenCalled();
@@ -109,7 +111,7 @@ describe('ConfigService', () => {
109
111
  });
110
112
 
111
113
  test('should throw error when config not initialized', () => {
112
- const service = new ConfigServiceImpl(mockLogger, null);
114
+ const service = new ConfigServiceImpl(mockLogger, undefined);
113
115
 
114
116
  expect(() => service.get('test.path')).toThrow(
115
117
  'Configuration not initialized. Provide envSchema in ApplicationOptions.',
@@ -117,8 +119,14 @@ describe('ConfigService', () => {
117
119
  });
118
120
 
119
121
  test('should return typed value', () => {
120
- mockConfig.get.mockReturnValue(42);
121
- const service = new ConfigServiceImpl(mockLogger, mockConfig);
122
+ const typedConfig = {
123
+ initialize: mock(async () => {}),
124
+ get: mock((path: string) => path === 'test.number' ? 42 : undefined),
125
+ values: { test: 'value' } as unknown as OneBunAppConfig,
126
+ getSafeConfig: mock(() => ({ test: '***' })) as unknown as () => OneBunAppConfig,
127
+ isInitialized: true,
128
+ } as unknown as IConfig<OneBunAppConfig>;
129
+ const service = new ConfigServiceImpl(mockLogger, typedConfig);
122
130
 
123
131
  const result = service.get<number>('test.number');
124
132
 
@@ -133,11 +141,12 @@ describe('ConfigService', () => {
133
141
 
134
142
  const result = service.values;
135
143
 
136
- expect(result).toEqual({ test: 'value' });
144
+ // Use unknown cast because tests use mock data that doesn't match OneBunAppConfig augmentation
145
+ expect(result as unknown).toEqual({ test: 'value' });
137
146
  });
138
147
 
139
148
  test('should throw error when config not initialized', () => {
140
- const service = new ConfigServiceImpl(mockLogger, null);
149
+ const service = new ConfigServiceImpl(mockLogger, undefined);
141
150
 
142
151
  expect(() => service.values).toThrow(
143
152
  'Configuration not initialized. Provide envSchema in ApplicationOptions.',
@@ -151,12 +160,13 @@ describe('ConfigService', () => {
151
160
 
152
161
  const result = service.getSafeConfig();
153
162
 
154
- expect(result).toEqual({ test: '***' });
163
+ // Use unknown cast because tests use mock data that doesn't match OneBunAppConfig augmentation
164
+ expect(result as unknown).toEqual({ test: '***' });
155
165
  expect(mockConfig.getSafeConfig).toHaveBeenCalled();
156
166
  });
157
167
 
158
168
  test('should throw error when config not initialized', () => {
159
- const service = new ConfigServiceImpl(mockLogger, null);
169
+ const service = new ConfigServiceImpl(mockLogger, undefined);
160
170
 
161
171
  expect(() => service.getSafeConfig()).toThrow(
162
172
  'Configuration not initialized. Provide envSchema in ApplicationOptions.',
@@ -172,14 +182,20 @@ describe('ConfigService', () => {
172
182
  });
173
183
 
174
184
  test('should return false when config not initialized', () => {
175
- mockConfig.isInitialized = false;
176
- const service = new ConfigServiceImpl(mockLogger, mockConfig);
185
+ const uninitConfig = {
186
+ initialize: mock(async () => {}),
187
+ get: mock((path: string): any => `value-for-${path}`),
188
+ values: { test: 'value' } as unknown as OneBunAppConfig,
189
+ getSafeConfig: mock(() => ({ test: '***' })) as unknown as () => OneBunAppConfig,
190
+ isInitialized: false,
191
+ } as unknown as IConfig<OneBunAppConfig>;
192
+ const service = new ConfigServiceImpl(mockLogger, uninitConfig);
177
193
 
178
194
  expect(service.isInitialized).toBe(false);
179
195
  });
180
196
 
181
- test('should return false when config instance is null', () => {
182
- const service = new ConfigServiceImpl(mockLogger, null);
197
+ test('should return false when config instance is undefined', () => {
198
+ const service = new ConfigServiceImpl(mockLogger, undefined);
183
199
 
184
200
  expect(service.isInitialized).toBe(false);
185
201
  });
@@ -193,7 +209,7 @@ describe('ConfigService', () => {
193
209
  });
194
210
 
195
211
  test('should return null when no config provided', () => {
196
- const service = new ConfigServiceImpl(mockLogger, null);
212
+ const service = new ConfigServiceImpl(mockLogger, undefined);
197
213
 
198
214
  expect(service.instance).toBeNull();
199
215
  });
@@ -210,7 +226,7 @@ describe('ConfigService', () => {
210
226
  test('should handle undefined config in constructor', () => {
211
227
  const service = new ConfigServiceImpl(mockLogger, undefined);
212
228
 
213
- expect(service.instance).toBeUndefined();
229
+ expect(service.instance).toBeNull();
214
230
  expect(service.isInitialized).toBe(false);
215
231
  });
216
232
  });
@@ -219,8 +235,11 @@ describe('ConfigService', () => {
219
235
  test('should handle initialization error gracefully', async () => {
220
236
  const errorConfig = {
221
237
  initialize: mock(() => Promise.reject(new Error('Init failed'))),
238
+ get: mock(() => undefined),
239
+ values: {} as unknown as OneBunAppConfig,
240
+ getSafeConfig: mock(() => ({})) as unknown as () => OneBunAppConfig,
222
241
  isInitialized: false,
223
- };
242
+ } as unknown as IConfig<OneBunAppConfig>;
224
243
 
225
244
  const service = new ConfigServiceImpl(mockLogger, errorConfig);
226
245
 
@@ -229,15 +248,16 @@ describe('ConfigService', () => {
229
248
 
230
249
  test('should handle config method errors', () => {
231
250
  const errorConfig = {
251
+ initialize: mock(async () => {}),
232
252
  get: mock(() => {
233
253
  throw new Error('Get failed');
234
254
  }),
235
- values: null,
255
+ values: null as unknown as unknown as OneBunAppConfig,
236
256
  getSafeConfig: mock(() => {
237
257
  throw new Error('Safe config failed');
238
- }),
258
+ }) as unknown as () => OneBunAppConfig,
239
259
  isInitialized: true,
240
- };
260
+ } as unknown as IConfig<OneBunAppConfig>;
241
261
 
242
262
  const service = new ConfigServiceImpl(mockLogger, errorConfig);
243
263
 
@@ -249,11 +269,12 @@ describe('ConfigService', () => {
249
269
  describe('edge cases', () => {
250
270
  test('should handle config with null values', () => {
251
271
  const nullConfig = {
272
+ initialize: mock(async () => {}),
252
273
  get: mock(() => null),
253
- values: null,
254
- getSafeConfig: mock(() => null),
274
+ values: null as unknown as unknown as OneBunAppConfig,
275
+ getSafeConfig: mock(() => null as unknown as unknown as OneBunAppConfig),
255
276
  isInitialized: true,
256
- };
277
+ } as unknown as IConfig<OneBunAppConfig>;
257
278
 
258
279
  const service = new ConfigServiceImpl(mockLogger, nullConfig);
259
280
 
@@ -264,11 +285,12 @@ describe('ConfigService', () => {
264
285
 
265
286
  test('should handle config with undefined values', () => {
266
287
  const undefinedConfig = {
288
+ initialize: mock(async () => {}),
267
289
  get: mock(() => undefined),
268
- values: undefined,
269
- getSafeConfig: mock(() => undefined),
290
+ values: undefined as unknown as unknown as OneBunAppConfig,
291
+ getSafeConfig: mock(() => undefined as unknown as unknown as OneBunAppConfig),
270
292
  isInitialized: false,
271
- };
293
+ } as unknown as IConfig<OneBunAppConfig>;
272
294
 
273
295
  const service = new ConfigServiceImpl(mockLogger, undefinedConfig);
274
296
 
@@ -318,19 +340,21 @@ describe('ConfigService', () => {
318
340
  api: { key: '***', timeout: 30000 },
319
341
  })),
320
342
  isInitialized: true,
321
- };
343
+ } as unknown as IConfig<OneBunAppConfig>;
322
344
 
323
345
  const service = new ConfigServiceImpl(mockLogger, complexConfig);
324
346
 
325
347
  expect((service as any).get('database.host')).toBe('localhost');
326
348
  expect((service as any).get('database.port')).toBe(5432);
327
- expect(service.values).toEqual({
349
+ // Use unknown cast because tests use mock data that doesn't match OneBunAppConfig augmentation
350
+ expect(service.values as unknown).toEqual({
328
351
  database: { host: 'localhost', port: 5432 },
329
352
  api: { key: 'secret', timeout: 30000 },
330
353
  });
331
354
 
332
355
  const safeConfig = service.getSafeConfig();
333
- expect(safeConfig).toEqual({
356
+ // Use unknown cast because tests use mock data that doesn't match OneBunAppConfig augmentation
357
+ expect(safeConfig as unknown).toEqual({
334
358
  database: { host: 'localhost', port: 5432 },
335
359
  api: { key: '***', timeout: 30000 },
336
360
  });