@onebun/core 0.1.17 → 0.1.19
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 +2 -2
- package/src/application/application.test.ts +234 -4
- package/src/application/application.ts +109 -12
- package/src/application/multi-service-application.ts +4 -23
- package/src/docs-examples.test.ts +357 -0
- package/src/index.ts +17 -0
- package/src/module/index.ts +1 -0
- package/src/module/lifecycle.ts +197 -0
- package/src/module/module.ts +225 -24
- package/src/module/service.ts +0 -10
- package/src/types.ts +48 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onebun/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"description": "Core package for OneBun framework - decorators, DI, modules, controllers",
|
|
5
5
|
"license": "LGPL-3.0",
|
|
6
6
|
"author": "RemRyahirev",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"effect": "^3.13.10",
|
|
43
43
|
"arktype": "^2.0.0",
|
|
44
|
-
"@onebun/logger": "^0.1.
|
|
44
|
+
"@onebun/logger": "^0.1.6",
|
|
45
45
|
"@onebun/envs": "^0.1.4",
|
|
46
46
|
"@onebun/metrics": "^0.1.6",
|
|
47
47
|
"@onebun/requests": "^0.1.3",
|
|
@@ -432,6 +432,236 @@ describe('OneBunApplication', () => {
|
|
|
432
432
|
expect(prodApp).toBeInstanceOf(OneBunApplication);
|
|
433
433
|
});
|
|
434
434
|
|
|
435
|
+
test('should use PORT env variable when port is not explicitly provided', () => {
|
|
436
|
+
const originalPort = process.env.PORT;
|
|
437
|
+
const originalHost = process.env.HOST;
|
|
438
|
+
try {
|
|
439
|
+
process.env.PORT = '4567';
|
|
440
|
+
delete process.env.HOST; // Clear HOST to get default
|
|
441
|
+
|
|
442
|
+
@Module({})
|
|
443
|
+
class TestModule {}
|
|
444
|
+
|
|
445
|
+
const app = createTestApp(TestModule);
|
|
446
|
+
expect(app.getHttpUrl()).toBe('http://0.0.0.0:4567');
|
|
447
|
+
} finally {
|
|
448
|
+
if (originalPort === undefined) {
|
|
449
|
+
delete process.env.PORT;
|
|
450
|
+
} else {
|
|
451
|
+
process.env.PORT = originalPort;
|
|
452
|
+
}
|
|
453
|
+
if (originalHost === undefined) {
|
|
454
|
+
delete process.env.HOST;
|
|
455
|
+
} else {
|
|
456
|
+
process.env.HOST = originalHost;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
test('should use HOST env variable when host is not explicitly provided', () => {
|
|
462
|
+
const originalHost = process.env.HOST;
|
|
463
|
+
const originalPort = process.env.PORT;
|
|
464
|
+
try {
|
|
465
|
+
process.env.HOST = '192.168.1.100';
|
|
466
|
+
delete process.env.PORT; // Clear PORT to get default
|
|
467
|
+
|
|
468
|
+
@Module({})
|
|
469
|
+
class TestModule {}
|
|
470
|
+
|
|
471
|
+
const app = createTestApp(TestModule);
|
|
472
|
+
expect(app.getHttpUrl()).toBe('http://192.168.1.100:3000');
|
|
473
|
+
} finally {
|
|
474
|
+
if (originalHost === undefined) {
|
|
475
|
+
delete process.env.HOST;
|
|
476
|
+
} else {
|
|
477
|
+
process.env.HOST = originalHost;
|
|
478
|
+
}
|
|
479
|
+
if (originalPort === undefined) {
|
|
480
|
+
delete process.env.PORT;
|
|
481
|
+
} else {
|
|
482
|
+
process.env.PORT = originalPort;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test('should use both PORT and HOST env variables when not explicitly provided', () => {
|
|
488
|
+
const originalPort = process.env.PORT;
|
|
489
|
+
const originalHost = process.env.HOST;
|
|
490
|
+
try {
|
|
491
|
+
process.env.PORT = '8888';
|
|
492
|
+
process.env.HOST = '10.0.0.1';
|
|
493
|
+
|
|
494
|
+
@Module({})
|
|
495
|
+
class TestModule {}
|
|
496
|
+
|
|
497
|
+
const app = createTestApp(TestModule);
|
|
498
|
+
expect(app.getHttpUrl()).toBe('http://10.0.0.1:8888');
|
|
499
|
+
} finally {
|
|
500
|
+
if (originalPort === undefined) {
|
|
501
|
+
delete process.env.PORT;
|
|
502
|
+
} else {
|
|
503
|
+
process.env.PORT = originalPort;
|
|
504
|
+
}
|
|
505
|
+
if (originalHost === undefined) {
|
|
506
|
+
delete process.env.HOST;
|
|
507
|
+
} else {
|
|
508
|
+
process.env.HOST = originalHost;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
test('should prefer explicit port over PORT env variable', () => {
|
|
514
|
+
const originalPort = process.env.PORT;
|
|
515
|
+
const originalHost = process.env.HOST;
|
|
516
|
+
try {
|
|
517
|
+
process.env.PORT = '9999';
|
|
518
|
+
delete process.env.HOST; // Clear HOST to get default
|
|
519
|
+
|
|
520
|
+
@Module({})
|
|
521
|
+
class TestModule {}
|
|
522
|
+
|
|
523
|
+
const app = createTestApp(TestModule, { port: 5555 });
|
|
524
|
+
expect(app.getHttpUrl()).toBe('http://0.0.0.0:5555');
|
|
525
|
+
} finally {
|
|
526
|
+
if (originalPort === undefined) {
|
|
527
|
+
delete process.env.PORT;
|
|
528
|
+
} else {
|
|
529
|
+
process.env.PORT = originalPort;
|
|
530
|
+
}
|
|
531
|
+
if (originalHost === undefined) {
|
|
532
|
+
delete process.env.HOST;
|
|
533
|
+
} else {
|
|
534
|
+
process.env.HOST = originalHost;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test('should prefer explicit host over HOST env variable', () => {
|
|
540
|
+
const originalHost = process.env.HOST;
|
|
541
|
+
const originalPort = process.env.PORT;
|
|
542
|
+
try {
|
|
543
|
+
process.env.HOST = '192.168.1.100';
|
|
544
|
+
delete process.env.PORT; // Clear PORT to get default
|
|
545
|
+
|
|
546
|
+
@Module({})
|
|
547
|
+
class TestModule {}
|
|
548
|
+
|
|
549
|
+
const app = createTestApp(TestModule, { host: 'localhost' });
|
|
550
|
+
expect(app.getHttpUrl()).toBe('http://localhost:3000');
|
|
551
|
+
} finally {
|
|
552
|
+
if (originalHost === undefined) {
|
|
553
|
+
delete process.env.HOST;
|
|
554
|
+
} else {
|
|
555
|
+
process.env.HOST = originalHost;
|
|
556
|
+
}
|
|
557
|
+
if (originalPort === undefined) {
|
|
558
|
+
delete process.env.PORT;
|
|
559
|
+
} else {
|
|
560
|
+
process.env.PORT = originalPort;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
test('should use defaults when env variables are not set', () => {
|
|
566
|
+
const originalPort = process.env.PORT;
|
|
567
|
+
const originalHost = process.env.HOST;
|
|
568
|
+
try {
|
|
569
|
+
delete process.env.PORT;
|
|
570
|
+
delete process.env.HOST;
|
|
571
|
+
|
|
572
|
+
@Module({})
|
|
573
|
+
class TestModule {}
|
|
574
|
+
|
|
575
|
+
const app = createTestApp(TestModule);
|
|
576
|
+
expect(app.getHttpUrl()).toBe('http://0.0.0.0:3000');
|
|
577
|
+
} finally {
|
|
578
|
+
if (originalPort !== undefined) {
|
|
579
|
+
process.env.PORT = originalPort;
|
|
580
|
+
}
|
|
581
|
+
if (originalHost !== undefined) {
|
|
582
|
+
process.env.HOST = originalHost;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
test('should ignore invalid PORT env variable and use default', () => {
|
|
588
|
+
const originalPort = process.env.PORT;
|
|
589
|
+
const originalHost = process.env.HOST;
|
|
590
|
+
try {
|
|
591
|
+
process.env.PORT = 'invalid';
|
|
592
|
+
delete process.env.HOST; // Clear HOST to get default
|
|
593
|
+
|
|
594
|
+
@Module({})
|
|
595
|
+
class TestModule {}
|
|
596
|
+
|
|
597
|
+
const app = createTestApp(TestModule);
|
|
598
|
+
expect(app.getHttpUrl()).toBe('http://0.0.0.0:3000');
|
|
599
|
+
} finally {
|
|
600
|
+
if (originalPort === undefined) {
|
|
601
|
+
delete process.env.PORT;
|
|
602
|
+
} else {
|
|
603
|
+
process.env.PORT = originalPort;
|
|
604
|
+
}
|
|
605
|
+
if (originalHost === undefined) {
|
|
606
|
+
delete process.env.HOST;
|
|
607
|
+
} else {
|
|
608
|
+
process.env.HOST = originalHost;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
test('should ignore empty PORT env variable and use default', () => {
|
|
614
|
+
const originalPort = process.env.PORT;
|
|
615
|
+
const originalHost = process.env.HOST;
|
|
616
|
+
try {
|
|
617
|
+
process.env.PORT = '';
|
|
618
|
+
delete process.env.HOST; // Clear HOST to get default
|
|
619
|
+
|
|
620
|
+
@Module({})
|
|
621
|
+
class TestModule {}
|
|
622
|
+
|
|
623
|
+
const app = createTestApp(TestModule);
|
|
624
|
+
expect(app.getHttpUrl()).toBe('http://0.0.0.0:3000');
|
|
625
|
+
} finally {
|
|
626
|
+
if (originalPort === undefined) {
|
|
627
|
+
delete process.env.PORT;
|
|
628
|
+
} else {
|
|
629
|
+
process.env.PORT = originalPort;
|
|
630
|
+
}
|
|
631
|
+
if (originalHost === undefined) {
|
|
632
|
+
delete process.env.HOST;
|
|
633
|
+
} else {
|
|
634
|
+
process.env.HOST = originalHost;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
test('should ignore empty HOST env variable and use default', () => {
|
|
640
|
+
const originalHost = process.env.HOST;
|
|
641
|
+
const originalPort = process.env.PORT;
|
|
642
|
+
try {
|
|
643
|
+
process.env.HOST = '';
|
|
644
|
+
delete process.env.PORT; // Clear PORT to get default
|
|
645
|
+
|
|
646
|
+
@Module({})
|
|
647
|
+
class TestModule {}
|
|
648
|
+
|
|
649
|
+
const app = createTestApp(TestModule);
|
|
650
|
+
expect(app.getHttpUrl()).toBe('http://0.0.0.0:3000');
|
|
651
|
+
} finally {
|
|
652
|
+
if (originalHost === undefined) {
|
|
653
|
+
delete process.env.HOST;
|
|
654
|
+
} else {
|
|
655
|
+
process.env.HOST = originalHost;
|
|
656
|
+
}
|
|
657
|
+
if (originalPort === undefined) {
|
|
658
|
+
delete process.env.PORT;
|
|
659
|
+
} else {
|
|
660
|
+
process.env.PORT = originalPort;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
435
665
|
test('should handle complex envSchema configuration', () => {
|
|
436
666
|
@Module({})
|
|
437
667
|
class TestModule {}
|
|
@@ -596,7 +826,7 @@ describe('OneBunApplication', () => {
|
|
|
596
826
|
await expect(app.start()).resolves.toBeUndefined();
|
|
597
827
|
});
|
|
598
828
|
|
|
599
|
-
test('should stop server when running', () => {
|
|
829
|
+
test('should stop server when running', async () => {
|
|
600
830
|
@Module({})
|
|
601
831
|
class TestModule {}
|
|
602
832
|
|
|
@@ -608,20 +838,20 @@ describe('OneBunApplication', () => {
|
|
|
608
838
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
609
839
|
(app as any).server = mockServer;
|
|
610
840
|
|
|
611
|
-
app.stop();
|
|
841
|
+
await app.stop();
|
|
612
842
|
|
|
613
843
|
expect(mockServer.stop).toHaveBeenCalled();
|
|
614
844
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
615
845
|
expect((app as any).server).toBeNull();
|
|
616
846
|
});
|
|
617
847
|
|
|
618
|
-
test('should handle stop when server is not running', () => {
|
|
848
|
+
test('should handle stop when server is not running', async () => {
|
|
619
849
|
@Module({})
|
|
620
850
|
class TestModule {}
|
|
621
851
|
|
|
622
852
|
const app = createTestApp(TestModule);
|
|
623
853
|
|
|
624
|
-
expect(
|
|
854
|
+
await expect(app.stop()).resolves.toBeUndefined();
|
|
625
855
|
});
|
|
626
856
|
});
|
|
627
857
|
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type Logger,
|
|
14
14
|
LoggerService,
|
|
15
15
|
makeLogger,
|
|
16
|
+
makeLoggerFromOptions,
|
|
16
17
|
type SyncLogger,
|
|
17
18
|
} from '@onebun/logger';
|
|
18
19
|
import {
|
|
@@ -112,17 +113,48 @@ function normalizePath(path: string): string {
|
|
|
112
113
|
return path.endsWith('/') ? path.slice(0, -1) : path;
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Resolve port from options, environment variable, or default.
|
|
118
|
+
* Priority: explicit option > PORT env > default (3000)
|
|
119
|
+
*/
|
|
120
|
+
function resolvePort(explicitPort: number | undefined): number {
|
|
121
|
+
if (explicitPort !== undefined) {
|
|
122
|
+
return explicitPort;
|
|
123
|
+
}
|
|
124
|
+
const envPort = process.env.PORT;
|
|
125
|
+
if (envPort !== undefined && envPort !== '') {
|
|
126
|
+
const parsed = parseInt(envPort, 10);
|
|
127
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
128
|
+
return parsed;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return 3000;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Resolve host from options, environment variable, or default.
|
|
137
|
+
* Priority: explicit option > HOST env > default ('0.0.0.0')
|
|
138
|
+
*/
|
|
139
|
+
function resolveHost(explicitHost: string | undefined): string {
|
|
140
|
+
if (explicitHost !== undefined) {
|
|
141
|
+
return explicitHost;
|
|
142
|
+
}
|
|
143
|
+
const envHost = process.env.HOST;
|
|
144
|
+
if (envHost !== undefined && envHost !== '') {
|
|
145
|
+
return envHost;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return '0.0.0.0';
|
|
149
|
+
}
|
|
150
|
+
|
|
115
151
|
/**
|
|
116
152
|
* OneBun Application
|
|
117
153
|
*/
|
|
118
154
|
export class OneBunApplication {
|
|
119
155
|
private rootModule: ModuleInstance;
|
|
120
156
|
private server: ReturnType<typeof Bun.serve> | null = null;
|
|
121
|
-
private options: ApplicationOptions
|
|
122
|
-
port: 3000,
|
|
123
|
-
host: '0.0.0.0',
|
|
124
|
-
development: process.env.NODE_ENV !== 'production',
|
|
125
|
-
};
|
|
157
|
+
private options: ApplicationOptions;
|
|
126
158
|
private logger: SyncLogger;
|
|
127
159
|
private config: IConfig<OneBunAppConfig>;
|
|
128
160
|
private configService: ConfigServiceImpl | null = null;
|
|
@@ -144,9 +176,13 @@ export class OneBunApplication {
|
|
|
144
176
|
moduleClass: new (...args: unknown[]) => object,
|
|
145
177
|
options?: Partial<ApplicationOptions>,
|
|
146
178
|
) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
179
|
+
// Resolve port and host with priority: explicit > env > default
|
|
180
|
+
this.options = {
|
|
181
|
+
port: resolvePort(options?.port),
|
|
182
|
+
host: resolveHost(options?.host),
|
|
183
|
+
development: options?.development ?? process.env.NODE_ENV !== 'production',
|
|
184
|
+
...options,
|
|
185
|
+
};
|
|
150
186
|
|
|
151
187
|
// Initialize configuration - TypedEnv if schema provided, otherwise NotInitializedConfig
|
|
152
188
|
if (this.options.envSchema) {
|
|
@@ -156,8 +192,12 @@ export class OneBunApplication {
|
|
|
156
192
|
this.config = new NotInitializedConfig();
|
|
157
193
|
}
|
|
158
194
|
|
|
159
|
-
// Use provided logger layer or create
|
|
160
|
-
|
|
195
|
+
// Use provided logger layer, or create from options, or use default
|
|
196
|
+
// Priority: loggerLayer > loggerOptions > env variables > NODE_ENV defaults
|
|
197
|
+
const loggerLayer = this.options.loggerLayer
|
|
198
|
+
?? (this.options.loggerOptions
|
|
199
|
+
? makeLoggerFromOptions(this.options.loggerOptions)
|
|
200
|
+
: makeLogger());
|
|
161
201
|
|
|
162
202
|
// Initialize logger with application class name as context
|
|
163
203
|
const effectLogger = Effect.runSync(
|
|
@@ -467,6 +507,12 @@ export class OneBunApplication {
|
|
|
467
507
|
}
|
|
468
508
|
}
|
|
469
509
|
|
|
510
|
+
// Call onApplicationInit lifecycle hook for all services and controllers
|
|
511
|
+
if (this.rootModule.callOnApplicationInit) {
|
|
512
|
+
await this.rootModule.callOnApplicationInit();
|
|
513
|
+
this.logger.debug('Application initialization hooks completed');
|
|
514
|
+
}
|
|
515
|
+
|
|
470
516
|
// Get metrics path
|
|
471
517
|
const metricsPath = this.options.metrics?.path || '/metrics';
|
|
472
518
|
|
|
@@ -1179,11 +1225,18 @@ export class OneBunApplication {
|
|
|
1179
1225
|
* Stop the application with graceful shutdown
|
|
1180
1226
|
* @param options - Shutdown options
|
|
1181
1227
|
*/
|
|
1182
|
-
async stop(options?: { closeSharedRedis?: boolean }): Promise<void> {
|
|
1228
|
+
async stop(options?: { closeSharedRedis?: boolean; signal?: string }): Promise<void> {
|
|
1183
1229
|
const closeRedis = options?.closeSharedRedis ?? true;
|
|
1230
|
+
const signal = options?.signal;
|
|
1184
1231
|
|
|
1185
1232
|
this.logger.info('Stopping OneBun application...');
|
|
1186
1233
|
|
|
1234
|
+
// Call beforeApplicationDestroy lifecycle hook
|
|
1235
|
+
if (this.rootModule.callBeforeApplicationDestroy) {
|
|
1236
|
+
this.logger.debug('Calling beforeApplicationDestroy hooks');
|
|
1237
|
+
await this.rootModule.callBeforeApplicationDestroy(signal);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1187
1240
|
// Cleanup WebSocket resources
|
|
1188
1241
|
if (this.wsHandler) {
|
|
1189
1242
|
this.logger.debug('Cleaning up WebSocket handler');
|
|
@@ -1212,12 +1265,24 @@ export class OneBunApplication {
|
|
|
1212
1265
|
this.logger.debug('HTTP server stopped');
|
|
1213
1266
|
}
|
|
1214
1267
|
|
|
1268
|
+
// Call onModuleDestroy lifecycle hook
|
|
1269
|
+
if (this.rootModule.callOnModuleDestroy) {
|
|
1270
|
+
this.logger.debug('Calling onModuleDestroy hooks');
|
|
1271
|
+
await this.rootModule.callOnModuleDestroy();
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1215
1274
|
// Close shared Redis connection if configured and requested
|
|
1216
1275
|
if (closeRedis && SharedRedisProvider.isConnected()) {
|
|
1217
1276
|
this.logger.debug('Disconnecting shared Redis');
|
|
1218
1277
|
await SharedRedisProvider.disconnect();
|
|
1219
1278
|
}
|
|
1220
1279
|
|
|
1280
|
+
// Call onApplicationDestroy lifecycle hook
|
|
1281
|
+
if (this.rootModule.callOnApplicationDestroy) {
|
|
1282
|
+
this.logger.debug('Calling onApplicationDestroy hooks');
|
|
1283
|
+
await this.rootModule.callOnApplicationDestroy(signal);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1221
1286
|
this.logger.info('OneBun application stopped');
|
|
1222
1287
|
}
|
|
1223
1288
|
|
|
@@ -1405,7 +1470,7 @@ export class OneBunApplication {
|
|
|
1405
1470
|
enableGracefulShutdown(): void {
|
|
1406
1471
|
const shutdown = async (signal: string) => {
|
|
1407
1472
|
this.logger.info(`Received ${signal}, initiating graceful shutdown...`);
|
|
1408
|
-
await this.stop();
|
|
1473
|
+
await this.stop({ signal });
|
|
1409
1474
|
process.exit(0);
|
|
1410
1475
|
};
|
|
1411
1476
|
|
|
@@ -1434,4 +1499,36 @@ export class OneBunApplication {
|
|
|
1434
1499
|
getHttpUrl(): string {
|
|
1435
1500
|
return `http://${this.options.host}:${this.options.port}`;
|
|
1436
1501
|
}
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Get a service instance by class from the module container.
|
|
1505
|
+
* Useful for accessing services outside of the request context.
|
|
1506
|
+
*
|
|
1507
|
+
* @param serviceClass - The service class to get
|
|
1508
|
+
* @returns The service instance
|
|
1509
|
+
* @throws Error if service is not found
|
|
1510
|
+
*
|
|
1511
|
+
* @example
|
|
1512
|
+
* ```typescript
|
|
1513
|
+
* const app = new OneBunApplication(AppModule, options);
|
|
1514
|
+
* await app.start();
|
|
1515
|
+
*
|
|
1516
|
+
* const userService = app.getService(UserService);
|
|
1517
|
+
* await userService.performBackgroundTask();
|
|
1518
|
+
* ```
|
|
1519
|
+
*/
|
|
1520
|
+
getService<T>(serviceClass: new (...args: unknown[]) => T): T {
|
|
1521
|
+
if (!this.rootModule.getServiceByClass) {
|
|
1522
|
+
throw new Error('Module does not support getServiceByClass');
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
const service = this.rootModule.getServiceByClass(serviceClass);
|
|
1526
|
+
if (!service) {
|
|
1527
|
+
throw new Error(
|
|
1528
|
+
`Service ${serviceClass.name} not found. Make sure it's registered in the module's providers.`,
|
|
1529
|
+
);
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
return service;
|
|
1533
|
+
}
|
|
1437
1534
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type Logger,
|
|
13
13
|
LoggerService,
|
|
14
14
|
makeLogger,
|
|
15
|
+
parseLogLevel,
|
|
15
16
|
type SyncLogger,
|
|
16
17
|
} from '@onebun/logger';
|
|
17
18
|
|
|
@@ -201,9 +202,9 @@ export class MultiServiceApplication<TServices extends ServicesMap = ServicesMap
|
|
|
201
202
|
...this.options.envOptions,
|
|
202
203
|
valueOverrides: resolvedOverrides,
|
|
203
204
|
},
|
|
204
|
-
// Logger configuration - use
|
|
205
|
-
|
|
206
|
-
?
|
|
205
|
+
// Logger configuration - use loggerOptions if minLevel provided
|
|
206
|
+
loggerOptions: mergedOptions.logger?.minLevel
|
|
207
|
+
? { minLevel: parseLogLevel(mergedOptions.logger.minLevel) }
|
|
207
208
|
: undefined,
|
|
208
209
|
metrics: {
|
|
209
210
|
...mergedOptions.metrics,
|
|
@@ -316,24 +317,4 @@ export class MultiServiceApplication<TServices extends ServicesMap = ServicesMap
|
|
|
316
317
|
getLogger(): SyncLogger {
|
|
317
318
|
return this.logger;
|
|
318
319
|
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Convert log level string to numeric LogLevel value.
|
|
322
|
-
* LogLevel values: Fatal=60, Error=50, Warning=40, Info=30, Debug=20, Trace=10
|
|
323
|
-
*/
|
|
324
|
-
private getLogLevel(level: string): number {
|
|
325
|
-
/* eslint-disable @typescript-eslint/no-magic-numbers */
|
|
326
|
-
const LOG_LEVEL_INFO = 30;
|
|
327
|
-
const levelMap: Record<string, number> = {
|
|
328
|
-
fatal: 60,
|
|
329
|
-
error: 50,
|
|
330
|
-
warning: 40,
|
|
331
|
-
info: LOG_LEVEL_INFO,
|
|
332
|
-
debug: 20,
|
|
333
|
-
trace: 10,
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
return levelMap[level.toLowerCase()] ?? LOG_LEVEL_INFO;
|
|
337
|
-
/* eslint-enable @typescript-eslint/no-magic-numbers */
|
|
338
|
-
}
|
|
339
320
|
}
|