1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# Write a module
Rust with the wasm target (`rustup target add wasm32-unknown-unknown`) and [the CLI](cli.md).
```bash run
atlibs new my-module
```
```output
my-module/ (cd my-module && cargo test && cargo wasm && atlibs run my-module describe '{"did":"<did>"}')
```
The SDK is pinned at the commit your CLI was built from:
```bash run
grep '^abi' my-module/Cargo.toml
```
```output
abi = { git = "https://knotless.dev/johnandersen777.bsky.social/atlibs", rev = "…" }
```
The module: one entry, `describe`, run once per event.
```bash run
sed -n '1,/^entries!/p' my-module/modules/my-module/src/lib.rs
```
```output
use abi::{Call, Did, Event, Handle, Run, entries};
use serde::Deserialize;
use serde_json::json;
#[derive(Deserialize)]
struct Args {
did: Did,
}
// The host calls this once per event. Start: ask the account's PDS. Result: answer.
fn describe_handler(run: Run<Args>) -> Result<Handle, String> {
Ok(match run.event {
Event::Start => Handle::new().call(
Call::new("repo", "com.atproto.repo.describeRepo")
.params(json!({ "repo": run.args.did }))
.service(run.args.did.as_str()),
),
Event::Result { id, result, .. } if id == "repo" => {
let repo = result.output()?;
let collections = repo["collections"].as_array().map_or(0, Vec::len);
Handle::new().result(json!({ "handle": repo["handle"], "collections": collections })).done()
}
_ => Handle::new(),
})
}
entries!(describe => describe_handler);
```
What it may call, the form a host shows for it, and what a composition may set (`config`):
```bash run
cat my-module/modules/my-module/module.json
```
```output
{
"name": "my-module",
"description": "Describe an account's repo: its handle and how many collections it has.",
"actions": [{ "entry": "describe", "label": "Describe", "readOnly": true, "fields": [{ "key": "did", "label": "DID", "required": true }] }],
"config": [{ "key": "did", "label": "DID", "description": "The account a composition's instance describes." }],
"calls": ["com.atproto.repo.describeRepo"]
}
```
## Test, build, run
The tests (in `lib.rs`) feed the handler events; no network.
```bash run=network
cd my-module
cargo test 2>&1 | grep '^test ' | LC_ALL=C sort
cargo wasm 2>&1 | tail -1
atlibs run my-module describe '{"did":"did:plc:ocnuqjlzgohypj3zoznermms"}'
```
```output
test tests::result_counts_collections ... ok
test tests::start_calls_describe_repo ... ok
…
"result": {
"collections": …,
"handle": "bobvmbuilder.bsky.social"
},
```
More: [the SDK](../modules/write.md), [calls](../modules/calls.md), [tests with the real host loop](../modules/test.md).
Previous: [Rust](rust.md) | Next: [Publish](publish.md)