dev:manual:scripting:shared-data

Shared state, events, and procedures

Packages communicate across sandbox boundaries through three related APIs:

  • State retains a current value that consumers can read, watch, or bind to a widget.
  • Events announce occurrences to every subscriber.
  • Procedures deliver a directed, fire-and-forget request to one producer.

Publish a package surface

Declare handles in the package's entry module and export them. The export name becomes the public handle name.

import { createEvent, createProcedure, createState } from "smudgy:core";
 
export interface Vitals {
  hp: number;
  maxHp: number;
}
 
export const vitals = createState<Vitals>();
export const prompt = createEvent<Vitals>();
export const refresh = createProcedure((args: { full: boolean }, sender) => {
  console.log(`refresh requested by ${sender}`, args.full);
});

Publish retained data with vitals.set(…) and occurrences with prompt.emit(…). Procedure calls do not return a result; publish a state or emit an event if consumers need to observe an outcome.

Consume another package

Consumers import handles from virtual modules. This does not import or execute the producer's code.

import { vitals } from "smudgy:state/owner/package";
import { prompt } from "smudgy:events/owner/package";
import { refresh } from "smudgy:procedures/owner/package";
 
const hp = vitals.value?.hp;
prompt.on((next) => console.log(next.hp));
refresh.post({ full: true });

If the producer is unavailable, state reads as undefined, events do not fire, and procedure posts do nothing. A package consumer must declare the producer under requires or dependencies; see Package manifests and publishing.

Watch versus onWrite

watch() delivers the final state once per producer update. onWrite() reports every write in order. Use watch() when the current value matters and onWrite() when each write is itself meaningful.

Bind state to widgets

bind() connects a state path directly to a widget property. The widget repaints when the value changes without running a callback or remounting the widget.

import { ProgressBar } from "smudgy:widgets";
import { vitals } from "smudgy:state/owner/package";
 
<ProgressBar
  value={vitals.bind("hp", { fallback: 0 })}
  max={vitals.bind("maxHp", { fallback: 100 })}
/>

See Shared-state API reference for handle types, paths, subscriptions, and derived state.