@on-the-ground/effect-dependency
v0.0.3
Published
> ๐ฅย **Dynamically route effectful calls to matching dependencies โ at runtime, with role-based duck typing.**\ > This is not your typical DI. This is actor-style delegation.
Readme
@on-the-ground/effect-dependency
๐ฅย Dynamically route effectful calls to matching dependencies โ at runtime, with role-based duck typing.
This is not your typical DI. This is actor-style delegation.
โจ Features
- ๐ Message-based delegation of effectful function calls
- ๐ฅ Duck-typed role matching via quackquack
- ๐ Fallback delegation chain for unhandled messages
- ๐ญ No need to wire static interfaces or inject services โ just declare a role and call it
๐ Quick Example
import {
withDependencyEffectHandler,
dependencyEffect,
} from "@on-the-ground/effect-dependency";
import { quackable } from "@on-the-ground/quackquack";
import { withSignal, emptyContext } from "@on-the-ground/effect";
const greeter = {
greet: quackable("(name: string) => string")((name) => `Hello, ${name}!`),
};
const pctx = withSignal(new AbortController().signal, emptyContext);
await withDependencyEffectHandler(pctx, [greeter], async (ctx) => {
const result = await dependencyEffect(ctx, {
role: { greet: "(name: string) => string" },
call: { name: "greet", args: ["Juno"] },
});
console.log(result); // => "Hello, Juno!"
});๐ง Concept
This is not a DI container.
This is a message-routing system based on duck-typed roles.
It answers the question:
โWhoever can handle this message โ please do.โ
The Delegation Chain
You describe the role you're trying to call:
role: { greet: "(name: string) => string"; }You issue a call into the effect system:
call: { name: "greet", args: ["Alice"] }Any dependency that matches the shape and name will be called.
If no match is found locally, the message bubbles up to the parent context (if available).
๐งช Testing Example
it("delegates to matching dependency", async () => {
const greeter = {
greet: quackable("(name: string) => string")((n) => `Hi ${n}`),
};
const pctx = withSignal(new AbortController().signal, emptyContext);
await withDependencyEffectHandler(pctx, [greeter], async (ctx) => {
const result = await dependencyEffect(ctx, {
role: { greet: "(name: string) => string" },
call: { name: "greet", args: ["Jo"] },
});
expect(result).toBe("Hi Jo");
});
});๐๏ธ When to Use This
โ
You want flexible message passing instead of tight coupling
โ
You want actor-style delegation across contexts
โ
You want role-based resolution rather than static DI graphs
โ
You want to support plugin-like dependency injection with fallback chaining
๐ฆ Installation
yarn add @on-the-ground/effect-dependencyThis package depends on:
@on-the-ground/effect@on-the-ground/quackquack
Make sure they're installed if you're using raw effect contexts.
๐ฌ Philosophy
Most dependency injection systems are about wiring who to call.
This system is about describing what kind of role should be fulfilled โ and letting the system find someone who can.
Think of it like actor delegation or post office routing:
If you match the signature, you get the message.
๐ ๏ธ API
withDependencyEffectHandler(ctx, dependencies, thunk)
Wraps a context with a handler that intercepts dependencyEffect calls and tries to match them to the given dependencies.
dependencyEffect(ctx, payload)
Performs a role-based message call.
Returns the result of the matched method, or null if not handled.
type Payload<Messages extends string = string> = {
role: { [K in Messages]: QuackDSL };
call: {
name: Messages;
args: unknown[];
};
};๐ Related Packages
@on-the-ground/effect: Core effect handling system@on-the-ground/quackquack: Structural type matcher for duck-typed roles
๐งฐ Designed for the effect-ive-* ecosystem
This package is part of the effect-ive programming family โ a new way to write real-world code with effects, not mocks.
