raea/app

The App, its plugins and schedules, and the registry a video's compositions are kept in.

View as Markdown

raea/app is Bevy's App in TypeScript. A video registers its compositions here (registerRoot, <Composition>). A game or tool written as an App adds plugins with definePlugin and systems in Bevy's schedules (Startup, Update, FixedUpdate and the rest, exported by name), and BevyApp opens a headless App you can step frame by frame from a script or a test.

TS
import { BevyApp, Startup, Update, definePlugin } from "raea/app";

let n = 0;
const Counter = definePlugin({
  name: "counter",
  build(app) {
    app.addSystems(Startup, () => { n = 0; });
    app.addSystems(Update, () => { n += 1; });
  },
});

const app = BevyApp.open({ plugins: [] }).addPlugins(Counter);
app.step(10);
TS
import { App, BevyApp, clearRegistry, Composition } from "raea/app"

raea/app exports 1 component, 4 functions, 3 classes, 17 values, 8 types. Bevy's counterpart: bevy_app. Source: sdk/raea/src/app.ts.

Components

Composition

Declares a composition inside the root. It draws nothing itself; a renderer finds it through registerRoot.

PropTypeWhat it does
id (required)stringthe composition's id: letters, digits and dashes (raea render --composition picks it)
component (required)ComponentType<P>the React component it renders
width (required)numberin px
height (required)numberin px
fps (required)numberframes a second
durationInFrames (required)numberits length in frames
defaultPropsPthe props it's rendered with (JSON-like values only)
editEdita hand edit laid over the composition (docs/craft-system.md, L): written back by the app's timeline

Functions

clearRegistry

Forgets the registered root and its compositions (for tests; exported from the test entry only).

TS
function clearRegistry(): void

definePlugin

A TypeScript plugin: definePlugin({ name: "hud", build(app) { app.addSystems(Update,...) } }).

TS
function definePlugin(p: Plugin): Plugin

getCompositions

The compositions the registered root declares, in order.

TS
function getCompositions(): RegisteredComposition[]

registerRoot

Names the root component the renderer starts from, and records its compositions. Call it once.

TS
function registerRoot(given: ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentType): void

Classes

App

A headless Bevy App driven from TypeScript through Bevy's remote protocol: open it with its plugins, then spawn, insert, query and step it, and add TypeScript systems to its schedules.

