an app to share curated trails sidetrail.app
1

Configure Feed

Select the types of activity you want to include in your feed.

1import "./dotenv.js"; // Must be first - loads .env before other imports 2import { getCursor, setCursor, db } from "./db.js"; 3import { Jetstream } from "./jetstream.js"; 4import { handleEvent, COLLECTIONS } from "./handler.js"; 5 6const JETSTREAM_URL = 7 process.env.JETSTREAM_URL || "wss://jetstream2.us-east.bsky.network/subscribe"; 8 9async function main() { 10 console.log("Sidetrail Ingester starting..."); 11 12 if (!process.env.DATABASE_URL) { 13 console.error("ERROR: DATABASE_URL environment variable is required"); 14 process.exit(1); 15 } 16 17 // Get cursor for resumable ingestion 18 const cursor = await getCursor(); 19 console.log(`Starting from cursor: ${cursor ?? "beginning"}`); 20 21 // Create Jetstream connection 22 const jetstream = new Jetstream({ 23 instanceUrl: JETSTREAM_URL, 24 wantedCollections: COLLECTIONS, 25 cursor, 26 setCursor, 27 onEvent: async (evt) => { 28 const { commit } = evt as { commit?: { collection: string; operation: string } }; 29 if (commit) { 30 console.log(`${commit.operation.toUpperCase()} ${commit.collection}`); 31 } 32 await handleEvent(db, evt); 33 }, 34 onError: (err) => { 35 console.error("Jetstream error:", err); 36 }, 37 }); 38 39 // Handle graceful shutdown 40 process.on("SIGINT", () => { 41 console.log("Shutting down..."); 42 jetstream.destroy(); 43 process.exit(0); 44 }); 45 46 process.on("SIGTERM", () => { 47 console.log("Shutting down..."); 48 jetstream.destroy(); 49 process.exit(0); 50 }); 51 52 // Start listening 53 jetstream.start(); 54 console.log(`Listening for: ${COLLECTIONS.join(", ")}`); 55} 56 57main().catch((err) => { 58 console.error("Fatal error:", err); 59 process.exit(1); 60});