
Research
/Security News
npm Author Qix Compromised via Phishing Email in Major Supply Chain Attack
npm author Qix’s account was compromised, with malicious versions of popular packages like chalk-template, color-convert, and strip-ansi published.
@analog-tools/inject
Advanced tools
⚠️ IMPORTANT: Early Development Stage ⚠️
This project is in its early development stage. Breaking changes may happen frequently as the APIs evolve. Use with caution in production environments.
A lightweight yet powerful dependency injection system for H3-based server applications (Nitro, AnalogJS), providing a clean, type-safe approach to managing services and dependencies.
# Using npm
npm install @analog-tools/inject
# Using pnpm
pnpm add @analog-tools/inject
# Using yarn
yarn add @analog-tools/inject
Here's a basic example of creating injectable services:
import { inject, registerService } from '@analog-tools/inject';
// Define a service class with the INJECTABLE static property
class LoggerService {
static INJECTABLE = true;
log(message: string): void {
console.log(`[Logger] ${message}`);
}
}
// Define another service that depends on LoggerService
class UserService {
static INJECTABLE = true;
private logger = inject(LoggerService);
getUserInfo(userId: string) {
this.logger.log(`Getting info for user: ${userId}`);
// Implementation...
return { id: userId, name: 'Example User' };
}
}
// Usage in your application
const userService = inject(UserService);
const userInfo = userService.getUserInfo('user123');
// src/server/routes/api/users/[id].ts
import { defineEventHandler } from 'h3';
import { inject } from '@analog-tools/inject';
import { UserService } from '../../../services/user.service';
export default defineEventHandler(async (event) => {
const userService = inject(UserService);
const userId = event.context.params.id;
const userData = await userService.getUserById(userId);
return userData;
});
// src/server/services/user.service.ts
import { inject } from '@analog-tools/inject';
import { DatabaseService } from './database.service';
export class UserService {
static INJECTABLE = true;
private db = inject(DatabaseService);
async getUserById(id: string) {
return this.db.query('SELECT * FROM users WHERE id = ?', [id]);
}
async createUser(userData: any) {
// Implementation...
}
}
// src/server/services/database.service.ts
export class DatabaseService {
static INJECTABLE = true;
private connection: any;
constructor() {
// Initialize database connection
this.connection = {}; // Example placeholder
}
async query(sql: string, params: any[] = []) {
// Implementation...
return [];
}
}
You can pass constructor parameters when registering services:
import { registerService } from '@analog-tools/inject';
class ConfigService {
static INJECTABLE = true;
constructor(private readonly apiUrl: string) {}
getApiUrl() {
return this.apiUrl;
}
}
// Register with constructor parameters
registerService(ConfigService, 'https://api.example.com');
// Now use it anywhere
const configService = inject(ConfigService);
const apiUrl = configService.getApiUrl(); // Returns 'https://api.example.com'
If you want to handle cases where a service might not be available:
import { inject } from '@analog-tools/inject';
const analytics = inject(AnalyticsService, { required: false });
if (analytics) {
analytics.trackEvent('page_view');
}
inject<T>(token: InjectionServiceClass<T>, options?: InjectOptions): T
The primary method to retrieve a service instance:
token
: The class/constructor function of the service to injectoptions
:
required
(default: true
): Whether to throw an error if the service doesn't existregisterService<T>(token: InjectionServiceClass<T>, ...properties: any[]): void
Registers a service with optional constructor parameters:
token
: The class/constructor function of the service to registerproperties
: Any typesafe constructor parameters the service requiresregisterServiceAsUndefined<T>(token: InjectionServiceClass<T>): void
Registers a service as undefined in the registry:
token
: The class/constructor function of the service to registerUseful for testing scenarios where you want to verify correct handling of missing services.
registerMockService<T>(token: InjectionServiceClass<T>, customObject: Partial<T>): void
Registers a custom implementation of a service for testing:
token
: The class/constructor function of the service to registercustomObject
: A custom object that will be used as the service instanceresetAllInjections(): void
Clears all registered services from the registry. Useful for testing to ensure a clean state between tests.
For a class to be injectable, it needs the INJECTABLE
static property:
class MyService {
static INJECTABLE = true;
// Service implementation...
}
The registerCustomServiceInstance
function accepts Partial<T>
objects, making it easy to create partial implementations with only the methods you need:
interface Logger {
info(message: string): void;
error(message: string, error?: Error): void;
debug(message: string): void;
}
class LoggerService implements Logger {
static INJECTABLE = true;
info(message: string): void { /* ... */ }
error(message: string, error?: Error): void { /* ... */ }
debug(message: string): void { /* ... */ }
}
// Only need to implement the methods you use in your tests
registerMockService(LoggerService, {
info: vi.fn(),
error: vi.fn()
// debug is optional since we're using Partial<T>
});
The @analog-tools/inject
package provides several utilities to make testing easier:
import { registerMockService, resetAllInjections } from '@analog-tools/inject';
import { describe, beforeEach, afterEach, it, expect, vi } from 'vitest';
// Service we want to test
class UserService {
static INJECTABLE = true;
private logger = inject(LoggerService);
createUser(userData: any) {
this.logger.info(`Creating user: ${userData.name}`);
// Implementation...
}
}
// Test suite
describe('UserService', () => {
// Mock logger for testing
const mockLogger = {
info: vi.fn(),
error: vi.fn()
};
beforeEach(() => {
// Register mock implementation before each test
registerMockService(LoggerService, mockLogger);
});
afterEach(() => {
// Clean up after each test
resetAllInjections();
vi.clearAllMocks();
});
it('should log user creation', () => {
const userService = inject(UserService);
userService.createUser({ name: 'Test User' });
expect(mockLogger.info).toHaveBeenCalledWith('Creating user: Test User');
});
});
resetAllInjections()
in test teardown to ensure tests don't affect each otherContributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
FAQs
Dependency injection for AnalogJS server-side applications
The npm package @analog-tools/inject receives a total of 3 weekly downloads. As such, @analog-tools/inject popularity was classified as not popular.
We found that @analog-tools/inject demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
npm author Qix’s account was compromised, with malicious versions of popular packages like chalk-template, color-convert, and strip-ansi published.
Research
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
Security News
Ruby maintainers from Bundler and rbenv teams are building rv to bring Python uv's speed and unified tooling approach to Ruby development.