chore: run format and lint
This commit is contained in:
Vendored
+2
-5
@@ -1,6 +1,3 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": ["netdi", "stevanfreeborn"]
|
||||||
"netdi",
|
}
|
||||||
"stevanfreeborn"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -29,7 +29,13 @@ pnpm add @stevanfreeborn/hono-netdi hono
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Hono } from 'hono';
|
import { Hono } from 'hono';
|
||||||
import { ServiceCollection, createServiceIdentifier, injectable, injectServices, useService } from '@stevanfreeborn/hono-netdi';
|
import {
|
||||||
|
ServiceCollection,
|
||||||
|
createServiceIdentifier,
|
||||||
|
injectable,
|
||||||
|
injectServices,
|
||||||
|
useService,
|
||||||
|
} from '@stevanfreeborn/hono-netdi';
|
||||||
|
|
||||||
// Define your service interface and implementation
|
// Define your service interface and implementation
|
||||||
interface IUserService {
|
interface IUserService {
|
||||||
@@ -56,7 +62,7 @@ const app = new Hono();
|
|||||||
app.use(injectServices(serviceProvider));
|
app.use(injectServices(serviceProvider));
|
||||||
|
|
||||||
// Use services in your routes
|
// Use services in your routes
|
||||||
app.get('/users/:id', async (c) => {
|
app.get('/users/:id', async c => {
|
||||||
const userService = useService(c, IUserService);
|
const userService = useService(c, IUserService);
|
||||||
const user = await userService.getUser(c.req.param('id'));
|
const user = await userService.getUser(c.req.param('id'));
|
||||||
return c.json(user);
|
return c.json(user);
|
||||||
@@ -135,7 +141,7 @@ import { injectable, inject } from '@stevanfreeborn/hono-netdi';
|
|||||||
class UserService implements IUserService {
|
class UserService implements IUserService {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(IUserRepository) private userRepository: IUserRepository,
|
@inject(IUserRepository) private userRepository: IUserRepository,
|
||||||
@inject(ILogger) private logger: ILogger
|
@inject(ILogger) private logger: ILogger,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -232,7 +238,7 @@ class UserRepository implements IUserRepository {
|
|||||||
class UserService implements IUserService {
|
class UserService implements IUserService {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(IUserRepository) private userRepository: IUserRepository,
|
@inject(IUserRepository) private userRepository: IUserRepository,
|
||||||
@inject(ILogger) private logger: ILogger
|
@inject(ILogger) private logger: ILogger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getUser(id: string): Promise<User> {
|
async getUser(id: string): Promise<User> {
|
||||||
@@ -284,13 +290,13 @@ services.addScoped(EmailNotification, EmailNotificationService);
|
|||||||
services.addScoped(SmsNotification, SmsNotificationService);
|
services.addScoped(SmsNotification, SmsNotificationService);
|
||||||
|
|
||||||
// Use in routes
|
// Use in routes
|
||||||
app.post('/notify', async (c) => {
|
app.post('/notify', async c => {
|
||||||
const emailService = useService(c, EmailNotification);
|
const emailService = useService(c, EmailNotification);
|
||||||
const smsService = useService(c, SmsNotification);
|
const smsService = useService(c, SmsNotification);
|
||||||
|
|
||||||
await emailService.send('Hello via email!');
|
await emailService.send('Hello via email!');
|
||||||
await smsService.send('Hello via SMS!');
|
await smsService.send('Hello via SMS!');
|
||||||
|
|
||||||
return c.json({ success: true });
|
return c.json({ success: true });
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -326,10 +332,10 @@ const IDatabase = createServiceIdentifier<IDatabase>();
|
|||||||
|
|
||||||
services.addSingleton<IDatabaseConfig>(IDatabaseConfig, () => ({
|
services.addSingleton<IDatabaseConfig>(IDatabaseConfig, () => ({
|
||||||
connectionString: process.env.DB_CONNECTION_STRING!,
|
connectionString: process.env.DB_CONNECTION_STRING!,
|
||||||
timeout: 30000
|
timeout: 30000,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
services.addScoped(IDatabase, (provider) => {
|
services.addScoped(IDatabase, provider => {
|
||||||
const config = provider.getService(IDatabaseConfig);
|
const config = provider.getService(IDatabaseConfig);
|
||||||
return new Database(config);
|
return new Database(config);
|
||||||
});
|
});
|
||||||
@@ -340,9 +346,9 @@ services.addScoped(IDatabase, (provider) => {
|
|||||||
The middleware automatically handles service scope disposal even when errors occur:
|
The middleware automatically handles service scope disposal even when errors occur:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
app.get('/error-example', async (c) => {
|
app.get('/error-example', async c => {
|
||||||
const userService = useService(c, IUserService);
|
const userService = useService(c, IUserService);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// This might throw an error
|
// This might throw an error
|
||||||
const user = await userService.getUser('invalid-id');
|
const user = await userService.getUser('invalid-id');
|
||||||
@@ -369,7 +375,7 @@ app.use(cors());
|
|||||||
app.use(logger());
|
app.use(logger());
|
||||||
|
|
||||||
// Routes can now use services
|
// Routes can now use services
|
||||||
app.get('/', (c) => {
|
app.get('/', c => {
|
||||||
const service = useService(c, IMyService);
|
const service = useService(c, IMyService);
|
||||||
return c.json(service.getData());
|
return c.json(service.getData());
|
||||||
});
|
});
|
||||||
@@ -408,7 +414,7 @@ const ILogger = createServiceIdentifier<ILogger>();
|
|||||||
@injectable()
|
@injectable()
|
||||||
class UserService {
|
class UserService {
|
||||||
constructor(@inject(ILogger) private logger: ILogger) {}
|
constructor(@inject(ILogger) private logger: ILogger) {}
|
||||||
|
|
||||||
async getUser(id: string): Promise<User> {
|
async getUser(id: string): Promise<User> {
|
||||||
this.logger.log(`Getting user ${id}`);
|
this.logger.log(`Getting user ${id}`);
|
||||||
// Implementation
|
// Implementation
|
||||||
@@ -444,7 +450,7 @@ Keep dependencies minimal and well-defined:
|
|||||||
class UserService implements IUserService {
|
class UserService implements IUserService {
|
||||||
constructor(
|
constructor(
|
||||||
@inject(IUserRepository) private userRepository: IUserRepository,
|
@inject(IUserRepository) private userRepository: IUserRepository,
|
||||||
@inject(ILogger) private logger: ILogger
|
@inject(ILogger) private logger: ILogger,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,10 +477,10 @@ class UserService implements IUserService {
|
|||||||
```typescript
|
```typescript
|
||||||
// ✅ Correct order
|
// ✅ Correct order
|
||||||
app.use(injectServices(serviceProvider));
|
app.use(injectServices(serviceProvider));
|
||||||
app.get('/', (c) => useService(c, IMyService));
|
app.get('/', c => useService(c, IMyService));
|
||||||
|
|
||||||
// ❌ Wrong order
|
// ❌ Wrong order
|
||||||
app.get('/', (c) => useService(c, IMyService));
|
app.get('/', c => useService(c, IMyService));
|
||||||
app.use(injectServices(serviceProvider));
|
app.use(injectServices(serviceProvider));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+16
-16
@@ -17,35 +17,35 @@ declare module 'hono' {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Hono middleware that manages dependency injection service scopes for each request.
|
* Creates a Hono middleware that manages dependency injection service scopes for each request.
|
||||||
*
|
*
|
||||||
* This middleware creates a new service scope at the beginning of each request and automatically
|
* This middleware creates a new service scope at the beginning of each request and automatically
|
||||||
* disposes of it when the request completes, ensuring proper resource cleanup and preventing
|
* disposes of it when the request completes, ensuring proper resource cleanup and preventing
|
||||||
* memory leaks. The service scope is stored in the Hono context and can be accessed by subsequent
|
* memory leaks. The service scope is stored in the Hono context and can be accessed by subsequent
|
||||||
* middleware and route handlers.
|
* middleware and route handlers.
|
||||||
*
|
*
|
||||||
* @param serviceProvider - The root service provider from which to create scoped instances
|
* @param serviceProvider - The root service provider from which to create scoped instances
|
||||||
* @returns A Hono middleware handler that manages service scope lifecycle
|
* @returns A Hono middleware handler that manages service scope lifecycle
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* import { Hono } from 'hono';
|
* import { Hono } from 'hono';
|
||||||
* import { injectServices, useService, ServiceCollection } from '@stevanfreeborn/hono-netdi';
|
* import { injectServices, useService, ServiceCollection } from '@stevanfreeborn/hono-netdi';
|
||||||
*
|
*
|
||||||
* // Configure services
|
* // Configure services
|
||||||
* const services = new ServiceCollection();
|
* const services = new ServiceCollection();
|
||||||
* services.addScoped(MyService);
|
* services.addScoped(MyService);
|
||||||
* const serviceProvider = services.build();
|
* const serviceProvider = services.build();
|
||||||
*
|
*
|
||||||
* // Create Hono app with DI middleware
|
* // Create Hono app with DI middleware
|
||||||
* const app = new Hono();
|
* const app = new Hono();
|
||||||
* app.use(injectServices(serviceProvider));
|
* app.use(injectServices(serviceProvider));
|
||||||
*
|
*
|
||||||
* app.get('/', (c) => {
|
* app.get('/', (c) => {
|
||||||
* const myService = useService(c, MyService);
|
* const myService = useService(c, MyService);
|
||||||
* return c.json({ data: myService.getData() });
|
* return c.json({ data: myService.getData() });
|
||||||
* });
|
* });
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @throws {Error} If the service provider is null or undefined
|
* @throws {Error} If the service provider is null or undefined
|
||||||
* @see {@link useService} for accessing services within request handlers
|
* @see {@link useService} for accessing services within request handlers
|
||||||
*/
|
*/
|
||||||
@@ -65,40 +65,40 @@ export function injectServices(serviceProvider: IServiceProvider): MiddlewareHan
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a service instance from the current request's dependency injection scope.
|
* Retrieves a service instance from the current request's dependency injection scope.
|
||||||
*
|
*
|
||||||
* This function extracts the service scope from the Hono context (which must have been
|
* This function extracts the service scope from the Hono context (which must have been
|
||||||
* set by the `injectServices` middleware) and uses it to resolve the requested service.
|
* set by the `injectServices` middleware) and uses it to resolve the requested service.
|
||||||
* Services are resolved according to their configured lifetime (singleton, scoped, or transient).
|
* Services are resolved according to their configured lifetime (singleton, scoped, or transient).
|
||||||
*
|
*
|
||||||
* @template T - The type of service to retrieve
|
* @template T - The type of service to retrieve
|
||||||
* @param c - The Hono context containing the service scope
|
* @param c - The Hono context containing the service scope
|
||||||
* @param serviceType - The service identifier used to resolve the service instance
|
* @param serviceType - The service identifier used to resolve the service instance
|
||||||
* @returns The resolved service instance of type T
|
* @returns The resolved service instance of type T
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* import { Context } from 'hono';
|
* import { Context } from 'hono';
|
||||||
* import { useService, createServiceIdentifier } from '@stevanfreeborn/hono-netdi';
|
* import { useService, createServiceIdentifier } from '@stevanfreeborn/hono-netdi';
|
||||||
*
|
*
|
||||||
* interface IUserService {
|
* interface IUserService {
|
||||||
* getUser(id: string): Promise<User>;
|
* getUser(id: string): Promise<User>;
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* const userServiceId = createServiceIdentifier<IUserService>();
|
* const userServiceId = createServiceIdentifier<IUserService>();
|
||||||
*
|
*
|
||||||
* app.get('/users/:id', async (c: Context) => {
|
* app.get('/users/:id', async (c: Context) => {
|
||||||
* const userService = useService(c, userServiceId);
|
* const userService = useService(c, userServiceId);
|
||||||
* const user = await userService.getUser(c.req.param('id'));
|
* const user = await userService.getUser(c.req.param('id'));
|
||||||
* return c.json(user);
|
* return c.json(user);
|
||||||
* });
|
* });
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @throws {Error} When the service scope is not found in the context (typically when
|
* @throws {Error} When the service scope is not found in the context (typically when
|
||||||
* `injectServices` middleware was not properly configured)
|
* `injectServices` middleware was not properly configured)
|
||||||
* @throws {Error} When the service scope is not a valid ServiceScope instance
|
* @throws {Error} When the service scope is not a valid ServiceScope instance
|
||||||
* @throws {Error} When the requested service cannot be resolved (service not registered,
|
* @throws {Error} When the requested service cannot be resolved (service not registered,
|
||||||
* missing dependencies, etc.)
|
* missing dependencies, etc.)
|
||||||
*
|
*
|
||||||
* @see {@link injectServices} for setting up the dependency injection middleware
|
* @see {@link injectServices} for setting up the dependency injection middleware
|
||||||
*/
|
*/
|
||||||
export function useService<T>(c: Context, serviceType: ServiceIdentifier<T>): T {
|
export function useService<T>(c: Context, serviceType: ServiceIdentifier<T>): T {
|
||||||
@@ -117,4 +117,4 @@ export function useService<T>(c: Context, serviceType: ServiceIdentifier<T>): T
|
|||||||
return scope.serviceProvider.getService(serviceType);
|
return scope.serviceProvider.getService(serviceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
export * from '@stevanfreeborn/netdi';
|
export * from '@stevanfreeborn/netdi';
|
||||||
|
|||||||
+3
-4
@@ -48,7 +48,7 @@ describe('injectServices', () => {
|
|||||||
describe('useService', () => {
|
describe('useService', () => {
|
||||||
test('it should throw an error if the service scope is not found in context', async () => {
|
test('it should throw an error if the service scope is not found in context', async () => {
|
||||||
type ITestService = object;
|
type ITestService = object;
|
||||||
|
|
||||||
const serviceId = createServiceIdentifier<ITestService>();
|
const serviceId = createServiceIdentifier<ITestService>();
|
||||||
const app = new Hono<Env>();
|
const app = new Hono<Env>();
|
||||||
|
|
||||||
@@ -68,13 +68,13 @@ describe('useService', () => {
|
|||||||
|
|
||||||
test('it should throw an error if the service scope is not an instance of ServiceScope', async () => {
|
test('it should throw an error if the service scope is not an instance of ServiceScope', async () => {
|
||||||
type ITestService = object;
|
type ITestService = object;
|
||||||
|
|
||||||
const serviceId = createServiceIdentifier<ITestService>();
|
const serviceId = createServiceIdentifier<ITestService>();
|
||||||
const app = new Hono<Env>();
|
const app = new Hono<Env>();
|
||||||
|
|
||||||
app.get('/', c => {
|
app.get('/', c => {
|
||||||
c.set('serviceScope', {} as IServiceScope);
|
c.set('serviceScope', {} as IServiceScope);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
useService(c, serviceId);
|
useService(c, serviceId);
|
||||||
return c.text('Service scope found', 200);
|
return c.text('Service scope found', 200);
|
||||||
@@ -115,7 +115,6 @@ describe('useService', () => {
|
|||||||
expect(res.status).toBe(500);
|
expect(res.status).toBe(500);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test('it should return the service from the service scope', async () => {
|
test('it should return the service from the service scope', async () => {
|
||||||
interface ITestService {
|
interface ITestService {
|
||||||
id(): string;
|
id(): string;
|
||||||
|
|||||||
Reference in New Issue
Block a user