KOIN - Kotlin simple Dependency Injection Framework for JavaScript/Browser platform
—
Type-safe dependency identification and parameter injection for complex dependency graphs and runtime configuration.
Use qualifiers to differentiate between multiple instances of the same type.
interface Qualifier {
/**
* Qualifier value for unique identification
*/
value: any;
}
/**
* Create named string-based qualifier
* @param name - Qualifier name
* @returns StringQualifier instance
*/
function named(name: string): Qualifier;
/**
* Create type-based qualifier using generics
* @returns TypeQualifier instance
*/
function named<T>(): Qualifier;
/**
* Create string qualifier (alias for named)
* @param name - Qualifier name
* @returns StringQualifier instance
*/
function qualifier(name: string): Qualifier;
/**
* Create type qualifier (alias for named)
* @returns TypeQualifier instance
*/
function qualifier<T>(): Qualifier;
/**
* Short qualifier function for string
* @param name - Qualifier name
* @returns StringQualifier instance
*/
function _q(name: string): Qualifier;
/**
* Short qualifier function for type
* @returns TypeQualifier instance
*/
function _q<T>(): Qualifier;Usage Examples:
import { module, named, qualifier, _q } from "koin-core";
const databaseModule = module((builder) => {
// Multiple database connections with qualifiers
builder.single(named("primary"), () =>
new DatabaseConnection("primary-db:5432")
);
builder.single(named("secondary"), () =>
new DatabaseConnection("secondary-db:5432")
);
builder.single(named("cache"), () =>
new DatabaseConnection("redis:6379")
);
// Multiple loggers with type qualifiers
builder.single(qualifier("console"), () => new ConsoleLogger());
builder.single(qualifier("file"), () => new FileLogger("/var/log/app.log"));
// Short syntax qualifiers
builder.factory(_q("temp"), () => new TemporaryStorage());
builder.factory(_q("persistent"), () => new PersistentStorage());
});
// Using qualified dependencies
class UserService extends KoinComponent {
constructor() {
super();
this.primaryDb = this.get(named("primary")); // Primary database
this.cacheDb = this.get(named("cache")); // Cache database
this.logger = this.get(qualifier("file")); // File logger
this.storage = this.get(_q("persistent")); // Persistent storage
}
}Different qualifier implementations for various identification strategies.
/**
* String-based qualifier for named identification
*/
class StringQualifier implements Qualifier {
constructor(name: string);
value: string;
toString(): string;
}
/**
* Type-based qualifier for class-based identification
*/
class TypeQualifier implements Qualifier {
constructor(type: any);
value: any;
toString(): string;
}Usage Examples:
import { StringQualifier, TypeQualifier } from "koin-core";
// Manual qualifier creation
const primaryQualifier = new StringQualifier("primary");
const typeQualifier = new TypeQualifier(DatabaseService);
// Using in module definitions
const manualModule = module((builder) => {
builder.single(primaryQualifier, () => new PrimaryService());
builder.single(typeQualifier, () => new DatabaseService());
});
// Qualifier comparison and identification
function logQualifier(qualifier) {
console.log(`Qualifier: ${qualifier.toString()}`);
console.log(`Value: ${qualifier.value}`);
}
logQualifier(named("test")); // "Qualifier: test, Value: test"
logQualifier(named<UserService>()); // "Qualifier: UserService, Value: [class]"Pass parameters during dependency resolution for dynamic instance creation.
/**
* Create parameters holder with ordered values
* @param parameters - Parameter values in injection order
* @returns ParametersHolder for injection
*/
function parametersOf(...parameters: any[]): ParametersHolder;
/**
* Create indexed parameter array for array-like access
* @param parameters - Parameter values
* @returns ParametersHolder with indexed access
*/
function parameterArrayOf(...parameters: any[]): ParametersHolder;
/**
* Create parameter set for type-based parameter access
* @param parameters - Parameter values
* @returns ParametersHolder with type-based access
*/
function parameterSetOf(...parameters: any[]): ParametersHolder;
/**
* Create empty parameters holder
* @returns Empty ParametersHolder
*/
function emptyParametersHolder(): ParametersHolder;Usage Examples:
import {
parametersOf,
parameterArrayOf,
parameterSetOf,
emptyParametersHolder
} from "koin-core";
// Different parameter creation methods
const orderedParams = parametersOf("localhost", 5432, "myapp", true);
const arrayParams = parameterArrayOf("item1", "item2", "item3");
const typedParams = parameterSetOf(new Date(), "config", 100);
const noParams = emptyParametersHolder();
// Usage in dependency injection
class DatabaseService extends KoinComponent {
async connect(host, port, database, ssl = false) {
const connection = this.get(named("dynamic"), () =>
parametersOf(host, port, database, ssl)
);
return await connection.connect();
}
}Access and retrieve parameters within dependency definitions.
class ParametersHolder {
/**
* Get parameter by index with optional type checking
* @param index - Parameter index (0-based)
* @param clazz - Optional expected parameter type
* @returns Parameter value of type T
* @throws NoParameterFoundException if index out of bounds
*/
elementAt<T>(index: number, clazz?: new (...args: any[]) => T): T;
/**
* Get parameter by type - returns first matching type
* @returns Parameter value of type T
* @throws NoParameterFoundException if type not found
*/
get<T>(): T;
/**
* Check if parameters are empty
* @returns true if no parameters
*/
isEmpty(): boolean;
/**
* Check if parameters exist
* @returns true if parameters are present
*/
isNotEmpty(): boolean;
/**
* Get parameter count
* @returns Number of parameters
*/
size(): number;
/**
* Get parameter values as array
* @returns Array of all parameter values
*/
values(): any[];
}Usage Examples:
import { module, parametersOf } from "koin-core";
// Module with parameter-accepting definitions
const dynamicModule = module((builder) => {
builder.factory((scope, params) => {
// Access parameters by index
const host = params.elementAt(0); // string
const port = params.elementAt(1); // number
const ssl = params.elementAt(2); // boolean
return new DatabaseConnection(host, port, ssl);
});
builder.single((scope, params) => {
// Access parameters by type
const config = params.get(); // ConfigObject
const logger = params.get(); // Logger
return new ConfigurationService(config, logger);
});
builder.factory((scope, params) => {
// Parameter validation and defaults
if (params.isEmpty()) {
return new DefaultApiClient();
}
const baseUrl = params.size() > 0 ? params.elementAt(0) : "https://api.default.com";
const timeout = params.size() > 1 ? params.elementAt(1) : 5000;
return new ApiClient(baseUrl, timeout);
});
});
// Using parameters in injection
class ServiceManager extends KoinComponent {
async createDatabaseConnection(host, port, useSSL) {
const connection = this.get(named("database"), () =>
parametersOf(host, port, useSSL)
);
return connection;
}
async createConfiguredService() {
const config = new ConfigObject({ env: "production" });
const logger = new FileLogger("/var/log/service.log");
const service = this.get(named("configured"), () =>
parametersOf(config, logger)
);
return service;
}
}Use destructuring syntax for convenient parameter access.
class ParametersHolder {
/**
* Destructuring support - get first parameter
* @returns First parameter value
*/
component1<T>(): T;
/**
* Destructuring support - get second parameter
* @returns Second parameter value
*/
component2<T>(): T;
/**
* Destructuring support - get third parameter
* @returns Third parameter value
*/
component3<T>(): T;
/**
* Destructuring support - get fourth parameter
* @returns Fourth parameter value
*/
component4<T>(): T;
/**
* Destructuring support - get fifth parameter
* @returns Fifth parameter value
*/
component5<T>(): T;
}Usage Examples:
import { module, parametersOf } from "koin-core";
const destructuringModule = module((builder) => {
builder.factory((scope, params) => {
// Destructuring parameter access
const host = params.component1(); // First parameter
const port = params.component2(); // Second parameter
const database = params.component3(); // Third parameter
const username = params.component4(); // Fourth parameter
const password = params.component5(); // Fifth parameter
return new DatabaseConfig(host, port, database, username, password);
});
builder.single((scope, params) => {
// Mixed access patterns
const [baseUrl, timeout] = [params.component1(), params.component2()];
const retries = params.elementAt(2, Number); // Type-checked access
return new HttpClientConfig(baseUrl, timeout, retries);
});
});
// Usage with destructuring
class ConfigurationManager extends KoinComponent {
async setupDatabase() {
const dbConfig = this.get(named("database"), () =>
parametersOf(
"prod-db.example.com",
5432,
"production_db",
"admin",
"secure_password"
)
);
return dbConfig;
}
}Handle parameter-related errors and validation.
/**
* Exception thrown when required parameter is not found
*/
class NoParameterFoundException extends Error {
constructor(message: string);
}
/**
* Exception thrown when parameter type doesn't match expected
*/
class DefinitionParameterException extends Error {
constructor(message: string);
}Usage Examples:
import {
module,
parametersOf,
NoParameterFoundException,
DefinitionParameterException
} from "koin-core";
const validatedModule = module((builder) => {
builder.factory((scope, params) => {
try {
// Validate required parameters
if (params.isEmpty()) {
throw new DefinitionParameterException("Database parameters required");
}
if (params.size() < 2) {
throw new DefinitionParameterException("Host and port parameters required");
}
const host = params.elementAt(0);
const port = params.elementAt(1);
// Type validation
if (typeof host !== 'string') {
throw new DefinitionParameterException("Host must be string");
}
if (typeof port !== 'number') {
throw new DefinitionParameterException("Port must be number");
}
return new DatabaseConnection(host, port);
} catch (error) {
if (error instanceof NoParameterFoundException) {
console.error("Missing required parameter:", error.message);
return new DatabaseConnection("localhost", 5432); // Default fallback
}
throw error;
}
});
});
// Safe parameter access
class SafeServiceManager extends KoinComponent {
async createConnection(host, port) {
try {
const connection = this.get(named("validated"), () =>
parametersOf(host, port)
);
return connection;
} catch (error) {
if (error instanceof DefinitionParameterException) {
console.error("Parameter validation failed:", error.message);
// Handle gracefully or re-throw
throw new Error(`Failed to create connection: ${error.message}`);
}
throw error;
}
}
}/** Function type for parameter definitions */
type ParametersDefinition = () => ParametersHolder;
/** Qualifier value type - can be any type */
type QualifierValue = any;
/** String-based qualifier implementation */
class StringQualifier implements Qualifier {
value: string;
constructor(name: string);
}
/** Type-based qualifier implementation */
class TypeQualifier implements Qualifier {
value: any;
constructor(type: any);
}
/** Parameter holder for dependency injection */
class ParametersHolder {
values(): any[];
elementAt<T>(index: number, clazz?: new (...args: any[]) => T): T;
get<T>(): T;
isEmpty(): boolean;
isNotEmpty(): boolean;
size(): number;
component1<T>(): T;
component2<T>(): T;
component3<T>(): T;
component4<T>(): T;
component5<T>(): T;
}
/** Exception for missing parameters */
class NoParameterFoundException extends Error {
constructor(message: string);
}
/** Exception for parameter definition errors */
class DefinitionParameterException extends Error {
constructor(message: string);
}Install with Tessl CLI
npx tessl i tessl/maven-io-insert-koin--koin-core-js