@onebun/core 0.1.11 → 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.11",
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",
@@ -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,
@@ -230,9 +234,21 @@ export class OneBunApplication {
230
234
  }
231
235
 
232
236
  /**
233
- * 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
234
250
  */
235
- getConfig(): ConfigServiceImpl {
251
+ getConfig(): IConfig<OneBunAppConfig> {
236
252
  if (!this.configService) {
237
253
  throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
238
254
  }
@@ -241,10 +257,25 @@ export class OneBunApplication {
241
257
  }
242
258
 
243
259
  /**
244
- * 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
245
273
  */
246
- getConfigValue<T = unknown>(path: string): T {
247
- 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);
248
279
  }
249
280
 
250
281
  /**
@@ -141,7 +141,8 @@ describe('ConfigService', () => {
141
141
 
142
142
  const result = service.values;
143
143
 
144
- 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' });
145
146
  });
146
147
 
147
148
  test('should throw error when config not initialized', () => {
@@ -159,7 +160,8 @@ describe('ConfigService', () => {
159
160
 
160
161
  const result = service.getSafeConfig();
161
162
 
162
- 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: '***' });
163
165
  expect(mockConfig.getSafeConfig).toHaveBeenCalled();
164
166
  });
165
167
 
@@ -344,13 +346,15 @@ describe('ConfigService', () => {
344
346
 
345
347
  expect((service as any).get('database.host')).toBe('localhost');
346
348
  expect((service as any).get('database.port')).toBe(5432);
347
- 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({
348
351
  database: { host: 'localhost', port: 5432 },
349
352
  api: { key: 'secret', timeout: 30000 },
350
353
  });
351
354
 
352
355
  const safeConfig = service.getSafeConfig();
353
- expect(safeConfig).toEqual({
356
+ // Use unknown cast because tests use mock data that doesn't match OneBunAppConfig augmentation
357
+ expect(safeConfig as unknown).toEqual({
354
358
  database: { host: 'localhost', port: 5432 },
355
359
  api: { key: '***', timeout: 30000 },
356
360
  });
@@ -2,6 +2,7 @@ import { Context } from 'effect';
2
2
 
3
3
  import type { IConfig, OneBunAppConfig } from './config.interface';
4
4
 
5
+ import type { DeepPaths, DeepValue } from '@onebun/envs';
5
6
  import type { SyncLogger } from '@onebun/logger';
6
7
 
7
8
  import { BaseService, Service } from './service';
@@ -40,20 +41,35 @@ export class ConfigServiceImpl extends BaseService {
40
41
  }
41
42
 
42
43
  /**
43
- * 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
44
57
  */
45
- 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 {
46
62
  if (!this.configInstance) {
47
63
  throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
48
64
  }
49
65
 
50
- return this.configInstance.get(path) as T;
66
+ return this.configInstance.get(path);
51
67
  }
52
68
 
53
69
  /**
54
70
  * Get all configuration values
55
71
  */
56
- get values(): unknown {
72
+ get values(): OneBunAppConfig {
57
73
  if (!this.configInstance) {
58
74
  throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
59
75
  }
@@ -64,7 +80,7 @@ export class ConfigServiceImpl extends BaseService {
64
80
  /**
65
81
  * Get safe configuration for logging (sensitive data masked)
66
82
  */
67
- getSafeConfig(): unknown {
83
+ getSafeConfig(): OneBunAppConfig {
68
84
  if (!this.configInstance) {
69
85
  throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
70
86
  }