JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 2
  • Score
    100M100P100Q38365F
  • License MIT

A lightweight and type-safe dependency injection library for JS/TS, designed to simplify dependency management

Package Exports

  • @fioc/core

Readme

FIOC

FIOC (Functional Inversion Of Control) is a lightweight dependency injection library for JS/TS applications. It simplifies the management of dependencies by providing a flexible and type-safe way to define, register, and resolve dependencies, without the need of reflection or decorators.

Features

  • ðŸŠķ Lightweight: Zero dependencies except for Immer, designed to integrate seamlessly into your existing projects
  • 🔒 Type-safe by design: Get compile-time validation of your dependency tree
  • ðŸŽŊ No Type Casting: Dependencies are automatically resolved to their correct types
  • ðŸ›Ąïļ Compile-time Validation: Catch dependency registration errors before running your app
  • 🏗ïļ Builder Pattern: Fluent API for registering and managing dependencies
  • 🔄 Immutable: Container state is immutable for safe concurrent usage
  • 🔌 Universal: Works in both front-end and back-end environments
  • ðŸŽŪ Flexible Factory System: Support for value registration, factory functions, and class constructors
  • ðŸ§Đ Modular Design: Merge containers and switch between different configurations easily

Jump to Basic Usage →

Table of Contents

Installation

Install the library using npm, pnpm or yarn:

npm install fioc
pnpm install fioc
yarn add fioc

Basic Usage

Creating Tokens

First, create tokens for your dependencies using the createDIToken function. The token is a type-safe identifier for your dependency:

import { createDIToken } from "fioc";

interface ApiService {
  getData: () => string;
}

const ApiServiceToken = createDIToken<ApiService>().as("ApiService");

Registering & Resolving

Register your implementations using the container builder and resolve them when needed:

import { buildDIContainer } from "fioc";
import { ApiService, ApiServiceToken } from "./interfaces/ApiService";

const HttpApiService: ApiService = {
  getData: () => "Hello, World!",
};

// Register dependencies
const container = buildDIContainer()
  .register(ApiServiceToken, HttpApiService)
  .getResult();

// Resolve dependencies
const apiService = container.resolve(ApiServiceToken);
apiService.getData(); // "Hello, World!"

Advanced Usage

Factories

Factories are functions that construct values based on other dependencies. Common use cases include creating use cases that depend on services or repositories:

import { ApiServiceToken } from "./interfaces/ApiService";

// Define a factory and its token
export const getDataUseCaseFactory =
  (apiService: ApiService) => (ids: string[]) =>
    apiService.getData(ids);

export const getDataUseCaseToken =
  createDIToken<ReturnType<typeof getDataUseCaseFactory>>().as(
    "getDataUseCase"
  );

// Register the factory with its dependencies
container.registerFactory({
  dependencies: [ApiServiceToken],
  token: getDataUseCaseToken,
  factory: getDataUseCaseFactory,
});

// Resolve and use the factory
const getDataUseCase = container.resolve(getDataUseCaseToken); // Type infers as (ids: string[]) => Promise<string>
getDataUseCase(); // Calls apiService.getData()

Class Factories

You can also use classes with FIOC. The constructorToFactory helper converts class constructors to factory functions:

import { constructorToFactory } from "fioc";

export class GetDataUseCase {
  constructor(private apiService: ApiService) {}
  execute = () => this.apiService.getData();
}

export const getDataUseCaseToken =
  createDIToken<GetDataUseCase>().as("getDataUseCase");

container.registerFactory({
  dependencies: [ApiServiceToken],
  token: getDataUseCaseToken,
  factory: constructorToFactory(GetDataUseCase),
});

Type-Safe Container Features

For enhanced type safety, use buildStrictDIContainer. It provides compile-time validation of your dependency tree:

import { buildStrictDIContainer } from "fioc";

const container = buildStrictDIContainer()
  // Error if token already registered
  .register(ApiServiceToken, HttpApiService)

  // Error if dependencies not registered
  .registerFactory({
    dependencies: [ApiServiceToken],
    token: useCaseToken,
    factory: myFactory,
  })

  // Safe replacement of existing registrations
  .replace(ApiServiceToken, newImplementation)
  .replaceFactory({
    dependencies: [NewApiServiceToken],
    token: useCaseToken,
    factory: newFactory,
  })
  .getResult();

Container Manager

The Container Manager allows you to manage multiple containers - useful for different environments or testing:

import { buildDIManager } from "fioc";

const manager = buildDIManager()
  .registerContainer(productionContainer, "prod")
  .registerContainer(testContainer, "test")
  .getResult()
  .setDefaultContainer("prod");

// Get the active container
const container = manager.getContainer();

// Switch containers
manager.setActiveContainer("test");

Use cases for Container Manager:

  • Managing different environments (production vs development)
  • Switching between online/offline implementations
  • Testing with mock implementations

Back to Top ↑

Contributing

Contributions are welcome! Feel free to open issues or submit pull requests on GitHub.

License

This library is licensed under the MIT License. See the LICENSE file for details.