@molecule/app-bond

Infrastructure · bond · App (browser) · v1.0.3 · Apache-2.0

Provider wiring mechanism for app-side packages

npm install @molecule/app-bond

npm · Source on GitHub

How it works

@molecule/app-bond is a infrastructure package for the app (browser) side (bond).

import { bond, get, require as bondRequire, isBonded } from '@molecule/app-bond'
import { provider as routingProvider } from '@molecule/app-routing-react-router'
import type { StateProvider } from '@molecule/app-state'
import { provider as stateProvider } from '@molecule/app-state-zustand'
import type { ThemeProvider } from '@molecule/app-theme'
import { provider as themeProvider } from '@molecule/app-theme-css-variables'

// Singleton providers (one per category)
bond('state', stateProvider)
bond('theme', themeProvider)

// Named providers (multiple per category)
bond('routing', 'react', routingProvider)

// Retrieve providers
const state = get<StateProvider>('state') // undefined if not bonded
const theme = bondRequire<ThemeProvider>('theme') // throws if not bonded
const router = get('routing', 'react')

// Check before using
if (isBonded('routing', 'react')) {
  // ...
}

Reference

Runtime provider wiring system for molecule.dev app-side packages.

Enables swappable providers for state management, theming, routing, styling, and any custom category — all through dynamic string-based keys. Most app cores' own setup helpers (setFont, setRouter, setClassMap, each setProvider) delegate INTO this registry, so bond('theme', p) and @molecule/app-theme's setProvider(p) write the same slot — use either.

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Quick Start

import { bond, get, require as bondRequire, isBonded } from '@molecule/app-bond'
import { provider as routingProvider } from '@molecule/app-routing-react-router'
import type { StateProvider } from '@molecule/app-state'
import { provider as stateProvider } from '@molecule/app-state-zustand'
import type { ThemeProvider } from '@molecule/app-theme'
import { provider as themeProvider } from '@molecule/app-theme-css-variables'

// Singleton providers (one per category)
bond('state', stateProvider)
bond('theme', themeProvider)

// Named providers (multiple per category)
bond('routing', 'react', routingProvider)

// Retrieve providers
const state = get<StateProvider>('state') // undefined if not bonded
const theme = bondRequire<ThemeProvider>('theme') // throws if not bonded
const router = get('routing', 'react')

// Check before using
if (isBonded('routing', 'react')) {
  // ...
}

Type

infrastructure

Installation

npm install @molecule/app-bond

API

Interfaces

Provider

Minimal base interface that every bondable provider can optionally satisfy. Provider implementations may include a name for logging and debugging but are not required to extend this interface.

interface Provider {
  /**
   * Optional human-readable identifier for this provider, used in log
   * output and error messages.
   */
  readonly name?: string
}

ProviderRegistry

Internal data structure that holds all bonded providers at runtime. The registry distinguishes between singleton providers (one per category) and named providers (multiple per category, keyed by a string name).

interface ProviderRegistry {
  /**
   * Map from category string (e.g. `'state'`, `'theme'`) to the single
   * active provider instance for that category.
   */
  singletons: Map<string, unknown>

  /**
   * Two-level map: outer key is the category string (e.g. `'routing'`),
   * inner key is the provider name (e.g. `'react'`), value is the provider instance.
   */
  named: Map<string, Map<string, unknown>>
}

BondConfig

Options that control bond registration behavior.

interface BondConfig {
  /**
   * When `true`, bonding a provider to a category that already has a bonded
   * provider throws an error instead of silently replacing it.
   * @default false
   */
  strict?: boolean

  /**
   * When `true`, every bond / unbond / clear / reset operation emits a
   * diagnostic line via `console.debug` (prefixed `[app-bond]`). Useful for
   * tracing wiring order at startup; leave `false` in production.
   * @default false
   */
  verbose?: boolean
}

Functions

bond(type, provider)

Registers a provider at runtime. With two arguments, bonds a singleton provider for the category. With three arguments, bonds a named provider under the category.

function bond(type: string, provider: unknown): void
  • type — The provider category (e.g. 'state', 'theme', 'routing').
  • provider — The provider instance to bond as a singleton.

get(type)

Retrieves a bonded singleton provider by category, or undefined if not bonded.

function get(type: string): T | undefined
  • type — The provider category to look up.

Returns: The bonded provider instance, or undefined if not bonded.

getAll(type)

Retrieves all named providers bonded under a category. Returns an empty Map if no named providers exist for the category.

function getAll(type: string): Map<string, T>
  • type — The provider category to look up.

Returns: A Map from provider name to provider instance.

require(type)

Retrieves a bonded singleton provider, throwing if not bonded. Use this when the provider is required for the application to function.

function require(type: string): T
  • type — The provider category to look up.

Returns: The bonded provider instance.

unbond(type)

Removes a bonded singleton provider from a category.

function unbond(type: string): boolean
  • type — The provider category to unbond.

Returns: true if a provider was removed, false if none was bonded.

unbondAll(type)

Removes all providers (both singleton and named) for a category.

function unbondAll(type: string): void
  • type — The provider category to clear entirely.

isBonded(type)

Checks whether a singleton provider is bonded for a category.

function isBonded(type: string): boolean
  • type — The provider category to check.

Returns: true if a singleton provider is bonded for the category.

