### Configuration-Based Service Selection with TypeScript DI Source: https://github.com/masyaka/injecute/blob/main/README.md Illustrates how to use configuration to dynamically select which service implementation to inject. This example uses environment variables to determine whether to use a production logger or the console logger, demonstrating flexibility in service aliasing. ```typescript type Logger = { log: (logLevel: string, message: string) => void; }; const config = { useProductionLogger: process.env.USE_PRODUCTION_LOGGER === 'true', }; class LoggerUsingService { constructor(logger: Logger) {} } const container = new DIContainer() .addSingleton('productionLogger', productionLoggerFactory, []) .addInstance('console', console) .addAlias( 'logger', config.useProductionLogger ? 'productionLogger' : 'console', ) .addTransient('service', construct(LoggerUsingService), ['logger']); // service will use as logger `productionLogger` or `console` based on config.; const service = container.get('service'); ``` -------------------------------- ### Create Service Facade in TypeScript Source: https://context7.com/masyaka/injecute/llms.txt This code demonstrates how to create a proxy object that exposes container services as properties using `createProxyAccessor`. It allows for creating clean APIs that hide the container implementation. The example shows how to access services and rename them. ```typescript import { DIContainer, createProxyAccessor } from 'injecute'; const container = new DIContainer() .addSingleton('_internalConfig', () => ({ secret: 'hidden' }), []) .addSingleton('userService', () => ({ getUser: (id: number) => ({ id, name: `User ${id}` }) }), []) .addSingleton('orderService', () => ({ getOrders: (userId: number) => [{ id: 1, userId, total: 99.99 }] }), []) .addSingleton('analyticsService', () => ({ track: (event: string) => console.log(`Tracked: ${event}`) }), []); // Expose only specific services with optional renaming const api = createProxyAccessor(container, { keys: [ 'userService', 'orderService', ['analyticsService', 'analytics'] // Rename analyticsService to analytics ] }); // Access services as properties const user = api.userService.getUser(42); console.log(user); // { id: 42, name: "User 42" } const orders = api.orderService.getOrders(42); console.log(orders); // [{ id: 1, userId: 42, total: 99.99 }] api.analytics.track('page_view'); // Logs: "Tracked: page_view" // _internalConfig is not accessible through the proxy console.log((api as any)._internalConfig); // undefined ``` -------------------------------- ### addInstance: Register Existing Objects (TypeScript) Source: https://context7.com/masyaka/injecute/llms.txt Shows how to register pre-created objects or values directly into the container using addInstance. The instance is returned as-is on every get() call. It also demonstrates replacing an existing instance by using the 'replace' option. ```typescript import { DIContainer } from 'injecute'; interface DatabaseConfig { host: string; port: number; database: string; } const dbConfig: DatabaseConfig = { host: 'localhost', port: 5432, database: 'myapp' }; const container = new DIContainer() .addInstance('dbConfig', dbConfig) .addInstance('logger', console) .addInstance('appName', 'My Application'); // All instances are immediately available console.log(container.get('dbConfig').host); // 'localhost' console.log(container.get('appName')); // 'My Application' // Replace an instance (requires replace option) container.addInstance('appName', 'New App Name', { replace: true }); console.log(container.get('appName')); // 'New App Name' ``` -------------------------------- ### Listen to Container Lifecycle Events with TypeScript Source: https://context7.com/masyaka/injecute/llms.txt This snippet demonstrates how to use `addEventListener` to listen for various container lifecycle events such as 'add', 'produce', 'get', and 'reset'. It shows how to log information about service additions, factory executions, service retrievals, and cache resets. This is useful for debugging and understanding the flow of dependency injection. ```typescript import { DIContainer } from 'injecute'; const container = new DIContainer(); // Track when services are added container.addEventListener('add', ({ key, replace }) => { console.log(`Service added: ${String(key)}${replace ? ' (replaced)' : ''}`); }); // Track when services are produced (factory executed) container.addEventListener('produce', ({ key, value }) => { console.log(`Produced: ${String(key)} ->`, typeof value); }); // Track when services are retrieved container.addEventListener('get', ({ key }) => { console.log(`Retrieved: ${String(key)}`); }); // Track when cache is reset container.addEventListener('reset', ({ keys, resetParent }) => { console.log(`Reset:`, keys || 'all', resetParent ? '(including parent)' : ''); }); container .addInstance('config', { debug: true }) // Logs: "Service added: config" .addSingleton('service', () => ({ name: 'MyService' }), []); // Logs: "Service added: service" container.get('service'); // Logs: "Produced: service -> object" // Logs: "Retrieved: service" container.get('service'); // Logs: "Retrieved: service" (no produce - singleton cached) container.reset(); // Logs: "Reset: all" ``` -------------------------------- ### Apply Extension Functions with extend() in TypeScript Source: https://context7.com/masyaka/injecute/llms.txt The `extend()` method applies extension functions to register multiple services at once, promoting modularity and composition. Extensions can encapsulate related service registrations, making the container setup cleaner and more organized. ```typescript import { DIContainer, construct, IDIContainer } from 'injecute'; // Extension for logging services function addLoggingServices(config: { elkUrl?: string }) { return >(container: IDIContainer) => { return container .addSingleton('elkLogger', () => ({ log: (msg: string) => console.log(`[ELK:${config.elkUrl}] ${msg}`) }), []) .addInstance('consoleLogger', console) .addAlias('logger', config.elkUrl ? 'elkLogger' : 'consoleLogger'); }; } // Extension for database services function addDatabaseServices( container: IDIContainer ) { return container .addSingleton('connectionPool', (logger) => { logger.log('Initializing connection pool'); return { connections: 10 }; }, ['logger']) .addSingleton('userRepo', (pool) => ({ findAll: () => ({ pool: pool.connections }) }), ['connectionPool']); } // Compose extensions const container = new DIContainer() .extend(addLoggingServices({ elkUrl: 'http://elk:9200' })) .extend(addDatabaseServices); const userRepo = container.get('userRepo'); console.log(userRepo.findAll()); // { pool: 10 } ``` -------------------------------- ### Override Cached Instances for Testing in TypeScript Source: https://context7.com/masyaka/injecute/llms.txt This code illustrates how to override singleton instances in the cache for testing purposes using `setCacheInstance`. This allows you to inject mock implementations without modifying the original factories. The example shows how to replace an email service with a mock and verify its usage. ```typescript import { DIContainer, setCacheInstance } from 'injecute'; class EmailService { send(to: string, subject: string) { // Real implementation sends emails return fetch('/api/send-email', { method: 'POST', body: JSON.stringify({ to, subject }) }); } } const container = new DIContainer() .addSingleton('emailService', () => new EmailService(), []) .addSingleton('userService', (email) => ({ notifyUser: (userId: number) => { email.send(`user-${userId}@example.com`, 'Welcome!'); } }), ['emailService']); // In tests: replace emailService with a mock const mockEmailService = { sentEmails: [] as Array<{ to: string; subject: string }>, send(to: string, subject: string) { this.sentEmails.push({ to, subject }); return Promise.resolve(); } }; // Override the cached instance setCacheInstance(container, 'emailService', mockEmailService); // Now userService uses the mock const userService = container.get('userService'); userService.notifyUser(42); console.log(mockEmailService.sentEmails); // [{ to: 'user-42@example.com', subject: 'Welcome!' }] // Reset to restore original behavior container.reset(); ``` -------------------------------- ### Container Extension with Overriding Service Source: https://github.com/masyaka/injecute/blob/main/README.md This example demonstrates extending a DI container and overriding a previously registered service. A transient service 's' is initially registered with `{ x: 1 }`. The container is then extended, and within the extension, the 's' service is re-registered as transient with `{ ...s, y: 2 }`, effectively merging the previous state with new properties. The assertion verifies that the extended container provides the updated service. ```typescript const p = new DIContainer().addTransient('s', () => ({ x: 1 }), []); const c = new DIContainer(p).extend((c) => { const s = c.get('s'); return c.addTransient('s', () => ({ ...s, y: 2 }), []); }); expect(c.get('s')).to.be.eql({ x: 1, y: 2 }); ``` -------------------------------- ### Basic Dependency Injection Container Usage in TypeScript Source: https://github.com/masyaka/injecute/blob/main/README.md Demonstrates the fundamental usage of the Injecute DI container. It shows how to add services (instance, singleton, transient) and retrieve them, leveraging TypeScript's type safety. Dependencies are explicitly defined. ```typescript interface IDependency { value: number; method(): string; } class NotBasicService { constructor(srv: IDependency, logger: Logger) {} } const container = new DIContainer() .addInstance('logger', console) .addSingleton( 'myService', (): IDependency => ({ value: 42, method() { return 'The answer'; }, }), [], ) .addTransient('notBasicService', construct(NotBasicService), [ 'myService', 'logger', ]); // TS will know that notBasicService is the NotBasicService; const notBasicService = container.get('notBasicService'); assert(myDependantService instanceof NotBasicService); ``` -------------------------------- ### Preload Services with preload Source: https://context7.com/masyaka/injecute/llms.txt Demonstrates the `preload` function for pre-instantiating services in a `DIContainer`. This is useful for validating configurations and warming up caches at application startup, allowing for faster failure detection. `preload` can be used to load all services, specific services by key, or services matching a predicate function. ```typescript import { DIContainer, preload } from 'injecute'; const container = new DIContainer() .addSingleton('Feature.Auth.tokenValidator', () => { console.log('Initializing token validator'); return { validate: (t: string) => t.length > 0 }; }, []) .addSingleton('Feature.Auth.sessionManager', () => { console.log('Initializing session manager'); return { sessions: new Map() }; }, []) .addSingleton('Feature.Users.repository', () => { console.log('Initializing user repository'); return { users: [] }; }, []) .addSingleton('Core.logger', () => { console.log('Initializing logger'); return console; }, []); // Preload all services console.log('--- Preloading all services ---'); preload(container); // Preload specific services console.log('--- Preloading specific services ---'); preload(container, ['Core.logger', 'Feature.Users.repository']); // Preload services matching a predicate console.log('--- Preloading Feature.Auth services ---'); preload(container, (key) => String(key).startsWith('Feature.Auth.')); ``` -------------------------------- ### DIContainer: Create and Register Services (TypeScript) Source: https://context7.com/masyaka/injecute/llms.txt Demonstrates creating a DIContainer instance and registering services using fluent API methods like addInstance, addSingleton, and addTransient. It shows how to retrieve registered services and highlights TypeScript's type inference. ```typescript import { DIContainer } from 'injecute'; // Create a new container and register services const container = new DIContainer() .addInstance('config', { apiUrl: 'https://api.example.com', timeout: 5000 }) .addSingleton('logger', () => console, []) .addTransient('requestId', () => Math.random().toString(36), []); // Retrieve services - TypeScript knows the types const config = container.get('config'); // { apiUrl: string; timeout: number } const logger = container.get('logger'); // Console const requestId = container.get('requestId'); // string (new value each time) ``` -------------------------------- ### Wrap Async Factories with defer Source: https://context7.com/masyaka/injecute/llms.txt Shows how to use the `defer` function to wrap factory functions that have asynchronous dependencies. `defer` ensures that all Promise dependencies are awaited before the factory is executed. The resulting factory always returns a Promise, making it suitable for asynchronous service creation. This is useful for services that depend on configuration loaded asynchronously. ```typescript import { DIContainer, defer } from 'injecute'; async function fetchConfig(): Promise<{ apiKey: string }> { // Simulate async config loading return new Promise(resolve => setTimeout(() => resolve({ apiKey: 'secret-123' }), 100) ); } async function createHttpClient(config: { apiKey: string }) { return { get: async (url: string) => { console.log(`Fetching ${url} with key ${config.apiKey}`); return { data: [] }; } }; } const container = new DIContainer() .addSingleton('config', fetchConfig, []) // Returns Promise .addSingleton( 'httpClient', defer(createHttpClient), // Awaits config before creating client ['config'] ); // httpClient factory waits for config to resolve const client = await container.get('httpClient'); await client.get('/api/users'); // Logs: "Fetching /api/users with key secret-123" ``` -------------------------------- ### OOP Factory Helper Source: https://github.com/masyaka/injecute/blob/main/README.md This utility function, `useOopFactory`, simplifies the integration of OOP-style factory classes with the DI container. It takes a factory object with a `build` method and returns a function that can be used to register the factory's output as a service in the container. The factory's build method accepts arguments of type `D` and returns an instance of type `T`. ```typescript type IFactory = { build: (args: D) => T; }; function useOopFactory(factory: IFactory) { return (args: D) => factory.build(args); } container.addSingleton( 'serviceFromFactory', useOopFactory(new ConcreteFactory()), ['some D'], ); ``` -------------------------------- ### Organize Services with Namespaces in TypeScript Source: https://context7.com/masyaka/injecute/llms.txt The `namespace()` method allows for organizing services into logical groups, preventing key collisions and improving maintainability. Services within a namespace are accessed using dot notation, and dependencies can be resolved across different namespaces. ```typescript import { DIContainer } from 'injecute'; const container = new DIContainer() .addInstance('sharedConfig', { env: 'production' }) .namespace('Auth', (c) => c .addSingleton('tokenService', (config) => ({ generate: () => `token-${config.env}-${Date.now()}` }), ['sharedConfig']) .addSingleton('sessionService', (tokenService) => ({ create: () => ({ token: tokenService.generate(), createdAt: new Date() }) }), ['tokenService']) ) .namespace('Users', (c) => c .addSingleton('repository', () => ({ findById: (id: number) => ({ id, name: `User ${id}` }) }), []) .addSingleton('service', (repo, authSession) => ({ getWithSession: (id: number) => ({ user: repo.findById(id), session: authSession.create() }) }), ['repository', 'Auth.sessionService']) ); // Access via dot notation const tokenService = container.get('Auth.tokenService'); console.log(tokenService.generate()); // "token-production-1699999999999" // Access namespace container directly const authContainer = container.get('Auth'); const session = authContainer.get('sessionService').create(); console.log(session); // { token: "token-...", createdAt: Date } // Cross-namespace dependencies work seamlessly const userService = container.get('Users.service'); console.log(userService.getWithSession(42)); // { user: { id: 42, name: "User 42" }, session: { token: "...", createdAt: ... } } ``` -------------------------------- ### addSingleton: Register Services Executing Once (TypeScript) Source: https://context7.com/masyaka/injecute/llms.txt Illustrates registering a factory function that executes only once upon its first request. Subsequent calls return the cached instance. Dependencies are passed as an array of service keys. The 'construct' helper is used for class instantiation. ```typescript import { DIContainer, construct } from 'injecute'; class DatabaseConnection { constructor( private config: { host: string; port: number }, private logger: Console ) { this.logger.log(`Connecting to ${config.host}:${config.port}`); } query(sql: string) { return { rows: [], sql }; } } class UserRepository { constructor(private db: DatabaseConnection) {} findById(id: number) { return this.db.query(`SELECT * FROM users WHERE id = ${id}`); } } const container = new DIContainer() .addInstance('config', { host: 'localhost', port: 5432 }) .addInstance('logger', console) .addSingleton('database', construct(DatabaseConnection), ['config', 'logger']) .addSingleton('userRepository', construct(UserRepository), ['database']); // First call creates the instance const repo1 = container.get('userRepository'); // Logs: "Connecting to localhost:5432" // Subsequent calls return the same instance const repo2 = container.get('userRepository'); // No log - same instance returned console.log(repo1 === repo2); // true ``` -------------------------------- ### Async-First Container in TypeScript Source: https://context7.com/masyaka/injecute/llms.txt This code demonstrates the usage of `AsyncDIContainer` for handling asynchronous dependencies. It automatically awaits Promise dependencies before factory execution. All service resolutions return Promises. ```typescript import { AsyncDIContainer, construct } from 'injecute'; async function loadDatabaseConfig() { // Simulate loading config from remote source return { connectionString: 'postgres://localhost/app' }; } class DatabaseService { constructor(private config: { connectionString: string }) {} async query(sql: string) { console.log(`Executing on ${this.config.connectionString}: ${sql}`); return [{ id: 1, name: 'Result' }]; } } class UserRepository { constructor(private db: DatabaseService) {} async findAll() { return this.db.query('SELECT * FROM users'); } } const container = new AsyncDIContainer() .addSingleton('dbConfig', loadDatabaseConfig, []) // Returns Promise .addSingleton('database', construct(DatabaseService), ['dbConfig']) // Auto-awaits dbConfig .addSingleton('userRepo', construct(UserRepository), ['database']); // All gets return Promises const userRepo = await container.get('userRepo'); const users = await userRepo.findAll(); console.log(users); // [{ id: 1, name: 'Result' }] ``` -------------------------------- ### Create Pre-bound Functions with bind Source: https://context7.com/masyaka/injecute/llms.txt The `bind` method creates a function that is pre-bound to specific container dependencies. The returned function can be invoked later without needing to pass the dependencies explicitly. This is useful for creating utility functions or callbacks that require access to container services. It takes an array of dependency names and a factory function. ```typescript import { DIContainer } from 'injecute'; const container = new DIContainer() .addInstance('logger', console) .addSingleton('metrics', () => ({ increment: (name: string) => console.log(`Metric: ${name}`) }), []); // Create a bound function for later use const logAndTrack = container.bind( ['logger', 'metrics'], (logger, metrics) => (eventName: string, data: object) => { logger.log(`Event: ${eventName}`, data); metrics.increment(`event.${eventName}`); } ); // Use anywhere without container reference const handler = logAndTrack(); handler('user.login', { userId: 42 }); // Logs: "Event: user.login { userId: 42 }" // Logs: "Metric: event.user.login" ``` -------------------------------- ### Add Logging Services Extension Source: https://github.com/masyaka/injecute/blob/main/README.md The `addLoggingServices` function is an extension that allows batch registration of logging-related services into the DI container. It configures services like an ELK URL, an ELK logger, and a console logger, aliasing the 'logger' service based on the environment. This promotes modularity and clean chaining of container configurations. ```typescript function addLoggingServices(config) { return (c) => c .addSingleton('elkUrl', () => config.ELK_URL, []) .addSingleton('elkLogger', (url) => ElkLogger(url), ['elkUrl']) .addInstance('console', console) .addAlias( 'logger', config.NODE_ENV === 'production' ? 'elkLogger' : 'console', ); } container .extend(addLoggingServices(config)) .extend(addCryptoModules) .extend(addBusinessServices); ``` -------------------------------- ### Add Middleware to DIContainer with use Source: https://context7.com/masyaka/injecute/llms.txt Demonstrates adding middleware functions to the `DIContainer` using the `use` method. Middlewares can intercept service resolution for tasks like logging or validation. They receive the service key and a `next` function to proceed with resolution. The `use` method returns the container instance for chaining. ```typescript import { DIContainer } from 'injecute'; const container = new DIContainer() // Logging middleware .use(function(key, next) { const start = performance.now(); console.log(`Resolving: ${String(key)}`); const result = next(key); const duration = (performance.now() - start).toFixed(2); console.log(`Resolved: ${String(key)} in ${duration}ms`); return result; }) // Validation middleware .use(function(key, next) { const result = next(key); if (result === undefined) { console.warn(`Service "${String(key)}" resolved to undefined`); } return result; }) .addSingleton('expensiveService', () => { // Simulate expensive initialization let sum = 0; for (let i = 0; i < 1000000; i++) sum += i; return { computed: sum }; }, []); container.get('expensiveService'); // Logs: "Resolving: expensiveService" // Logs: "Resolved: expensiveService in X.XXms" ``` -------------------------------- ### addTransient: Register Services Executing Per Request (TypeScript) Source: https://context7.com/masyaka/injecute/llms.txt Demonstrates registering a factory function that creates a new instance every time it is requested. This is useful for services requiring fresh state for each usage. Dependencies are specified as an array of service keys. ```typescript import { DIContainer } from 'injecute'; interface RequestContext { requestId: string; timestamp: Date; correlationId: string; } const container = new DIContainer() .addInstance('config', { prefix: 'REQ' }) .addTransient( 'requestContext', (config): RequestContext => ({ requestId: `${config.prefix}-${Date.now()}`, timestamp: new Date(), correlationId: crypto.randomUUID() }), ['config'] ); // Each call creates a new instance const ctx1 = container.get('requestContext'); const ctx2 = container.get('requestContext'); console.log(ctx1 === ctx2); // false console.log(ctx1.requestId !== ctx2.requestId); // true ``` -------------------------------- ### Create Nested Request Containers with Middleware Source: https://github.com/masyaka/injecute/blob/main/README.md This JavaScript snippet demonstrates how to create a middleware for an Express server that generates a nested container for each incoming request. This nested container inherits services from a root container and can have its own request-specific services added, such as user ID and business services, without affecting the root container. ```javascript import { DIContainer } from './container'; import * as Express from 'express'; const server = new Express(); const rootContainer = new DIContainer() .addInstance('logger', console) .addSingleton( 'db', (logger) => { /* some db init with logger */ }, ['logger'], ); const addContainerMiddlewareCreator = (container) => (req, res, next) => { // lazy create container with lazy user resolving // this container will have access to all `rootContainer` services but adding services to this container will not modify root container req.getContainer = () => container .fork() // make nested service .addSingleton( 'userId', () => { /* get user id from req, from the auth header for example */ }, [], ) .addSingleton('user', (userId, db) => db.getUserById(userId), [ 'userId', 'db', ]) .addSingleton( 'businessService', async (user) => new MyBusinessService(await user), ['user'], ); // add other request related stuff for exapmle apm / audit based on user / business services bounded to user or request next(); }; server.use(addContainerMiddlewareCreator(rootContainer)); server.get('/api/business', (req, res) => { req .getContainer() .get('businessService') .then((srv) => { // src.user is the resolved user from some auth data req.json(srv.doMyBusiness()); }); }); ``` -------------------------------- ### Create Child Containers with fork() in TypeScript Source: https://context7.com/masyaka/injecute/llms.txt The `fork()` method creates a child container that inherits services from its parent but allows for isolated additions. This is useful for request-scoped services where child containers can be created per request, accessing parent services while maintaining their own scope. ```typescript import { DIContainer, construct } from 'injecute'; class RequestLogger { constructor(private requestId: string) {} log(msg: string) { console.log(`[${this.requestId}] ${msg}`); } } // Root container with shared services const rootContainer = new DIContainer() .addInstance('config', { dbUrl: 'postgres://localhost/app' }) .addSingleton('database', (config) => ({ url: config.dbUrl }), ['config']); // Per-request container with request-scoped services function handleRequest(requestId: string) { const requestContainer = rootContainer .fork() .addInstance('requestId', requestId) .addSingleton('logger', construct(RequestLogger), ['requestId']); // Access both parent and child services const db = requestContainer.get('database'); // From parent const logger = requestContainer.get('logger'); // From this container logger.log('Processing request'); logger.log(`Using database: ${db.url}`); return requestContainer; } handleRequest('req-001'); // Logs: "[req-001] Processing request" // Logs: "[req-001] Using database: postgres://localhost/app" handleRequest('req-002'); // Logs: "[req-002] Processing request" // Logs: "[req-002] Using database: postgres://localhost/app" ``` -------------------------------- ### Execute Functions with Dependencies using injecute Source: https://context7.com/masyaka/injecute/llms.txt The `injecute` method allows executing any function by resolving its dependencies from the container. This is useful for one-off operations or when you don't need to register a service explicitly. The function receives the resolved dependencies as arguments. It takes the function to execute and an array of dependency names. ```typescript import { DIContainer } from 'injecute'; const container = new DIContainer() .addInstance('apiUrl', 'https://api.example.com') .addInstance('authToken', 'Bearer xyz123') .addSingleton('httpClient', (apiUrl, authToken) => ({ get: async (path: string) => { const response = await fetch(`${apiUrl}${path}`, { headers: { Authorization: authToken } }); return response.json(); } }), ['apiUrl', 'authToken']); // Execute a function with container dependencies without registering it const users = await container.injecute( async (httpClient) => { return httpClient.get('/users'); }, ['httpClient'] ); // Useful for one-off operations that need container services container.injecute( (apiUrl, authToken) => { console.log(`API: ${apiUrl}`); console.log(`Auth: ${authToken.substring(0, 10)}...`); }, ['apiUrl', 'authToken'] ); ``` -------------------------------- ### Use Container Services with Express Handler Source: https://github.com/masyaka/injecute/blob/main/README.md This helper function simplifies pulling services from a DI container and injecting them into Express route handlers. It takes an array of service names and a handler creator function, returning an Express handler. Dependencies are resolved from the provided container. ```typescript import { default as Express, Handler } from 'express'; import { ArgumentsKey, DependenciesTypes, DIContainer, Func, IDIContainer, } from 'injecute'; // helper that helps to pull services from container export const useContainerServices = >(container: IDIContainer) => , H extends Func, >( servicesNames: [...Keys], handlerCreator: H, ): Handler => { return container.injecute<() => Handler, any, any>( handlerCreator, servicesNames, ); }; // business stuff service class MyBusinessService { constructor(private readonly logger: any) {} doBusinessStuff(parameter: string) { this.logger.log(parameter); return Number(parameter); } } // root app container const c = new DIContainer() .addInstance('logger', console) .addSingleton('businessService', construct(MyBusinessService), ['logger']); // handler creator bounded to your app container const useServices = useContainerServices(c); const app = Express(); // use handler on route app.use( '/api/business/stuff/:id', useServices(['businessService'], (service) => (req, res, next) => { res.json(service.doBusinessStuff(req.params.id)); }), ); app.listen(3000); c.get('logger').log('Listening at port 3000'); ``` -------------------------------- ### Create Service Aliases with addAlias Source: https://context7.com/masyaka/injecute/llms.txt The `addAlias` method creates a new service name that points to an existing registered service. This is useful for abstracting interfaces or providing configuration-based service selection. It takes the new alias name and the name of the existing service as arguments. ```typescript import { DIContainer } from 'injecute'; interface Logger { log(message: string): void; } const productionLogger: Logger = { log: (msg) => fetch('/api/logs', { method: 'POST', body: msg }) }; const devLogger: Logger = { log: (msg) => console.log(`[DEV] ${msg}`) }; const isProduction = process.env.NODE_ENV === 'production'; const container = new DIContainer() .addInstance('productionLogger', productionLogger) .addInstance('devLogger', devLogger) .addAlias('logger', isProduction ? 'productionLogger' : 'devLogger'); // 'logger' resolves to the appropriate implementation const logger = container.get('logger'); logger.log('Application started'); ``` -------------------------------- ### DI Container Middleware for Logging Source: https://github.com/masyaka/injecute/blob/main/README.md This middleware demonstrates how to intercept service resolution within a DI container. It logs a message when a new instance of a service is about to be created (i.e., when the service is not yet in the container's instances but a factory exists). The middleware has access to the container instance via `this` and uses the container's logger to output debug information before proceeding with the next step in the resolution chain. ```typescript container.use(function (key, next) { const willCreateNewInstance = !this.instances[key] && !!this.factories[key]; if (willCreateNewInstance) { this.get('logger').debug(`New instance will be created for ${key} key.`); } return next(key); }); ``` -------------------------------- ### Wrap Class Constructors with construct Source: https://context7.com/masyaka/injecute/llms.txt The `construct` utility allows class constructors to be used directly as factory functions without the `new` keyword. This is beneficial when registering classes with a DI container, simplifying the registration process. It takes a class constructor as an argument and returns a function that can be used in `addSingleton` or `addTransient`. ```typescript import { DIContainer, construct } from 'injecute'; class EmailService { constructor( private apiKey: string, private fromAddress: string ) {} async send(to: string, subject: string, body: string) { console.log(`Sending email from ${this.fromAddress} to ${to}`); return { success: true, messageId: crypto.randomUUID() }; } } class NotificationService { constructor(private emailService: EmailService) {} async notifyUser(userId: number, message: string) { return this.emailService.send( `user-${userId}@example.com`, 'Notification', message ); } } const container = new DIContainer() .addInstance('emailApiKey', 'sk-123456') .addInstance('fromAddress', 'noreply@example.com') .addSingleton('emailService', construct(EmailService), ['emailApiKey', 'fromAddress']) .addSingleton('notificationService', construct(NotificationService), ['emailService']); const notifier = container.get('notificationService'); await notifier.notifyUser(42, 'Hello!'); // Logs: "Sending email from noreply@example.com to user-42@example.com" ``` -------------------------------- ### Create Request-Scoped Containers Functionally Source: https://github.com/masyaka/injecute/blob/main/README.md This TypeScript snippet illustrates a functional approach to creating request-scoped containers using a `createRequestContainerWrapper` function. It allows defining extensions for request-specific services and then using a higher-order function to create handlers that automatically resolve required services from the dynamically created container. ```typescript export const createRequestContainerWrapper = < RootServices extends Record, RequestServices extends Record, >( container: IDIContainer, extension: IDIContainerExtension< RootServices & { req: Request }, RequestServices >, ) => { return < Keys extends readonly (keyof RequestServices)[], RequiredServices extends DependenciesTypes, >( servicesNames: [...Keys], handlerCreator: Callable, ): Handler => (req, res, next) => { const targetHandler = container // make nested service .fork() // add request to container .addInstance('req', req) // apply extension which will register services related to request context .extend(extension) // create handler using required services from container .injecute(handlerCreator, servicesNames); targetHandler(req, res, next); }; }; const rootContainer = new DIContainer().addSingleton( 'userResolvingService', construct(UserResolvingService), ['db', 'etc...'], ); const useRequestContainer = createRequestContainerWrapper( rootContainer, (c) => { return ( c // get token from request .addSingleton('authToken', (req) => req.headers['Authorization'], [ 'req', ]) // get user by token using service from root container .addSingleton( 'user', (userResolvingService, token) => userResolvingService.getUserByToken(token), ['userResolvingService', 'authToken'], ) // use user in RequestContextService constructor .addSingleton('requestContextService', RequestContextService, ['user']) ); // Add more your request related services here }, ); app.post( '/api/user-stuff', useRequestContainer( ['requestContextService'], (requestContextService) => (req, res) => { res.send(requestContextService.doUserRelatedStuff(req.body)); }, ), ); ``` -------------------------------- ### Clear DIContainer Cache with reset Source: https://context7.com/masyaka/injecute/llms.txt Explains how to clear singleton caches in `DIContainer` using the `reset` method. This forces re-creation of services on their next access, which is useful for testing or when dependencies change. The `reset` method can clear all caches or specific keys, and optionally reset parent containers. ```typescript import { DIContainer } from 'injecute'; let connectionCount = 0; const container = new DIContainer() .addInstance('config', { host: 'db1.example.com' }) .addSingleton('dbConnection', (config) => { connectionCount++; return { host: config.host, connectionId: connectionCount }; }, ['config']); // First access creates the connection const conn1 = container.get('dbConnection'); console.log(conn1); // { host: 'db1.example.com', connectionId: 1 } // Same instance returned const conn2 = container.get('dbConnection'); console.log(conn1 === conn2); // true // Update config container.addInstance('config', { host: 'db2.example.com' }, { replace: true }); // Still returns old instance (cached) console.log(container.get('dbConnection').host); // 'db1.example.com' // Reset clears the cache container.reset(); // Now gets new instance with updated config const conn3 = container.get('dbConnection'); console.log(conn3); // { host: 'db2.example.com', connectionId: 2 } // Reset specific keys only container.reset({ keys: ['dbConnection'] }); // Reset parent container too (for forked containers) container.reset({ resetParent: true }); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.