MemberWhat it does
App.open(o: OpenOptions): BevyAppA headless App with these plugins; Startup has run, no frame has stepped. TypeScript plugins are built in order once the App is open, then finished and cleaned up; their Startup systems run before the first frame.
App.stepTogether(apps: BevyApp[], frames?: number): number[]Apps stepped together, a frame at a time: a network's peers. Returns each one's frame.
.methods: string[]the methods this App answers (BRP's, raea's and its plugins')
.id: numberthe App's handle in the engine
.isPluginAdded(name: string): booleanWhether a plugin of this name is in the App (Bevy's is_plugin_added), Rust or TypeScript.
.addPlugins(...refs: PluginRef[]): BevyAppAdds TypeScript plugins and groups (Bevy's add_plugins): each built in order, then each finished, then each cleaned up. A Rust plugin is compiled in and must be named when the App opens; naming a new one here throws.
.addSystems(schedule: string, systems: ((app: BevyApp, frame: number) => void) | ((app: BevyApp, frame: number) => void)[], o?: SystemOptions): BevyAppSystems in a schedule (Bevy's add_systems), each as system adds one; one of the startup schedules (PreStartup, Startup, PostStartup) runs them once, before the App's next frame. Returns the App, to chain.
.insertResource<T>(r: Resource<T>, value: T): BevyAppBevy's insert_resource: the resource's value set (setResource).
.addObserver<T>(e: string | Event<T>, o?: { entity?: number | undefined; then?: Then[] | undefined; }): Observing<T>Bevy's add_observer: an observer of an event inside the App (observer).
.step(frames?: number): numberframes frames on. Returns the frame it's on. With TypeScript systems (system), each frame is run schedule by schedule and they run in theirs.
.system(schedule: string, run: (app: BevyApp, frame: number) => void, o?: SystemOptions): () => voidA system written in TypeScript: run is called every frame right after Bevy's own systems of schedule (one of the frame's: "First", "PreUpdate", "StateTransition", "RunFixedMainLoop", the fixed step's "FixedFirst", "FixedPreUpdate", "FixedUpdate", "FixedPostUpdate", "FixedLast" (one fixed step a frame: the clock's step is the timestep), "Update", "SpawnScene", "PostUpdate", "Last"), on that frame's World, before the next schedule's; it queries, changes and observes through the App's methods (query, get, insert, call("world.mutate_components"), observe...). Returns a function that removes it. o places it as Bevy's add_systems(...).run_if().before().after().in_set() would: when is its run condition (asked every frame before it runs; see conditions), before/after name other systems or sets of the same schedule it runs before or after (a cycle is refused), set puts it in a set that configureSet gives a condition and an order. Without an order, systems run in the order they were added.
.configureSet(name: string, o: { when?: Condition | undefined; before?: string | string[] | undefined; after?: string | string[] | undefined; }): voidA set of TypeScript systems (Bevy's SystemSet with configure_sets): when is asked once per schedule run, before its first member, and a false skips them all; before/after order the whole set against systems or sets.
.oneShot<R = void>(fn: (app: BevyApp, frame: number) => R): OneShot<R>A one-shot system (Bevy's register_system + run_system): run() runs it now, on the World as it is; queue() runs it once the current TypeScript system returns (as Commands::run_system applies at the next sync point), or at the end of the frame if called outside a step. remove() unregisters it.
.frameSchedules(): string[]The schedules a frame runs, in order (Bevy's MainScheduleOrder).
.call<A = unknown>(method: string, params?: unknown): AAny method of the App's table, run now: its answer, or a throw with the method's message.
.spawn(...components: With[]): numberA new entity with these components.
.insert(entity: number, ...components: With[]): voidAdds these components to an entity, replacing any it has of the same type.
.get<T>(entity: number, c: Component<T>): T | undefinedOne component of an entity, or undefined when it hasn't got one.
.query(o: { data?: Component<unknown>[] | undefined; has?: Component<unknown>[] | undefined; without?: Component<unknown>[] | undefined; }): { entity: number…Entities with every component in has (and none in without), with the data components' values.
.resource<T>(r: Resource<T>): TA resource's value.
.setResource<T>(r: Resource<T>, value: T): voidSets a resource's value, adding it if the app hasn't got one.
.trigger<T>(e: Event<T>, value: T): voidTriggers an event: its observers run now.
.write<T>(m: Message<T>, value: T): voidWrites a message, for its readers' next run.
.observe<T>(e: Event<T>, entity?: number | undefined): () => T[]Starts observing an event (BRP's world.observe+watch). Each call of the returned function gives the events triggered since the last call, in order. entity limits it to events aimed at that entity.
.hook(on: "Add" | "Insert" | "Discard" | "Remove" | "Despawn", component: string | Component<unknown>, then?: Then[]): Observing<unknown>A component hook as an observer inside the App: when component is added, inserted, discarded, removed or its entity despawned, then's calls (BRP methods, "$entity" in their params standing for the entity) run right there, in the same flush, before anything else sees the World. seen() gives the entities it fired for since the last call; stop() removes it.
.observer<T>(e: string | Event<T>, o?: { entity?: number | undefined; then?: Then[] | undefined; }): Observing<T>An observer inside the App for a reflected event: on each trigger (or each aimed at entity), then's calls run in the same flush, "$event" standing for the event's value and "$entity" for its target.
.state<T>(s: State<T>): T | nullA state's value (null before it exists).
.setState<T>(s: State<T>, value: T): voidA state's next value: it changes at the next frame's StateTransition.
.schema(crates?: string[] | undefined): Record<string, unknown>The App's registered types as JSON schema (BRP's registry.schema), filtered by crate.
.close(): voidShuts the app down; its methods fail after.

BevyApp

A headless Bevy App driven from TypeScript through Bevy's remote protocol: open it with its plugins, then spawn, insert, query and step it, and add TypeScript systems to its schedules.

MemberWhat it does
BevyApp.open(o: OpenOptions): BevyAppA headless App with these plugins; Startup has run, no frame has stepped. TypeScript plugins are built in order once the App is open, then finished and cleaned up; their Startup systems run before the first frame.
BevyApp.stepTogether(apps: BevyApp[], frames?: number): number[]Apps stepped together, a frame at a time: a network's peers. Returns each one's frame.
.methods: string[]the methods this App answers (BRP's, raea's and its plugins')
.id: numberthe App's handle in the engine
.isPluginAdded(name: string): booleanWhether a plugin of this name is in the App (Bevy's is_plugin_added), Rust or TypeScript.
.addPlugins(...refs: PluginRef[]): BevyAppAdds TypeScript plugins and groups (Bevy's add_plugins): each built in order, then each finished, then each cleaned up. A Rust plugin is compiled in and must be named when the App opens; naming a new one here throws.
.addSystems(schedule: string, systems: ((app: BevyApp, frame: number) => void) | ((app: BevyApp, frame: number) => void)[], o?: SystemOptions): BevyAppSystems in a schedule (Bevy's add_systems), each as system adds one; one of the startup schedules (PreStartup, Startup, PostStartup) runs them once, before the App's next frame. Returns the App, to chain.
.insertResource<T>(r: Resource<T>, value: T): BevyAppBevy's insert_resource: the resource's value set (setResource).
.addObserver<T>(e: string | Event<T>, o?: { entity?: number | undefined; then?: Then[] | undefined; }): Observing<T>Bevy's add_observer: an observer of an event inside the App (observer).
.step(frames?: number): numberframes frames on. Returns the frame it's on. With TypeScript systems (system), each frame is run schedule by schedule and they run in theirs.
.system(schedule: string, run: (app: BevyApp, frame: number) => void, o?: SystemOptions): () => voidA system written in TypeScript: run is called every frame right after Bevy's own systems of schedule (one of the frame's: "First", "PreUpdate", "StateTransition", "RunFixedMainLoop", the fixed step's "FixedFirst", "FixedPreUpdate", "FixedUpdate", "FixedPostUpdate", "FixedLast" (one fixed step a frame: the clock's step is the timestep), "Update", "SpawnScene", "PostUpdate", "Last"), on that frame's World, before the next schedule's; it queries, changes and observes through the App's methods (query, get, insert, call("world.mutate_components"), observe...). Returns a function that removes it. o places it as Bevy's add_systems(...).run_if().before().after().in_set() would: when is its run condition (asked every frame before it runs; see conditions), before/after name other systems or sets of the same schedule it runs before or after (a cycle is refused), set puts it in a set that configureSet gives a condition and an order. Without an order, systems run in the order they were added.
.configureSet(name: string, o: { when?: Condition | undefined; before?: string | string[] | undefined; after?: string | string[] | undefined; }): voidA set of TypeScript systems (Bevy's SystemSet with configure_sets): when is asked once per schedule run, before its first member, and a false skips them all; before/after order the whole set against systems or sets.
.oneShot<R = void>(fn: (app: BevyApp, frame: number) => R): OneShot<R>A one-shot system (Bevy's register_system + run_system): run() runs it now, on the World as it is; queue() runs it once the current TypeScript system returns (as Commands::run_system applies at the next sync point), or at the end of the frame if called outside a step. remove() unregisters it.
.frameSchedules(): string[]The schedules a frame runs, in order (Bevy's MainScheduleOrder).
.call<A = unknown>(method: string, params?: unknown): AAny method of the App's table, run now: its answer, or a throw with the method's message.
.spawn(...components: With[]): numberA new entity with these components.
.insert(entity: number, ...components: With[]): voidAdds these components to an entity, replacing any it has of the same type.
.get<T>(entity: number, c: Component<T>): T | undefinedOne component of an entity, or undefined when it hasn't got one.
.query(o: { data?: Component<unknown>[] | undefined; has?: Component<unknown>[] | undefined; without?: Component<unknown>[] | undefined; }): { entity: number…Entities with every component in has (and none in without), with the data components' values.
.resource<T>(r: Resource<T>): TA resource's value.
.setResource<T>(r: Resource<T>, value: T): voidSets a resource's value, adding it if the app hasn't got one.
.trigger<T>(e: Event<T>, value: T): voidTriggers an event: its observers run now.
.write<T>(m: Message<T>, value: T): voidWrites a message, for its readers' next run.
.observe<T>(e: Event<T>, entity?: number | undefined): () => T[]Starts observing an event (BRP's world.observe+watch). Each call of the returned function gives the events triggered since the last call, in order. entity limits it to events aimed at that entity.
.hook(on: "Add" | "Insert" | "Discard" | "Remove" | "Despawn", component: string | Component<unknown>, then?: Then[]): Observing<unknown>A component hook as an observer inside the App: when component is added, inserted, discarded, removed or its entity despawned, then's calls (BRP methods, "$entity" in their params standing for the entity) run right there, in the same flush, before anything else sees the World. seen() gives the entities it fired for since the last call; stop() removes it.
.observer<T>(e: string | Event<T>, o?: { entity?: number | undefined; then?: Then[] | undefined; }): Observing<T>An observer inside the App for a reflected event: on each trigger (or each aimed at entity), then's calls run in the same flush, "$event" standing for the event's value and "$entity" for its target.
.state<T>(s: State<T>): T | nullA state's value (null before it exists).
.setState<T>(s: State<T>, value: T): voidA state's next value: it changes at the next frame's StateTransition.
.schema(crates?: string[] | undefined): Record<string, unknown>The App's registered types as JSON schema (BRP's registry.schema), filtered by crate.
.close(): voidShuts the app down; its methods fail after.

PluginGroup

Plugins added together, as Bevy's PluginGroup: group.set(p) replaces the member of p's name, disable(name) leaves one out, and the App adds the rest in order.

MemberWhat it does
new PluginGroup(name: string, plugins: (string | Plugin | { readonly plugin: string; })[])A group named name of these plugins, in the order the App adds them.
.name: stringthe group's name
.set(p: Plugin): PluginGroupReplaces the member of p's name with p.
.disable(name: string): PluginGroupLeaves the member of this name out.
.enable(name: string): PluginGroupPuts a disabled member back.
.build(): (string | Plugin)[]The plugins to add, in order, without the disabled ones.

Values

conditions

Bevy's common run conditions (bevy_ecs::schedule::common_conditions, bevy_state's in_state, bevy_time's on_timer), as Conditions for system(..., { when }) and configureSet. Each call makes a fresh condition with its own memory (runOnce, resourceChanged, onTimer).

First

The frame's schedules, in the order Bevy runs them (with StateTransition and the fixed step's inside).

FixedFirst

The fixed step's schedules, run once a frame at the clock's step.

FixedLast

FixedPostUpdate

FixedPreUpdate

FixedUpdate

Last

PostStartup

PostUpdate

PreStartup

Bevy's schedules by their labels, for app.addSystems(Update,...) as in Rust.

PreUpdate

RunFixedMainLoop

SpawnScene

Startup

StateTransition

Update

The frame's main schedules: Update for game logic, then SpawnScene, PostUpdate and Last.

Types

CompositionProps

The props of Composition, tabled there.

Condition

A run condition: asked on the frame's World before a system or set runs; false skips it this frame.

TS
type Condition = (app: BevyApp, frame: number) => boolean

Edit

A hand edit: segments are the composition's own frame ranges, [from, to), played one after another, so a trim, a split, a cut word or a moved span is a change to this list; the length becomes their sum.

FieldTypeWhat it is
segmentsreadonly (readonly [number, number])[]the composition's own frame ranges, [from, to), played one after another

OpenOptions

FieldTypeWhat it is
plugins (required)PluginRef[]the App's plugins, in order: Rust ones by the name this build registered them under (a declaration's name, or its halves: lightyear_server), compiled in and added before Startup; TypeScript ones (definePlugin) built after
fpsnumberframes a second: one step is 1/fps of Time (default 60)
servenumberserve BRP over HTTP on this port (a dev tool, a BRP client, curl)

Plugin

A plugin written in TypeScript, as bevy_app's Plugin trait: build adds its systems, resources, observers and states to the App; once every plugin is built, each one's finish runs, then each one's cleanup, as App::run does. Unique by name unless isUnique is false (Bevy panics on a unique plugin added twice; this throws).

FieldTypeWhat it is
name (required)stringthe plugin's name: two unique plugins of one name can't both be added
build (required)(app: BevyApp) => voidadds the plugin's systems, resources and other plugins to the app
finish((app: BevyApp) => void) | undefinedcalled once every plugin is built, for setup that needs the others
cleanup((app: BevyApp) => void) | undefinedcalled after every plugin's finish
isUniquebooleanwhether adding it twice is an error (true)

PluginRef

A plugin of any author: a Rust plugin in raea's plugin build by its declared name (package.json raea.plugins: "avian", or a half: "lightyear_server") or by its generated handles (avian from avian.gen.tsx, which carries plugin: "avian"), a TypeScript Plugin, or a group of them.

TS
type PluginRef = string | { readonly plugin: string } | Plugin | PluginGroup

RegisteredComposition

FieldTypeWhat it is
id (required)stringthe composition's id: letters, digits and dashes (raea render --composition picks it)
component (required)ComponentType<any>the React component it renders
width (required)numberin px
height (required)numberin px
fps (required)numberframes a second
durationInFrames (required)numberits length in frames
defaultProps (required)Record<string, unknown>the props it's rendered with (JSON-like values only)

SystemOptions

FieldTypeWhat it is
namestringits name, for other systems' before/after
setstringthe set it's in (configureSet)
whenConditionits run condition
beforestring | string[]systems or sets (by name) it runs before
afterstring | string[]systems or sets (by name) it runs after