npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@speajus/diblob

v1.0.2

Published

A dependency injection framework where the proxy (blob) is the key

Readme

diblob

A dependency injection framework where the proxy (blob) is the key. Pass around the blob, and it will act like the interface you assign to it. docs

Key Features

  • Blob as Key: The proxy itself is both the identifier and the interface
  • Automatic Dependency Resolution: Dependencies are automatically inspected and resolved
  • Reactive Dependencies: When a blob is re-registered, all dependents automatically update
  • Lifecycle Hooks: Initialize and dispose hooks for resource management
  • Async Support: Full support for async factories and async resolution
  • Container Nesting & Merging: Create child containers or merge multiple containers
  • Constructor & Property Injection: Blobs work as default parameters and property initializers
  • Type-Safe: Full TypeScript support with type inference

Installation

npm install @speajus/diblob

Quick Start

import { createBlob, createContainer } from '@speajus/diblob';

// 1. Define your interfaces
interface UserService {
  getUser(id: number): User;
}

// 2. Create blobs
const userService = createBlob<UserService>();
const logger = createBlob<Logger>();
const database = createBlob<Database>();

// 3. Create a container and register blobs with their dependencies
const container = createContainer();
container.register(logger, ConsoleLogger);
container.register(database, DatabaseImpl);
container.register(userService, UserServiceImpl, logger, database);

// 4. Use the blob directly - it acts as UserService!
const user = userService.getUser(123);

Core Concepts

Blobs

A blob is a proxy object that serves as both:

  • A unique identifier for the dependency
  • The interface/type that consumers interact with
const logger = createBlob<Logger>();
const database = createBlob<Database>();

Container

The container manages blob registrations and automatically resolves dependencies:

const container = createContainer();

// Register with a constructor (no dependencies)
container.register(logger, ConsoleLogger);

// Register with a constructor and blob dependencies
// Dependencies are automatically inspected and resolved!
container.register(userService, UserServiceImpl, logger, database);

// You can also use factory functions
container.register(config, () => new ConfigImpl());

// Factory functions can receive injected dependencies
container.register(service, (log: Logger, db: Database) => {
  return new ServiceImpl(log, db);
}, logger, database);

// Mix blob dependencies with plain values
container.register(service, ServiceImpl, logger, "production", 8080);

Reactive Dependencies

When a blob's registration changes, all dependent blobs automatically invalidate and recalculate:

// Initial registration
container.register(logger, ConsoleLogger);
container.register(userService, UserServiceImpl, logger);

// Use the service
userService.doSomething(); // Uses ConsoleLogger

// Re-register logger with new implementation
container.register(logger, FileLogger);

// userService automatically uses the new logger!
userService.doSomething(); // Now uses FileLogger

Constructor & Property Injection

Blobs can be used as default parameters in constructors or as property initializers:

// Constructor parameter with default blob
class MyService {
  constructor(private logger = loggerBlob) {}

  doSomething() {
    this.logger.log('Doing something');
  }
}

// Property initialization with blob
class AnotherService {
  private logger = loggerBlob;

  doSomething() {
    this.logger.log('Doing something');
  }
}

// Both work automatically!
const service = new MyService();
service.doSomething(); // Uses the registered logger

Async Resolution

Full support for async factories and async dependency resolution:

// Async factory
container.register(myBlob, async () => {
  const data = await fetchData();
  return new MyImpl(data);
});

// Resolve async
const instance = await container.resolve(myBlob);

// Or use directly (returns Promise)
await myBlob.someMethod();

// Async dependencies are handled automatically
class MyService {
  constructor(private asyncDep = asyncBlob) {}
}

const service = await container.resolve(MyService);

Container Nesting & Merging

Create hierarchical container structures or merge multiple containers:

// Nesting - child inherits from parent
const parent = createContainer();
parent.register(sharedBlob, SharedImpl);

const child = createContainer(parent);
child.register(childBlob, ChildImpl);

// child can resolve both sharedBlob and childBlob
// parent can only resolve sharedBlob

// Merging - combine multiple containers
const c1 = createContainer();
const c2 = createContainer();

c1.register(blob1, Impl1);
c2.register(blob2, Impl2);

const merged = createContainer(c1, c2);
// merged can resolve both blob1 and blob2
// Last parent wins for conflicts

API Reference

createBlob<T>()

Creates a new blob that acts as type T.

const service = createBlob<MyService>();

createContainer(...parents)

Creates a new DI container. Optionally accepts parent containers for nesting or merging.

// Simple container
const container = createContainer();

// Nested container (child inherits from parent)
const parent = createContainer();
const child = createContainer(parent);

// Merged containers (last parent wins for conflicts)
const container1 = createContainer();
const container2 = createContainer();
const merged = createContainer(container1, container2);

container.register<T>(blob, factory, ...deps)

Registers a blob with a factory/constructor and its dependencies.

Parameters:

  • blob: The blob to register
  • factory: Constructor or factory function
  • ...deps: Dependencies to inject (blobs are auto-resolved, plain values passed as-is)
    • Last argument can be { lifecycle: Lifecycle } for options
// With constructor and dependencies
container.register(myService, MyServiceImpl, logger, database);

// With factory function
container.register(config, () => new ConfigImpl());

// With lifecycle option
container.register(
  myService,
  MyServiceImpl,
  logger,
  { lifecycle: Lifecycle.Transient }
);

container.resolve<T>(blobOrConstructor)

Manually resolve a blob or class constructor to its instance.

For blobs (usually not needed - just use the blob directly):

const instance = container.resolve(myService);

For unregistered classes (automatically detects and resolves blob default parameters):

class MyClass {
  constructor(private service = myBlob) {}
}

// MyClass is NOT registered as a blob, but container.resolve still works!
const instance = await container.resolve(MyClass);

// How it works:
// 1. Container tracks blob accesses during constructor execution
// 2. Each blob pushes itself into a singleton tracking array
// 3. Container resolves those blobs and handles async dependencies
// 4. Returns the instantiated class with all dependencies resolved

Async resolution:

// Async factory
container.register(myBlob, async () => new MyImpl());

// Resolve returns a Promise
const instance = await container.resolve(myBlob);

// Or use the blob directly (returns Promise)
await myBlob.someMethod();

container.has<T>(blob)

Check if a blob is registered.

if (container.has(myService)) {
  // ...
}

container.unregister<T>(blob)

Unregister a blob.

container.unregister(myService);

container.clear()

Clear all registrations.

container.clear();

Lifecycle

Singleton (default)

Creates one instance and reuses it:

container.register(service, () => new ServiceImpl());
// or explicitly:
container.register(service, () => new ServiceImpl(), {
  lifecycle: Lifecycle.Singleton
});

Transient

Creates a new instance every time:

container.register(service, () => new ServiceImpl(), {
  lifecycle: Lifecycle.Transient
});

Comparison with pbj

| Feature | pbj | diblob | |---------|-----|--------| | Key | Separate token/key | The blob itself | | Usage | container.resolve(key) | Use blob directly | | Type safety | Token must match type | Blob IS the type | | Reactivity | Manual | Automatic |

License

MIT