@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
|
@@ -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 {
|
|
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():
|
|
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<
|
|
247
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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<
|
|
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)
|
|
66
|
+
return this.configInstance.get(path);
|
|
51
67
|
}
|
|
52
68
|
|
|
53
69
|
/**
|
|
54
70
|
* Get all configuration values
|
|
55
71
|
*/
|
|
56
|
-
get values():
|
|
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():
|
|
83
|
+
getSafeConfig(): OneBunAppConfig {
|
|
68
84
|
if (!this.configInstance) {
|
|
69
85
|
throw new Error('Configuration not initialized. Provide envSchema in ApplicationOptions.');
|
|
70
86
|
}
|