@canvas-js/atproto-object
AT Protocol object utilities.
Installation
npm install @canvas-js/atproto-object
Usage
import { AtObject } from "@canvas-js/atproto-object"
const app = await AtObject.initialize(["app.bsky.feed.post"], null)
app.listen("wss://bsky.network")
app.close()
Listen to a relay
app.listen("wss://bsky.network", {
onConnect: () => console.log("Connected to firehose"),
onDisconnect: () => console.log("Disconnected from firehose"),
onError: (error) => console.error("Firehose error:", error)
})
Backfill from a relay (limited to ~12h of history)
const cursor = "123456789"
await app.backfill("wss://bsky.network", cursor, {
onConnect: () => console.log("Started backfill"),
onDisconnect: () => console.log("Backfill complete")
})
Backfill from PDSes
const users = ["alice.bsky.social", "bob.bsky.social", "did:plc:example"]
await app.backfillUsers(users)
Initialization
Simple Collections
const app = await AtObject.initialize([
"com.whtwnd.blog.entry",
"app.bsky.feed.post"
], null)
Named Tables
const app = await AtObject.initialize([
{ $type: "com.whtwnd.blog.entry", table: "entries" },
{ $type: "app.bsky.feed.post", table: "posts" }
], null)
Filters
const app = await AtObject.initialize({
entries: "com.whtwnd.blog.entry",
comments: {
nsid: "app.bsky.feed.post",
filter: (nsid: string, rkey: string, post: Post) => {
return post.reply && post.reply.parent && post.reply.root
}
}
}, null)
Custom Handlers
const app = await AtObject.initialize({
posts: {
nsid: "app.bsky.feed.post",
handler: async (nsid: string, rkey: string, post: Post | null, db) => {
if (post === null) {
await db.delete("posts", rkey)
} else {
if (post.text.includes("canvas")) {
await db.set("posts", { rkey, record: post })
}
}
}
}
}, null)
Database Setup
const app = await AtObject.initialize(collections, "./data.db")
const app2 = await AtObject.initialize(collections, "postgres://user:pass@localhost/db")
const app3 = await AtObject.initialize(collections, null)
Database Querying
const posts = await app.db.query("posts")
const post = await app.db.get("posts", "specific-rkey")
await app.db.delete("posts", "rkey-to-delete")