WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node typescript hono htmx atproto

Phase 0: Foundation — monorepo scaffolding, lexicons, and package stubs

Set up Turborepo monorepo with pnpm workspaces and devenv for the atBB
decentralized forum project. Defines 5 new AT Proto lexicon schemas
(category, role, membership, reaction, modAction) alongside the 3
existing ones, with a YAML→JSON→TypeScript build pipeline. Scaffolds
the Hono AppView API server, HTMX web UI server, and PDS spike script.

+2136
+12
.env.example
··· 1 + # AppView configuration 2 + PORT=3000 3 + FORUM_DID=did:plc:your-forum-did-here 4 + PDS_URL=https://your-pds.example.com 5 + 6 + # Web UI configuration 7 + # PORT=3001 (set in web package, or override here) 8 + APPVIEW_URL=http://localhost:3000 9 + 10 + # Forum Service Account credentials (for spike and AppView writes) 11 + FORUM_HANDLE=your-forum-handle 12 + FORUM_PASSWORD=your-forum-password
+19
.gitignore
··· 1 + # Dependencies 2 + node_modules/ 3 + 4 + # Build output 5 + dist/ 6 + 7 + # Turborepo 8 + .turbo/ 9 + 10 + # Environment 11 + .env 12 + .env.local 13 + 14 + # devenv 15 + .devenv/ 16 + .devenv.flake.nix 17 + 18 + # OS 19 + .DS_Store
+2
.npmrc
··· 1 + shamefully-hoist=false 2 + strict-peer-dependencies=false
+103
devenv.lock
··· 1 + { 2 + "nodes": { 3 + "devenv": { 4 + "locked": { 5 + "dir": "src/modules", 6 + "lastModified": 1770399425, 7 + "owner": "cachix", 8 + "repo": "devenv", 9 + "rev": "0f006b2e9f591cc44cf219048b5e33793530403b", 10 + "type": "github" 11 + }, 12 + "original": { 13 + "dir": "src/modules", 14 + "owner": "cachix", 15 + "repo": "devenv", 16 + "type": "github" 17 + } 18 + }, 19 + "flake-compat": { 20 + "flake": false, 21 + "locked": { 22 + "lastModified": 1767039857, 23 + "owner": "NixOS", 24 + "repo": "flake-compat", 25 + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", 26 + "type": "github" 27 + }, 28 + "original": { 29 + "owner": "NixOS", 30 + "repo": "flake-compat", 31 + "type": "github" 32 + } 33 + }, 34 + "git-hooks": { 35 + "inputs": { 36 + "flake-compat": "flake-compat", 37 + "gitignore": "gitignore", 38 + "nixpkgs": [ 39 + "nixpkgs" 40 + ] 41 + }, 42 + "locked": { 43 + "lastModified": 1769939035, 44 + "owner": "cachix", 45 + "repo": "git-hooks.nix", 46 + "rev": "a8ca480175326551d6c4121498316261cbb5b260", 47 + "type": "github" 48 + }, 49 + "original": { 50 + "owner": "cachix", 51 + "repo": "git-hooks.nix", 52 + "type": "github" 53 + } 54 + }, 55 + "gitignore": { 56 + "inputs": { 57 + "nixpkgs": [ 58 + "git-hooks", 59 + "nixpkgs" 60 + ] 61 + }, 62 + "locked": { 63 + "lastModified": 1762808025, 64 + "owner": "hercules-ci", 65 + "repo": "gitignore.nix", 66 + "rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c", 67 + "type": "github" 68 + }, 69 + "original": { 70 + "owner": "hercules-ci", 71 + "repo": "gitignore.nix", 72 + "type": "github" 73 + } 74 + }, 75 + "nixpkgs": { 76 + "locked": { 77 + "lastModified": 1770169770, 78 + "owner": "NixOS", 79 + "repo": "nixpkgs", 80 + "rev": "aa290c9891fa4ebe88f8889e59633d20cc06a5f2", 81 + "type": "github" 82 + }, 83 + "original": { 84 + "owner": "NixOS", 85 + "ref": "nixpkgs-unstable", 86 + "repo": "nixpkgs", 87 + "type": "github" 88 + } 89 + }, 90 + "root": { 91 + "inputs": { 92 + "devenv": "devenv", 93 + "git-hooks": "git-hooks", 94 + "nixpkgs": "nixpkgs", 95 + "pre-commit-hooks": [ 96 + "git-hooks" 97 + ] 98 + } 99 + } 100 + }, 101 + "root": "root", 102 + "version": 7 103 + }
+17
devenv.nix
··· 1 + { pkgs, ... }: 2 + 3 + { 4 + languages.javascript = { 5 + enable = true; 6 + corepack.enable = true; 7 + }; 8 + 9 + packages = [ 10 + pkgs.turbo 11 + ]; 12 + 13 + processes = { 14 + appview.exec = "pnpm --filter @atbb/appview dev"; 15 + web.exec = "pnpm --filter @atbb/web dev"; 16 + }; 17 + }
+3
devenv.yaml
··· 1 + inputs: 2 + nixpkgs: 3 + url: github:NixOS/nixpkgs/nixpkgs-unstable
+15
package.json
··· 1 + { 2 + "name": "atbb-monorepo", 3 + "private": true, 4 + "packageManager": "pnpm@9.15.4", 5 + "scripts": { 6 + "build": "turbo run build", 7 + "dev": "turbo run dev", 8 + "lint": "turbo run lint", 9 + "clean": "turbo run clean" 10 + }, 11 + "devDependencies": { 12 + "turbo": "^2.4.0", 13 + "typescript": "^5.7.0" 14 + } 15 + }
+25
packages/appview/package.json
··· 1 + { 2 + "name": "@atbb/appview", 3 + "version": "0.1.0", 4 + "private": true, 5 + "type": "module", 6 + "scripts": { 7 + "build": "tsc", 8 + "dev": "tsx watch src/index.ts", 9 + "start": "node dist/index.js", 10 + "lint": "tsc --noEmit", 11 + "clean": "rm -rf dist" 12 + }, 13 + "dependencies": { 14 + "@atbb/lexicon": "workspace:*", 15 + "@atproto/api": "^0.15.0", 16 + "@atproto/common-web": "^0.4.0", 17 + "hono": "^4.7.0", 18 + "@hono/node-server": "^1.14.0" 19 + }, 20 + "devDependencies": { 21 + "@types/node": "^22.0.0", 22 + "tsx": "^4.0.0", 23 + "typescript": "^5.7.0" 24 + } 25 + }
+21
packages/appview/src/index.ts
··· 1 + import { Hono } from "hono"; 2 + import { serve } from "@hono/node-server"; 3 + import { logger } from "hono/logger"; 4 + import { apiRoutes } from "./routes/index.js"; 5 + import { loadConfig } from "./lib/config.js"; 6 + 7 + const config = loadConfig(); 8 + const app = new Hono(); 9 + 10 + app.use("*", logger()); 11 + app.route("/api", apiRoutes); 12 + 13 + serve( 14 + { 15 + fetch: app.fetch, 16 + port: config.port, 17 + }, 18 + (info) => { 19 + console.log(`atBB AppView listening on http://localhost:${info.port}`); 20 + } 21 + );
+6
packages/appview/src/lib/atproto.ts
··· 1 + import { AtpAgent } from "@atproto/api"; 2 + import type { AppConfig } from "./config.js"; 3 + 4 + export function createAgent(config: AppConfig): AtpAgent { 5 + return new AtpAgent({ service: config.pdsUrl }); 6 + }
+13
packages/appview/src/lib/config.ts
··· 1 + export interface AppConfig { 2 + port: number; 3 + forumDid: string; 4 + pdsUrl: string; 5 + } 6 + 7 + export function loadConfig(): AppConfig { 8 + return { 9 + port: parseInt(process.env.PORT ?? "3000", 10), 10 + forumDid: process.env.FORUM_DID ?? "", 11 + pdsUrl: process.env.PDS_URL ?? "https://bsky.social", 12 + }; 13 + }
+12
packages/appview/src/routes/categories.ts
··· 1 + import { Hono } from "hono"; 2 + 3 + export const categoriesRoutes = new Hono() 4 + .get("/", (c) => { 5 + // Phase 1: query indexed categories from database 6 + return c.json({ categories: [] }); 7 + }) 8 + .get("/:id/topics", (c) => { 9 + const { id } = c.req.param(); 10 + // Phase 1: query indexed topics (posts without reply ref) for this category 11 + return c.json({ categoryId: id, topics: [] }); 12 + });
+10
packages/appview/src/routes/forum.ts
··· 1 + import { Hono } from "hono"; 2 + 3 + export const forumRoutes = new Hono().get("/", (c) => { 4 + // Phase 1: query indexed forum metadata from database 5 + return c.json({ 6 + name: "My atBB Forum", 7 + description: "A forum on the ATmosphere", 8 + did: "did:plc:placeholder", 9 + }); 10 + });
+5
packages/appview/src/routes/health.ts
··· 1 + import { Hono } from "hono"; 2 + 3 + export const healthRoutes = new Hono() 4 + .get("/", (c) => c.json({ status: "ok", version: "0.1.0" })) 5 + .get("/ready", (c) => c.json({ status: "ready" }));
+13
packages/appview/src/routes/index.ts
··· 1 + import { Hono } from "hono"; 2 + import { healthRoutes } from "./health.js"; 3 + import { forumRoutes } from "./forum.js"; 4 + import { categoriesRoutes } from "./categories.js"; 5 + import { topicsRoutes } from "./topics.js"; 6 + import { postsRoutes } from "./posts.js"; 7 + 8 + export const apiRoutes = new Hono() 9 + .route("/healthz", healthRoutes) 10 + .route("/forum", forumRoutes) 11 + .route("/categories", categoriesRoutes) 12 + .route("/topics", topicsRoutes) 13 + .route("/posts", postsRoutes);
+6
packages/appview/src/routes/posts.ts
··· 1 + import { Hono } from "hono"; 2 + 3 + export const postsRoutes = new Hono().post("/", (c) => { 4 + // Phase 1: create space.atbb.post record with forumRef and reply ref 5 + return c.json({ error: "not implemented" }, 501); 6 + });
+12
packages/appview/src/routes/topics.ts
··· 1 + import { Hono } from "hono"; 2 + 3 + export const topicsRoutes = new Hono() 4 + .get("/:id", (c) => { 5 + const { id } = c.req.param(); 6 + // Phase 1: query thread starter + reply posts from database 7 + return c.json({ topicId: id, post: null, replies: [] }); 8 + }) 9 + .post("/", (c) => { 10 + // Phase 1: create space.atbb.post record with forumRef but no reply ref 11 + return c.json({ error: "not implemented" }, 501); 12 + });
+8
packages/appview/tsconfig.json
··· 1 + { 2 + "extends": "../../tsconfig.base.json", 3 + "compilerOptions": { 4 + "outDir": "./dist", 5 + "rootDir": "./src" 6 + }, 7 + "include": ["src/**/*.ts"] 8 + }
+18
packages/lexicon/lexicons/com/atproto/repo/strongRef.yaml
··· 1 + # yaml-language-server: $schema=https://boat.kelinci.net/lexicon-document.json 2 + --- 3 + lexicon: 1 4 + id: com.atproto.repo.strongRef 5 + description: A URI with a content-hash fingerprint. 6 + defs: 7 + main: 8 + type: object 9 + required: 10 + - uri 11 + - cid 12 + properties: 13 + uri: 14 + type: string 15 + format: at-uri 16 + cid: 17 + type: string 18 + format: cid
+45
packages/lexicon/lexicons/space/atbb/forum/category.yaml
··· 1 + # yaml-language-server: $schema=https://boat.kelinci.net/lexicon-document.json 2 + --- 3 + lexicon: 1 4 + id: space.atbb.forum.category 5 + defs: 6 + main: 7 + type: record 8 + description: >- 9 + A category (subforum) within an atBB forum. 10 + Owned by the Forum DID. 11 + key: tid 12 + record: 13 + type: object 14 + required: 15 + - name 16 + - createdAt 17 + properties: 18 + name: 19 + type: string 20 + maxLength: 300 21 + maxGraphemes: 100 22 + description: >- 23 + Display name of the category. 24 + description: 25 + type: string 26 + maxLength: 3000 27 + maxGraphemes: 300 28 + description: >- 29 + A short description for the category. 30 + slug: 31 + type: string 32 + maxLength: 100 33 + description: >- 34 + URL-friendly identifier for the category. 35 + Must be lowercase alphanumeric with hyphens. 36 + sortOrder: 37 + type: integer 38 + minimum: 0 39 + description: >- 40 + Numeric sort position. Lower values appear first. 41 + createdAt: 42 + type: string 43 + format: datetime 44 + description: >- 45 + Timestamp when this category was created.
+28
packages/lexicon/lexicons/space/atbb/forum/forum.yaml
··· 1 + # yaml-language-server: $schema=https://boat.kelinci.net/lexicon-document.json 2 + --- 3 + lexicon: 1 4 + id: space.atbb.forum.forum 5 + revision: 1 6 + defs: 7 + main: 8 + type: record 9 + description: >- 10 + Record describing an AtBB forum, which is a collection of posts. 11 + key: literal:self 12 + record: 13 + type: object 14 + required: 15 + - name 16 + properties: 17 + name: 18 + type: string 19 + maxLength: 3000 20 + maxGraphemes: 300 21 + description: >- 22 + The name of the forum. 23 + description: 24 + type: string 25 + maxLength: 3000 26 + maxGraphemes: 300 27 + description: >- 28 + A short description for the forum.
+58
packages/lexicon/lexicons/space/atbb/forum/role.yaml
··· 1 + # yaml-language-server: $schema=https://boat.kelinci.net/lexicon-document.json 2 + --- 3 + lexicon: 1 4 + id: space.atbb.forum.role 5 + defs: 6 + main: 7 + type: record 8 + description: >- 9 + A role definition within an atBB forum. 10 + Roles define a named set of permissions that can be assigned to members. 11 + Owned by the Forum DID. 12 + key: tid 13 + record: 14 + type: object 15 + required: 16 + - name 17 + - permissions 18 + - createdAt 19 + properties: 20 + name: 21 + type: string 22 + maxLength: 300 23 + maxGraphemes: 100 24 + description: >- 25 + Display name of the role (e.g., "Admin", "Moderator", "Member"). 26 + description: 27 + type: string 28 + maxLength: 3000 29 + maxGraphemes: 300 30 + description: >- 31 + Description of what this role is for. 32 + permissions: 33 + type: array 34 + description: >- 35 + List of permission tokens granted by this role. 36 + items: 37 + type: string 38 + knownValues: 39 + - space.atbb.permission.manageCategories 40 + - space.atbb.permission.manageRoles 41 + - space.atbb.permission.manageMembers 42 + - space.atbb.permission.moderatePosts 43 + - space.atbb.permission.banUsers 44 + - space.atbb.permission.pinTopics 45 + - space.atbb.permission.lockTopics 46 + - space.atbb.permission.createTopics 47 + - space.atbb.permission.createPosts 48 + priority: 49 + type: integer 50 + minimum: 0 51 + description: >- 52 + Role hierarchy priority. Lower values indicate higher authority. 53 + Prevents lower-priority roles from acting on higher-priority members. 54 + createdAt: 55 + type: string 56 + format: datetime 57 + description: >- 58 + Timestamp when this role was created.
+55
packages/lexicon/lexicons/space/atbb/membership.yaml
··· 1 + # yaml-language-server: $schema=https://boat.kelinci.net/lexicon-document.json 2 + --- 3 + lexicon: 1 4 + id: space.atbb.membership 5 + defs: 6 + main: 7 + type: record 8 + description: >- 9 + A user's membership in an atBB forum. 10 + Created on the user's PDS (owned by User DID). 11 + References the forum and optionally a role assigned by the forum admin. 12 + key: tid 13 + record: 14 + type: object 15 + required: 16 + - forum 17 + - createdAt 18 + properties: 19 + forum: 20 + type: ref 21 + ref: "#forumRef" 22 + description: >- 23 + Reference to the forum this membership is for. 24 + role: 25 + type: ref 26 + ref: "#roleRef" 27 + description: >- 28 + Reference to the role assigned to this member. 29 + If absent, the member has default/guest permissions. 30 + joinedAt: 31 + type: string 32 + format: datetime 33 + description: >- 34 + Timestamp when the user joined the forum. 35 + createdAt: 36 + type: string 37 + format: datetime 38 + description: >- 39 + Client-declared timestamp when this record was created. 40 + forumRef: 41 + type: object 42 + required: 43 + - forum 44 + properties: 45 + forum: 46 + type: ref 47 + ref: com.atproto.repo.strongRef 48 + roleRef: 49 + type: object 50 + required: 51 + - role 52 + properties: 53 + role: 54 + type: ref 55 + ref: com.atproto.repo.strongRef
+76
packages/lexicon/lexicons/space/atbb/modAction.yaml
··· 1 + # yaml-language-server: $schema=https://boat.kelinci.net/lexicon-document.json 2 + --- 3 + lexicon: 1 4 + id: space.atbb.modAction 5 + defs: 6 + main: 7 + type: record 8 + description: >- 9 + A moderation action in an atBB forum. 10 + Owned by the Forum DID. Written by the AppView on behalf of 11 + authorized moderators/admins after verifying their role. 12 + key: tid 13 + record: 14 + type: object 15 + required: 16 + - action 17 + - subject 18 + - createdBy 19 + - createdAt 20 + properties: 21 + action: 22 + type: string 23 + knownValues: 24 + - space.atbb.modAction.ban 25 + - space.atbb.modAction.mute 26 + - space.atbb.modAction.pin 27 + - space.atbb.modAction.lock 28 + - space.atbb.modAction.delete 29 + - space.atbb.modAction.unban 30 + - space.atbb.modAction.unmute 31 + - space.atbb.modAction.unpin 32 + - space.atbb.modAction.unlock 33 + description: >- 34 + The type of moderation action. 35 + subject: 36 + type: ref 37 + ref: "#subjectRef" 38 + description: >- 39 + The target of the moderation action. 40 + reason: 41 + type: string 42 + maxLength: 3000 43 + maxGraphemes: 300 44 + description: >- 45 + Moderator-provided reason for the action. 46 + createdBy: 47 + type: string 48 + format: did 49 + description: >- 50 + The DID of the moderator/admin who initiated this action. 51 + expiresAt: 52 + type: string 53 + format: datetime 54 + description: >- 55 + Optional expiration time for time-limited actions (e.g., temp bans). 56 + createdAt: 57 + type: string 58 + format: datetime 59 + description: >- 60 + Timestamp when this action was taken. 61 + subjectRef: 62 + type: object 63 + description: >- 64 + The subject of a moderation action. Can target either a specific 65 + post (by strongRef) or a user (by DID). 66 + properties: 67 + post: 68 + type: ref 69 + ref: com.atproto.repo.strongRef 70 + description: >- 71 + Reference to a specific post. Used for pin, lock, delete actions. 72 + did: 73 + type: string 74 + format: did 75 + description: >- 76 + DID of the target user. Used for ban, mute actions.
+54
packages/lexicon/lexicons/space/atbb/post.yaml
··· 1 + # yaml-language-server: $schema=https://boat.kelinci.net/lexicon-document.json 2 + --- 3 + lexicon: 1 4 + id: space.atbb.post 5 + defs: 6 + main: 7 + type: record 8 + description: >- 9 + Record containing an atBB post. 10 + key: tid 11 + record: 12 + type: object 13 + required: 14 + - text 15 + - createdAt 16 + properties: 17 + text: 18 + type: string 19 + maxLength: 3000 20 + maxGraphemes: 300 21 + description: >- 22 + The primary post content. 23 + May be an empty string, if there are embeds. 24 + forum: 25 + type: ref 26 + ref: "#forumRef" 27 + reply: 28 + type: ref 29 + ref: "#replyRef" 30 + createdAt: 31 + type: string 32 + format: datetime 33 + description: >- 34 + Client-declared timestamp when this post was originally created. 35 + forumRef: 36 + type: object 37 + required: 38 + - forum 39 + properties: 40 + forum: 41 + type: ref 42 + ref: com.atproto.repo.strongRef 43 + replyRef: 44 + type: object 45 + required: 46 + - root 47 + - parent 48 + properties: 49 + root: 50 + type: ref 51 + ref: com.atproto.repo.strongRef 52 + parent: 53 + type: ref 54 + ref: com.atproto.repo.strongRef
+45
packages/lexicon/lexicons/space/atbb/reaction.yaml
··· 1 + # yaml-language-server: $schema=https://boat.kelinci.net/lexicon-document.json 2 + --- 3 + lexicon: 1 4 + id: space.atbb.reaction 5 + defs: 6 + main: 7 + type: record 8 + description: >- 9 + A reaction (upvote, like, or emoji reaction) on an atBB post. 10 + Created on the user's PDS (owned by User DID). 11 + key: tid 12 + record: 13 + type: object 14 + required: 15 + - subject 16 + - type 17 + - createdAt 18 + properties: 19 + subject: 20 + type: ref 21 + ref: "#subjectRef" 22 + description: >- 23 + Reference to the post being reacted to. 24 + type: 25 + type: string 26 + maxLength: 100 27 + knownValues: 28 + - space.atbb.reaction.like 29 + - space.atbb.reaction.upvote 30 + - space.atbb.reaction.downvote 31 + description: >- 32 + The type of reaction. Uses token-style values for extensibility. 33 + createdAt: 34 + type: string 35 + format: datetime 36 + description: >- 37 + Client-declared timestamp when this reaction was created. 38 + subjectRef: 39 + type: object 40 + required: 41 + - post 42 + properties: 43 + post: 44 + type: ref 45 + ref: com.atproto.repo.strongRef
+24
packages/lexicon/package.json
··· 1 + { 2 + "name": "@atbb/lexicon", 3 + "version": "0.1.0", 4 + "private": true, 5 + "type": "module", 6 + "main": "./dist/types/index.js", 7 + "types": "./dist/types/index.d.ts", 8 + "exports": { 9 + ".": "./dist/types/index.js", 10 + "./json/*": "./dist/json/*" 11 + }, 12 + "scripts": { 13 + "build": "pnpm run build:json && pnpm run build:types", 14 + "build:json": "tsx scripts/build.ts", 15 + "build:types": "bash -c 'shopt -s globstar && lex gen-api --yes ./dist/types ./dist/json/**/*.json'", 16 + "clean": "rm -rf dist" 17 + }, 18 + "devDependencies": { 19 + "@atproto/lex-cli": "^0.5.0", 20 + "tsx": "^4.0.0", 21 + "yaml": "^2.7.0", 22 + "glob": "^11.0.0" 23 + } 24 + }
+33
packages/lexicon/scripts/build.ts
··· 1 + import { readFileSync, writeFileSync, mkdirSync } from "node:fs"; 2 + import { dirname, join, relative } from "node:path"; 3 + import { parse as parseYaml } from "yaml"; 4 + import { glob } from "glob"; 5 + 6 + const LEXICONS_DIR = join(import.meta.dirname, "..", "lexicons"); 7 + const OUTPUT_DIR = join(import.meta.dirname, "..", "dist", "json"); 8 + 9 + async function main() { 10 + const yamlFiles = await glob("**/*.yaml", { cwd: LEXICONS_DIR }); 11 + 12 + for (const yamlFile of yamlFiles) { 13 + const inputPath = join(LEXICONS_DIR, yamlFile); 14 + const outputPath = join(OUTPUT_DIR, yamlFile.replace(/\.yaml$/, ".json")); 15 + 16 + mkdirSync(dirname(outputPath), { recursive: true }); 17 + 18 + const yamlContent = readFileSync(inputPath, "utf-8"); 19 + const parsed = parseYaml(yamlContent); 20 + writeFileSync(outputPath, JSON.stringify(parsed, null, 2) + "\n"); 21 + 22 + console.log( 23 + ` ${yamlFile} -> ${relative(join(import.meta.dirname, ".."), outputPath)}` 24 + ); 25 + } 26 + 27 + console.log(`\nConverted ${yamlFiles.length} lexicon files.`); 28 + } 29 + 30 + main().catch((err) => { 31 + console.error(err); 32 + process.exit(1); 33 + });
+8
packages/lexicon/tsconfig.json
··· 1 + { 2 + "extends": "../../tsconfig.base.json", 3 + "compilerOptions": { 4 + "outDir": "./dist/types", 5 + "rootDir": "./dist/types" 6 + }, 7 + "include": ["dist/types/**/*.ts"] 8 + }
+20
packages/spike/package.json
··· 1 + { 2 + "name": "@atbb/spike", 3 + "version": "0.1.0", 4 + "private": true, 5 + "type": "module", 6 + "scripts": { 7 + "spike": "tsx src/index.ts", 8 + "clean": "rm -rf dist" 9 + }, 10 + "dependencies": { 11 + "@atbb/lexicon": "workspace:*", 12 + "@atproto/api": "^0.15.0", 13 + "@atproto/common-web": "^0.4.0" 14 + }, 15 + "devDependencies": { 16 + "@types/node": "^22.0.0", 17 + "tsx": "^4.0.0", 18 + "typescript": "^5.7.0" 19 + } 20 + }
+141
packages/spike/src/index.ts
··· 1 + import { AtpAgent } from "@atproto/api"; 2 + import { TID } from "@atproto/common-web"; 3 + 4 + const PDS_URL = process.env.PDS_URL ?? ""; 5 + const HANDLE = process.env.FORUM_HANDLE ?? ""; 6 + const PASSWORD = process.env.FORUM_PASSWORD ?? ""; 7 + 8 + if (!PDS_URL || !HANDLE || !PASSWORD) { 9 + console.error( 10 + "Missing required env vars: PDS_URL, FORUM_HANDLE, FORUM_PASSWORD" 11 + ); 12 + console.error("Copy .env.example to .env and fill in your credentials."); 13 + process.exit(1); 14 + } 15 + 16 + async function main() { 17 + // 1. Authenticate 18 + const agent = new AtpAgent({ service: PDS_URL }); 19 + await agent.login({ identifier: HANDLE, password: PASSWORD }); 20 + const did = agent.session!.did; 21 + console.log(`Authenticated as: ${did}\n`); 22 + 23 + // 2. Write a forum record (space.atbb.forum.forum, key: self) 24 + console.log("--- Writing forum record ---"); 25 + const forumResult = await agent.com.atproto.repo.putRecord({ 26 + repo: did, 27 + collection: "space.atbb.forum.forum", 28 + rkey: "self", 29 + record: { 30 + $type: "space.atbb.forum.forum", 31 + name: "Test Forum", 32 + description: "A test forum created by the PDS spike", 33 + }, 34 + }); 35 + console.log(`Forum record written: ${forumResult.data.uri}`); 36 + 37 + // 3. Write a category record (space.atbb.forum.category, key: tid) 38 + console.log("\n--- Writing category record ---"); 39 + const categoryTid = TID.nextStr(); 40 + const categoryResult = await agent.com.atproto.repo.putRecord({ 41 + repo: did, 42 + collection: "space.atbb.forum.category", 43 + rkey: categoryTid, 44 + record: { 45 + $type: "space.atbb.forum.category", 46 + name: "General Discussion", 47 + description: "Talk about anything", 48 + slug: "general", 49 + sortOrder: 0, 50 + createdAt: new Date().toISOString(), 51 + }, 52 + }); 53 + console.log(`Category record written: ${categoryResult.data.uri}`); 54 + 55 + // 4. Write a post record (space.atbb.post, key: tid) 56 + console.log("\n--- Writing post record ---"); 57 + const postTid = TID.nextStr(); 58 + const postResult = await agent.com.atproto.repo.putRecord({ 59 + repo: did, 60 + collection: "space.atbb.post", 61 + rkey: postTid, 62 + record: { 63 + $type: "space.atbb.post", 64 + text: "Hello from the atBB spike!", 65 + forum: { 66 + forum: { 67 + uri: forumResult.data.uri, 68 + cid: forumResult.data.cid, 69 + }, 70 + }, 71 + createdAt: new Date().toISOString(), 72 + }, 73 + }); 74 + console.log(`Post record written: ${postResult.data.uri}`); 75 + 76 + // 5. Read records back 77 + console.log("\n--- Reading records back ---"); 78 + 79 + const readForum = await agent.com.atproto.repo.getRecord({ 80 + repo: did, 81 + collection: "space.atbb.forum.forum", 82 + rkey: "self", 83 + }); 84 + console.log("Forum:", JSON.stringify(readForum.data.value, null, 2)); 85 + 86 + const readCategory = await agent.com.atproto.repo.getRecord({ 87 + repo: did, 88 + collection: "space.atbb.forum.category", 89 + rkey: categoryTid, 90 + }); 91 + console.log("Category:", JSON.stringify(readCategory.data.value, null, 2)); 92 + 93 + const readPost = await agent.com.atproto.repo.getRecord({ 94 + repo: did, 95 + collection: "space.atbb.post", 96 + rkey: postTid, 97 + }); 98 + console.log("Post:", JSON.stringify(readPost.data.value, null, 2)); 99 + 100 + // 6. List records in a collection 101 + console.log("\n--- Listing forum.category records ---"); 102 + const listResult = await agent.com.atproto.repo.listRecords({ 103 + repo: did, 104 + collection: "space.atbb.forum.category", 105 + limit: 10, 106 + }); 107 + console.log(`Found ${listResult.data.records.length} category records`); 108 + for (const rec of listResult.data.records) { 109 + console.log(` - ${rec.uri}`); 110 + } 111 + 112 + // 7. Clean up test records 113 + console.log("\n--- Cleaning up test records ---"); 114 + await agent.com.atproto.repo.deleteRecord({ 115 + repo: did, 116 + collection: "space.atbb.post", 117 + rkey: postTid, 118 + }); 119 + console.log("Deleted post record"); 120 + 121 + await agent.com.atproto.repo.deleteRecord({ 122 + repo: did, 123 + collection: "space.atbb.forum.category", 124 + rkey: categoryTid, 125 + }); 126 + console.log("Deleted category record"); 127 + 128 + await agent.com.atproto.repo.deleteRecord({ 129 + repo: did, 130 + collection: "space.atbb.forum.forum", 131 + rkey: "self", 132 + }); 133 + console.log("Deleted forum record"); 134 + 135 + console.log("\nSpike complete!"); 136 + } 137 + 138 + main().catch((err) => { 139 + console.error("Spike failed:", err); 140 + process.exit(1); 141 + });
+8
packages/spike/tsconfig.json
··· 1 + { 2 + "extends": "../../tsconfig.base.json", 3 + "compilerOptions": { 4 + "outDir": "./dist", 5 + "rootDir": "./src" 6 + }, 7 + "include": ["src/**/*.ts"] 8 + }
+23
packages/web/package.json
··· 1 + { 2 + "name": "@atbb/web", 3 + "version": "0.1.0", 4 + "private": true, 5 + "type": "module", 6 + "scripts": { 7 + "build": "tsc", 8 + "dev": "tsx watch src/index.ts", 9 + "start": "node dist/index.js", 10 + "lint": "tsc --noEmit", 11 + "clean": "rm -rf dist" 12 + }, 13 + "dependencies": { 14 + "hono": "^4.7.0", 15 + "@hono/node-server": "^1.14.0" 16 + }, 17 + "devDependencies": { 18 + "@types/node": "^22.0.0", 19 + "tsx": "^4.0.0", 20 + "typescript": "^5.7.0", 21 + "typed-htmx": "^0.3.0" 22 + } 23 + }
+7
packages/web/src/global.d.ts
··· 1 + import "typed-htmx"; 2 + 3 + declare module "hono/jsx" { 4 + namespace JSX { 5 + interface HTMLAttributes extends HtmxAttributes {} 6 + } 7 + }
+21
packages/web/src/index.ts
··· 1 + import { Hono } from "hono"; 2 + import { serve } from "@hono/node-server"; 3 + import { logger } from "hono/logger"; 4 + import { webRoutes } from "./routes/index.js"; 5 + import { loadConfig } from "./lib/config.js"; 6 + 7 + const config = loadConfig(); 8 + const app = new Hono(); 9 + 10 + app.use("*", logger()); 11 + app.route("/", webRoutes); 12 + 13 + serve( 14 + { 15 + fetch: app.fetch, 16 + port: config.port, 17 + }, 18 + (info) => { 19 + console.log(`atBB Web UI listening on http://localhost:${info.port}`); 20 + } 21 + );
+26
packages/web/src/layouts/base.tsx
··· 1 + import type { FC, PropsWithChildren } from "hono/jsx"; 2 + 3 + export const BaseLayout: FC<PropsWithChildren<{ title?: string }>> = (props) => { 4 + return ( 5 + <html lang="en"> 6 + <head> 7 + <meta charset="UTF-8" /> 8 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 9 + <title>{props.title ?? "atBB Forum"}</title> 10 + <script src="https://unpkg.com/htmx.org@2.0.4" /> 11 + </head> 12 + <body> 13 + <header> 14 + <h1> 15 + <a href="/">atBB Forum</a> 16 + </h1> 17 + <nav>{/* login/logout will go here in Phase 2 */}</nav> 18 + </header> 19 + <main>{props.children}</main> 20 + <footer> 21 + <p>Powered by atBB on the ATmosphere</p> 22 + </footer> 23 + </body> 24 + </html> 25 + ); 26 + };
+12
packages/web/src/lib/api.ts
··· 1 + import { loadConfig } from "./config.js"; 2 + 3 + const config = loadConfig(); 4 + 5 + export async function fetchApi<T>(path: string): Promise<T> { 6 + const url = `${config.appviewUrl}/api${path}`; 7 + const res = await fetch(url); 8 + if (!res.ok) { 9 + throw new Error(`AppView API error: ${res.status} ${res.statusText}`); 10 + } 11 + return res.json() as Promise<T>; 12 + }
+11
packages/web/src/lib/config.ts
··· 1 + export interface WebConfig { 2 + port: number; 3 + appviewUrl: string; 4 + } 5 + 6 + export function loadConfig(): WebConfig { 7 + return { 8 + port: parseInt(process.env.PORT ?? "3001", 10), 9 + appviewUrl: process.env.APPVIEW_URL ?? "http://localhost:3000", 10 + }; 11 + }
+15
packages/web/src/routes/home.tsx
··· 1 + import { Hono } from "hono"; 2 + import { BaseLayout } from "../layouts/base.js"; 3 + 4 + export const homeRoutes = new Hono().get("/", (c) => { 5 + return c.html( 6 + <BaseLayout title="Home - atBB Forum"> 7 + <h2>Welcome to atBB</h2> 8 + <p>A BB-style forum on the ATmosphere.</p> 9 + <section> 10 + <h3>Categories</h3> 11 + <div id="categories">Loading categories...</div> 12 + </section> 13 + </BaseLayout> 14 + ); 15 + });
+4
packages/web/src/routes/index.ts
··· 1 + import { Hono } from "hono"; 2 + import { homeRoutes } from "./home.js"; 3 + 4 + export const webRoutes = new Hono().route("/", homeRoutes);
+8
packages/web/tsconfig.json
··· 1 + { 2 + "extends": "../../tsconfig.base.json", 3 + "compilerOptions": { 4 + "outDir": "./dist", 5 + "rootDir": "./src" 6 + }, 7 + "include": ["src/**/*.ts", "src/**/*.tsx"] 8 + }
+1054
pnpm-lock.yaml
··· 1 + lockfileVersion: '9.0' 2 + 3 + settings: 4 + autoInstallPeers: true 5 + excludeLinksFromLockfile: false 6 + 7 + importers: 8 + 9 + .: 10 + devDependencies: 11 + turbo: 12 + specifier: ^2.4.0 13 + version: 2.8.3 14 + typescript: 15 + specifier: ^5.7.0 16 + version: 5.9.3 17 + 18 + packages/appview: 19 + dependencies: 20 + '@atbb/lexicon': 21 + specifier: workspace:* 22 + version: link:../lexicon 23 + '@atproto/api': 24 + specifier: ^0.15.0 25 + version: 0.15.27 26 + '@atproto/common-web': 27 + specifier: ^0.4.0 28 + version: 0.4.16 29 + '@hono/node-server': 30 + specifier: ^1.14.0 31 + version: 1.19.9(hono@4.11.8) 32 + hono: 33 + specifier: ^4.7.0 34 + version: 4.11.8 35 + devDependencies: 36 + '@types/node': 37 + specifier: ^22.0.0 38 + version: 22.19.9 39 + tsx: 40 + specifier: ^4.0.0 41 + version: 4.21.0 42 + typescript: 43 + specifier: ^5.7.0 44 + version: 5.9.3 45 + 46 + packages/lexicon: 47 + devDependencies: 48 + '@atproto/lex-cli': 49 + specifier: ^0.5.0 50 + version: 0.5.7 51 + glob: 52 + specifier: ^11.0.0 53 + version: 11.1.0 54 + tsx: 55 + specifier: ^4.0.0 56 + version: 4.21.0 57 + yaml: 58 + specifier: ^2.7.0 59 + version: 2.8.2 60 + 61 + packages/spike: 62 + dependencies: 63 + '@atbb/lexicon': 64 + specifier: workspace:* 65 + version: link:../lexicon 66 + '@atproto/api': 67 + specifier: ^0.15.0 68 + version: 0.15.27 69 + '@atproto/common-web': 70 + specifier: ^0.4.0 71 + version: 0.4.16 72 + devDependencies: 73 + '@types/node': 74 + specifier: ^22.0.0 75 + version: 22.19.9 76 + tsx: 77 + specifier: ^4.0.0 78 + version: 4.21.0 79 + typescript: 80 + specifier: ^5.7.0 81 + version: 5.9.3 82 + 83 + packages/web: 84 + dependencies: 85 + '@hono/node-server': 86 + specifier: ^1.14.0 87 + version: 1.19.9(hono@4.11.8) 88 + hono: 89 + specifier: ^4.7.0 90 + version: 4.11.8 91 + devDependencies: 92 + '@types/node': 93 + specifier: ^22.0.0 94 + version: 22.19.9 95 + tsx: 96 + specifier: ^4.0.0 97 + version: 4.21.0 98 + typed-htmx: 99 + specifier: ^0.3.0 100 + version: 0.3.1 101 + typescript: 102 + specifier: ^5.7.0 103 + version: 5.9.3 104 + 105 + packages: 106 + 107 + '@atproto/api@0.15.27': 108 + resolution: {integrity: sha512-ok/WGafh1nz4t8pEQGtAF/32x2E2VDWU4af6BajkO5Gky2jp2q6cv6aB2A5yuvNNcc3XkYMYipsqVHVwLPMF9g==} 109 + 110 + '@atproto/common-web@0.4.16': 111 + resolution: {integrity: sha512-Ufvaff5JgxUyUyTAG0/3o7ltpy3lnZ1DvLjyAnvAf+hHfiK7OMQg+8byr+orN+KP9MtIQaRTsCgYPX+PxMKUoA==} 112 + 113 + '@atproto/lex-cli@0.5.7': 114 + resolution: {integrity: sha512-V5rsU95Th57KICxUGwTjudN5wmFBHL/fLkl7banl6izsQBiUrVvrj3EScNW/Wx2PnwlJwxtTpa1rTnP30+i5/A==} 115 + engines: {node: '>=18.7.0'} 116 + hasBin: true 117 + 118 + '@atproto/lex-data@0.0.11': 119 + resolution: {integrity: sha512-4+KTtHdqwlhiTKA7D4SACea4jprsNpCQsNALW09wsZ6IHhCDGO5tr1cmV+QnLYe3G3mu1E1yXHXbPUHrUUDT/A==} 120 + 121 + '@atproto/lex-json@0.0.11': 122 + resolution: {integrity: sha512-2IExAoQ4KsR5fyPa1JjIvtR316PvdgRH/l3BVGLBd3cSxM3m5MftIv1B6qZ9HjNiK60SgkWp0mi9574bTNDhBQ==} 123 + 124 + '@atproto/lexicon@0.4.14': 125 + resolution: {integrity: sha512-jiKpmH1QER3Gvc7JVY5brwrfo+etFoe57tKPQX/SmPwjvUsFnJAow5xLIryuBaJgFAhnTZViXKs41t//pahGHQ==} 126 + 127 + '@atproto/lexicon@0.6.1': 128 + resolution: {integrity: sha512-/vI1kVlY50Si+5MXpvOucelnYwb0UJ6Qto5mCp+7Q5C+Jtp+SoSykAPVvjVtTnQUH2vrKOFOwpb3C375vSKzXw==} 129 + 130 + '@atproto/syntax@0.3.4': 131 + resolution: {integrity: sha512-8CNmi5DipOLaVeSMPggMe7FCksVag0aO6XZy9WflbduTKM4dFZVCs4686UeMLfGRXX+X966XgwECHoLYrovMMg==} 132 + 133 + '@atproto/syntax@0.4.3': 134 + resolution: {integrity: sha512-YoZUz40YAJr5nPwvCDWgodEOlt5IftZqPJvA0JDWjuZKD8yXddTwSzXSaKQAzGOpuM+/A3uXRtPzJJqlScc+iA==} 135 + 136 + '@atproto/xrpc@0.7.7': 137 + resolution: {integrity: sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==} 138 + 139 + '@esbuild/aix-ppc64@0.27.3': 140 + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} 141 + engines: {node: '>=18'} 142 + cpu: [ppc64] 143 + os: [aix] 144 + 145 + '@esbuild/android-arm64@0.27.3': 146 + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} 147 + engines: {node: '>=18'} 148 + cpu: [arm64] 149 + os: [android] 150 + 151 + '@esbuild/android-arm@0.27.3': 152 + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} 153 + engines: {node: '>=18'} 154 + cpu: [arm] 155 + os: [android] 156 + 157 + '@esbuild/android-x64@0.27.3': 158 + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} 159 + engines: {node: '>=18'} 160 + cpu: [x64] 161 + os: [android] 162 + 163 + '@esbuild/darwin-arm64@0.27.3': 164 + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} 165 + engines: {node: '>=18'} 166 + cpu: [arm64] 167 + os: [darwin] 168 + 169 + '@esbuild/darwin-x64@0.27.3': 170 + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} 171 + engines: {node: '>=18'} 172 + cpu: [x64] 173 + os: [darwin] 174 + 175 + '@esbuild/freebsd-arm64@0.27.3': 176 + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} 177 + engines: {node: '>=18'} 178 + cpu: [arm64] 179 + os: [freebsd] 180 + 181 + '@esbuild/freebsd-x64@0.27.3': 182 + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} 183 + engines: {node: '>=18'} 184 + cpu: [x64] 185 + os: [freebsd] 186 + 187 + '@esbuild/linux-arm64@0.27.3': 188 + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} 189 + engines: {node: '>=18'} 190 + cpu: [arm64] 191 + os: [linux] 192 + 193 + '@esbuild/linux-arm@0.27.3': 194 + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} 195 + engines: {node: '>=18'} 196 + cpu: [arm] 197 + os: [linux] 198 + 199 + '@esbuild/linux-ia32@0.27.3': 200 + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} 201 + engines: {node: '>=18'} 202 + cpu: [ia32] 203 + os: [linux] 204 + 205 + '@esbuild/linux-loong64@0.27.3': 206 + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} 207 + engines: {node: '>=18'} 208 + cpu: [loong64] 209 + os: [linux] 210 + 211 + '@esbuild/linux-mips64el@0.27.3': 212 + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} 213 + engines: {node: '>=18'} 214 + cpu: [mips64el] 215 + os: [linux] 216 + 217 + '@esbuild/linux-ppc64@0.27.3': 218 + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} 219 + engines: {node: '>=18'} 220 + cpu: [ppc64] 221 + os: [linux] 222 + 223 + '@esbuild/linux-riscv64@0.27.3': 224 + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} 225 + engines: {node: '>=18'} 226 + cpu: [riscv64] 227 + os: [linux] 228 + 229 + '@esbuild/linux-s390x@0.27.3': 230 + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} 231 + engines: {node: '>=18'} 232 + cpu: [s390x] 233 + os: [linux] 234 + 235 + '@esbuild/linux-x64@0.27.3': 236 + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} 237 + engines: {node: '>=18'} 238 + cpu: [x64] 239 + os: [linux] 240 + 241 + '@esbuild/netbsd-arm64@0.27.3': 242 + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} 243 + engines: {node: '>=18'} 244 + cpu: [arm64] 245 + os: [netbsd] 246 + 247 + '@esbuild/netbsd-x64@0.27.3': 248 + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} 249 + engines: {node: '>=18'} 250 + cpu: [x64] 251 + os: [netbsd] 252 + 253 + '@esbuild/openbsd-arm64@0.27.3': 254 + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} 255 + engines: {node: '>=18'} 256 + cpu: [arm64] 257 + os: [openbsd] 258 + 259 + '@esbuild/openbsd-x64@0.27.3': 260 + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} 261 + engines: {node: '>=18'} 262 + cpu: [x64] 263 + os: [openbsd] 264 + 265 + '@esbuild/openharmony-arm64@0.27.3': 266 + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} 267 + engines: {node: '>=18'} 268 + cpu: [arm64] 269 + os: [openharmony] 270 + 271 + '@esbuild/sunos-x64@0.27.3': 272 + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} 273 + engines: {node: '>=18'} 274 + cpu: [x64] 275 + os: [sunos] 276 + 277 + '@esbuild/win32-arm64@0.27.3': 278 + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} 279 + engines: {node: '>=18'} 280 + cpu: [arm64] 281 + os: [win32] 282 + 283 + '@esbuild/win32-ia32@0.27.3': 284 + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} 285 + engines: {node: '>=18'} 286 + cpu: [ia32] 287 + os: [win32] 288 + 289 + '@esbuild/win32-x64@0.27.3': 290 + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} 291 + engines: {node: '>=18'} 292 + cpu: [x64] 293 + os: [win32] 294 + 295 + '@hono/node-server@1.19.9': 296 + resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} 297 + engines: {node: '>=18.14.1'} 298 + peerDependencies: 299 + hono: ^4 300 + 301 + '@isaacs/balanced-match@4.0.1': 302 + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} 303 + engines: {node: 20 || >=22} 304 + 305 + '@isaacs/brace-expansion@5.0.1': 306 + resolution: {integrity: sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==} 307 + engines: {node: 20 || >=22} 308 + 309 + '@isaacs/cliui@9.0.0': 310 + resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} 311 + engines: {node: '>=18'} 312 + 313 + '@nodelib/fs.scandir@2.1.5': 314 + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 315 + engines: {node: '>= 8'} 316 + 317 + '@nodelib/fs.stat@2.0.5': 318 + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 319 + engines: {node: '>= 8'} 320 + 321 + '@nodelib/fs.walk@1.2.8': 322 + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 323 + engines: {node: '>= 8'} 324 + 325 + '@ts-morph/common@0.17.0': 326 + resolution: {integrity: sha512-RMSSvSfs9kb0VzkvQ2NWobwnj7TxCA9vI/IjR9bDHqgAyVbu2T0DN4wiKVqomyDWqO7dPr/tErSfq7urQ1Q37g==} 327 + 328 + '@types/node@22.19.9': 329 + resolution: {integrity: sha512-PD03/U8g1F9T9MI+1OBisaIARhSzeidsUjQaf51fOxrfjeiKN9bLVO06lHuHYjxdnqLWJijJHfqXPSJri2EM2A==} 330 + 331 + ansi-styles@4.3.0: 332 + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 333 + engines: {node: '>=8'} 334 + 335 + await-lock@2.2.2: 336 + resolution: {integrity: sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==} 337 + 338 + balanced-match@1.0.2: 339 + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 340 + 341 + brace-expansion@2.0.2: 342 + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 343 + 344 + braces@3.0.3: 345 + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 346 + engines: {node: '>=8'} 347 + 348 + chalk@4.1.2: 349 + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 350 + engines: {node: '>=10'} 351 + 352 + code-block-writer@11.0.3: 353 + resolution: {integrity: sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==} 354 + 355 + color-convert@2.0.1: 356 + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 357 + engines: {node: '>=7.0.0'} 358 + 359 + color-name@1.1.4: 360 + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 361 + 362 + commander@9.5.0: 363 + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} 364 + engines: {node: ^12.20.0 || >=14} 365 + 366 + cross-spawn@7.0.6: 367 + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 368 + engines: {node: '>= 8'} 369 + 370 + esbuild@0.27.3: 371 + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} 372 + engines: {node: '>=18'} 373 + hasBin: true 374 + 375 + fast-glob@3.3.3: 376 + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 377 + engines: {node: '>=8.6.0'} 378 + 379 + fastq@1.20.1: 380 + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} 381 + 382 + fill-range@7.1.1: 383 + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 384 + engines: {node: '>=8'} 385 + 386 + foreground-child@3.3.1: 387 + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 388 + engines: {node: '>=14'} 389 + 390 + fsevents@2.3.3: 391 + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 392 + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 393 + os: [darwin] 394 + 395 + get-tsconfig@4.13.6: 396 + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} 397 + 398 + glob-parent@5.1.2: 399 + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 400 + engines: {node: '>= 6'} 401 + 402 + glob@11.1.0: 403 + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} 404 + engines: {node: 20 || >=22} 405 + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me 406 + hasBin: true 407 + 408 + has-flag@4.0.0: 409 + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 410 + engines: {node: '>=8'} 411 + 412 + hono@4.11.8: 413 + resolution: {integrity: sha512-eVkB/CYCCei7K2WElZW9yYQFWssG0DhaDhVvr7wy5jJ22K+ck8fWW0EsLpB0sITUTvPnc97+rrbQqIr5iqiy9Q==} 414 + engines: {node: '>=16.9.0'} 415 + 416 + is-extglob@2.1.1: 417 + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 418 + engines: {node: '>=0.10.0'} 419 + 420 + is-glob@4.0.3: 421 + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 422 + engines: {node: '>=0.10.0'} 423 + 424 + is-number@7.0.0: 425 + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 426 + engines: {node: '>=0.12.0'} 427 + 428 + isexe@2.0.0: 429 + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 430 + 431 + iso-datestring-validator@2.2.2: 432 + resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==} 433 + 434 + jackspeak@4.2.1: 435 + resolution: {integrity: sha512-GPBXyfcZSGujjddPeA+V34bW70ZJT7jzCEbloVasSH4yjiqWqXHX8iZQtZdVbOhc5esSeAIuiSmMutRZQB/olg==} 436 + engines: {node: 20 || >=22} 437 + 438 + lru-cache@11.2.5: 439 + resolution: {integrity: sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==} 440 + engines: {node: 20 || >=22} 441 + 442 + merge2@1.4.1: 443 + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 444 + engines: {node: '>= 8'} 445 + 446 + micromatch@4.0.8: 447 + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 448 + engines: {node: '>=8.6'} 449 + 450 + minimatch@10.1.2: 451 + resolution: {integrity: sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==} 452 + engines: {node: 20 || >=22} 453 + 454 + minimatch@5.1.6: 455 + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} 456 + engines: {node: '>=10'} 457 + 458 + minipass@7.1.2: 459 + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 460 + engines: {node: '>=16 || 14 >=14.17'} 461 + 462 + mkdirp@1.0.4: 463 + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} 464 + engines: {node: '>=10'} 465 + hasBin: true 466 + 467 + multiformats@9.9.0: 468 + resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} 469 + 470 + package-json-from-dist@1.0.1: 471 + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 472 + 473 + path-browserify@1.0.1: 474 + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} 475 + 476 + path-key@3.1.1: 477 + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 478 + engines: {node: '>=8'} 479 + 480 + path-scurry@2.0.1: 481 + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} 482 + engines: {node: 20 || >=22} 483 + 484 + picomatch@2.3.1: 485 + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 486 + engines: {node: '>=8.6'} 487 + 488 + prettier@3.8.1: 489 + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} 490 + engines: {node: '>=14'} 491 + hasBin: true 492 + 493 + queue-microtask@1.2.3: 494 + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 495 + 496 + resolve-pkg-maps@1.0.0: 497 + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} 498 + 499 + reusify@1.1.0: 500 + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} 501 + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 502 + 503 + run-parallel@1.2.0: 504 + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 505 + 506 + shebang-command@2.0.0: 507 + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 508 + engines: {node: '>=8'} 509 + 510 + shebang-regex@3.0.0: 511 + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 512 + engines: {node: '>=8'} 513 + 514 + signal-exit@4.1.0: 515 + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 516 + engines: {node: '>=14'} 517 + 518 + supports-color@7.2.0: 519 + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 520 + engines: {node: '>=8'} 521 + 522 + tlds@1.261.0: 523 + resolution: {integrity: sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==} 524 + hasBin: true 525 + 526 + to-regex-range@5.0.1: 527 + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 528 + engines: {node: '>=8.0'} 529 + 530 + ts-morph@16.0.0: 531 + resolution: {integrity: sha512-jGNF0GVpFj0orFw55LTsQxVYEUOCWBAbR5Ls7fTYE5pQsbW18ssTb/6UXx/GYAEjS+DQTp8VoTw0vqYMiaaQuw==} 532 + 533 + tslib@2.8.1: 534 + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 535 + 536 + tsx@4.21.0: 537 + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} 538 + engines: {node: '>=18.0.0'} 539 + hasBin: true 540 + 541 + turbo-darwin-64@2.8.3: 542 + resolution: {integrity: sha512-4kXRLfcygLOeNcP6JquqRLmGB/ATjjfehiojL2dJkL7GFm3SPSXbq7oNj8UbD8XriYQ5hPaSuz59iF1ijPHkTw==} 543 + cpu: [x64] 544 + os: [darwin] 545 + 546 + turbo-darwin-arm64@2.8.3: 547 + resolution: {integrity: sha512-xF7uCeC0UY0Hrv/tqax0BMbFlVP1J/aRyeGQPZT4NjvIPj8gSPDgFhfkfz06DhUwDg5NgMo04uiSkAWE8WB/QQ==} 548 + cpu: [arm64] 549 + os: [darwin] 550 + 551 + turbo-linux-64@2.8.3: 552 + resolution: {integrity: sha512-vxMDXwaOjweW/4etY7BxrXCSkvtwh0PbwVafyfT1Ww659SedUxd5rM3V2ZCmbwG8NiCfY7d6VtxyHx3Wh1GoZA==} 553 + cpu: [x64] 554 + os: [linux] 555 + 556 + turbo-linux-arm64@2.8.3: 557 + resolution: {integrity: sha512-mQX7uYBZFkuPLLlKaNe9IjR1JIef4YvY8f21xFocvttXvdPebnq3PK1Zjzl9A1zun2BEuWNUwQIL8lgvN9Pm3Q==} 558 + cpu: [arm64] 559 + os: [linux] 560 + 561 + turbo-windows-64@2.8.3: 562 + resolution: {integrity: sha512-YLGEfppGxZj3VWcNOVa08h6ISsVKiG85aCAWosOKNUjb6yErWEuydv6/qImRJUI+tDLvDvW7BxopAkujRnWCrw==} 563 + cpu: [x64] 564 + os: [win32] 565 + 566 + turbo-windows-arm64@2.8.3: 567 + resolution: {integrity: sha512-afTUGKBRmOJU1smQSBnFGcbq0iabAPwh1uXu2BVk7BREg30/1gMnJh9DFEQTah+UD3n3ru8V55J83RQNFfqoyw==} 568 + cpu: [arm64] 569 + os: [win32] 570 + 571 + turbo@2.8.3: 572 + resolution: {integrity: sha512-8Osxz5Tu/Dw2kb31EAY+nhq/YZ3wzmQSmYa1nIArqxgCAldxv9TPlrAiaBUDVnKA4aiPn0OFBD1ACcpc5VFOAQ==} 573 + hasBin: true 574 + 575 + typed-html@3.0.1: 576 + resolution: {integrity: sha512-JKCM9zTfPDuPqQqdGZBWSEiItShliKkBFg5c6yOR8zth43v763XkAzTWaOlVqc0Y6p9ee8AaAbipGfUnCsYZUA==} 577 + engines: {node: '>=12'} 578 + 579 + typed-htmx@0.3.1: 580 + resolution: {integrity: sha512-6WSPsukTIOEMsVbx5wzgVSvldLmgBUVcFIm2vJlBpRPtcbDOGC5y1IYrCWNX1yUlNsrv1Ngcw4gGM8jsPyNV7w==} 581 + engines: {node: '>=12'} 582 + 583 + typescript@5.9.3: 584 + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 585 + engines: {node: '>=14.17'} 586 + hasBin: true 587 + 588 + uint8arrays@3.0.0: 589 + resolution: {integrity: sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==} 590 + 591 + undici-types@6.21.0: 592 + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 593 + 594 + unicode-segmenter@0.14.5: 595 + resolution: {integrity: sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==} 596 + 597 + which@2.0.2: 598 + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 599 + engines: {node: '>= 8'} 600 + hasBin: true 601 + 602 + yaml@2.8.2: 603 + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} 604 + engines: {node: '>= 14.6'} 605 + hasBin: true 606 + 607 + yesno@0.4.0: 608 + resolution: {integrity: sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA==} 609 + 610 + zod@3.25.76: 611 + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} 612 + 613 + snapshots: 614 + 615 + '@atproto/api@0.15.27': 616 + dependencies: 617 + '@atproto/common-web': 0.4.16 618 + '@atproto/lexicon': 0.4.14 619 + '@atproto/syntax': 0.4.3 620 + '@atproto/xrpc': 0.7.7 621 + await-lock: 2.2.2 622 + multiformats: 9.9.0 623 + tlds: 1.261.0 624 + zod: 3.25.76 625 + 626 + '@atproto/common-web@0.4.16': 627 + dependencies: 628 + '@atproto/lex-data': 0.0.11 629 + '@atproto/lex-json': 0.0.11 630 + '@atproto/syntax': 0.4.3 631 + zod: 3.25.76 632 + 633 + '@atproto/lex-cli@0.5.7': 634 + dependencies: 635 + '@atproto/lexicon': 0.4.14 636 + '@atproto/syntax': 0.3.4 637 + chalk: 4.1.2 638 + commander: 9.5.0 639 + prettier: 3.8.1 640 + ts-morph: 16.0.0 641 + yesno: 0.4.0 642 + zod: 3.25.76 643 + 644 + '@atproto/lex-data@0.0.11': 645 + dependencies: 646 + multiformats: 9.9.0 647 + tslib: 2.8.1 648 + uint8arrays: 3.0.0 649 + unicode-segmenter: 0.14.5 650 + 651 + '@atproto/lex-json@0.0.11': 652 + dependencies: 653 + '@atproto/lex-data': 0.0.11 654 + tslib: 2.8.1 655 + 656 + '@atproto/lexicon@0.4.14': 657 + dependencies: 658 + '@atproto/common-web': 0.4.16 659 + '@atproto/syntax': 0.4.3 660 + iso-datestring-validator: 2.2.2 661 + multiformats: 9.9.0 662 + zod: 3.25.76 663 + 664 + '@atproto/lexicon@0.6.1': 665 + dependencies: 666 + '@atproto/common-web': 0.4.16 667 + '@atproto/syntax': 0.4.3 668 + iso-datestring-validator: 2.2.2 669 + multiformats: 9.9.0 670 + zod: 3.25.76 671 + 672 + '@atproto/syntax@0.3.4': {} 673 + 674 + '@atproto/syntax@0.4.3': 675 + dependencies: 676 + tslib: 2.8.1 677 + 678 + '@atproto/xrpc@0.7.7': 679 + dependencies: 680 + '@atproto/lexicon': 0.6.1 681 + zod: 3.25.76 682 + 683 + '@esbuild/aix-ppc64@0.27.3': 684 + optional: true 685 + 686 + '@esbuild/android-arm64@0.27.3': 687 + optional: true 688 + 689 + '@esbuild/android-arm@0.27.3': 690 + optional: true 691 + 692 + '@esbuild/android-x64@0.27.3': 693 + optional: true 694 + 695 + '@esbuild/darwin-arm64@0.27.3': 696 + optional: true 697 + 698 + '@esbuild/darwin-x64@0.27.3': 699 + optional: true 700 + 701 + '@esbuild/freebsd-arm64@0.27.3': 702 + optional: true 703 + 704 + '@esbuild/freebsd-x64@0.27.3': 705 + optional: true 706 + 707 + '@esbuild/linux-arm64@0.27.3': 708 + optional: true 709 + 710 + '@esbuild/linux-arm@0.27.3': 711 + optional: true 712 + 713 + '@esbuild/linux-ia32@0.27.3': 714 + optional: true 715 + 716 + '@esbuild/linux-loong64@0.27.3': 717 + optional: true 718 + 719 + '@esbuild/linux-mips64el@0.27.3': 720 + optional: true 721 + 722 + '@esbuild/linux-ppc64@0.27.3': 723 + optional: true 724 + 725 + '@esbuild/linux-riscv64@0.27.3': 726 + optional: true 727 + 728 + '@esbuild/linux-s390x@0.27.3': 729 + optional: true 730 + 731 + '@esbuild/linux-x64@0.27.3': 732 + optional: true 733 + 734 + '@esbuild/netbsd-arm64@0.27.3': 735 + optional: true 736 + 737 + '@esbuild/netbsd-x64@0.27.3': 738 + optional: true 739 + 740 + '@esbuild/openbsd-arm64@0.27.3': 741 + optional: true 742 + 743 + '@esbuild/openbsd-x64@0.27.3': 744 + optional: true 745 + 746 + '@esbuild/openharmony-arm64@0.27.3': 747 + optional: true 748 + 749 + '@esbuild/sunos-x64@0.27.3': 750 + optional: true 751 + 752 + '@esbuild/win32-arm64@0.27.3': 753 + optional: true 754 + 755 + '@esbuild/win32-ia32@0.27.3': 756 + optional: true 757 + 758 + '@esbuild/win32-x64@0.27.3': 759 + optional: true 760 + 761 + '@hono/node-server@1.19.9(hono@4.11.8)': 762 + dependencies: 763 + hono: 4.11.8 764 + 765 + '@isaacs/balanced-match@4.0.1': {} 766 + 767 + '@isaacs/brace-expansion@5.0.1': 768 + dependencies: 769 + '@isaacs/balanced-match': 4.0.1 770 + 771 + '@isaacs/cliui@9.0.0': {} 772 + 773 + '@nodelib/fs.scandir@2.1.5': 774 + dependencies: 775 + '@nodelib/fs.stat': 2.0.5 776 + run-parallel: 1.2.0 777 + 778 + '@nodelib/fs.stat@2.0.5': {} 779 + 780 + '@nodelib/fs.walk@1.2.8': 781 + dependencies: 782 + '@nodelib/fs.scandir': 2.1.5 783 + fastq: 1.20.1 784 + 785 + '@ts-morph/common@0.17.0': 786 + dependencies: 787 + fast-glob: 3.3.3 788 + minimatch: 5.1.6 789 + mkdirp: 1.0.4 790 + path-browserify: 1.0.1 791 + 792 + '@types/node@22.19.9': 793 + dependencies: 794 + undici-types: 6.21.0 795 + 796 + ansi-styles@4.3.0: 797 + dependencies: 798 + color-convert: 2.0.1 799 + 800 + await-lock@2.2.2: {} 801 + 802 + balanced-match@1.0.2: {} 803 + 804 + brace-expansion@2.0.2: 805 + dependencies: 806 + balanced-match: 1.0.2 807 + 808 + braces@3.0.3: 809 + dependencies: 810 + fill-range: 7.1.1 811 + 812 + chalk@4.1.2: 813 + dependencies: 814 + ansi-styles: 4.3.0 815 + supports-color: 7.2.0 816 + 817 + code-block-writer@11.0.3: {} 818 + 819 + color-convert@2.0.1: 820 + dependencies: 821 + color-name: 1.1.4 822 + 823 + color-name@1.1.4: {} 824 + 825 + commander@9.5.0: {} 826 + 827 + cross-spawn@7.0.6: 828 + dependencies: 829 + path-key: 3.1.1 830 + shebang-command: 2.0.0 831 + which: 2.0.2 832 + 833 + esbuild@0.27.3: 834 + optionalDependencies: 835 + '@esbuild/aix-ppc64': 0.27.3 836 + '@esbuild/android-arm': 0.27.3 837 + '@esbuild/android-arm64': 0.27.3 838 + '@esbuild/android-x64': 0.27.3 839 + '@esbuild/darwin-arm64': 0.27.3 840 + '@esbuild/darwin-x64': 0.27.3 841 + '@esbuild/freebsd-arm64': 0.27.3 842 + '@esbuild/freebsd-x64': 0.27.3 843 + '@esbuild/linux-arm': 0.27.3 844 + '@esbuild/linux-arm64': 0.27.3 845 + '@esbuild/linux-ia32': 0.27.3 846 + '@esbuild/linux-loong64': 0.27.3 847 + '@esbuild/linux-mips64el': 0.27.3 848 + '@esbuild/linux-ppc64': 0.27.3 849 + '@esbuild/linux-riscv64': 0.27.3 850 + '@esbuild/linux-s390x': 0.27.3 851 + '@esbuild/linux-x64': 0.27.3 852 + '@esbuild/netbsd-arm64': 0.27.3 853 + '@esbuild/netbsd-x64': 0.27.3 854 + '@esbuild/openbsd-arm64': 0.27.3 855 + '@esbuild/openbsd-x64': 0.27.3 856 + '@esbuild/openharmony-arm64': 0.27.3 857 + '@esbuild/sunos-x64': 0.27.3 858 + '@esbuild/win32-arm64': 0.27.3 859 + '@esbuild/win32-ia32': 0.27.3 860 + '@esbuild/win32-x64': 0.27.3 861 + 862 + fast-glob@3.3.3: 863 + dependencies: 864 + '@nodelib/fs.stat': 2.0.5 865 + '@nodelib/fs.walk': 1.2.8 866 + glob-parent: 5.1.2 867 + merge2: 1.4.1 868 + micromatch: 4.0.8 869 + 870 + fastq@1.20.1: 871 + dependencies: 872 + reusify: 1.1.0 873 + 874 + fill-range@7.1.1: 875 + dependencies: 876 + to-regex-range: 5.0.1 877 + 878 + foreground-child@3.3.1: 879 + dependencies: 880 + cross-spawn: 7.0.6 881 + signal-exit: 4.1.0 882 + 883 + fsevents@2.3.3: 884 + optional: true 885 + 886 + get-tsconfig@4.13.6: 887 + dependencies: 888 + resolve-pkg-maps: 1.0.0 889 + 890 + glob-parent@5.1.2: 891 + dependencies: 892 + is-glob: 4.0.3 893 + 894 + glob@11.1.0: 895 + dependencies: 896 + foreground-child: 3.3.1 897 + jackspeak: 4.2.1 898 + minimatch: 10.1.2 899 + minipass: 7.1.2 900 + package-json-from-dist: 1.0.1 901 + path-scurry: 2.0.1 902 + 903 + has-flag@4.0.0: {} 904 + 905 + hono@4.11.8: {} 906 + 907 + is-extglob@2.1.1: {} 908 + 909 + is-glob@4.0.3: 910 + dependencies: 911 + is-extglob: 2.1.1 912 + 913 + is-number@7.0.0: {} 914 + 915 + isexe@2.0.0: {} 916 + 917 + iso-datestring-validator@2.2.2: {} 918 + 919 + jackspeak@4.2.1: 920 + dependencies: 921 + '@isaacs/cliui': 9.0.0 922 + 923 + lru-cache@11.2.5: {} 924 + 925 + merge2@1.4.1: {} 926 + 927 + micromatch@4.0.8: 928 + dependencies: 929 + braces: 3.0.3 930 + picomatch: 2.3.1 931 + 932 + minimatch@10.1.2: 933 + dependencies: 934 + '@isaacs/brace-expansion': 5.0.1 935 + 936 + minimatch@5.1.6: 937 + dependencies: 938 + brace-expansion: 2.0.2 939 + 940 + minipass@7.1.2: {} 941 + 942 + mkdirp@1.0.4: {} 943 + 944 + multiformats@9.9.0: {} 945 + 946 + package-json-from-dist@1.0.1: {} 947 + 948 + path-browserify@1.0.1: {} 949 + 950 + path-key@3.1.1: {} 951 + 952 + path-scurry@2.0.1: 953 + dependencies: 954 + lru-cache: 11.2.5 955 + minipass: 7.1.2 956 + 957 + picomatch@2.3.1: {} 958 + 959 + prettier@3.8.1: {} 960 + 961 + queue-microtask@1.2.3: {} 962 + 963 + resolve-pkg-maps@1.0.0: {} 964 + 965 + reusify@1.1.0: {} 966 + 967 + run-parallel@1.2.0: 968 + dependencies: 969 + queue-microtask: 1.2.3 970 + 971 + shebang-command@2.0.0: 972 + dependencies: 973 + shebang-regex: 3.0.0 974 + 975 + shebang-regex@3.0.0: {} 976 + 977 + signal-exit@4.1.0: {} 978 + 979 + supports-color@7.2.0: 980 + dependencies: 981 + has-flag: 4.0.0 982 + 983 + tlds@1.261.0: {} 984 + 985 + to-regex-range@5.0.1: 986 + dependencies: 987 + is-number: 7.0.0 988 + 989 + ts-morph@16.0.0: 990 + dependencies: 991 + '@ts-morph/common': 0.17.0 992 + code-block-writer: 11.0.3 993 + 994 + tslib@2.8.1: {} 995 + 996 + tsx@4.21.0: 997 + dependencies: 998 + esbuild: 0.27.3 999 + get-tsconfig: 4.13.6 1000 + optionalDependencies: 1001 + fsevents: 2.3.3 1002 + 1003 + turbo-darwin-64@2.8.3: 1004 + optional: true 1005 + 1006 + turbo-darwin-arm64@2.8.3: 1007 + optional: true 1008 + 1009 + turbo-linux-64@2.8.3: 1010 + optional: true 1011 + 1012 + turbo-linux-arm64@2.8.3: 1013 + optional: true 1014 + 1015 + turbo-windows-64@2.8.3: 1016 + optional: true 1017 + 1018 + turbo-windows-arm64@2.8.3: 1019 + optional: true 1020 + 1021 + turbo@2.8.3: 1022 + optionalDependencies: 1023 + turbo-darwin-64: 2.8.3 1024 + turbo-darwin-arm64: 2.8.3 1025 + turbo-linux-64: 2.8.3 1026 + turbo-linux-arm64: 2.8.3 1027 + turbo-windows-64: 2.8.3 1028 + turbo-windows-arm64: 2.8.3 1029 + 1030 + typed-html@3.0.1: {} 1031 + 1032 + typed-htmx@0.3.1: 1033 + dependencies: 1034 + typed-html: 3.0.1 1035 + 1036 + typescript@5.9.3: {} 1037 + 1038 + uint8arrays@3.0.0: 1039 + dependencies: 1040 + multiformats: 9.9.0 1041 + 1042 + undici-types@6.21.0: {} 1043 + 1044 + unicode-segmenter@0.14.5: {} 1045 + 1046 + which@2.0.2: 1047 + dependencies: 1048 + isexe: 2.0.0 1049 + 1050 + yaml@2.8.2: {} 1051 + 1052 + yesno@0.4.0: {} 1053 + 1054 + zod@3.25.76: {}
+2
pnpm-workspace.yaml
··· 1 + packages: 2 + - "packages/*"
+17
tsconfig.base.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2022", 4 + "module": "Node16", 5 + "moduleResolution": "Node16", 6 + "declaration": true, 7 + "declarationMap": true, 8 + "sourceMap": true, 9 + "strict": true, 10 + "esModuleInterop": true, 11 + "skipLibCheck": true, 12 + "forceConsistentCasingInFileNames": true, 13 + "resolveJsonModule": true, 14 + "jsx": "react-jsx", 15 + "jsxImportSource": "hono/jsx" 16 + } 17 + }
+21
turbo.json
··· 1 + { 2 + "$schema": "https://turborepo.dev/schema.json", 3 + "tasks": { 4 + "build": { 5 + "dependsOn": ["^build"], 6 + "inputs": ["src/**", "lexicons/**", "scripts/**", "package.json", "tsconfig.json"], 7 + "outputs": ["dist/**"] 8 + }, 9 + "dev": { 10 + "dependsOn": ["^build"], 11 + "cache": false, 12 + "persistent": true 13 + }, 14 + "lint": { 15 + "dependsOn": ["^build"] 16 + }, 17 + "clean": { 18 + "cache": false 19 + } 20 + } 21 + }