@onebun/core 0.1.9 → 0.1.10
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
|
@@ -13,7 +13,13 @@ import {
|
|
|
13
13
|
afterEach,
|
|
14
14
|
} from 'bun:test';
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { OneBunApplication } from '../application';
|
|
17
|
+
import {
|
|
18
|
+
BaseController,
|
|
19
|
+
BaseService,
|
|
20
|
+
Service,
|
|
21
|
+
} from '../module';
|
|
22
|
+
import { makeMockLoggerLayer } from '../testing';
|
|
17
23
|
import { HttpMethod, ParamType } from '../types';
|
|
18
24
|
|
|
19
25
|
import {
|
|
@@ -809,6 +815,53 @@ describe('decorators', () => {
|
|
|
809
815
|
const deps = getConstructorParamTypes(malformedClass);
|
|
810
816
|
expect(deps).toBeUndefined();
|
|
811
817
|
});
|
|
818
|
+
|
|
819
|
+
test('should properly inject module service into controller without @Inject', async () => {
|
|
820
|
+
@Service()
|
|
821
|
+
class TestService extends BaseService {
|
|
822
|
+
getValue() {
|
|
823
|
+
return 'injected-value';
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// No @Inject needed - automatic DI via emitDecoratorMetadata
|
|
828
|
+
@Controller('')
|
|
829
|
+
class TestController extends BaseController {
|
|
830
|
+
constructor(private service: TestService) {
|
|
831
|
+
super();
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
getServiceValue() {
|
|
835
|
+
return this.service.getValue();
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
@Module({
|
|
840
|
+
controllers: [TestController],
|
|
841
|
+
providers: [TestService],
|
|
842
|
+
})
|
|
843
|
+
class TestModule {}
|
|
844
|
+
|
|
845
|
+
const app = new OneBunApplication(TestModule, {
|
|
846
|
+
loggerLayer: makeMockLoggerLayer(),
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
// Access rootModule to setup and verify DI
|
|
850
|
+
const rootModule = (app as any).rootModule;
|
|
851
|
+
|
|
852
|
+
// Setup module to trigger dependency injection
|
|
853
|
+
const { Effect } = await import('effect');
|
|
854
|
+
await Effect.runPromise(rootModule.setup());
|
|
855
|
+
|
|
856
|
+
// Get controller instance and verify service was injected
|
|
857
|
+
const controllerInstance = rootModule.getControllerInstance(TestController) as TestController;
|
|
858
|
+
|
|
859
|
+
expect(controllerInstance).toBeDefined();
|
|
860
|
+
expect(controllerInstance).toBeInstanceOf(TestController);
|
|
861
|
+
|
|
862
|
+
// Verify the injected service works correctly
|
|
863
|
+
expect(controllerInstance.getServiceValue()).toBe('injected-value');
|
|
864
|
+
});
|
|
812
865
|
});
|
|
813
866
|
|
|
814
867
|
describe('ApiResponse decorator', () => {
|
|
@@ -111,9 +111,17 @@ export function registerControllerDependencies(
|
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
113
|
* Get constructor parameter types (automatically detected or explicitly set)
|
|
114
|
+
* Priority: 1) Explicit @Inject registrations, 2) TypeScript's design:paramtypes
|
|
114
115
|
*/
|
|
115
116
|
export function getConstructorParamTypes(target: Function): Function[] | undefined {
|
|
116
|
-
|
|
117
|
+
// First check explicit @Inject registrations
|
|
118
|
+
const explicitDeps = META_CONSTRUCTOR_PARAMS.get(target);
|
|
119
|
+
if (explicitDeps && explicitDeps.length > 0) {
|
|
120
|
+
return explicitDeps;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Fallback to TypeScript's design:paramtypes (automatic DI)
|
|
124
|
+
return getDesignParamTypes(target);
|
|
117
125
|
}
|
|
118
126
|
|
|
119
127
|
/**
|
|
@@ -174,6 +182,15 @@ export function controllerDecorator(basePath: string = '') {
|
|
|
174
182
|
// Mark controller as injectable automatically
|
|
175
183
|
injectable()(WrappedController);
|
|
176
184
|
|
|
185
|
+
// Copy design:paramtypes from original class to wrapped class
|
|
186
|
+
// This enables automatic DI without @Inject when emitDecoratorMetadata is enabled
|
|
187
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
188
|
+
const designParamTypes = (globalThis as any).Reflect?.getMetadata?.('design:paramtypes', target);
|
|
189
|
+
if (designParamTypes) {
|
|
190
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
191
|
+
(globalThis as any).Reflect?.defineMetadata?.('design:paramtypes', designParamTypes, WrappedController);
|
|
192
|
+
}
|
|
193
|
+
|
|
177
194
|
// Copy constructor params metadata from original class to wrapped class
|
|
178
195
|
// This is needed for @Inject decorator to work correctly with @Controller wrapping
|
|
179
196
|
const existingDeps = META_CONSTRUCTOR_PARAMS.get(target);
|
|
@@ -1974,17 +1974,19 @@ describe('Architecture Documentation (docs/architecture.md)', () => {
|
|
|
1974
1974
|
});
|
|
1975
1975
|
|
|
1976
1976
|
/**
|
|
1977
|
-
* @source docs/architecture.md#
|
|
1977
|
+
* @source docs/architecture.md#automatic-injection
|
|
1978
1978
|
*/
|
|
1979
|
-
it('should demonstrate
|
|
1980
|
-
// From docs:
|
|
1979
|
+
it('should demonstrate automatic DI without @Inject', () => {
|
|
1980
|
+
// From docs: Automatic DI example
|
|
1981
|
+
// TypeScript's emitDecoratorMetadata provides type info automatically
|
|
1981
1982
|
@Service()
|
|
1982
1983
|
class UserService extends BaseService {}
|
|
1983
1984
|
|
|
1984
1985
|
@Service()
|
|
1985
1986
|
class CacheService extends BaseService {}
|
|
1986
1987
|
|
|
1987
|
-
//
|
|
1988
|
+
// No @Inject needed - automatic DI works via emitDecoratorMetadata
|
|
1989
|
+
// @Inject is only needed for: interfaces, abstract classes, token-based injection
|
|
1988
1990
|
@Controller('/users')
|
|
1989
1991
|
class UserController extends BaseController {
|
|
1990
1992
|
constructor(
|