# Compose modules A composition runs modules as one program: instances of modules (yours, or anyone's by `/`), routes from one instance's hook to a module that implements it, policies that may veto a call, and parameters. You write it in TypeScript; the `composition.json` it writes is what hosts load. Commit both. In the module repo from [Write a module](write.md) (or a new one): ```bash run=network atlibs new my-module > /dev/null && cd my-module && cargo wasm 2>&1 | tail -1 npm init -y > /dev/null && npm pkg set type=module && npm i @publicdomainrelay/atlibs@0.5.0 > /dev/null atlibs add bobvmbuilder.bsky.social/compose 2>&1 | tail -1 ``` ```output wrote ./atlibs.modules.js ``` `atlibs add` vendored `compose` and wrote `atlibs.modules.js`: `compose` (pinned) and `myModule` (`modules/my-module`). Save as `compose.ts`: ```bash run=network cat > compose.ts <<'EOF' import { composition, policy } from "@publicdomainrelay/atlibs/compose/build.js"; import { compose, myModule } from "./atlibs.modules.js"; const c = composition({ name: "my-composition", description: "me describes an account; ui asks lookup to double n, if the even policy allows." }); const did = c.param("did", { label: "DID", values: { default: "did:plc:ocnuqjlzgohypj3zoznermms" } }); c.instance("me", myModule, { input: { did } }); const ui = c.instance("ui", compose); const lookup = c.instance("lookup", compose); const even = c.instance("even", compose); c.route(ui, "com.example.compose.lookup", lookup, "ui needs n doubled", { policies: [policy(even, "even")] }); process.stdout.write(await c.json()); EOF node compose.ts > my-composition.json && atlibs compose check my-composition.json | jq -c '[.instances[].name]' ``` ```output ["me","ui","lookup","even"] ``` A route to a module that does not implement the hook, or an input outside the module's `config`, is a type error in your editor, and `node compose.ts` names the line. ## Run it `atlibs run [input]`: ```bash run=network atlibs run my-composition.json me describe | jq -c .result atlibs run my-composition.json me describe --params '{"inputs":{"did":"did:plc:z72i7hdynmk6r22z27h6tvur"}}' | jq -c .result atlibs run my-composition.json ui caller '{"nsid":"com.example.compose.lookup","input":{"n":4}}' | jq -c .result.q.output.doubled atlibs run my-composition.json ui caller '{"nsid":"com.example.compose.lookup","input":{"n":3}}' | jq -c .result.q ``` ```output {"collections":…,"handle":"bobvmbuilder.bsky.social"} {"collections":…,"handle":"bsky.app"} 8 {"error":"policy even denied: n 3 is not even","ok":false} ``` ## Publish, add, run After [publishing my-module](publish.md) (the record names each module by at:// URI: a key becomes yours, `/[@pin]` its publisher's, with the full pin): ```bash run=account git add compose.ts my-composition.json && git commit -m my-composition atlibs publish composition my-composition.json --as you.bsky.social cd "$(mktemp -d)" atlibs add you.bsky.social/my-composition atlibs run you.bsky.social/my-composition me describe ``` The rkey is the file name (`--rkey` to choose). Remove it: `atlibs publish delete com.fedfork.exec.composition/my-composition`. ## In a page `atlibs stage --target spa` writes the composition and every module it names: ```bash run=network atlibs stage --target spa --out site my-composition.json > /dev/null 2>&1 find site -type f | LC_ALL=C sort ``` ```output site/composition.json site/modules/compose.json site/modules/compose.wasm site/modules/my-module.json site/modules/my-module.wasm site/stage.json ``` Save as `site/index.html`: ```html run=network browser site/index.html ``` ```output 8 ``` Serve `site/` (`python3 -m http.server --directory site`). An instance whose input has a `$param`: `bindParameters(manifest, { inputs })` before `runCompose`. More: handlers, callbacks, hooks, exports, bindings: [Compositions](../compositions.md). Previous: [Publish](publish.md)