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

@medamajs/compose

v0.0.9

Published

Composable state management extension for medama with support for hierarchical state layers, dynamic composition, and parallel state synchronization

Readme

@medamajs/compose

@medamajs/compose extends medama's capabilities by enabling composition of multiple state layers into a cohesive unit. It maintains all the original guarantees while allowing for sophisticated state hierarchies.

Installation

npm i @medamajs/compose

Basic Usage

The main function composeMedama allows you to combine multiple medama states:

const { readState, subscribeToState, setState, pupil } = composeMedama(
  {
    layer1: createMedama<Layer1State>(),
    layer2: createMedama<Layer2State>(),
  },

  {
    layer1: { /* initial state */ },
    layer2: { /* initial state */ }
  }
);

State Layers

Each layer in a composite state maintains its independence while being part of a larger state structure. Layers can be accessed and modified individually or as part of the whole:

const selector = (state: { layer1: Layer1State; layer2: Layer2State }) => ({
  ...state.layer1,
  ...state.layer2
});

subscribeToState(selector, (value) => {
  // Reacts to changes in either layer
  console.log(value);
});

Dynamic Layer Management

Layers can be added or removed dynamically:

const { addLayers, deleteLayers } = composeMedama(/* ... */);

// Add new layers
const extendedState = addLayers({
  layer3: createMedama<Layer3State>()
});

// Remove layers
const reducedState = deleteLayers('layer2');

/**
 * Can also pass array of layer names
 *
 * const reducedState = deleteLayers(['layer1', 'layer2']);
*/

Nested Composition

States can be composed hierarchically to create complex state structures:

const nestedState = composeMedama({
  outer: composeMedama({
    inner1: createMedama<InnerState1>(),
    inner2: createMedama<InnerState2>()
  }),

  sibling: createMedama<SiblingState>()
});

Performance Optimization

The composition maintains medama's performance characteristics:

  • Efficient memoization of selector results
  • Targeted subscription updates
  • Garbage collection of unused selectors
  • Smart dependency tracking through the layer hierarchy

Note that state objects passed to selectors and setters maintain the same safety guarantees as the base medama implementation, preventing unauthorized state access outside of designated operations.

Powerful Subscription Behavior

Unlike the original medama, composite states allow state changes during subscription callbacks, both when the subscription is initially created and when it's triggered by state updates. This opens up powerful capabilities like:

  • Self-adjusting states
  • Automatic state synchronization
  • Reactive state chains
  • State-based finite state machines

Here's an example of a self-adjusting counter:

const { subscribeToState, setState } = composeMedama({
  counter: createMedama<{ value: number }>(),
}, {
  counter: { value: 0 }
});

subscribeToState(
  (state) => state.counter.value,

  (value) => {
    // Safe to call setState here
    if (value < 5) {
      setState({
        counter: { value: value + 1 };
      });
    }
  }
);

This feature enables complex state automation while maintaining predictable state updates and preserving all composition benefits.

Parallel Compositions

A unique characteristic of @medamajs/compose is that different compositions of the same layers can coexist independently, regardless of their configuration or nesting depth. When a state update occurs in one composite state, it automatically triggers subscriptions in other composite states that share the affected layers:

// Create two different compositions sharing some layers
const stateA = createMedama<SharedLayerState>();
const stateB = createMedama<SharedLayerState>();

const composition1 = composeMedama({
  shared1: stateA,
  local1: createMedama<LocalState>()
});

const composition2 = composeMedama({
  nested: composeMedama({
    shared1: stateA,  // Same layer as in composition1
    shared2: stateB
  })
});

// Updates in composition1 trigger subscriptions in composition2
composition1.setState({
  shared1: { value: 42 }
});

// Subscriptions in composition2 react to shared layer changes
composition2.subscribeToState(
  (state) => state.nested.shared1.value,
  (value) => console.log(value) // Logs: 42
);

This powerful feature enables decentralized state management where different parts of your application can maintain their own state compositions while staying synchronized through shared layers.

This approach also allows you to organize selectors based on different logical requirements throughout your application. Each part of your application can compose exactly the states it needs to observe:

// Authentication related composition
const authComposition = composeMedama({
  user: userState,
  session: sessionState
});

// Feature specific composition combining auth and feature state
const featureComposition = composeMedama({
  user: userState,
  featureA: featureState,
  settings: settingsState
});

// Different selectors for different needs
authComposition.subscribeToState(
  (state) => ({
    isLoggedIn: state.user.loggedIn,
    sessionValid: state.session.valid
  }),

  // ...
);

featureComposition.subscribeToState(
  (state) => ({
    canAccess: state.user.permissions.includes('featureA'),
    featureEnabled: state.featureA.enabled && state.settings.featureAEnabled
  }),

  // ...
);

Checking Composite State Layers

The isComposite utility function allows you to check if a state layer within a selector is itself a composite state. This is useful when working with nested compositions and needing to handle layers differently based on their composition status:

import { isComposite } from '@medamajs/compose';

const complexState = composeMedama({
  simple: createMedama<SimpleState>(),

  nested: composeMedama({
    inner1: createMedama<InnerState>(),
    inner2: createMedama<InnerState>()
  })
});

complexState.subscribeToState(
  (state) => {
    // isComposite can only be used within selectors
    const isNestedComposite = isComposite(state.nested); // true
    const isSimpleComposite = isComposite(state.simple); // false

    return {
      // Use the composition information to handle state differently
    };
  },

  // ...
);

Note that isComposite can only be used within selector functions. Attempting to use it outside of a selector context will result in an error, as the state objects are only accessible during selector execution.