The registry is a global singleton that manages the lifecycle of state container instances. It handles creation, sharing, ref counting, and disposal. Every signature on this page is quoted from the @blac/core source.
Think of the registry as a lending library for bloc instances:
Each (class, instanceKey) pair is one book on the shelf. Ask for the same pair twice and you get the same physical book — that is how two components share state without prop-drilling.
A consumer that needs a book checks it out (acquire) and returns it (release) when done. Every checkout is a reference (a “ref”).
The library keeps a count of who has each book checked out. When the count drops to zero, the book is discarded (dispose) — its state is gone and any timers or subscriptions it set up are torn down.
keepAlive marks a book as a permanent reference copy: it stays on the shelf even at zero checkouts.
Two consequences fall straight out of this model:
Sharing is automatic. The first acquire of a key creates the instance; every later acquire of the same key reuses it. You never wire instances together by hand.
Cleanup is automatic — but only if every checkout is returned. A missing release is like never returning a library book: the count never reaches zero, the instance never disposes, and its memory (and subscriptions) leak.
The state-container class to acquire an instance of.
opts.args
ExtractArgs<T>
no
Serializable construction data passed to init(args). Derives the instance key.
opts.refId
string
no
Caller-supplied ref identifier. Used by useBloc to pair with release. Rarely set manually.
Returns:InstanceType<T> — the live instance (newly created or existing).
Behavior. The instance key is derived from opts.args (static key if declared, else the structural hash, else 'default'). If no instance exists for the resolved key, acquire creates one, calls init(args), and fires the internal 'created' event. If an instance already exists, it is returned as-is. The ref count is always incremented. Every acquire must have a matching release — a missing release is the canonical instance leak. In React, useBloc owns this pair; call acquire directly only in server-side or scripting contexts.
Acquire an instance with ref tracking (ownership semantics). Instance
identity is derived purely from args (via the class's static key(args),
a structural hash of args, or the default sentinel when there are none).
@param ― BlocClass - The StateContainer class constructor
@param ― opts.args - Construction/identity args; derives the instance key
@param ― opts.refId - Named reference ID for debugging; auto-generated if omitted
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Acquire an instance with ref tracking (ownership semantics). Instance
identity is derived purely from args (via the class's static key(args),
a structural hash of args, or the default sentinel when there are none).
@param ― BlocClass - The StateContainer class constructor
@param ― opts.args - Construction/identity args; derives the instance key
@param ― opts.refId - Named reference ID for debugging; auto-generated if omitted
acquire(
classCounterCubit
CounterCubit);
const counter:CounterCubit
counter.
CounterCubit.increment: ()=>void
increment();
var console:Console
console.
Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
Construction data for init(args) and key derivation.
Returns:InstanceType<T> — the live instance.
Behavior. Like acquire, but takes no ref — the instance can be disposed by another caller while you hold the returned reference. Use ensure inside other cubits (via depend()) or in tooling that only needs the instance to exist without pinning its lifecycle. Because no ref is taken, no matching release is needed.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<{
userId: string |null
userId:string|null }> {
constructor() {
super({
userId: string |null
userId: null });
}
}
// ensure creates AuthCubit if it's not alive yet, but does not keep it alive
Args identifying the instance. Defaults to the 'default' key.
Returns:InstanceType<T> — the live instance.
Behavior. Does not create the instance if it is absent; throws an Error instead. Use borrow when the absence of an instance is a programming error — it makes the failure loud and immediate rather than silently returning undefined. No ref is taken, so no release is needed.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<{
count: number
count:number }> {
constructor() {
super({
count: number
count: 0 });
}
}
// Safe read-only access — no acquire/release pair needed
Args identifying the instance. Defaults to the 'default' key.
Returns:{ error: null; instance: InstanceType<T> } when the instance exists, or { error: Error; instance: null } when it does not.
Behavior. Identical to borrow but returns a discriminated union instead of throwing. Prefer borrowSafe in code paths where absence is expected (e.g. optional integrations) and borrow where absence is always a bug.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
The args used when acquire was called. Must resolve to the same key.
opts.refId
string
no
The ref identifier passed to acquire. Rarely set manually.
opts.forceDispose
boolean
no
When true, dispose immediately even if keepAlive is set. Default false.
Returns:void.
Behavior.release is idempotent for an already-dropped ref — releasing more times than you acquired won’t throw, it just no-ops once the count is gone. The args you release with must resolve to the same key you acquired with; a mismatch leaves the original ref dangling forever. At ref count zero, the instance is disposed synchronously (unless keepAlive is set or forceDispose is false).
Acquire an instance with ref tracking (ownership semantics). Instance
identity is derived purely from args (via the class's static key(args),
a structural hash of args, or the default sentinel when there are none).
@param ― BlocClass - The StateContainer class constructor
@param ― opts.args - Construction/identity args; derives the instance key
@param ― opts.refId - Named reference ID for debugging; auto-generated if omitted
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Acquire an instance with ref tracking (ownership semantics). Instance
identity is derived purely from args (via the class's static key(args),
a structural hash of args, or the default sentinel when there are none).
@param ― BlocClass - The StateContainer class constructor
@param ― opts.args - Construction/identity args; derives the instance key
@param ― opts.refId - Named reference ID for debugging; auto-generated if omitted
To manage distinct named instances, declare an Args shape and pass args to any registry function. The instance key is derived from those args — a static key if the class declares one, otherwise the structural hash of the args:
Acquire an instance with ref tracking (ownership semantics). Instance
identity is derived purely from args (via the class's static key(args),
a structural hash of args, or the default sentinel when there are none).
@param ― BlocClass - The StateContainer class constructor
@param ― opts.args - Construction/identity args; derives the instance key
@param ― opts.refId - Named reference ID for debugging; auto-generated if omitted
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Acquire an instance with ref tracking (ownership semantics). Instance
identity is derived purely from args (via the class's static key(args),
a structural hash of args, or the default sentinel when there are none).
@param ― BlocClass - The StateContainer class constructor
@param ― opts.args - Construction/identity args; derives the instance key
@param ― opts.refId - Named reference ID for debugging; auto-generated if omitted
Acquire an instance with ref tracking (ownership semantics). Instance
identity is derived purely from args (via the class's static key(args),
a structural hash of args, or the default sentinel when there are none).
@param ― BlocClass - The StateContainer class constructor
@param ― opts.args - Construction/identity args; derives the instance key
@param ― opts.refId - Named reference ID for debugging; auto-generated if omitted
Identity precedence: own args (via static key(args), else structural hash of args) > <BlocProvider> context args > 'default'.
The resolved key is the registry’s single source of truth — acquire and release both run their args through the same resolution, so a ref taken under a given args key is dropped under the same key. See Passing Inputs for the full model and Configuration for static key.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Acquire an instance with ref tracking (ownership semantics). Instance
identity is derived purely from args (via the class's static key(args),
a structural hash of args, or the default sentinel when there are none).
@param ― BlocClass - The StateContainer class constructor
@param ― opts.args - Construction/identity args; derives the instance key
@param ― opts.refId - Named reference ID for debugging; auto-generated if omitted
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Acquire an instance with ref tracking (ownership semantics). Instance
identity is derived purely from args (via the class's static key(args),
a structural hash of args, or the default sentinel when there are none).
@param ― BlocClass - The StateContainer class constructor
@param ― opts.args - Construction/identity args; derives the instance key
@param ― opts.refId - Named reference ID for debugging; auto-generated if omitted
acquire(
classCounterCubit
CounterCubit);
const counter:CounterCubit
counter.
CounterCubit.increment: ()=>void
increment();
var console:Console
console.
Console.log(...data: any[]): void
The console.log() static method outputs a message to the console.
Symptom: A component unmounts and remounts and finds the bloc in its initial state — any previous state is gone.
Cause: When the last consumer releases, the instance is disposed immediately (ref count 0 → dispose). The next consumer acquires a brand-new instance with fresh state.
Fix: If state must persist across unmounts, mark the class keepAlive:
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<
interface SessionState
SessionState> {
constructor() {
super({
SessionState.token: string |null
token: null });
}
}
keepAlive instances are never auto-disposed at ref count 0 — tear them down explicitly with release(Class, { forceDispose: true }) or clear(Class) in teardown. See Configuration.
Symptom: DevTools shows an instance that should have been disposed is still alive, or ref count never returns to 0.
Cause: An acquire outside React has no matching release. Every call to acquire increments the ref count; without a matching release the count never reaches zero.
Fix: Pair every acquire with a release, ideally in a try/finally. If you only need to read without owning the lifecycle, use borrow or ensure — neither takes a ref:
Acquire an instance with ref tracking (ownership semantics). Instance
identity is derived purely from args (via the class's static key(args),
a structural hash of args, or the default sentinel when there are none).
@param ― BlocClass - The StateContainer class constructor
@param ― opts.args - Construction/identity args; derives the instance key
@param ― opts.refId - Named reference ID for debugging; auto-generated if omitted
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks (see A2 audit).
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). If a caller
needs the old "skip if no real change" patch semantics, they can wrap
patch themselves or call emit after a manual equality check.
Acquire an instance with ref tracking (ownership semantics). Instance
identity is derived purely from args (via the class's static key(args),
a structural hash of args, or the default sentinel when there are none).
@param ― BlocClass - The StateContainer class constructor
@param ― opts.args - Construction/identity args; derives the instance key
@param ― opts.refId - Named reference ID for debugging; auto-generated if omitted
Symptom:acquire throws with a message like “exceeded the maximum live instances” or “exceeded the maximum refs.”
Cause: Almost always a leak — an unstable instance key (non-serializable args) spawning endless instances, or a missing release accumulating refs. The limit is not too low; the leak is real.
Fix: Find and fix the leak before raising the limit. Common causes: non-serializable values in args (fix: move to deps), acquire without release (fix: pair them), args that differ structurally each render (fix: normalise types). See Configuration: circuit breakers and Troubleshooting.