configure(newConfig)

Merges new configuration options into the current bond configuration.

function configure(newConfig: Partial<BondConfig>): void
  • newConfig — Partial configuration to merge. Unspecified fields retain their current values.

bondSingleton(type, provider)

Registers a singleton provider for the given category. If provider is null/undefined, the existing singleton for that category is removed instead. In strict mode, throws if a provider is already bonded to the category.

function bondSingleton(type: string, provider: T): void
  • type — The provider category (e.g. 'state', 'theme', 'routing').
  • provider — The provider instance to bond, or null/undefined to remove.

getSingleton(type)

Retrieves the singleton provider for a category, or undefined if none is bonded.

function getSingleton(type: string): T | undefined
  • type — The provider category to look up.

Returns: The bonded provider instance cast to T, or undefined if not bonded.

requireSingleton(type)

Retrieves the singleton provider for a category, throwing if none is bonded. Use this when the provider is required for the application to function.

The error distinguishes the two ways this fails, because they need different fixes: if nothing has been bonded yet the accessor ran BEFORE the app's bond setup (a module-level call, which executes at import time — move it into a function/hook/effect), while if other categories are bonded the setup ran and this one was simply never wired. Both messages name the calling module.

function requireSingleton(type: string): T
  • type — The provider category to look up.

Returns: The bonded provider instance cast to T.

bondNamed(type, name, provider)

Registers a named provider under a category. Named providers allow multiple implementations per category (e.g. bond('routing', 'react', reactProvider)). In strict mode, throws if that name is already bonded in the category.

function bondNamed(type: string, name: string, provider: T): void
  • type — The provider category (e.g. 'routing', 'oauth').
  • name — The unique name within the category (e.g. 'react', 'github').
  • provider — The provider instance to bond.

getNamed(type, name)

Retrieves a named provider from a category, or undefined if not bonded.

function getNamed(type: string, name: string): T | undefined
  • type — The provider category to look up.
  • name — The provider name within the category.

Returns: The bonded provider instance cast to T, or undefined if not found.

getAllNamed(type)

Retrieves all named providers bonded under a category as a Map keyed by provider name. Returns an empty Map if no providers are bonded for the category.

function getAllNamed(type: string): Map<string, T>
  • type — The provider category to look up.

Returns: A Map from provider name to provider instance for the given category.

requireNamed(type, name)

Retrieves a named provider from a category, throwing if not bonded. Use this when the provider is required for the application to function.

Like requireSingleton, the error says whether the call ran before the app's bond setup or whether the setup ran without wiring this provider, and names the calling module.

function requireNamed(type: string, name: string): T
  • type — The provider category to look up.
  • name — The provider name within the category.

Returns: The bonded provider instance cast to T.

unbondSingleton(type)

Removes a singleton provider from a category.

function unbondSingleton(type: string): boolean
  • type — The provider category to unbond.

Returns: true if a provider was removed, false if none was bonded.

unbondNamed(type, name)

Removes a named provider from a category.

function unbondNamed(type: string, name: string): boolean
  • type — The provider category containing the named provider.
  • name — The provider name to remove.

Returns: true if the provider was removed, false if it was not found.

clearProviders(type)

Removes all providers (both singleton and named) for a given category.

function clearProviders(type: string): void
  • type — The provider category to clear.

reset()

Removes all bonded providers across all categories, restoring the registry to its initial empty state. Primarily used in test teardown.

function reset(): void

Constants

registry

Global provider registry instance shared across all bond operations. Contains both singleton providers (one per category) and named providers (multiple per category, keyed by name).

const registry: ProviderRegistry

config

Current bond configuration controlling strict mode and verbosity.

const config: BondConfig

Injection Notes

  • Wire before ANY module evaluates a bond-backed accessor. get() returns undefined and require() THROWS until a provider is bonded — and app modules are imported (module top-level runs) BEFORE your setupProviders()/bonds/index.ts executes. A module-scope const cm = getClassMap() or const router = require('routing') in a component file therefore runs pre-wiring and breaks the app at load. Call accessors inside components/functions, never at module top-level. The thrown error says WHICH case it is — "ran before the app's bonds were set up" (nothing bonded yet: this call is at module scope, so move it into a function/hook/effect or import the module after setupProviders()), or "the app's bond setup has already run" (other categories are bonded and this one was never wired: add bond(...) to the file that defines setupProviders()) — and names the calling module from the stack.
  • All cores now read this registry. Every @molecule/app-* core routes its setProvider()/getProvider() through this bond registry (the ai-*, audio, color-picker, date-range-picker, gallery, image-crop, keyboard-shortcuts, markdown, stepper, timeline, tour, tree-view, battery, bluetooth, brightness, nfc, and screen-orientation cores were migrated off their old module-local singletons), so bond('<category>', p) and the core's own setProvider() write the same slot. Use either.
  • Re-bonding a category silently replaces the provider (last bond wins). Call configure({ strict: true }) to make double-bonding throw instead. bond(type, null) / bond(type, undefined) REMOVES the singleton for that category. configure({ verbose: true }) logs each bond / unbond / clear / reset to console.debug (prefixed [app-bond]) for wiring-order tracing.
  • require collides with CommonJS — import it renamed: import { require as bondRequire } from '@molecule/app-bond'.