Exosphere is a set of small, modular, self-hostable community tools built on the AT Protocol. app.exosphere.site
6
fork

Configure Feed

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

refactor: use bun fs

Hugo 4c4b9d16 b14efa92

+15 -32
+5 -6
packages/app/e2e/seed.ts
··· 7 7 */ 8 8 9 9 import { Database } from "bun:sqlite"; 10 - import { readFileSync } from "node:fs"; 11 10 import { mkdirSync, rmSync } from "node:fs"; 12 11 import { resolve } from "node:path"; 13 12 ··· 19 18 mkdirSync(E2E_DB_DIR, { recursive: true }); 20 19 21 20 /** Create a fresh database with migrations applied. */ 22 - function createDatabase(dbPath: string): Database { 21 + async function createDatabase(dbPath: string): Promise<Database> { 23 22 const db = new Database(dbPath, { create: true }); 24 23 db.run("PRAGMA journal_mode = WAL;"); 25 24 db.run("PRAGMA foreign_keys = ON;"); 26 25 27 - const journal = JSON.parse(readFileSync(resolve(MIGRATIONS_DIR, "meta/_journal.json"), "utf-8")); 26 + const journal = await Bun.file(resolve(MIGRATIONS_DIR, "meta/_journal.json")).json(); 28 27 for (const entry of journal.entries) { 29 - const sql = readFileSync(resolve(MIGRATIONS_DIR, `${entry.tag}.sql`), "utf-8"); 28 + const sql = await Bun.file(resolve(MIGRATIONS_DIR, `${entry.tag}.sql`)).text(); 30 29 const statements = sql.split("--> statement-breakpoint"); 31 30 for (const stmt of statements) { 32 31 const trimmed = stmt.trim(); ··· 45 44 // Self-hosted database — single sphere 46 45 // ============================================================ 47 46 { 48 - const db = createDatabase(`${E2E_DB_DIR}/self-hosted.sqlite`); 47 + const db = await createDatabase(`${E2E_DB_DIR}/self-hosted.sqlite`); 49 48 50 49 const SPHERE_ID = "e2e-sphere-001"; 51 50 ··· 162 161 // Multi-sphere database — two spheres with separate data 163 162 // ============================================================ 164 163 { 165 - const db = createDatabase(`${E2E_DB_DIR}/multi-sphere.sqlite`); 164 + const db = await createDatabase(`${E2E_DB_DIR}/multi-sphere.sqlite`); 166 165 167 166 // ---- Sphere A: alpha.test ---- 168 167
+1 -2
packages/app/src/vite-ssr-plugin.ts
··· 1 1 import type { Plugin, ViteDevServer, ModuleNode } from "vite"; 2 - import fs from "node:fs"; 3 2 import path from "node:path"; 4 3 import { ssrPrefetch } from "./ssr-prefetch.ts"; 5 4 ··· 107 106 } 108 107 109 108 // Read and transform the index.html template 110 - let template = fs.readFileSync(path.resolve(server.config.root, "index.html"), "utf-8"); 109 + let template = await Bun.file(path.resolve(server.config.root, "index.html")).text(); 111 110 template = await server.transformIndexHtml(url, template); 112 111 113 112 // Load the SSR entry via Vite's module graph (supports HMR)
+2 -16
scripts/generate-lexicon-types.ts
··· 6 6 */ 7 7 8 8 import { resolve } from "node:path"; 9 - import { readdir } from "node:fs/promises"; 10 9 11 10 const LEXICON_DIR = resolve(import.meta.dirname!, "../../landing/lexicons/site/exosphere"); 12 11 const OUTPUT_FILE = resolve( ··· 15 14 ); 16 15 17 16 const PREFIX = "site.exosphere."; 18 - 19 - async function collectJsonFiles(dir: string): Promise<string[]> { 20 - const entries = await readdir(dir, { withFileTypes: true }); 21 - const files: string[] = []; 22 - for (const entry of entries) { 23 - const full = resolve(dir, entry.name); 24 - if (entry.isDirectory()) { 25 - files.push(...(await collectJsonFiles(full))); 26 - } else if (entry.name.endsWith(".json")) { 27 - files.push(full); 28 - } 29 - } 30 - return files.sort(); 31 - } 32 17 33 18 interface LexiconProperty { 34 19 type: string; ··· 96 81 } 97 82 98 83 async function main() { 99 - const files = await collectJsonFiles(LEXICON_DIR); 84 + const glob = new Bun.Glob("**/*.json"); 85 + const files = Array.from(glob.scanSync({ cwd: LEXICON_DIR, absolute: true })).sort(); 100 86 101 87 const schemas: LexiconSchema[] = []; 102 88 for (const file of files) {
+4 -5
scripts/pds-account.ts
··· 14 14 * Outputs the created DID which you can use to log in during local development. 15 15 */ 16 16 17 - import { readFileSync } from "node:fs"; 18 17 import { join } from "node:path"; 19 18 20 19 const PDS_URL = process.env.PDS_URL ?? "http://localhost:3000"; 21 20 const ENV_FILE = join(import.meta.dir, "..", "pds", "pds.env"); 22 21 23 - function readEnvVar(name: string): string { 24 - const env = readFileSync(ENV_FILE, "utf-8"); 22 + async function readEnvVar(name: string): Promise<string> { 23 + const env = await Bun.file(ENV_FILE).text(); 25 24 const match = env.match(new RegExp(`^${name}=(.+)$`, "m")); 26 25 if (!match) throw new Error(`${name} not found in pds/pds.env`); 27 26 return match[1].trim(); ··· 36 35 process.exit(1); 37 36 } 38 37 39 - const pdsHostname = readEnvVar("PDS_HOSTNAME"); 38 + const pdsHostname = await readEnvVar("PDS_HOSTNAME"); 40 39 const handle = `${name}.${pdsHostname}`; 41 40 42 - const adminPassword = readEnvVar("PDS_ADMIN_PASSWORD"); 41 + const adminPassword = await readEnvVar("PDS_ADMIN_PASSWORD"); 43 42 44 43 // Use the PDS admin API to create an invite code first 45 44 const inviteRes = await fetch(`${PDS_URL}/xrpc/com.atproto.server.createInviteCode`, {
+3 -3
scripts/pds-init.ts
··· 7 7 * Usage: bun run scripts/pds-init.ts 8 8 */ 9 9 10 - import { existsSync, mkdirSync, writeFileSync } from "node:fs"; 10 + import { mkdirSync } from "node:fs"; 11 11 import { join } from "node:path"; 12 12 import { randomBytes } from "node:crypto"; 13 13 ··· 15 15 const PDS_DIR = join(ROOT, "pds"); 16 16 const ENV_FILE = join(PDS_DIR, "pds.env"); 17 17 18 - if (existsSync(ENV_FILE)) { 18 + if (await Bun.file(ENV_FILE).exists()) { 19 19 console.log("pds/pds.env already exists. Delete it to regenerate."); 20 20 process.exit(0); 21 21 } ··· 54 54 PDS_CRAWLERS= 55 55 `; 56 56 57 - writeFileSync(ENV_FILE, env); 57 + await Bun.write(ENV_FILE, env); 58 58 console.log("Created pds/pds.env"); 59 59 console.log(` Admin password: ${adminPassword}`); 60 60 console.log("");