the statusphere demo reworked into a vite/react app in a monorepo

use xrpc

+1 -1
README.md
··· 68 68 69 69 The backend server will: 70 70 71 - - Serve the API at `/api/*` endpoints 71 + - Serve the API at `/xrpc/*` and `/oauth/*` endpoints 72 72 - Serve the frontend static files from the client's build directory 73 73 - Handle client-side routing by serving index.html for all non-API routes 74 74
+39
lexicons/xyz/statusphere/getStatuses.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "xyz.statusphere.getStatuses", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "Get a list of the most recent statuses on the network.", 8 + "parameters": { 9 + "type": "params", 10 + "properties": { 11 + "limit": { 12 + "type": "integer", 13 + "minimum": 1, 14 + "maximum": 100, 15 + "default": 50 16 + }, 17 + "cursor": { "type": "string" } 18 + } 19 + }, 20 + "output": { 21 + "encoding": "application/json", 22 + "schema": { 23 + "type": "object", 24 + "required": ["statuses"], 25 + "properties": { 26 + "cursor": { "type": "string" }, 27 + "statuses": { 28 + "type": "array", 29 + "items": { 30 + "type": "ref", 31 + "ref": "xyz.statusphere.defs#statusView" 32 + } 33 + } 34 + } 35 + } 36 + } 37 + } 38 + } 39 + }
+31
lexicons/xyz/statusphere/getUser.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "xyz.statusphere.getUser", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "Get the current user's profile and status.", 8 + "parameters": { 9 + "type": "params", 10 + "properties": {} 11 + }, 12 + "output": { 13 + "encoding": "application/json", 14 + "schema": { 15 + "type": "object", 16 + "required": ["profile"], 17 + "properties": { 18 + "profile": { 19 + "type": "ref", 20 + "ref": "app.bsky.actor.defs#profileView" 21 + }, 22 + "status": { 23 + "type": "ref", 24 + "ref": "xyz.statusphere.defs#statusView" 25 + } 26 + } 27 + } 28 + } 29 + } 30 + } 31 + }
+38
lexicons/xyz/statusphere/sendStatus.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "xyz.statusphere.sendStatus", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Send a status into the ATmosphere.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["status"], 13 + "properties": { 14 + "status": { 15 + "type": "string", 16 + "minLength": 1, 17 + "maxGraphemes": 1, 18 + "maxLength": 32 19 + } 20 + } 21 + } 22 + }, 23 + "output": { 24 + "encoding": "application/json", 25 + "schema": { 26 + "type": "object", 27 + "required": ["status"], 28 + "properties": { 29 + "status": { 30 + "type": "ref", 31 + "ref": "xyz.statusphere.defs#statusView" 32 + } 33 + } 34 + } 35 + } 36 + } 37 + } 38 + }
+1 -1
package.json
··· 10 10 "dev:lexicon": "pnpm --filter @statusphere/lexicon dev", 11 11 "dev:appview": "pnpm --filter @statusphere/appview dev", 12 12 "dev:client": "pnpm --filter @statusphere/client dev", 13 - "lexgen": "pnpm --filter @statusphere/lexicon lexgen", 13 + "lexgen": "pnpm -r lexgen", 14 14 "build": "pnpm build:lexicon && pnpm build:client && pnpm build:appview", 15 15 "build:lexicon": "pnpm --filter @statusphere/lexicon build", 16 16 "build:appview": "pnpm --filter @statusphere/appview build",
+4
packages/appview/package.json
··· 10 10 "dev": "tsx watch --clear-screen=false src/index.ts | pino-pretty", 11 11 "build": "tsup", 12 12 "start": "node dist/index.js", 13 + "lexgen": "lex gen-server ./src/lexicons ../../lexicons/xyz/statusphere/* ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/* --yes && pnpm format", 13 14 "clean": "rimraf dist coverage", 14 15 "format": "prettier --write src", 15 16 "typecheck": "tsc --noEmit" ··· 25 26 "@atproto/xrpc-server": "^0.7.11", 26 27 "@statusphere/lexicon": "workspace:*", 27 28 "better-sqlite3": "^11.8.1", 29 + "compression": "^1.8.0", 28 30 "cors": "^2.8.5", 29 31 "dotenv": "^16.4.7", 30 32 "envalid": "^8.0.0", ··· 35 37 "pino": "^9.6.0" 36 38 }, 37 39 "devDependencies": { 40 + "@atproto/lex-cli": "^0.6.1", 38 41 "@types/better-sqlite3": "^7.6.12", 42 + "@types/compression": "^1.7.5", 39 43 "@types/cors": "^2.8.17", 40 44 "@types/express": "^5.0.0", 41 45 "@types/node": "^22.13.8",
+13
packages/appview/src/api/health.ts
··· 1 + import { Router } from 'express' 2 + 3 + import { AppContext } from '#/context' 4 + 5 + export const createRouter = (ctx: AppContext) => { 6 + const router = Router() 7 + 8 + router.get('/health', async function (req, res) { 9 + res.status(200).send('OK') 10 + }) 11 + 12 + return router 13 + }
+15
packages/appview/src/api/index.ts
··· 1 + import { AppContext } from '#/context' 2 + import { Server } from '#/lexicons' 3 + import getStatuses from './lexicons/getStatuses' 4 + import getUser from './lexicons/getUser' 5 + import sendStatus from './lexicons/sendStatus' 6 + 7 + export * as health from './health' 8 + export * as oauth from './oauth' 9 + 10 + export default function (server: Server, ctx: AppContext) { 11 + getStatuses(server, ctx) 12 + sendStatus(server, ctx) 13 + getUser(server, ctx) 14 + return server 15 + }
+26
packages/appview/src/api/lexicons/getStatuses.ts
··· 1 + import { AppContext } from '#/context' 2 + import { Server } from '#/lexicons' 3 + import { statusToStatusView } from '#/lib/hydrate' 4 + 5 + export default function (server: Server, ctx: AppContext) { 6 + server.xyz.statusphere.getStatuses({ 7 + handler: async ({ params }) => { 8 + // Fetch data stored in our SQLite 9 + const statuses = await ctx.db 10 + .selectFrom('status') 11 + .selectAll() 12 + .orderBy('indexedAt', 'desc') 13 + .limit(params.limit) 14 + .execute() 15 + 16 + return { 17 + encoding: 'application/json', 18 + body: { 19 + statuses: await Promise.all( 20 + statuses.map((status) => statusToStatusView(status, ctx)), 21 + ), 22 + }, 23 + } 24 + }, 25 + }) 26 + }
+61
packages/appview/src/api/lexicons/getUser.ts
··· 1 + import { AuthRequiredError } from '@atproto/xrpc-server' 2 + import { AppBskyActorProfile } from '@statusphere/lexicon' 3 + 4 + import { AppContext } from '#/context' 5 + import { Server } from '#/lexicons' 6 + import { bskyProfileToProfileView, statusToStatusView } from '#/lib/hydrate' 7 + import { getSessionAgent } from '#/session' 8 + 9 + export default function (server: Server, ctx: AppContext) { 10 + server.xyz.statusphere.getUser({ 11 + handler: async ({ req, res }) => { 12 + const agent = await getSessionAgent(req, res, ctx) 13 + if (!agent) { 14 + throw new AuthRequiredError('Authentication required') 15 + } 16 + 17 + const did = agent.assertDid 18 + 19 + const profileResponse = await agent.com.atproto.repo 20 + .getRecord({ 21 + repo: did, 22 + collection: 'app.bsky.actor.profile', 23 + rkey: 'self', 24 + }) 25 + .catch(() => undefined) 26 + 27 + const profileRecord = profileResponse?.data 28 + let profile: AppBskyActorProfile.Record = {} as AppBskyActorProfile.Record 29 + 30 + if (profileRecord && AppBskyActorProfile.isRecord(profileRecord.value)) { 31 + const validated = AppBskyActorProfile.validateRecord( 32 + profileRecord.value, 33 + ) 34 + if (validated.success) { 35 + profile = profileRecord.value 36 + } else { 37 + ctx.logger.error( 38 + { err: validated.error }, 39 + 'Failed to validate user profile', 40 + ) 41 + } 42 + } 43 + 44 + // Fetch user status 45 + const status = await ctx.db 46 + .selectFrom('status') 47 + .selectAll() 48 + .where('authorDid', '=', did) 49 + .orderBy('indexedAt', 'desc') 50 + .executeTakeFirst() 51 + 52 + return { 53 + encoding: 'application/json', 54 + body: { 55 + profile: await bskyProfileToProfileView(did, profile, ctx), 56 + status: status ? await statusToStatusView(status, ctx) : undefined, 57 + }, 58 + } 59 + }, 60 + }) 61 + }
+79
packages/appview/src/api/lexicons/sendStatus.ts
··· 1 + import { TID } from '@atproto/common' 2 + import { 3 + AuthRequiredError, 4 + InvalidRequestError, 5 + UpstreamFailureError, 6 + } from '@atproto/xrpc-server' 7 + import { XyzStatusphereStatus } from '@statusphere/lexicon' 8 + 9 + import { AppContext } from '#/context' 10 + import { Server } from '#/lexicons' 11 + import { statusToStatusView } from '#/lib/hydrate' 12 + import { getSessionAgent } from '#/session' 13 + 14 + export default function (server: Server, ctx: AppContext) { 15 + server.xyz.statusphere.sendStatus({ 16 + handler: async ({ input, req, res }) => { 17 + const agent = await getSessionAgent(req, res, ctx) 18 + if (!agent) { 19 + throw new AuthRequiredError('Authentication required') 20 + } 21 + 22 + // Construct & validate their status record 23 + const rkey = TID.nextStr() 24 + const record = { 25 + $type: 'xyz.statusphere.status', 26 + status: input.body.status, 27 + createdAt: new Date().toISOString(), 28 + } 29 + 30 + const validation = XyzStatusphereStatus.validateRecord(record) 31 + if (!validation.success) { 32 + throw new InvalidRequestError('Invalid status') 33 + } 34 + 35 + let uri 36 + try { 37 + // Write the status record to the user's repository 38 + const response = await agent.com.atproto.repo.putRecord({ 39 + repo: agent.assertDid, 40 + collection: 'xyz.statusphere.status', 41 + rkey, 42 + record: validation.value, 43 + validate: false, 44 + }) 45 + uri = response.data.uri 46 + } catch (err) { 47 + throw new UpstreamFailureError('Failed to write record') 48 + } 49 + 50 + const optimisticStatus = { 51 + uri, 52 + authorDid: agent.assertDid, 53 + status: record.status, 54 + createdAt: record.createdAt, 55 + indexedAt: new Date().toISOString(), 56 + } 57 + 58 + try { 59 + // Optimistically update our SQLite 60 + // This isn't strictly necessary because the write event will be 61 + // handled in #/firehose/ingestor.ts, but it ensures that future reads 62 + // will be up-to-date after this method finishes. 63 + await ctx.db.insertInto('status').values(optimisticStatus).execute() 64 + } catch (err) { 65 + ctx.logger.warn( 66 + { err }, 67 + 'failed to update computed view; ignoring as it should be caught by the firehose', 68 + ) 69 + } 70 + 71 + return { 72 + encoding: 'application/json', 73 + body: { 74 + status: await statusToStatusView(optimisticStatus, ctx), 75 + }, 76 + } 77 + }, 78 + }) 79 + }
+83
packages/appview/src/api/oauth.ts
··· 1 + import { OAuthResolverError } from '@atproto/oauth-client-node' 2 + import { isValidHandle } from '@atproto/syntax' 3 + import express from 'express' 4 + 5 + import { AppContext } from '#/context' 6 + import { getSession } from '#/session' 7 + 8 + export const createRouter = (ctx: AppContext) => { 9 + const router = express.Router() 10 + 11 + // OAuth metadata 12 + router.get('/client-metadata.json', (_req, res) => { 13 + res.json(ctx.oauthClient.clientMetadata) 14 + }) 15 + 16 + // OAuth callback to complete session creation 17 + router.get('/oauth/callback', async (req, res) => { 18 + // Get the query parameters from the URL 19 + const params = new URLSearchParams(req.originalUrl.split('?')[1]) 20 + 21 + try { 22 + const { session } = await ctx.oauthClient.callback(params) 23 + 24 + // Use the common session options 25 + const clientSession = await getSession(req, res) 26 + 27 + // Set the DID on the session 28 + clientSession.did = session.did 29 + await clientSession.save() 30 + 31 + // Get the origin and determine appropriate redirect 32 + const host = req.get('host') || '' 33 + const protocol = req.protocol || 'http' 34 + const baseUrl = `${protocol}://${host}` 35 + 36 + ctx.logger.info( 37 + `OAuth callback successful, redirecting to ${baseUrl}/oauth-callback`, 38 + ) 39 + 40 + // Redirect to the frontend oauth-callback page 41 + res.redirect('/oauth-callback') 42 + } catch (err) { 43 + ctx.logger.error({ err }, 'oauth callback failed') 44 + 45 + // Handle error redirect - stay on same domain 46 + res.redirect('/oauth-callback?error=auth') 47 + } 48 + }) 49 + 50 + // Login handler 51 + router.post('/oauth/initiate', async (req, res) => { 52 + // Validate 53 + const handle = req.body?.handle 54 + if (typeof handle !== 'string' || !isValidHandle(handle)) { 55 + res.status(400).json({ error: 'Invalid handle' }) 56 + return 57 + } 58 + 59 + // Initiate the OAuth flow 60 + try { 61 + const url = await ctx.oauthClient.authorize(handle, { 62 + scope: 'atproto transition:generic', 63 + }) 64 + res.json({ redirectUrl: url.toString() }) 65 + } catch (err) { 66 + ctx.logger.error({ err }, 'oauth authorize failed') 67 + const errorMsg = 68 + err instanceof OAuthResolverError 69 + ? err.message 70 + : "Couldn't initiate login" 71 + res.status(500).json({ error: errorMsg }) 72 + } 73 + }) 74 + 75 + // Logout handler 76 + router.post('/oauth/logout', async (req, res) => { 77 + const session = await getSession(req, res) 78 + session.destroy() 79 + res.json({ success: true }) 80 + }) 81 + 82 + return router 83 + }
+3 -3
packages/appview/src/auth/client.ts
··· 17 17 clientMetadata: { 18 18 client_name: 'Statusphere React App', 19 19 client_id: publicUrl 20 - ? `${url}/api/client-metadata.json` 21 - : `http://localhost?redirect_uri=${enc(`${url}/api/oauth/callback`)}&scope=${enc('atproto transition:generic')}`, 20 + ? `${url}/client-metadata.json` 21 + : `http://localhost?redirect_uri=${enc(`${url}/oauth/callback`)}&scope=${enc('atproto transition:generic')}`, 22 22 client_uri: url, 23 - redirect_uris: [`${url}/api/oauth/callback`], 23 + redirect_uris: [`${url}/oauth/callback`], 24 24 scope: 'atproto transition:generic', 25 25 grant_types: ['authorization_code', 'refresh_token'], 26 26 response_types: ['code'],
+15
packages/appview/src/context.ts
··· 1 + import { OAuthClient } from '@atproto/oauth-client-node' 2 + import { Firehose } from '@atproto/sync' 3 + import pino from 'pino' 4 + 5 + import { Database } from './db' 6 + import { BidirectionalResolver } from './id-resolver' 7 + 8 + // Application state passed to the router and elsewhere 9 + export type AppContext = { 10 + db: Database 11 + ingester: Firehose 12 + logger: pino.Logger 13 + oauthClient: OAuthClient 14 + resolver: BidirectionalResolver 15 + }
+14
packages/appview/src/error.ts
··· 1 + import { XRPCError } from '@atproto/xrpc-server' 2 + import { ErrorRequestHandler } from 'express' 3 + 4 + import { AppContext } from '#/context' 5 + 6 + export const createHandler: (ctx: AppContext) => ErrorRequestHandler = 7 + (ctx) => (err, _req, res, next) => { 8 + ctx.logger.error('unexpected internal server error', err) 9 + if (res.headersSent) { 10 + return next(err) 11 + } 12 + const serverError = XRPCError.fromError(err) 13 + res.status(serverError.type).json(serverError.payload) 14 + }
+34 -74
packages/appview/src/index.ts
··· 2 2 import fs from 'node:fs' 3 3 import type http from 'node:http' 4 4 import path from 'node:path' 5 - import type { OAuthClient } from '@atproto/oauth-client-node' 6 - import { Firehose } from '@atproto/sync' 5 + import { DAY, SECOND } from '@atproto/common' 6 + import compression from 'compression' 7 7 import cors from 'cors' 8 - import express, { type Express } from 'express' 8 + import express from 'express' 9 9 import { pino } from 'pino' 10 10 11 + import API, { health, oauth } from '#/api' 11 12 import { createClient } from '#/auth/client' 13 + import { AppContext } from '#/context' 12 14 import { createDb, migrateToLatest } from '#/db' 13 - import type { Database } from '#/db' 14 - import { 15 - BidirectionalResolver, 16 - createBidirectionalResolver, 17 - createIdResolver, 18 - } from '#/id-resolver' 15 + import * as error from '#/error' 16 + import { createBidirectionalResolver, createIdResolver } from '#/id-resolver' 19 17 import { createIngester } from '#/ingester' 18 + import { createServer } from '#/lexicons' 20 19 import { env } from '#/lib/env' 21 - import { createRouter } from '#/routes' 22 - 23 - // Application state passed to the router and elsewhere 24 - export type AppContext = { 25 - db: Database 26 - ingester: Firehose 27 - logger: pino.Logger 28 - oauthClient: OAuthClient 29 - resolver: BidirectionalResolver 30 - } 31 20 32 21 export class Server { 33 22 constructor( ··· 60 49 // Subscribe to events on the firehose 61 50 ingester.start() 62 51 63 - // Create our server 64 - const app: Express = express() 65 - app.set('trust proxy', true) 66 - 67 - // CORS configuration based on environment 68 - if (env.NODE_ENV === 'development') { 69 - // In development, allow multiple origins including ngrok 70 - app.use( 71 - cors({ 72 - origin: function (origin, callback) { 73 - // Allow requests with no origin (like mobile apps, curl) 74 - if (!origin) return callback(null, true) 75 - 76 - // List of allowed origins 77 - const allowedOrigins = [ 78 - 'http://localhost:3000', // Standard React port 79 - 'http://127.0.0.1:3000', // Alternative React address 80 - ] 81 - 82 - // Check if the request origin is in our allowed list or is an ngrok domain 83 - if (allowedOrigins.indexOf(origin) !== -1) { 84 - callback(null, true) 85 - } else { 86 - console.warn(`⚠️ CORS blocked origin: ${origin}`) 87 - callback(null, false) 88 - } 89 - }, 90 - credentials: true, 91 - methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], 92 - allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'], 93 - }), 94 - ) 95 - } else { 96 - // In production, CORS is not needed if frontend and API are on same domain 97 - // But we'll still enable it for flexibility with minimal configuration 98 - app.use( 99 - cors({ 100 - origin: true, // Use req.origin, which means same-origin requests will always be allowed 101 - credentials: true, 102 - }), 103 - ) 104 - } 105 - 106 - // Routes & middlewares 107 - const router = createRouter(ctx) 52 + const app = express() 53 + app.use(cors({ maxAge: DAY / SECOND })) 54 + app.use(compression()) 108 55 app.use(express.json()) 109 56 app.use(express.urlencoded({ extended: true })) 110 57 111 - app.use('/api', router) 58 + // Create our server 59 + let server = createServer({ 60 + validateResponse: env.isDevelopment, 61 + payload: { 62 + jsonLimit: 100 * 1024, // 100kb 63 + textLimit: 100 * 1024, // 100kb 64 + // no blobs 65 + blobLimit: 0, 66 + }, 67 + }) 68 + 69 + server = API(server, ctx) 70 + 71 + app.use(health.createRouter(ctx)) 72 + app.use(oauth.createRouter(ctx)) 73 + app.use(server.xrpc.router) 74 + app.use(error.createHandler(ctx)) 112 75 113 76 // Serve static files from the frontend build - prod only 114 77 if (env.isProduction) { ··· 124 87 // Serve static files 125 88 app.use(express.static(frontendPath)) 126 89 127 - // Heathcheck 128 - app.get('/health', (req, res) => { 129 - res.status(200).json({ status: 'ok' }) 130 - }) 131 - 132 90 // For any other requests, send the index.html file 133 91 app.get('*', (req, res) => { 134 92 // Only handle non-API paths 135 - if (!req.path.startsWith('/api/')) { 93 + if (!req.path.startsWith('/xrpc/')) { 136 94 res.sendFile(path.join(frontendPath, 'index.html')) 137 95 } else { 138 96 res.status(404).json({ error: 'API endpoint not found' }) ··· 144 102 res.sendStatus(404) 145 103 }) 146 104 } 105 + } else { 106 + server.xrpc.router.set('trust proxy', true) 147 107 } 148 108 149 109 // Use the port from env (should be 3001 for the API server) 150 - const server = app.listen(env.PORT) 151 - await events.once(server, 'listening') 110 + const httpServer = app.listen(env.PORT) 111 + await events.once(httpServer, 'listening') 152 112 logger.info( 153 113 `API Server (${NODE_ENV}) running on port http://${HOST}:${env.PORT}`, 154 114 ) 155 115 156 - return new Server(app, server, ctx) 116 + return new Server(app, httpServer, ctx) 157 117 } 158 118 159 119 async close() {
+286
packages/appview/src/lexicons/index.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { 5 + AuthVerifier, 6 + createServer as createXrpcServer, 7 + StreamAuthVerifier, 8 + Options as XrpcOptions, 9 + Server as XrpcServer, 10 + } from '@atproto/xrpc-server' 11 + 12 + import { schemas } from './lexicons.js' 13 + import * as ComAtprotoRepoApplyWrites from './types/com/atproto/repo/applyWrites.js' 14 + import * as ComAtprotoRepoCreateRecord from './types/com/atproto/repo/createRecord.js' 15 + import * as ComAtprotoRepoDeleteRecord from './types/com/atproto/repo/deleteRecord.js' 16 + import * as ComAtprotoRepoDescribeRepo from './types/com/atproto/repo/describeRepo.js' 17 + import * as ComAtprotoRepoGetRecord from './types/com/atproto/repo/getRecord.js' 18 + import * as ComAtprotoRepoImportRepo from './types/com/atproto/repo/importRepo.js' 19 + import * as ComAtprotoRepoListMissingBlobs from './types/com/atproto/repo/listMissingBlobs.js' 20 + import * as ComAtprotoRepoListRecords from './types/com/atproto/repo/listRecords.js' 21 + import * as ComAtprotoRepoPutRecord from './types/com/atproto/repo/putRecord.js' 22 + import * as ComAtprotoRepoUploadBlob from './types/com/atproto/repo/uploadBlob.js' 23 + import * as XyzStatusphereGetStatuses from './types/xyz/statusphere/getStatuses.js' 24 + import * as XyzStatusphereGetUser from './types/xyz/statusphere/getUser.js' 25 + import * as XyzStatusphereSendStatus from './types/xyz/statusphere/sendStatus.js' 26 + 27 + export function createServer(options?: XrpcOptions): Server { 28 + return new Server(options) 29 + } 30 + 31 + export class Server { 32 + xrpc: XrpcServer 33 + xyz: XyzNS 34 + com: ComNS 35 + app: AppNS 36 + 37 + constructor(options?: XrpcOptions) { 38 + this.xrpc = createXrpcServer(schemas, options) 39 + this.xyz = new XyzNS(this) 40 + this.com = new ComNS(this) 41 + this.app = new AppNS(this) 42 + } 43 + } 44 + 45 + export class XyzNS { 46 + _server: Server 47 + statusphere: XyzStatusphereNS 48 + 49 + constructor(server: Server) { 50 + this._server = server 51 + this.statusphere = new XyzStatusphereNS(server) 52 + } 53 + } 54 + 55 + export class XyzStatusphereNS { 56 + _server: Server 57 + 58 + constructor(server: Server) { 59 + this._server = server 60 + } 61 + 62 + getStatuses<AV extends AuthVerifier>( 63 + cfg: ConfigOf< 64 + AV, 65 + XyzStatusphereGetStatuses.Handler<ExtractAuth<AV>>, 66 + XyzStatusphereGetStatuses.HandlerReqCtx<ExtractAuth<AV>> 67 + >, 68 + ) { 69 + const nsid = 'xyz.statusphere.getStatuses' // @ts-ignore 70 + return this._server.xrpc.method(nsid, cfg) 71 + } 72 + 73 + getUser<AV extends AuthVerifier>( 74 + cfg: ConfigOf< 75 + AV, 76 + XyzStatusphereGetUser.Handler<ExtractAuth<AV>>, 77 + XyzStatusphereGetUser.HandlerReqCtx<ExtractAuth<AV>> 78 + >, 79 + ) { 80 + const nsid = 'xyz.statusphere.getUser' // @ts-ignore 81 + return this._server.xrpc.method(nsid, cfg) 82 + } 83 + 84 + sendStatus<AV extends AuthVerifier>( 85 + cfg: ConfigOf< 86 + AV, 87 + XyzStatusphereSendStatus.Handler<ExtractAuth<AV>>, 88 + XyzStatusphereSendStatus.HandlerReqCtx<ExtractAuth<AV>> 89 + >, 90 + ) { 91 + const nsid = 'xyz.statusphere.sendStatus' // @ts-ignore 92 + return this._server.xrpc.method(nsid, cfg) 93 + } 94 + } 95 + 96 + export class ComNS { 97 + _server: Server 98 + atproto: ComAtprotoNS 99 + 100 + constructor(server: Server) { 101 + this._server = server 102 + this.atproto = new ComAtprotoNS(server) 103 + } 104 + } 105 + 106 + export class ComAtprotoNS { 107 + _server: Server 108 + repo: ComAtprotoRepoNS 109 + 110 + constructor(server: Server) { 111 + this._server = server 112 + this.repo = new ComAtprotoRepoNS(server) 113 + } 114 + } 115 + 116 + export class ComAtprotoRepoNS { 117 + _server: Server 118 + 119 + constructor(server: Server) { 120 + this._server = server 121 + } 122 + 123 + applyWrites<AV extends AuthVerifier>( 124 + cfg: ConfigOf< 125 + AV, 126 + ComAtprotoRepoApplyWrites.Handler<ExtractAuth<AV>>, 127 + ComAtprotoRepoApplyWrites.HandlerReqCtx<ExtractAuth<AV>> 128 + >, 129 + ) { 130 + const nsid = 'com.atproto.repo.applyWrites' // @ts-ignore 131 + return this._server.xrpc.method(nsid, cfg) 132 + } 133 + 134 + createRecord<AV extends AuthVerifier>( 135 + cfg: ConfigOf< 136 + AV, 137 + ComAtprotoRepoCreateRecord.Handler<ExtractAuth<AV>>, 138 + ComAtprotoRepoCreateRecord.HandlerReqCtx<ExtractAuth<AV>> 139 + >, 140 + ) { 141 + const nsid = 'com.atproto.repo.createRecord' // @ts-ignore 142 + return this._server.xrpc.method(nsid, cfg) 143 + } 144 + 145 + deleteRecord<AV extends AuthVerifier>( 146 + cfg: ConfigOf< 147 + AV, 148 + ComAtprotoRepoDeleteRecord.Handler<ExtractAuth<AV>>, 149 + ComAtprotoRepoDeleteRecord.HandlerReqCtx<ExtractAuth<AV>> 150 + >, 151 + ) { 152 + const nsid = 'com.atproto.repo.deleteRecord' // @ts-ignore 153 + return this._server.xrpc.method(nsid, cfg) 154 + } 155 + 156 + describeRepo<AV extends AuthVerifier>( 157 + cfg: ConfigOf< 158 + AV, 159 + ComAtprotoRepoDescribeRepo.Handler<ExtractAuth<AV>>, 160 + ComAtprotoRepoDescribeRepo.HandlerReqCtx<ExtractAuth<AV>> 161 + >, 162 + ) { 163 + const nsid = 'com.atproto.repo.describeRepo' // @ts-ignore 164 + return this._server.xrpc.method(nsid, cfg) 165 + } 166 + 167 + getRecord<AV extends AuthVerifier>( 168 + cfg: ConfigOf< 169 + AV, 170 + ComAtprotoRepoGetRecord.Handler<ExtractAuth<AV>>, 171 + ComAtprotoRepoGetRecord.HandlerReqCtx<ExtractAuth<AV>> 172 + >, 173 + ) { 174 + const nsid = 'com.atproto.repo.getRecord' // @ts-ignore 175 + return this._server.xrpc.method(nsid, cfg) 176 + } 177 + 178 + importRepo<AV extends AuthVerifier>( 179 + cfg: ConfigOf< 180 + AV, 181 + ComAtprotoRepoImportRepo.Handler<ExtractAuth<AV>>, 182 + ComAtprotoRepoImportRepo.HandlerReqCtx<ExtractAuth<AV>> 183 + >, 184 + ) { 185 + const nsid = 'com.atproto.repo.importRepo' // @ts-ignore 186 + return this._server.xrpc.method(nsid, cfg) 187 + } 188 + 189 + listMissingBlobs<AV extends AuthVerifier>( 190 + cfg: ConfigOf< 191 + AV, 192 + ComAtprotoRepoListMissingBlobs.Handler<ExtractAuth<AV>>, 193 + ComAtprotoRepoListMissingBlobs.HandlerReqCtx<ExtractAuth<AV>> 194 + >, 195 + ) { 196 + const nsid = 'com.atproto.repo.listMissingBlobs' // @ts-ignore 197 + return this._server.xrpc.method(nsid, cfg) 198 + } 199 + 200 + listRecords<AV extends AuthVerifier>( 201 + cfg: ConfigOf< 202 + AV, 203 + ComAtprotoRepoListRecords.Handler<ExtractAuth<AV>>, 204 + ComAtprotoRepoListRecords.HandlerReqCtx<ExtractAuth<AV>> 205 + >, 206 + ) { 207 + const nsid = 'com.atproto.repo.listRecords' // @ts-ignore 208 + return this._server.xrpc.method(nsid, cfg) 209 + } 210 + 211 + putRecord<AV extends AuthVerifier>( 212 + cfg: ConfigOf< 213 + AV, 214 + ComAtprotoRepoPutRecord.Handler<ExtractAuth<AV>>, 215 + ComAtprotoRepoPutRecord.HandlerReqCtx<ExtractAuth<AV>> 216 + >, 217 + ) { 218 + const nsid = 'com.atproto.repo.putRecord' // @ts-ignore 219 + return this._server.xrpc.method(nsid, cfg) 220 + } 221 + 222 + uploadBlob<AV extends AuthVerifier>( 223 + cfg: ConfigOf< 224 + AV, 225 + ComAtprotoRepoUploadBlob.Handler<ExtractAuth<AV>>, 226 + ComAtprotoRepoUploadBlob.HandlerReqCtx<ExtractAuth<AV>> 227 + >, 228 + ) { 229 + const nsid = 'com.atproto.repo.uploadBlob' // @ts-ignore 230 + return this._server.xrpc.method(nsid, cfg) 231 + } 232 + } 233 + 234 + export class AppNS { 235 + _server: Server 236 + bsky: AppBskyNS 237 + 238 + constructor(server: Server) { 239 + this._server = server 240 + this.bsky = new AppBskyNS(server) 241 + } 242 + } 243 + 244 + export class AppBskyNS { 245 + _server: Server 246 + actor: AppBskyActorNS 247 + 248 + constructor(server: Server) { 249 + this._server = server 250 + this.actor = new AppBskyActorNS(server) 251 + } 252 + } 253 + 254 + export class AppBskyActorNS { 255 + _server: Server 256 + 257 + constructor(server: Server) { 258 + this._server = server 259 + } 260 + } 261 + 262 + type SharedRateLimitOpts<T> = { 263 + name: string 264 + calcKey?: (ctx: T) => string | null 265 + calcPoints?: (ctx: T) => number 266 + } 267 + type RouteRateLimitOpts<T> = { 268 + durationMs: number 269 + points: number 270 + calcKey?: (ctx: T) => string | null 271 + calcPoints?: (ctx: T) => number 272 + } 273 + type HandlerOpts = { blobLimit?: number } 274 + type HandlerRateLimitOpts<T> = SharedRateLimitOpts<T> | RouteRateLimitOpts<T> 275 + type ConfigOf<Auth, Handler, ReqCtx> = 276 + | Handler 277 + | { 278 + auth?: Auth 279 + opts?: HandlerOpts 280 + rateLimit?: HandlerRateLimitOpts<ReqCtx> | HandlerRateLimitOpts<ReqCtx>[] 281 + handler: Handler 282 + } 283 + type ExtractAuth<AV extends AuthVerifier | StreamAuthVerifier> = Extract< 284 + Awaited<ReturnType<AV>>, 285 + { credentials: unknown } 286 + >
+1303
packages/appview/src/lexicons/lexicons.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { 5 + LexiconDoc, 6 + Lexicons, 7 + ValidationError, 8 + ValidationResult, 9 + } from '@atproto/lexicon' 10 + 11 + import { $Typed, is$typed, maybe$typed } from './util.js' 12 + 13 + export const schemaDict = { 14 + XyzStatusphereDefs: { 15 + lexicon: 1, 16 + id: 'xyz.statusphere.defs', 17 + defs: { 18 + statusView: { 19 + type: 'object', 20 + required: ['uri', 'status', 'profile', 'createdAt'], 21 + properties: { 22 + uri: { 23 + type: 'string', 24 + format: 'at-uri', 25 + }, 26 + status: { 27 + type: 'string', 28 + minLength: 1, 29 + maxGraphemes: 1, 30 + maxLength: 32, 31 + }, 32 + createdAt: { 33 + type: 'string', 34 + format: 'datetime', 35 + }, 36 + profile: { 37 + type: 'ref', 38 + ref: 'lex:xyz.statusphere.defs#profileView', 39 + }, 40 + }, 41 + }, 42 + profileView: { 43 + type: 'object', 44 + required: ['did', 'handle'], 45 + properties: { 46 + did: { 47 + type: 'string', 48 + format: 'did', 49 + }, 50 + handle: { 51 + type: 'string', 52 + format: 'handle', 53 + }, 54 + }, 55 + }, 56 + }, 57 + }, 58 + XyzStatusphereGetStatuses: { 59 + lexicon: 1, 60 + id: 'xyz.statusphere.getStatuses', 61 + defs: { 62 + main: { 63 + type: 'query', 64 + description: 'Get a list of the most recent statuses on the network.', 65 + parameters: { 66 + type: 'params', 67 + properties: { 68 + limit: { 69 + type: 'integer', 70 + minimum: 1, 71 + maximum: 100, 72 + default: 50, 73 + }, 74 + cursor: { 75 + type: 'string', 76 + }, 77 + }, 78 + }, 79 + output: { 80 + encoding: 'application/json', 81 + schema: { 82 + type: 'object', 83 + required: ['statuses'], 84 + properties: { 85 + cursor: { 86 + type: 'string', 87 + }, 88 + statuses: { 89 + type: 'array', 90 + items: { 91 + type: 'ref', 92 + ref: 'lex:xyz.statusphere.defs#statusView', 93 + }, 94 + }, 95 + }, 96 + }, 97 + }, 98 + }, 99 + }, 100 + }, 101 + XyzStatusphereGetUser: { 102 + lexicon: 1, 103 + id: 'xyz.statusphere.getUser', 104 + defs: { 105 + main: { 106 + type: 'query', 107 + description: "Get the current user's profile and status.", 108 + parameters: { 109 + type: 'params', 110 + properties: {}, 111 + }, 112 + output: { 113 + encoding: 'application/json', 114 + schema: { 115 + type: 'object', 116 + required: ['profile'], 117 + properties: { 118 + profile: { 119 + type: 'ref', 120 + ref: 'lex:app.bsky.actor.defs#profileView', 121 + }, 122 + status: { 123 + type: 'ref', 124 + ref: 'lex:xyz.statusphere.defs#statusView', 125 + }, 126 + }, 127 + }, 128 + }, 129 + }, 130 + }, 131 + }, 132 + XyzStatusphereSendStatus: { 133 + lexicon: 1, 134 + id: 'xyz.statusphere.sendStatus', 135 + defs: { 136 + main: { 137 + type: 'procedure', 138 + description: 'Send a status into the ATmosphere.', 139 + input: { 140 + encoding: 'application/json', 141 + schema: { 142 + type: 'object', 143 + required: ['status'], 144 + properties: { 145 + status: { 146 + type: 'string', 147 + minLength: 1, 148 + maxGraphemes: 1, 149 + maxLength: 32, 150 + }, 151 + }, 152 + }, 153 + }, 154 + output: { 155 + encoding: 'application/json', 156 + schema: { 157 + type: 'object', 158 + required: ['status'], 159 + properties: { 160 + status: { 161 + type: 'ref', 162 + ref: 'lex:xyz.statusphere.defs#statusView', 163 + }, 164 + }, 165 + }, 166 + }, 167 + }, 168 + }, 169 + }, 170 + XyzStatusphereStatus: { 171 + lexicon: 1, 172 + id: 'xyz.statusphere.status', 173 + defs: { 174 + main: { 175 + type: 'record', 176 + key: 'tid', 177 + record: { 178 + type: 'object', 179 + required: ['status', 'createdAt'], 180 + properties: { 181 + status: { 182 + type: 'string', 183 + minLength: 1, 184 + maxGraphemes: 1, 185 + maxLength: 32, 186 + }, 187 + createdAt: { 188 + type: 'string', 189 + format: 'datetime', 190 + }, 191 + }, 192 + }, 193 + }, 194 + }, 195 + }, 196 + ComAtprotoLabelDefs: { 197 + lexicon: 1, 198 + id: 'com.atproto.label.defs', 199 + defs: { 200 + label: { 201 + type: 'object', 202 + description: 203 + 'Metadata tag on an atproto resource (eg, repo or record).', 204 + required: ['src', 'uri', 'val', 'cts'], 205 + properties: { 206 + ver: { 207 + type: 'integer', 208 + description: 'The AT Protocol version of the label object.', 209 + }, 210 + src: { 211 + type: 'string', 212 + format: 'did', 213 + description: 'DID of the actor who created this label.', 214 + }, 215 + uri: { 216 + type: 'string', 217 + format: 'uri', 218 + description: 219 + 'AT URI of the record, repository (account), or other resource that this label applies to.', 220 + }, 221 + cid: { 222 + type: 'string', 223 + format: 'cid', 224 + description: 225 + "Optionally, CID specifying the specific version of 'uri' resource this label applies to.", 226 + }, 227 + val: { 228 + type: 'string', 229 + maxLength: 128, 230 + description: 231 + 'The short string name of the value or type of this label.', 232 + }, 233 + neg: { 234 + type: 'boolean', 235 + description: 236 + 'If true, this is a negation label, overwriting a previous label.', 237 + }, 238 + cts: { 239 + type: 'string', 240 + format: 'datetime', 241 + description: 'Timestamp when this label was created.', 242 + }, 243 + exp: { 244 + type: 'string', 245 + format: 'datetime', 246 + description: 247 + 'Timestamp at which this label expires (no longer applies).', 248 + }, 249 + sig: { 250 + type: 'bytes', 251 + description: 'Signature of dag-cbor encoded label.', 252 + }, 253 + }, 254 + }, 255 + selfLabels: { 256 + type: 'object', 257 + description: 258 + 'Metadata tags on an atproto record, published by the author within the record.', 259 + required: ['values'], 260 + properties: { 261 + values: { 262 + type: 'array', 263 + items: { 264 + type: 'ref', 265 + ref: 'lex:com.atproto.label.defs#selfLabel', 266 + }, 267 + maxLength: 10, 268 + }, 269 + }, 270 + }, 271 + selfLabel: { 272 + type: 'object', 273 + description: 274 + 'Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel.', 275 + required: ['val'], 276 + properties: { 277 + val: { 278 + type: 'string', 279 + maxLength: 128, 280 + description: 281 + 'The short string name of the value or type of this label.', 282 + }, 283 + }, 284 + }, 285 + labelValueDefinition: { 286 + type: 'object', 287 + description: 288 + 'Declares a label value and its expected interpretations and behaviors.', 289 + required: ['identifier', 'severity', 'blurs', 'locales'], 290 + properties: { 291 + identifier: { 292 + type: 'string', 293 + description: 294 + "The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).", 295 + maxLength: 100, 296 + maxGraphemes: 100, 297 + }, 298 + severity: { 299 + type: 'string', 300 + description: 301 + "How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.", 302 + knownValues: ['inform', 'alert', 'none'], 303 + }, 304 + blurs: { 305 + type: 'string', 306 + description: 307 + "What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.", 308 + knownValues: ['content', 'media', 'none'], 309 + }, 310 + defaultSetting: { 311 + type: 'string', 312 + description: 'The default setting for this label.', 313 + knownValues: ['ignore', 'warn', 'hide'], 314 + default: 'warn', 315 + }, 316 + adultOnly: { 317 + type: 'boolean', 318 + description: 319 + 'Does the user need to have adult content enabled in order to configure this label?', 320 + }, 321 + locales: { 322 + type: 'array', 323 + items: { 324 + type: 'ref', 325 + ref: 'lex:com.atproto.label.defs#labelValueDefinitionStrings', 326 + }, 327 + }, 328 + }, 329 + }, 330 + labelValueDefinitionStrings: { 331 + type: 'object', 332 + description: 333 + 'Strings which describe the label in the UI, localized into a specific language.', 334 + required: ['lang', 'name', 'description'], 335 + properties: { 336 + lang: { 337 + type: 'string', 338 + description: 339 + 'The code of the language these strings are written in.', 340 + format: 'language', 341 + }, 342 + name: { 343 + type: 'string', 344 + description: 'A short human-readable name for the label.', 345 + maxGraphemes: 64, 346 + maxLength: 640, 347 + }, 348 + description: { 349 + type: 'string', 350 + description: 351 + 'A longer description of what the label means and why it might be applied.', 352 + maxGraphemes: 10000, 353 + maxLength: 100000, 354 + }, 355 + }, 356 + }, 357 + labelValue: { 358 + type: 'string', 359 + knownValues: [ 360 + '!hide', 361 + '!no-promote', 362 + '!warn', 363 + '!no-unauthenticated', 364 + 'dmca-violation', 365 + 'doxxing', 366 + 'porn', 367 + 'sexual', 368 + 'nudity', 369 + 'nsfl', 370 + 'gore', 371 + ], 372 + }, 373 + }, 374 + }, 375 + ComAtprotoRepoApplyWrites: { 376 + lexicon: 1, 377 + id: 'com.atproto.repo.applyWrites', 378 + defs: { 379 + main: { 380 + type: 'procedure', 381 + description: 382 + 'Apply a batch transaction of repository creates, updates, and deletes. Requires auth, implemented by PDS.', 383 + input: { 384 + encoding: 'application/json', 385 + schema: { 386 + type: 'object', 387 + required: ['repo', 'writes'], 388 + properties: { 389 + repo: { 390 + type: 'string', 391 + format: 'at-identifier', 392 + description: 393 + 'The handle or DID of the repo (aka, current account).', 394 + }, 395 + validate: { 396 + type: 'boolean', 397 + description: 398 + "Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons.", 399 + }, 400 + writes: { 401 + type: 'array', 402 + items: { 403 + type: 'union', 404 + refs: [ 405 + 'lex:com.atproto.repo.applyWrites#create', 406 + 'lex:com.atproto.repo.applyWrites#update', 407 + 'lex:com.atproto.repo.applyWrites#delete', 408 + ], 409 + closed: true, 410 + }, 411 + }, 412 + swapCommit: { 413 + type: 'string', 414 + description: 415 + 'If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations.', 416 + format: 'cid', 417 + }, 418 + }, 419 + }, 420 + }, 421 + output: { 422 + encoding: 'application/json', 423 + schema: { 424 + type: 'object', 425 + required: [], 426 + properties: { 427 + commit: { 428 + type: 'ref', 429 + ref: 'lex:com.atproto.repo.defs#commitMeta', 430 + }, 431 + results: { 432 + type: 'array', 433 + items: { 434 + type: 'union', 435 + refs: [ 436 + 'lex:com.atproto.repo.applyWrites#createResult', 437 + 'lex:com.atproto.repo.applyWrites#updateResult', 438 + 'lex:com.atproto.repo.applyWrites#deleteResult', 439 + ], 440 + closed: true, 441 + }, 442 + }, 443 + }, 444 + }, 445 + }, 446 + errors: [ 447 + { 448 + name: 'InvalidSwap', 449 + description: 450 + "Indicates that the 'swapCommit' parameter did not match current commit.", 451 + }, 452 + ], 453 + }, 454 + create: { 455 + type: 'object', 456 + description: 'Operation which creates a new record.', 457 + required: ['collection', 'value'], 458 + properties: { 459 + collection: { 460 + type: 'string', 461 + format: 'nsid', 462 + }, 463 + rkey: { 464 + type: 'string', 465 + maxLength: 512, 466 + format: 'record-key', 467 + description: 468 + 'NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility.', 469 + }, 470 + value: { 471 + type: 'unknown', 472 + }, 473 + }, 474 + }, 475 + update: { 476 + type: 'object', 477 + description: 'Operation which updates an existing record.', 478 + required: ['collection', 'rkey', 'value'], 479 + properties: { 480 + collection: { 481 + type: 'string', 482 + format: 'nsid', 483 + }, 484 + rkey: { 485 + type: 'string', 486 + format: 'record-key', 487 + }, 488 + value: { 489 + type: 'unknown', 490 + }, 491 + }, 492 + }, 493 + delete: { 494 + type: 'object', 495 + description: 'Operation which deletes an existing record.', 496 + required: ['collection', 'rkey'], 497 + properties: { 498 + collection: { 499 + type: 'string', 500 + format: 'nsid', 501 + }, 502 + rkey: { 503 + type: 'string', 504 + format: 'record-key', 505 + }, 506 + }, 507 + }, 508 + createResult: { 509 + type: 'object', 510 + required: ['uri', 'cid'], 511 + properties: { 512 + uri: { 513 + type: 'string', 514 + format: 'at-uri', 515 + }, 516 + cid: { 517 + type: 'string', 518 + format: 'cid', 519 + }, 520 + validationStatus: { 521 + type: 'string', 522 + knownValues: ['valid', 'unknown'], 523 + }, 524 + }, 525 + }, 526 + updateResult: { 527 + type: 'object', 528 + required: ['uri', 'cid'], 529 + properties: { 530 + uri: { 531 + type: 'string', 532 + format: 'at-uri', 533 + }, 534 + cid: { 535 + type: 'string', 536 + format: 'cid', 537 + }, 538 + validationStatus: { 539 + type: 'string', 540 + knownValues: ['valid', 'unknown'], 541 + }, 542 + }, 543 + }, 544 + deleteResult: { 545 + type: 'object', 546 + required: [], 547 + properties: {}, 548 + }, 549 + }, 550 + }, 551 + ComAtprotoRepoCreateRecord: { 552 + lexicon: 1, 553 + id: 'com.atproto.repo.createRecord', 554 + defs: { 555 + main: { 556 + type: 'procedure', 557 + description: 558 + 'Create a single new repository record. Requires auth, implemented by PDS.', 559 + input: { 560 + encoding: 'application/json', 561 + schema: { 562 + type: 'object', 563 + required: ['repo', 'collection', 'record'], 564 + properties: { 565 + repo: { 566 + type: 'string', 567 + format: 'at-identifier', 568 + description: 569 + 'The handle or DID of the repo (aka, current account).', 570 + }, 571 + collection: { 572 + type: 'string', 573 + format: 'nsid', 574 + description: 'The NSID of the record collection.', 575 + }, 576 + rkey: { 577 + type: 'string', 578 + format: 'record-key', 579 + description: 'The Record Key.', 580 + maxLength: 512, 581 + }, 582 + validate: { 583 + type: 'boolean', 584 + description: 585 + "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.", 586 + }, 587 + record: { 588 + type: 'unknown', 589 + description: 'The record itself. Must contain a $type field.', 590 + }, 591 + swapCommit: { 592 + type: 'string', 593 + format: 'cid', 594 + description: 595 + 'Compare and swap with the previous commit by CID.', 596 + }, 597 + }, 598 + }, 599 + }, 600 + output: { 601 + encoding: 'application/json', 602 + schema: { 603 + type: 'object', 604 + required: ['uri', 'cid'], 605 + properties: { 606 + uri: { 607 + type: 'string', 608 + format: 'at-uri', 609 + }, 610 + cid: { 611 + type: 'string', 612 + format: 'cid', 613 + }, 614 + commit: { 615 + type: 'ref', 616 + ref: 'lex:com.atproto.repo.defs#commitMeta', 617 + }, 618 + validationStatus: { 619 + type: 'string', 620 + knownValues: ['valid', 'unknown'], 621 + }, 622 + }, 623 + }, 624 + }, 625 + errors: [ 626 + { 627 + name: 'InvalidSwap', 628 + description: 629 + "Indicates that 'swapCommit' didn't match current repo commit.", 630 + }, 631 + ], 632 + }, 633 + }, 634 + }, 635 + ComAtprotoRepoDefs: { 636 + lexicon: 1, 637 + id: 'com.atproto.repo.defs', 638 + defs: { 639 + commitMeta: { 640 + type: 'object', 641 + required: ['cid', 'rev'], 642 + properties: { 643 + cid: { 644 + type: 'string', 645 + format: 'cid', 646 + }, 647 + rev: { 648 + type: 'string', 649 + format: 'tid', 650 + }, 651 + }, 652 + }, 653 + }, 654 + }, 655 + ComAtprotoRepoDeleteRecord: { 656 + lexicon: 1, 657 + id: 'com.atproto.repo.deleteRecord', 658 + defs: { 659 + main: { 660 + type: 'procedure', 661 + description: 662 + "Delete a repository record, or ensure it doesn't exist. Requires auth, implemented by PDS.", 663 + input: { 664 + encoding: 'application/json', 665 + schema: { 666 + type: 'object', 667 + required: ['repo', 'collection', 'rkey'], 668 + properties: { 669 + repo: { 670 + type: 'string', 671 + format: 'at-identifier', 672 + description: 673 + 'The handle or DID of the repo (aka, current account).', 674 + }, 675 + collection: { 676 + type: 'string', 677 + format: 'nsid', 678 + description: 'The NSID of the record collection.', 679 + }, 680 + rkey: { 681 + type: 'string', 682 + format: 'record-key', 683 + description: 'The Record Key.', 684 + }, 685 + swapRecord: { 686 + type: 'string', 687 + format: 'cid', 688 + description: 689 + 'Compare and swap with the previous record by CID.', 690 + }, 691 + swapCommit: { 692 + type: 'string', 693 + format: 'cid', 694 + description: 695 + 'Compare and swap with the previous commit by CID.', 696 + }, 697 + }, 698 + }, 699 + }, 700 + output: { 701 + encoding: 'application/json', 702 + schema: { 703 + type: 'object', 704 + properties: { 705 + commit: { 706 + type: 'ref', 707 + ref: 'lex:com.atproto.repo.defs#commitMeta', 708 + }, 709 + }, 710 + }, 711 + }, 712 + errors: [ 713 + { 714 + name: 'InvalidSwap', 715 + }, 716 + ], 717 + }, 718 + }, 719 + }, 720 + ComAtprotoRepoDescribeRepo: { 721 + lexicon: 1, 722 + id: 'com.atproto.repo.describeRepo', 723 + defs: { 724 + main: { 725 + type: 'query', 726 + description: 727 + 'Get information about an account and repository, including the list of collections. Does not require auth.', 728 + parameters: { 729 + type: 'params', 730 + required: ['repo'], 731 + properties: { 732 + repo: { 733 + type: 'string', 734 + format: 'at-identifier', 735 + description: 'The handle or DID of the repo.', 736 + }, 737 + }, 738 + }, 739 + output: { 740 + encoding: 'application/json', 741 + schema: { 742 + type: 'object', 743 + required: [ 744 + 'handle', 745 + 'did', 746 + 'didDoc', 747 + 'collections', 748 + 'handleIsCorrect', 749 + ], 750 + properties: { 751 + handle: { 752 + type: 'string', 753 + format: 'handle', 754 + }, 755 + did: { 756 + type: 'string', 757 + format: 'did', 758 + }, 759 + didDoc: { 760 + type: 'unknown', 761 + description: 'The complete DID document for this account.', 762 + }, 763 + collections: { 764 + type: 'array', 765 + description: 766 + 'List of all the collections (NSIDs) for which this repo contains at least one record.', 767 + items: { 768 + type: 'string', 769 + format: 'nsid', 770 + }, 771 + }, 772 + handleIsCorrect: { 773 + type: 'boolean', 774 + description: 775 + 'Indicates if handle is currently valid (resolves bi-directionally)', 776 + }, 777 + }, 778 + }, 779 + }, 780 + }, 781 + }, 782 + }, 783 + ComAtprotoRepoGetRecord: { 784 + lexicon: 1, 785 + id: 'com.atproto.repo.getRecord', 786 + defs: { 787 + main: { 788 + type: 'query', 789 + description: 790 + 'Get a single record from a repository. Does not require auth.', 791 + parameters: { 792 + type: 'params', 793 + required: ['repo', 'collection', 'rkey'], 794 + properties: { 795 + repo: { 796 + type: 'string', 797 + format: 'at-identifier', 798 + description: 'The handle or DID of the repo.', 799 + }, 800 + collection: { 801 + type: 'string', 802 + format: 'nsid', 803 + description: 'The NSID of the record collection.', 804 + }, 805 + rkey: { 806 + type: 'string', 807 + description: 'The Record Key.', 808 + format: 'record-key', 809 + }, 810 + cid: { 811 + type: 'string', 812 + format: 'cid', 813 + description: 814 + 'The CID of the version of the record. If not specified, then return the most recent version.', 815 + }, 816 + }, 817 + }, 818 + output: { 819 + encoding: 'application/json', 820 + schema: { 821 + type: 'object', 822 + required: ['uri', 'value'], 823 + properties: { 824 + uri: { 825 + type: 'string', 826 + format: 'at-uri', 827 + }, 828 + cid: { 829 + type: 'string', 830 + format: 'cid', 831 + }, 832 + value: { 833 + type: 'unknown', 834 + }, 835 + }, 836 + }, 837 + }, 838 + errors: [ 839 + { 840 + name: 'RecordNotFound', 841 + }, 842 + ], 843 + }, 844 + }, 845 + }, 846 + ComAtprotoRepoImportRepo: { 847 + lexicon: 1, 848 + id: 'com.atproto.repo.importRepo', 849 + defs: { 850 + main: { 851 + type: 'procedure', 852 + description: 853 + 'Import a repo in the form of a CAR file. Requires Content-Length HTTP header to be set.', 854 + input: { 855 + encoding: 'application/vnd.ipld.car', 856 + }, 857 + }, 858 + }, 859 + }, 860 + ComAtprotoRepoListMissingBlobs: { 861 + lexicon: 1, 862 + id: 'com.atproto.repo.listMissingBlobs', 863 + defs: { 864 + main: { 865 + type: 'query', 866 + description: 867 + 'Returns a list of missing blobs for the requesting account. Intended to be used in the account migration flow.', 868 + parameters: { 869 + type: 'params', 870 + properties: { 871 + limit: { 872 + type: 'integer', 873 + minimum: 1, 874 + maximum: 1000, 875 + default: 500, 876 + }, 877 + cursor: { 878 + type: 'string', 879 + }, 880 + }, 881 + }, 882 + output: { 883 + encoding: 'application/json', 884 + schema: { 885 + type: 'object', 886 + required: ['blobs'], 887 + properties: { 888 + cursor: { 889 + type: 'string', 890 + }, 891 + blobs: { 892 + type: 'array', 893 + items: { 894 + type: 'ref', 895 + ref: 'lex:com.atproto.repo.listMissingBlobs#recordBlob', 896 + }, 897 + }, 898 + }, 899 + }, 900 + }, 901 + }, 902 + recordBlob: { 903 + type: 'object', 904 + required: ['cid', 'recordUri'], 905 + properties: { 906 + cid: { 907 + type: 'string', 908 + format: 'cid', 909 + }, 910 + recordUri: { 911 + type: 'string', 912 + format: 'at-uri', 913 + }, 914 + }, 915 + }, 916 + }, 917 + }, 918 + ComAtprotoRepoListRecords: { 919 + lexicon: 1, 920 + id: 'com.atproto.repo.listRecords', 921 + defs: { 922 + main: { 923 + type: 'query', 924 + description: 925 + 'List a range of records in a repository, matching a specific collection. Does not require auth.', 926 + parameters: { 927 + type: 'params', 928 + required: ['repo', 'collection'], 929 + properties: { 930 + repo: { 931 + type: 'string', 932 + format: 'at-identifier', 933 + description: 'The handle or DID of the repo.', 934 + }, 935 + collection: { 936 + type: 'string', 937 + format: 'nsid', 938 + description: 'The NSID of the record type.', 939 + }, 940 + limit: { 941 + type: 'integer', 942 + minimum: 1, 943 + maximum: 100, 944 + default: 50, 945 + description: 'The number of records to return.', 946 + }, 947 + cursor: { 948 + type: 'string', 949 + }, 950 + rkeyStart: { 951 + type: 'string', 952 + description: 953 + 'DEPRECATED: The lowest sort-ordered rkey to start from (exclusive)', 954 + }, 955 + rkeyEnd: { 956 + type: 'string', 957 + description: 958 + 'DEPRECATED: The highest sort-ordered rkey to stop at (exclusive)', 959 + }, 960 + reverse: { 961 + type: 'boolean', 962 + description: 'Flag to reverse the order of the returned records.', 963 + }, 964 + }, 965 + }, 966 + output: { 967 + encoding: 'application/json', 968 + schema: { 969 + type: 'object', 970 + required: ['records'], 971 + properties: { 972 + cursor: { 973 + type: 'string', 974 + }, 975 + records: { 976 + type: 'array', 977 + items: { 978 + type: 'ref', 979 + ref: 'lex:com.atproto.repo.listRecords#record', 980 + }, 981 + }, 982 + }, 983 + }, 984 + }, 985 + }, 986 + record: { 987 + type: 'object', 988 + required: ['uri', 'cid', 'value'], 989 + properties: { 990 + uri: { 991 + type: 'string', 992 + format: 'at-uri', 993 + }, 994 + cid: { 995 + type: 'string', 996 + format: 'cid', 997 + }, 998 + value: { 999 + type: 'unknown', 1000 + }, 1001 + }, 1002 + }, 1003 + }, 1004 + }, 1005 + ComAtprotoRepoPutRecord: { 1006 + lexicon: 1, 1007 + id: 'com.atproto.repo.putRecord', 1008 + defs: { 1009 + main: { 1010 + type: 'procedure', 1011 + description: 1012 + 'Write a repository record, creating or updating it as needed. Requires auth, implemented by PDS.', 1013 + input: { 1014 + encoding: 'application/json', 1015 + schema: { 1016 + type: 'object', 1017 + required: ['repo', 'collection', 'rkey', 'record'], 1018 + nullable: ['swapRecord'], 1019 + properties: { 1020 + repo: { 1021 + type: 'string', 1022 + format: 'at-identifier', 1023 + description: 1024 + 'The handle or DID of the repo (aka, current account).', 1025 + }, 1026 + collection: { 1027 + type: 'string', 1028 + format: 'nsid', 1029 + description: 'The NSID of the record collection.', 1030 + }, 1031 + rkey: { 1032 + type: 'string', 1033 + format: 'record-key', 1034 + description: 'The Record Key.', 1035 + maxLength: 512, 1036 + }, 1037 + validate: { 1038 + type: 'boolean', 1039 + description: 1040 + "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.", 1041 + }, 1042 + record: { 1043 + type: 'unknown', 1044 + description: 'The record to write.', 1045 + }, 1046 + swapRecord: { 1047 + type: 'string', 1048 + format: 'cid', 1049 + description: 1050 + 'Compare and swap with the previous record by CID. WARNING: nullable and optional field; may cause problems with golang implementation', 1051 + }, 1052 + swapCommit: { 1053 + type: 'string', 1054 + format: 'cid', 1055 + description: 1056 + 'Compare and swap with the previous commit by CID.', 1057 + }, 1058 + }, 1059 + }, 1060 + }, 1061 + output: { 1062 + encoding: 'application/json', 1063 + schema: { 1064 + type: 'object', 1065 + required: ['uri', 'cid'], 1066 + properties: { 1067 + uri: { 1068 + type: 'string', 1069 + format: 'at-uri', 1070 + }, 1071 + cid: { 1072 + type: 'string', 1073 + format: 'cid', 1074 + }, 1075 + commit: { 1076 + type: 'ref', 1077 + ref: 'lex:com.atproto.repo.defs#commitMeta', 1078 + }, 1079 + validationStatus: { 1080 + type: 'string', 1081 + knownValues: ['valid', 'unknown'], 1082 + }, 1083 + }, 1084 + }, 1085 + }, 1086 + errors: [ 1087 + { 1088 + name: 'InvalidSwap', 1089 + }, 1090 + ], 1091 + }, 1092 + }, 1093 + }, 1094 + ComAtprotoRepoStrongRef: { 1095 + lexicon: 1, 1096 + id: 'com.atproto.repo.strongRef', 1097 + description: 'A URI with a content-hash fingerprint.', 1098 + defs: { 1099 + main: { 1100 + type: 'object', 1101 + required: ['uri', 'cid'], 1102 + properties: { 1103 + uri: { 1104 + type: 'string', 1105 + format: 'at-uri', 1106 + }, 1107 + cid: { 1108 + type: 'string', 1109 + format: 'cid', 1110 + }, 1111 + }, 1112 + }, 1113 + }, 1114 + }, 1115 + ComAtprotoRepoUploadBlob: { 1116 + lexicon: 1, 1117 + id: 'com.atproto.repo.uploadBlob', 1118 + defs: { 1119 + main: { 1120 + type: 'procedure', 1121 + description: 1122 + 'Upload a new blob, to be referenced from a repository record. The blob will be deleted if it is not referenced within a time window (eg, minutes). Blob restrictions (mimetype, size, etc) are enforced when the reference is created. Requires auth, implemented by PDS.', 1123 + input: { 1124 + encoding: '*/*', 1125 + }, 1126 + output: { 1127 + encoding: 'application/json', 1128 + schema: { 1129 + type: 'object', 1130 + required: ['blob'], 1131 + properties: { 1132 + blob: { 1133 + type: 'blob', 1134 + }, 1135 + }, 1136 + }, 1137 + }, 1138 + }, 1139 + }, 1140 + }, 1141 + AppBskyActorDefs: { 1142 + lexicon: 1, 1143 + id: 'app.bsky.actor.defs', 1144 + defs: { 1145 + profileView: { 1146 + type: 'object', 1147 + required: ['did', 'handle'], 1148 + properties: { 1149 + did: { 1150 + type: 'string', 1151 + format: 'did', 1152 + }, 1153 + handle: { 1154 + type: 'string', 1155 + format: 'handle', 1156 + }, 1157 + displayName: { 1158 + type: 'string', 1159 + maxGraphemes: 64, 1160 + maxLength: 640, 1161 + }, 1162 + description: { 1163 + type: 'string', 1164 + maxGraphemes: 256, 1165 + maxLength: 2560, 1166 + }, 1167 + avatar: { 1168 + type: 'string', 1169 + format: 'uri', 1170 + }, 1171 + indexedAt: { 1172 + type: 'string', 1173 + format: 'datetime', 1174 + }, 1175 + createdAt: { 1176 + type: 'string', 1177 + format: 'datetime', 1178 + }, 1179 + labels: { 1180 + type: 'array', 1181 + items: { 1182 + type: 'ref', 1183 + ref: 'lex:com.atproto.label.defs#label', 1184 + }, 1185 + }, 1186 + }, 1187 + }, 1188 + }, 1189 + }, 1190 + AppBskyActorProfile: { 1191 + lexicon: 1, 1192 + id: 'app.bsky.actor.profile', 1193 + defs: { 1194 + main: { 1195 + type: 'record', 1196 + description: 'A declaration of a Bluesky account profile.', 1197 + key: 'literal:self', 1198 + record: { 1199 + type: 'object', 1200 + properties: { 1201 + displayName: { 1202 + type: 'string', 1203 + maxGraphemes: 64, 1204 + maxLength: 640, 1205 + }, 1206 + description: { 1207 + type: 'string', 1208 + description: 'Free-form profile description text.', 1209 + maxGraphemes: 256, 1210 + maxLength: 2560, 1211 + }, 1212 + avatar: { 1213 + type: 'blob', 1214 + description: 1215 + "Small image to be displayed next to posts from account. AKA, 'profile picture'", 1216 + accept: ['image/png', 'image/jpeg'], 1217 + maxSize: 1000000, 1218 + }, 1219 + banner: { 1220 + type: 'blob', 1221 + description: 1222 + 'Larger horizontal image to display behind profile view.', 1223 + accept: ['image/png', 'image/jpeg'], 1224 + maxSize: 1000000, 1225 + }, 1226 + labels: { 1227 + type: 'union', 1228 + description: 1229 + 'Self-label values, specific to the Bluesky application, on the overall account.', 1230 + refs: ['lex:com.atproto.label.defs#selfLabels'], 1231 + }, 1232 + joinedViaStarterPack: { 1233 + type: 'ref', 1234 + ref: 'lex:com.atproto.repo.strongRef', 1235 + }, 1236 + pinnedPost: { 1237 + type: 'ref', 1238 + ref: 'lex:com.atproto.repo.strongRef', 1239 + }, 1240 + createdAt: { 1241 + type: 'string', 1242 + format: 'datetime', 1243 + }, 1244 + }, 1245 + }, 1246 + }, 1247 + }, 1248 + }, 1249 + } as const satisfies Record<string, LexiconDoc> 1250 + 1251 + export const schemas = Object.values(schemaDict) satisfies LexiconDoc[] 1252 + export const lexicons: Lexicons = new Lexicons(schemas) 1253 + 1254 + export function validate<T extends { $type: string }>( 1255 + v: unknown, 1256 + id: string, 1257 + hash: string, 1258 + requiredType: true, 1259 + ): ValidationResult<T> 1260 + export function validate<T extends { $type?: string }>( 1261 + v: unknown, 1262 + id: string, 1263 + hash: string, 1264 + requiredType?: false, 1265 + ): ValidationResult<T> 1266 + export function validate( 1267 + v: unknown, 1268 + id: string, 1269 + hash: string, 1270 + requiredType?: boolean, 1271 + ): ValidationResult { 1272 + return (requiredType ? is$typed : maybe$typed)(v, id, hash) 1273 + ? lexicons.validate(`${id}#${hash}`, v) 1274 + : { 1275 + success: false, 1276 + error: new ValidationError( 1277 + `Must be an object with "${hash === 'main' ? id : `${id}#${hash}`}" $type property`, 1278 + ), 1279 + } 1280 + } 1281 + 1282 + export const ids = { 1283 + XyzStatusphereDefs: 'xyz.statusphere.defs', 1284 + XyzStatusphereGetStatuses: 'xyz.statusphere.getStatuses', 1285 + XyzStatusphereGetUser: 'xyz.statusphere.getUser', 1286 + XyzStatusphereSendStatus: 'xyz.statusphere.sendStatus', 1287 + XyzStatusphereStatus: 'xyz.statusphere.status', 1288 + ComAtprotoLabelDefs: 'com.atproto.label.defs', 1289 + ComAtprotoRepoApplyWrites: 'com.atproto.repo.applyWrites', 1290 + ComAtprotoRepoCreateRecord: 'com.atproto.repo.createRecord', 1291 + ComAtprotoRepoDefs: 'com.atproto.repo.defs', 1292 + ComAtprotoRepoDeleteRecord: 'com.atproto.repo.deleteRecord', 1293 + ComAtprotoRepoDescribeRepo: 'com.atproto.repo.describeRepo', 1294 + ComAtprotoRepoGetRecord: 'com.atproto.repo.getRecord', 1295 + ComAtprotoRepoImportRepo: 'com.atproto.repo.importRepo', 1296 + ComAtprotoRepoListMissingBlobs: 'com.atproto.repo.listMissingBlobs', 1297 + ComAtprotoRepoListRecords: 'com.atproto.repo.listRecords', 1298 + ComAtprotoRepoPutRecord: 'com.atproto.repo.putRecord', 1299 + ComAtprotoRepoStrongRef: 'com.atproto.repo.strongRef', 1300 + ComAtprotoRepoUploadBlob: 'com.atproto.repo.uploadBlob', 1301 + AppBskyActorDefs: 'app.bsky.actor.defs', 1302 + AppBskyActorProfile: 'app.bsky.actor.profile', 1303 + } as const
+35
packages/appview/src/lexicons/types/app/bsky/actor/defs.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + 7 + import { validate as _validate } from '../../../../lexicons' 8 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 9 + import type * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs.js' 10 + 11 + const is$typed = _is$typed, 12 + validate = _validate 13 + const id = 'app.bsky.actor.defs' 14 + 15 + export interface ProfileView { 16 + $type?: 'app.bsky.actor.defs#profileView' 17 + did: string 18 + handle: string 19 + displayName?: string 20 + description?: string 21 + avatar?: string 22 + indexedAt?: string 23 + createdAt?: string 24 + labels?: ComAtprotoLabelDefs.Label[] 25 + } 26 + 27 + const hashProfileView = 'profileView' 28 + 29 + export function isProfileView<V>(v: V) { 30 + return is$typed(v, id, hashProfileView) 31 + } 32 + 33 + export function validateProfileView<V>(v: V) { 34 + return validate<ProfileView & V>(v, id, hashProfileView) 35 + }
+40
packages/appview/src/lexicons/types/app/bsky/actor/profile.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + 7 + import { validate as _validate } from '../../../../lexicons' 8 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 9 + import type * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs.js' 10 + import type * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef.js' 11 + 12 + const is$typed = _is$typed, 13 + validate = _validate 14 + const id = 'app.bsky.actor.profile' 15 + 16 + export interface Record { 17 + $type: 'app.bsky.actor.profile' 18 + displayName?: string 19 + /** Free-form profile description text. */ 20 + description?: string 21 + /** Small image to be displayed next to posts from account. AKA, 'profile picture' */ 22 + avatar?: BlobRef 23 + /** Larger horizontal image to display behind profile view. */ 24 + banner?: BlobRef 25 + labels?: $Typed<ComAtprotoLabelDefs.SelfLabels> | { $type: string } 26 + joinedViaStarterPack?: ComAtprotoRepoStrongRef.Main 27 + pinnedPost?: ComAtprotoRepoStrongRef.Main 28 + createdAt?: string 29 + [k: string]: unknown 30 + } 31 + 32 + const hashRecord = 'main' 33 + 34 + export function isRecord<V>(v: V) { 35 + return is$typed(v, id, hashRecord) 36 + } 37 + 38 + export function validateRecord<V>(v: V) { 39 + return validate<Record & V>(v, id, hashRecord, true) 40 + }
+143
packages/appview/src/lexicons/types/com/atproto/label/defs.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + 7 + import { validate as _validate } from '../../../../lexicons' 8 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 9 + 10 + const is$typed = _is$typed, 11 + validate = _validate 12 + const id = 'com.atproto.label.defs' 13 + 14 + /** Metadata tag on an atproto resource (eg, repo or record). */ 15 + export interface Label { 16 + $type?: 'com.atproto.label.defs#label' 17 + /** The AT Protocol version of the label object. */ 18 + ver?: number 19 + /** DID of the actor who created this label. */ 20 + src: string 21 + /** AT URI of the record, repository (account), or other resource that this label applies to. */ 22 + uri: string 23 + /** Optionally, CID specifying the specific version of 'uri' resource this label applies to. */ 24 + cid?: string 25 + /** The short string name of the value or type of this label. */ 26 + val: string 27 + /** If true, this is a negation label, overwriting a previous label. */ 28 + neg?: boolean 29 + /** Timestamp when this label was created. */ 30 + cts: string 31 + /** Timestamp at which this label expires (no longer applies). */ 32 + exp?: string 33 + /** Signature of dag-cbor encoded label. */ 34 + sig?: Uint8Array 35 + } 36 + 37 + const hashLabel = 'label' 38 + 39 + export function isLabel<V>(v: V) { 40 + return is$typed(v, id, hashLabel) 41 + } 42 + 43 + export function validateLabel<V>(v: V) { 44 + return validate<Label & V>(v, id, hashLabel) 45 + } 46 + 47 + /** Metadata tags on an atproto record, published by the author within the record. */ 48 + export interface SelfLabels { 49 + $type?: 'com.atproto.label.defs#selfLabels' 50 + values: SelfLabel[] 51 + } 52 + 53 + const hashSelfLabels = 'selfLabels' 54 + 55 + export function isSelfLabels<V>(v: V) { 56 + return is$typed(v, id, hashSelfLabels) 57 + } 58 + 59 + export function validateSelfLabels<V>(v: V) { 60 + return validate<SelfLabels & V>(v, id, hashSelfLabels) 61 + } 62 + 63 + /** Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel. */ 64 + export interface SelfLabel { 65 + $type?: 'com.atproto.label.defs#selfLabel' 66 + /** The short string name of the value or type of this label. */ 67 + val: string 68 + } 69 + 70 + const hashSelfLabel = 'selfLabel' 71 + 72 + export function isSelfLabel<V>(v: V) { 73 + return is$typed(v, id, hashSelfLabel) 74 + } 75 + 76 + export function validateSelfLabel<V>(v: V) { 77 + return validate<SelfLabel & V>(v, id, hashSelfLabel) 78 + } 79 + 80 + /** Declares a label value and its expected interpretations and behaviors. */ 81 + export interface LabelValueDefinition { 82 + $type?: 'com.atproto.label.defs#labelValueDefinition' 83 + /** The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+). */ 84 + identifier: string 85 + /** How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing. */ 86 + severity: 'inform' | 'alert' | 'none' | (string & {}) 87 + /** What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing. */ 88 + blurs: 'content' | 'media' | 'none' | (string & {}) 89 + /** The default setting for this label. */ 90 + defaultSetting: 'ignore' | 'warn' | 'hide' | (string & {}) 91 + /** Does the user need to have adult content enabled in order to configure this label? */ 92 + adultOnly?: boolean 93 + locales: LabelValueDefinitionStrings[] 94 + } 95 + 96 + const hashLabelValueDefinition = 'labelValueDefinition' 97 + 98 + export function isLabelValueDefinition<V>(v: V) { 99 + return is$typed(v, id, hashLabelValueDefinition) 100 + } 101 + 102 + export function validateLabelValueDefinition<V>(v: V) { 103 + return validate<LabelValueDefinition & V>(v, id, hashLabelValueDefinition) 104 + } 105 + 106 + /** Strings which describe the label in the UI, localized into a specific language. */ 107 + export interface LabelValueDefinitionStrings { 108 + $type?: 'com.atproto.label.defs#labelValueDefinitionStrings' 109 + /** The code of the language these strings are written in. */ 110 + lang: string 111 + /** A short human-readable name for the label. */ 112 + name: string 113 + /** A longer description of what the label means and why it might be applied. */ 114 + description: string 115 + } 116 + 117 + const hashLabelValueDefinitionStrings = 'labelValueDefinitionStrings' 118 + 119 + export function isLabelValueDefinitionStrings<V>(v: V) { 120 + return is$typed(v, id, hashLabelValueDefinitionStrings) 121 + } 122 + 123 + export function validateLabelValueDefinitionStrings<V>(v: V) { 124 + return validate<LabelValueDefinitionStrings & V>( 125 + v, 126 + id, 127 + hashLabelValueDefinitionStrings, 128 + ) 129 + } 130 + 131 + export type LabelValue = 132 + | '!hide' 133 + | '!no-promote' 134 + | '!warn' 135 + | '!no-unauthenticated' 136 + | 'dmca-violation' 137 + | 'doxxing' 138 + | 'porn' 139 + | 'sexual' 140 + | 'nudity' 141 + | 'nsfl' 142 + | 'gore' 143 + | (string & {})
+168
packages/appview/src/lexicons/types/com/atproto/repo/applyWrites.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 6 + import express from 'express' 7 + import { CID } from 'multiformats/cid' 8 + 9 + import { validate as _validate } from '../../../../lexicons' 10 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 11 + import type * as ComAtprotoRepoDefs from './defs.js' 12 + 13 + const is$typed = _is$typed, 14 + validate = _validate 15 + const id = 'com.atproto.repo.applyWrites' 16 + 17 + export interface QueryParams {} 18 + 19 + export interface InputSchema { 20 + /** The handle or DID of the repo (aka, current account). */ 21 + repo: string 22 + /** Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons. */ 23 + validate?: boolean 24 + writes: ($Typed<Create> | $Typed<Update> | $Typed<Delete>)[] 25 + /** If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations. */ 26 + swapCommit?: string 27 + } 28 + 29 + export interface OutputSchema { 30 + commit?: ComAtprotoRepoDefs.CommitMeta 31 + results?: ( 32 + | $Typed<CreateResult> 33 + | $Typed<UpdateResult> 34 + | $Typed<DeleteResult> 35 + )[] 36 + } 37 + 38 + export interface HandlerInput { 39 + encoding: 'application/json' 40 + body: InputSchema 41 + } 42 + 43 + export interface HandlerSuccess { 44 + encoding: 'application/json' 45 + body: OutputSchema 46 + headers?: { [key: string]: string } 47 + } 48 + 49 + export interface HandlerError { 50 + status: number 51 + message?: string 52 + error?: 'InvalidSwap' 53 + } 54 + 55 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 56 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 57 + auth: HA 58 + params: QueryParams 59 + input: HandlerInput 60 + req: express.Request 61 + res: express.Response 62 + resetRouteRateLimits: () => Promise<void> 63 + } 64 + export type Handler<HA extends HandlerAuth = never> = ( 65 + ctx: HandlerReqCtx<HA>, 66 + ) => Promise<HandlerOutput> | HandlerOutput 67 + 68 + /** Operation which creates a new record. */ 69 + export interface Create { 70 + $type?: 'com.atproto.repo.applyWrites#create' 71 + collection: string 72 + /** NOTE: maxLength is redundant with record-key format. Keeping it temporarily to ensure backwards compatibility. */ 73 + rkey?: string 74 + value: { [_ in string]: unknown } 75 + } 76 + 77 + const hashCreate = 'create' 78 + 79 + export function isCreate<V>(v: V) { 80 + return is$typed(v, id, hashCreate) 81 + } 82 + 83 + export function validateCreate<V>(v: V) { 84 + return validate<Create & V>(v, id, hashCreate) 85 + } 86 + 87 + /** Operation which updates an existing record. */ 88 + export interface Update { 89 + $type?: 'com.atproto.repo.applyWrites#update' 90 + collection: string 91 + rkey: string 92 + value: { [_ in string]: unknown } 93 + } 94 + 95 + const hashUpdate = 'update' 96 + 97 + export function isUpdate<V>(v: V) { 98 + return is$typed(v, id, hashUpdate) 99 + } 100 + 101 + export function validateUpdate<V>(v: V) { 102 + return validate<Update & V>(v, id, hashUpdate) 103 + } 104 + 105 + /** Operation which deletes an existing record. */ 106 + export interface Delete { 107 + $type?: 'com.atproto.repo.applyWrites#delete' 108 + collection: string 109 + rkey: string 110 + } 111 + 112 + const hashDelete = 'delete' 113 + 114 + export function isDelete<V>(v: V) { 115 + return is$typed(v, id, hashDelete) 116 + } 117 + 118 + export function validateDelete<V>(v: V) { 119 + return validate<Delete & V>(v, id, hashDelete) 120 + } 121 + 122 + export interface CreateResult { 123 + $type?: 'com.atproto.repo.applyWrites#createResult' 124 + uri: string 125 + cid: string 126 + validationStatus?: 'valid' | 'unknown' | (string & {}) 127 + } 128 + 129 + const hashCreateResult = 'createResult' 130 + 131 + export function isCreateResult<V>(v: V) { 132 + return is$typed(v, id, hashCreateResult) 133 + } 134 + 135 + export function validateCreateResult<V>(v: V) { 136 + return validate<CreateResult & V>(v, id, hashCreateResult) 137 + } 138 + 139 + export interface UpdateResult { 140 + $type?: 'com.atproto.repo.applyWrites#updateResult' 141 + uri: string 142 + cid: string 143 + validationStatus?: 'valid' | 'unknown' | (string & {}) 144 + } 145 + 146 + const hashUpdateResult = 'updateResult' 147 + 148 + export function isUpdateResult<V>(v: V) { 149 + return is$typed(v, id, hashUpdateResult) 150 + } 151 + 152 + export function validateUpdateResult<V>(v: V) { 153 + return validate<UpdateResult & V>(v, id, hashUpdateResult) 154 + } 155 + 156 + export interface DeleteResult { 157 + $type?: 'com.atproto.repo.applyWrites#deleteResult' 158 + } 159 + 160 + const hashDeleteResult = 'deleteResult' 161 + 162 + export function isDeleteResult<V>(v: V) { 163 + return is$typed(v, id, hashDeleteResult) 164 + } 165 + 166 + export function validateDeleteResult<V>(v: V) { 167 + return validate<DeleteResult & V>(v, id, hashDeleteResult) 168 + }
+69
packages/appview/src/lexicons/types/com/atproto/repo/createRecord.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 6 + import express from 'express' 7 + import { CID } from 'multiformats/cid' 8 + 9 + import { validate as _validate } from '../../../../lexicons' 10 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 11 + import type * as ComAtprotoRepoDefs from './defs.js' 12 + 13 + const is$typed = _is$typed, 14 + validate = _validate 15 + const id = 'com.atproto.repo.createRecord' 16 + 17 + export interface QueryParams {} 18 + 19 + export interface InputSchema { 20 + /** The handle or DID of the repo (aka, current account). */ 21 + repo: string 22 + /** The NSID of the record collection. */ 23 + collection: string 24 + /** The Record Key. */ 25 + rkey?: string 26 + /** Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons. */ 27 + validate?: boolean 28 + /** The record itself. Must contain a $type field. */ 29 + record: { [_ in string]: unknown } 30 + /** Compare and swap with the previous commit by CID. */ 31 + swapCommit?: string 32 + } 33 + 34 + export interface OutputSchema { 35 + uri: string 36 + cid: string 37 + commit?: ComAtprotoRepoDefs.CommitMeta 38 + validationStatus?: 'valid' | 'unknown' | (string & {}) 39 + } 40 + 41 + export interface HandlerInput { 42 + encoding: 'application/json' 43 + body: InputSchema 44 + } 45 + 46 + export interface HandlerSuccess { 47 + encoding: 'application/json' 48 + body: OutputSchema 49 + headers?: { [key: string]: string } 50 + } 51 + 52 + export interface HandlerError { 53 + status: number 54 + message?: string 55 + error?: 'InvalidSwap' 56 + } 57 + 58 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 59 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 60 + auth: HA 61 + params: QueryParams 62 + input: HandlerInput 63 + req: express.Request 64 + res: express.Response 65 + resetRouteRateLimits: () => Promise<void> 66 + } 67 + export type Handler<HA extends HandlerAuth = never> = ( 68 + ctx: HandlerReqCtx<HA>, 69 + ) => Promise<HandlerOutput> | HandlerOutput
+28
packages/appview/src/lexicons/types/com/atproto/repo/defs.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + 7 + import { validate as _validate } from '../../../../lexicons' 8 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 9 + 10 + const is$typed = _is$typed, 11 + validate = _validate 12 + const id = 'com.atproto.repo.defs' 13 + 14 + export interface CommitMeta { 15 + $type?: 'com.atproto.repo.defs#commitMeta' 16 + cid: string 17 + rev: string 18 + } 19 + 20 + const hashCommitMeta = 'commitMeta' 21 + 22 + export function isCommitMeta<V>(v: V) { 23 + return is$typed(v, id, hashCommitMeta) 24 + } 25 + 26 + export function validateCommitMeta<V>(v: V) { 27 + return validate<CommitMeta & V>(v, id, hashCommitMeta) 28 + }
+64
packages/appview/src/lexicons/types/com/atproto/repo/deleteRecord.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 6 + import express from 'express' 7 + import { CID } from 'multiformats/cid' 8 + 9 + import { validate as _validate } from '../../../../lexicons' 10 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 11 + import type * as ComAtprotoRepoDefs from './defs.js' 12 + 13 + const is$typed = _is$typed, 14 + validate = _validate 15 + const id = 'com.atproto.repo.deleteRecord' 16 + 17 + export interface QueryParams {} 18 + 19 + export interface InputSchema { 20 + /** The handle or DID of the repo (aka, current account). */ 21 + repo: string 22 + /** The NSID of the record collection. */ 23 + collection: string 24 + /** The Record Key. */ 25 + rkey: string 26 + /** Compare and swap with the previous record by CID. */ 27 + swapRecord?: string 28 + /** Compare and swap with the previous commit by CID. */ 29 + swapCommit?: string 30 + } 31 + 32 + export interface OutputSchema { 33 + commit?: ComAtprotoRepoDefs.CommitMeta 34 + } 35 + 36 + export interface HandlerInput { 37 + encoding: 'application/json' 38 + body: InputSchema 39 + } 40 + 41 + export interface HandlerSuccess { 42 + encoding: 'application/json' 43 + body: OutputSchema 44 + headers?: { [key: string]: string } 45 + } 46 + 47 + export interface HandlerError { 48 + status: number 49 + message?: string 50 + error?: 'InvalidSwap' 51 + } 52 + 53 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 54 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 55 + auth: HA 56 + params: QueryParams 57 + input: HandlerInput 58 + req: express.Request 59 + res: express.Response 60 + resetRouteRateLimits: () => Promise<void> 61 + } 62 + export type Handler<HA extends HandlerAuth = never> = ( 63 + ctx: HandlerReqCtx<HA>, 64 + ) => Promise<HandlerOutput> | HandlerOutput
+58
packages/appview/src/lexicons/types/com/atproto/repo/describeRepo.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 6 + import express from 'express' 7 + import { CID } from 'multiformats/cid' 8 + 9 + import { validate as _validate } from '../../../../lexicons' 10 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 11 + 12 + const is$typed = _is$typed, 13 + validate = _validate 14 + const id = 'com.atproto.repo.describeRepo' 15 + 16 + export interface QueryParams { 17 + /** The handle or DID of the repo. */ 18 + repo: string 19 + } 20 + 21 + export type InputSchema = undefined 22 + 23 + export interface OutputSchema { 24 + handle: string 25 + did: string 26 + /** The complete DID document for this account. */ 27 + didDoc: { [_ in string]: unknown } 28 + /** List of all the collections (NSIDs) for which this repo contains at least one record. */ 29 + collections: string[] 30 + /** Indicates if handle is currently valid (resolves bi-directionally) */ 31 + handleIsCorrect: boolean 32 + } 33 + 34 + export type HandlerInput = undefined 35 + 36 + export interface HandlerSuccess { 37 + encoding: 'application/json' 38 + body: OutputSchema 39 + headers?: { [key: string]: string } 40 + } 41 + 42 + export interface HandlerError { 43 + status: number 44 + message?: string 45 + } 46 + 47 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 48 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 49 + auth: HA 50 + params: QueryParams 51 + input: HandlerInput 52 + req: express.Request 53 + res: express.Response 54 + resetRouteRateLimits: () => Promise<void> 55 + } 56 + export type Handler<HA extends HandlerAuth = never> = ( 57 + ctx: HandlerReqCtx<HA>, 58 + ) => Promise<HandlerOutput> | HandlerOutput
+60
packages/appview/src/lexicons/types/com/atproto/repo/getRecord.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 6 + import express from 'express' 7 + import { CID } from 'multiformats/cid' 8 + 9 + import { validate as _validate } from '../../../../lexicons' 10 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 11 + 12 + const is$typed = _is$typed, 13 + validate = _validate 14 + const id = 'com.atproto.repo.getRecord' 15 + 16 + export interface QueryParams { 17 + /** The handle or DID of the repo. */ 18 + repo: string 19 + /** The NSID of the record collection. */ 20 + collection: string 21 + /** The Record Key. */ 22 + rkey: string 23 + /** The CID of the version of the record. If not specified, then return the most recent version. */ 24 + cid?: string 25 + } 26 + 27 + export type InputSchema = undefined 28 + 29 + export interface OutputSchema { 30 + uri: string 31 + cid?: string 32 + value: { [_ in string]: unknown } 33 + } 34 + 35 + export type HandlerInput = undefined 36 + 37 + export interface HandlerSuccess { 38 + encoding: 'application/json' 39 + body: OutputSchema 40 + headers?: { [key: string]: string } 41 + } 42 + 43 + export interface HandlerError { 44 + status: number 45 + message?: string 46 + error?: 'RecordNotFound' 47 + } 48 + 49 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 50 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 51 + auth: HA 52 + params: QueryParams 53 + input: HandlerInput 54 + req: express.Request 55 + res: express.Response 56 + resetRouteRateLimits: () => Promise<void> 57 + } 58 + export type Handler<HA extends HandlerAuth = never> = ( 59 + ctx: HandlerReqCtx<HA>, 60 + ) => Promise<HandlerOutput> | HandlerOutput
+42
packages/appview/src/lexicons/types/com/atproto/repo/importRepo.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import stream from 'node:stream' 5 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 6 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 7 + import express from 'express' 8 + import { CID } from 'multiformats/cid' 9 + 10 + import { validate as _validate } from '../../../../lexicons' 11 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 12 + 13 + const is$typed = _is$typed, 14 + validate = _validate 15 + const id = 'com.atproto.repo.importRepo' 16 + 17 + export interface QueryParams {} 18 + 19 + export type InputSchema = string | Uint8Array | Blob 20 + 21 + export interface HandlerInput { 22 + encoding: 'application/vnd.ipld.car' 23 + body: stream.Readable 24 + } 25 + 26 + export interface HandlerError { 27 + status: number 28 + message?: string 29 + } 30 + 31 + export type HandlerOutput = HandlerError | void 32 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 33 + auth: HA 34 + params: QueryParams 35 + input: HandlerInput 36 + req: express.Request 37 + res: express.Response 38 + resetRouteRateLimits: () => Promise<void> 39 + } 40 + export type Handler<HA extends HandlerAuth = never> = ( 41 + ctx: HandlerReqCtx<HA>, 42 + ) => Promise<HandlerOutput> | HandlerOutput
+68
packages/appview/src/lexicons/types/com/atproto/repo/listMissingBlobs.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 6 + import express from 'express' 7 + import { CID } from 'multiformats/cid' 8 + 9 + import { validate as _validate } from '../../../../lexicons' 10 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 11 + 12 + const is$typed = _is$typed, 13 + validate = _validate 14 + const id = 'com.atproto.repo.listMissingBlobs' 15 + 16 + export interface QueryParams { 17 + limit: number 18 + cursor?: string 19 + } 20 + 21 + export type InputSchema = undefined 22 + 23 + export interface OutputSchema { 24 + cursor?: string 25 + blobs: RecordBlob[] 26 + } 27 + 28 + export type HandlerInput = undefined 29 + 30 + export interface HandlerSuccess { 31 + encoding: 'application/json' 32 + body: OutputSchema 33 + headers?: { [key: string]: string } 34 + } 35 + 36 + export interface HandlerError { 37 + status: number 38 + message?: string 39 + } 40 + 41 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 42 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 43 + auth: HA 44 + params: QueryParams 45 + input: HandlerInput 46 + req: express.Request 47 + res: express.Response 48 + resetRouteRateLimits: () => Promise<void> 49 + } 50 + export type Handler<HA extends HandlerAuth = never> = ( 51 + ctx: HandlerReqCtx<HA>, 52 + ) => Promise<HandlerOutput> | HandlerOutput 53 + 54 + export interface RecordBlob { 55 + $type?: 'com.atproto.repo.listMissingBlobs#recordBlob' 56 + cid: string 57 + recordUri: string 58 + } 59 + 60 + const hashRecordBlob = 'recordBlob' 61 + 62 + export function isRecordBlob<V>(v: V) { 63 + return is$typed(v, id, hashRecordBlob) 64 + } 65 + 66 + export function validateRecordBlob<V>(v: V) { 67 + return validate<RecordBlob & V>(v, id, hashRecordBlob) 68 + }
+80
packages/appview/src/lexicons/types/com/atproto/repo/listRecords.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 6 + import express from 'express' 7 + import { CID } from 'multiformats/cid' 8 + 9 + import { validate as _validate } from '../../../../lexicons' 10 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 11 + 12 + const is$typed = _is$typed, 13 + validate = _validate 14 + const id = 'com.atproto.repo.listRecords' 15 + 16 + export interface QueryParams { 17 + /** The handle or DID of the repo. */ 18 + repo: string 19 + /** The NSID of the record type. */ 20 + collection: string 21 + /** The number of records to return. */ 22 + limit: number 23 + cursor?: string 24 + /** DEPRECATED: The lowest sort-ordered rkey to start from (exclusive) */ 25 + rkeyStart?: string 26 + /** DEPRECATED: The highest sort-ordered rkey to stop at (exclusive) */ 27 + rkeyEnd?: string 28 + /** Flag to reverse the order of the returned records. */ 29 + reverse?: boolean 30 + } 31 + 32 + export type InputSchema = undefined 33 + 34 + export interface OutputSchema { 35 + cursor?: string 36 + records: Record[] 37 + } 38 + 39 + export type HandlerInput = undefined 40 + 41 + export interface HandlerSuccess { 42 + encoding: 'application/json' 43 + body: OutputSchema 44 + headers?: { [key: string]: string } 45 + } 46 + 47 + export interface HandlerError { 48 + status: number 49 + message?: string 50 + } 51 + 52 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 53 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 54 + auth: HA 55 + params: QueryParams 56 + input: HandlerInput 57 + req: express.Request 58 + res: express.Response 59 + resetRouteRateLimits: () => Promise<void> 60 + } 61 + export type Handler<HA extends HandlerAuth = never> = ( 62 + ctx: HandlerReqCtx<HA>, 63 + ) => Promise<HandlerOutput> | HandlerOutput 64 + 65 + export interface Record { 66 + $type?: 'com.atproto.repo.listRecords#record' 67 + uri: string 68 + cid: string 69 + value: { [_ in string]: unknown } 70 + } 71 + 72 + const hashRecord = 'record' 73 + 74 + export function isRecord<V>(v: V) { 75 + return is$typed(v, id, hashRecord) 76 + } 77 + 78 + export function validateRecord<V>(v: V) { 79 + return validate<Record & V>(v, id, hashRecord) 80 + }
+71
packages/appview/src/lexicons/types/com/atproto/repo/putRecord.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 6 + import express from 'express' 7 + import { CID } from 'multiformats/cid' 8 + 9 + import { validate as _validate } from '../../../../lexicons' 10 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 11 + import type * as ComAtprotoRepoDefs from './defs.js' 12 + 13 + const is$typed = _is$typed, 14 + validate = _validate 15 + const id = 'com.atproto.repo.putRecord' 16 + 17 + export interface QueryParams {} 18 + 19 + export interface InputSchema { 20 + /** The handle or DID of the repo (aka, current account). */ 21 + repo: string 22 + /** The NSID of the record collection. */ 23 + collection: string 24 + /** The Record Key. */ 25 + rkey: string 26 + /** Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons. */ 27 + validate?: boolean 28 + /** The record to write. */ 29 + record: { [_ in string]: unknown } 30 + /** Compare and swap with the previous record by CID. WARNING: nullable and optional field; may cause problems with golang implementation */ 31 + swapRecord?: string | null 32 + /** Compare and swap with the previous commit by CID. */ 33 + swapCommit?: string 34 + } 35 + 36 + export interface OutputSchema { 37 + uri: string 38 + cid: string 39 + commit?: ComAtprotoRepoDefs.CommitMeta 40 + validationStatus?: 'valid' | 'unknown' | (string & {}) 41 + } 42 + 43 + export interface HandlerInput { 44 + encoding: 'application/json' 45 + body: InputSchema 46 + } 47 + 48 + export interface HandlerSuccess { 49 + encoding: 'application/json' 50 + body: OutputSchema 51 + headers?: { [key: string]: string } 52 + } 53 + 54 + export interface HandlerError { 55 + status: number 56 + message?: string 57 + error?: 'InvalidSwap' 58 + } 59 + 60 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 61 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 62 + auth: HA 63 + params: QueryParams 64 + input: HandlerInput 65 + req: express.Request 66 + res: express.Response 67 + resetRouteRateLimits: () => Promise<void> 68 + } 69 + export type Handler<HA extends HandlerAuth = never> = ( 70 + ctx: HandlerReqCtx<HA>, 71 + ) => Promise<HandlerOutput> | HandlerOutput
+28
packages/appview/src/lexicons/types/com/atproto/repo/strongRef.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + 7 + import { validate as _validate } from '../../../../lexicons' 8 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 9 + 10 + const is$typed = _is$typed, 11 + validate = _validate 12 + const id = 'com.atproto.repo.strongRef' 13 + 14 + export interface Main { 15 + $type?: 'com.atproto.repo.strongRef' 16 + uri: string 17 + cid: string 18 + } 19 + 20 + const hashMain = 'main' 21 + 22 + export function isMain<V>(v: V) { 23 + return is$typed(v, id, hashMain) 24 + } 25 + 26 + export function validateMain<V>(v: V) { 27 + return validate<Main & V>(v, id, hashMain) 28 + }
+52
packages/appview/src/lexicons/types/com/atproto/repo/uploadBlob.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import stream from 'node:stream' 5 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 6 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 7 + import express from 'express' 8 + import { CID } from 'multiformats/cid' 9 + 10 + import { validate as _validate } from '../../../../lexicons' 11 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../../util' 12 + 13 + const is$typed = _is$typed, 14 + validate = _validate 15 + const id = 'com.atproto.repo.uploadBlob' 16 + 17 + export interface QueryParams {} 18 + 19 + export type InputSchema = string | Uint8Array | Blob 20 + 21 + export interface OutputSchema { 22 + blob: BlobRef 23 + } 24 + 25 + export interface HandlerInput { 26 + encoding: '*/*' 27 + body: stream.Readable 28 + } 29 + 30 + export interface HandlerSuccess { 31 + encoding: 'application/json' 32 + body: OutputSchema 33 + headers?: { [key: string]: string } 34 + } 35 + 36 + export interface HandlerError { 37 + status: number 38 + message?: string 39 + } 40 + 41 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 42 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 43 + auth: HA 44 + params: QueryParams 45 + input: HandlerInput 46 + req: express.Request 47 + res: express.Response 48 + resetRouteRateLimits: () => Promise<void> 49 + } 50 + export type Handler<HA extends HandlerAuth = never> = ( 51 + ctx: HandlerReqCtx<HA>, 52 + ) => Promise<HandlerOutput> | HandlerOutput
+46
packages/appview/src/lexicons/types/xyz/statusphere/defs.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + 7 + import { validate as _validate } from '../../../lexicons' 8 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../util' 9 + 10 + const is$typed = _is$typed, 11 + validate = _validate 12 + const id = 'xyz.statusphere.defs' 13 + 14 + export interface StatusView { 15 + $type?: 'xyz.statusphere.defs#statusView' 16 + uri: string 17 + status: string 18 + createdAt: string 19 + profile: ProfileView 20 + } 21 + 22 + const hashStatusView = 'statusView' 23 + 24 + export function isStatusView<V>(v: V) { 25 + return is$typed(v, id, hashStatusView) 26 + } 27 + 28 + export function validateStatusView<V>(v: V) { 29 + return validate<StatusView & V>(v, id, hashStatusView) 30 + } 31 + 32 + export interface ProfileView { 33 + $type?: 'xyz.statusphere.defs#profileView' 34 + did: string 35 + handle: string 36 + } 37 + 38 + const hashProfileView = 'profileView' 39 + 40 + export function isProfileView<V>(v: V) { 41 + return is$typed(v, id, hashProfileView) 42 + } 43 + 44 + export function validateProfileView<V>(v: V) { 45 + return validate<ProfileView & V>(v, id, hashProfileView) 46 + }
+53
packages/appview/src/lexicons/types/xyz/statusphere/getStatuses.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 6 + import express from 'express' 7 + import { CID } from 'multiformats/cid' 8 + 9 + import { validate as _validate } from '../../../lexicons' 10 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../util' 11 + import type * as XyzStatusphereDefs from './defs.js' 12 + 13 + const is$typed = _is$typed, 14 + validate = _validate 15 + const id = 'xyz.statusphere.getStatuses' 16 + 17 + export interface QueryParams { 18 + limit: number 19 + cursor?: string 20 + } 21 + 22 + export type InputSchema = undefined 23 + 24 + export interface OutputSchema { 25 + cursor?: string 26 + statuses: XyzStatusphereDefs.StatusView[] 27 + } 28 + 29 + export type HandlerInput = undefined 30 + 31 + export interface HandlerSuccess { 32 + encoding: 'application/json' 33 + body: OutputSchema 34 + headers?: { [key: string]: string } 35 + } 36 + 37 + export interface HandlerError { 38 + status: number 39 + message?: string 40 + } 41 + 42 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 43 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 44 + auth: HA 45 + params: QueryParams 46 + input: HandlerInput 47 + req: express.Request 48 + res: express.Response 49 + resetRouteRateLimits: () => Promise<void> 50 + } 51 + export type Handler<HA extends HandlerAuth = never> = ( 52 + ctx: HandlerReqCtx<HA>, 53 + ) => Promise<HandlerOutput> | HandlerOutput
+51
packages/appview/src/lexicons/types/xyz/statusphere/getUser.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 6 + import express from 'express' 7 + import { CID } from 'multiformats/cid' 8 + 9 + import { validate as _validate } from '../../../lexicons' 10 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../util' 11 + import type * as AppBskyActorDefs from '../../app/bsky/actor/defs.js' 12 + import type * as XyzStatusphereDefs from './defs.js' 13 + 14 + const is$typed = _is$typed, 15 + validate = _validate 16 + const id = 'xyz.statusphere.getUser' 17 + 18 + export interface QueryParams {} 19 + 20 + export type InputSchema = undefined 21 + 22 + export interface OutputSchema { 23 + profile: AppBskyActorDefs.ProfileView 24 + status?: XyzStatusphereDefs.StatusView 25 + } 26 + 27 + export type HandlerInput = undefined 28 + 29 + export interface HandlerSuccess { 30 + encoding: 'application/json' 31 + body: OutputSchema 32 + headers?: { [key: string]: string } 33 + } 34 + 35 + export interface HandlerError { 36 + status: number 37 + message?: string 38 + } 39 + 40 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 41 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 42 + auth: HA 43 + params: QueryParams 44 + input: HandlerInput 45 + req: express.Request 46 + res: express.Response 47 + resetRouteRateLimits: () => Promise<void> 48 + } 49 + export type Handler<HA extends HandlerAuth = never> = ( 50 + ctx: HandlerReqCtx<HA>, 51 + ) => Promise<HandlerOutput> | HandlerOutput
+54
packages/appview/src/lexicons/types/xyz/statusphere/sendStatus.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server' 6 + import express from 'express' 7 + import { CID } from 'multiformats/cid' 8 + 9 + import { validate as _validate } from '../../../lexicons' 10 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../util' 11 + import type * as XyzStatusphereDefs from './defs.js' 12 + 13 + const is$typed = _is$typed, 14 + validate = _validate 15 + const id = 'xyz.statusphere.sendStatus' 16 + 17 + export interface QueryParams {} 18 + 19 + export interface InputSchema { 20 + status: string 21 + } 22 + 23 + export interface OutputSchema { 24 + status: XyzStatusphereDefs.StatusView 25 + } 26 + 27 + export interface HandlerInput { 28 + encoding: 'application/json' 29 + body: InputSchema 30 + } 31 + 32 + export interface HandlerSuccess { 33 + encoding: 'application/json' 34 + body: OutputSchema 35 + headers?: { [key: string]: string } 36 + } 37 + 38 + export interface HandlerError { 39 + status: number 40 + message?: string 41 + } 42 + 43 + export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough 44 + export type HandlerReqCtx<HA extends HandlerAuth = never> = { 45 + auth: HA 46 + params: QueryParams 47 + input: HandlerInput 48 + req: express.Request 49 + res: express.Response 50 + resetRouteRateLimits: () => Promise<void> 51 + } 52 + export type Handler<HA extends HandlerAuth = never> = ( 53 + ctx: HandlerReqCtx<HA>, 54 + ) => Promise<HandlerOutput> | HandlerOutput
+29
packages/appview/src/lexicons/types/xyz/statusphere/status.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + 7 + import { validate as _validate } from '../../../lexicons' 8 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../util' 9 + 10 + const is$typed = _is$typed, 11 + validate = _validate 12 + const id = 'xyz.statusphere.status' 13 + 14 + export interface Record { 15 + $type: 'xyz.statusphere.status' 16 + status: string 17 + createdAt: string 18 + [k: string]: unknown 19 + } 20 + 21 + const hashRecord = 'main' 22 + 23 + export function isRecord<V>(v: V) { 24 + return is$typed(v, id, hashRecord) 25 + } 26 + 27 + export function validateRecord<V>(v: V) { 28 + return validate<Record & V>(v, id, hashRecord, true) 29 + }
+82
packages/appview/src/lexicons/util.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + 5 + import { ValidationResult } from '@atproto/lexicon' 6 + 7 + export type OmitKey<T, K extends keyof T> = { 8 + [K2 in keyof T as K2 extends K ? never : K2]: T[K2] 9 + } 10 + 11 + export type $Typed<V, T extends string = string> = V & { $type: T } 12 + export type Un$Typed<V extends { $type?: string }> = OmitKey<V, '$type'> 13 + 14 + export type $Type<Id extends string, Hash extends string> = Hash extends 'main' 15 + ? Id 16 + : `${Id}#${Hash}` 17 + 18 + function isObject<V>(v: V): v is V & object { 19 + return v != null && typeof v === 'object' 20 + } 21 + 22 + function is$type<Id extends string, Hash extends string>( 23 + $type: unknown, 24 + id: Id, 25 + hash: Hash, 26 + ): $type is $Type<Id, Hash> { 27 + return hash === 'main' 28 + ? $type === id 29 + : // $type === `${id}#${hash}` 30 + typeof $type === 'string' && 31 + $type.length === id.length + 1 + hash.length && 32 + $type.charCodeAt(id.length) === 35 /* '#' */ && 33 + $type.startsWith(id) && 34 + $type.endsWith(hash) 35 + } 36 + 37 + export type $TypedObject< 38 + V, 39 + Id extends string, 40 + Hash extends string, 41 + > = V extends { 42 + $type: $Type<Id, Hash> 43 + } 44 + ? V 45 + : V extends { $type?: string } 46 + ? V extends { $type?: infer T extends $Type<Id, Hash> } 47 + ? V & { $type: T } 48 + : never 49 + : V & { $type: $Type<Id, Hash> } 50 + 51 + export function is$typed<V, Id extends string, Hash extends string>( 52 + v: V, 53 + id: Id, 54 + hash: Hash, 55 + ): v is $TypedObject<V, Id, Hash> { 56 + return isObject(v) && '$type' in v && is$type(v.$type, id, hash) 57 + } 58 + 59 + export function maybe$typed<V, Id extends string, Hash extends string>( 60 + v: V, 61 + id: Id, 62 + hash: Hash, 63 + ): v is V & object & { $type?: $Type<Id, Hash> } { 64 + return ( 65 + isObject(v) && 66 + ('$type' in v ? v.$type === undefined || is$type(v.$type, id, hash) : true) 67 + ) 68 + } 69 + 70 + export type Validator<R = unknown> = (v: unknown) => ValidationResult<R> 71 + export type ValidatorParam<V extends Validator> = 72 + V extends Validator<infer R> ? R : never 73 + 74 + /** 75 + * Utility function that allows to convert a "validate*" utility function into a 76 + * type predicate. 77 + */ 78 + export function asPredicate<V extends Validator>(validate: V) { 79 + return function <T>(v: T): v is T & ValidatorParam<V> { 80 + return validate(v).success 81 + } 82 + }
+1 -1
packages/appview/src/lib/hydrate.ts
··· 4 4 XyzStatusphereDefs, 5 5 } from '@statusphere/lexicon' 6 6 7 + import { AppContext } from '#/context' 7 8 import { Status } from '#/db' 8 - import { AppContext } from '#/index' 9 9 10 10 export async function statusToStatusView( 11 11 status: Status,
-347
packages/appview/src/routes.ts
··· 1 - import type { IncomingMessage, ServerResponse } from 'node:http' 2 - import { Agent } from '@atproto/api' 3 - import { TID } from '@atproto/common' 4 - import { OAuthResolverError } from '@atproto/oauth-client-node' 5 - import { isValidHandle } from '@atproto/syntax' 6 - import { 7 - AppBskyActorDefs, 8 - AppBskyActorProfile, 9 - XyzStatusphereStatus, 10 - } from '@statusphere/lexicon' 11 - import express from 'express' 12 - import { getIronSession, SessionOptions } from 'iron-session' 13 - 14 - import type { AppContext } from '#/index' 15 - import { env } from '#/lib/env' 16 - import { bskyProfileToProfileView, statusToStatusView } from '#/lib/hydrate' 17 - 18 - type Session = { did: string } 19 - 20 - // Common session options 21 - const sessionOptions: SessionOptions = { 22 - cookieName: 'sid', 23 - password: env.COOKIE_SECRET, 24 - cookieOptions: { 25 - secure: env.NODE_ENV === 'production', 26 - httpOnly: true, 27 - sameSite: true, 28 - path: '/', 29 - // Don't set domain explicitly - let browser determine it 30 - domain: undefined, 31 - }, 32 - } 33 - 34 - // Helper function for defining routes 35 - const handler = 36 - ( 37 - fn: ( 38 - req: express.Request, 39 - res: express.Response, 40 - next: express.NextFunction, 41 - ) => Promise<void> | void, 42 - ) => 43 - async ( 44 - req: express.Request, 45 - res: express.Response, 46 - next: express.NextFunction, 47 - ) => { 48 - try { 49 - await fn(req, res, next) 50 - } catch (err) { 51 - next(err) 52 - } 53 - } 54 - 55 - // Helper function to get the Atproto Agent for the active session 56 - async function getSessionAgent( 57 - req: IncomingMessage | express.Request, 58 - res: ServerResponse<IncomingMessage> | express.Response, 59 - ctx: AppContext, 60 - ) { 61 - const session = await getIronSession<Session>(req, res, sessionOptions) 62 - 63 - if (!session.did) { 64 - return null 65 - } 66 - 67 - try { 68 - const oauthSession = await ctx.oauthClient.restore(session.did) 69 - return oauthSession ? new Agent(oauthSession) : null 70 - } catch (err) { 71 - ctx.logger.warn({ err }, 'oauth restore failed') 72 - session.destroy() 73 - return null 74 - } 75 - } 76 - 77 - export const createRouter = (ctx: AppContext) => { 78 - const router = express.Router() 79 - 80 - // Simple CORS configuration for all routes 81 - router.use((req, res, next) => { 82 - // Allow requests from either the specific origin or any origin during development 83 - res.header('Access-Control-Allow-Origin', req.headers.origin || '*') 84 - res.header('Access-Control-Allow-Credentials', 'true') 85 - res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') 86 - res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization') 87 - 88 - if (req.method === 'OPTIONS') { 89 - res.status(200).end() 90 - return 91 - } 92 - next() 93 - }) 94 - 95 - // OAuth metadata 96 - router.get( 97 - '/client-metadata.json', 98 - handler((_req, res) => { 99 - res.json(ctx.oauthClient.clientMetadata) 100 - }), 101 - ) 102 - 103 - // OAuth callback to complete session creation 104 - router.get( 105 - '/oauth/callback', 106 - handler(async (req, res) => { 107 - // Get the query parameters from the URL 108 - const params = new URLSearchParams(req.originalUrl.split('?')[1]) 109 - 110 - try { 111 - const { session } = await ctx.oauthClient.callback(params) 112 - 113 - // Use the common session options 114 - const clientSession = await getIronSession<Session>( 115 - req, 116 - res, 117 - sessionOptions, 118 - ) 119 - 120 - // Set the DID on the session 121 - clientSession.did = session.did 122 - await clientSession.save() 123 - 124 - // Get the origin and determine appropriate redirect 125 - const host = req.get('host') || '' 126 - const protocol = req.protocol || 'http' 127 - const baseUrl = `${protocol}://${host}` 128 - 129 - ctx.logger.info( 130 - `OAuth callback successful, redirecting to ${baseUrl}/oauth-callback`, 131 - ) 132 - 133 - // Redirect to the frontend oauth-callback page 134 - res.redirect('/oauth-callback') 135 - } catch (err) { 136 - ctx.logger.error({ err }, 'oauth callback failed') 137 - 138 - // Handle error redirect - stay on same domain 139 - res.redirect('/oauth-callback?error=auth') 140 - } 141 - }), 142 - ) 143 - 144 - // Login handler 145 - router.post( 146 - '/login', 147 - handler(async (req, res) => { 148 - // Validate 149 - const handle = req.body?.handle 150 - if (typeof handle !== 'string' || !isValidHandle(handle)) { 151 - res.status(400).json({ error: 'invalid handle' }) 152 - return 153 - } 154 - 155 - // Initiate the OAuth flow 156 - try { 157 - const url = await ctx.oauthClient.authorize(handle, { 158 - scope: 'atproto transition:generic', 159 - }) 160 - res.json({ redirectUrl: url.toString() }) 161 - } catch (err) { 162 - ctx.logger.error({ err }, 'oauth authorize failed') 163 - const errorMsg = 164 - err instanceof OAuthResolverError 165 - ? err.message 166 - : "couldn't initiate login" 167 - res.status(500).json({ error: errorMsg }) 168 - } 169 - }), 170 - ) 171 - 172 - // Logout handler 173 - router.post( 174 - '/logout', 175 - handler(async (req, res) => { 176 - const session = await getIronSession<Session>(req, res, sessionOptions) 177 - session.destroy() 178 - res.json({ success: true }) 179 - }), 180 - ) 181 - 182 - // Get current user info 183 - router.get( 184 - '/user', 185 - handler(async (req, res) => { 186 - const agent = await getSessionAgent(req, res, ctx) 187 - if (!agent) { 188 - res.status(401).json({ error: 'Not logged in' }) 189 - return 190 - } 191 - 192 - const did = agent.assertDid 193 - 194 - // Fetch user profile 195 - try { 196 - const profileResponse = await agent.com.atproto.repo 197 - .getRecord({ 198 - repo: did, 199 - collection: 'app.bsky.actor.profile', 200 - rkey: 'self', 201 - }) 202 - .catch(() => undefined) 203 - 204 - const profileRecord = profileResponse?.data 205 - let profile: AppBskyActorProfile.Record = 206 - {} as AppBskyActorProfile.Record 207 - 208 - if ( 209 - profileRecord && 210 - AppBskyActorProfile.isRecord(profileRecord.value) 211 - ) { 212 - const validated = AppBskyActorProfile.validateRecord( 213 - profileRecord.value, 214 - ) 215 - if (validated.success) { 216 - profile = profileRecord.value 217 - } else { 218 - ctx.logger.error( 219 - { err: validated.error }, 220 - 'Failed to validate user profile', 221 - ) 222 - } 223 - } 224 - 225 - // Fetch user status 226 - const status = await ctx.db 227 - .selectFrom('status') 228 - .selectAll() 229 - .where('authorDid', '=', did) 230 - .orderBy('indexedAt', 'desc') 231 - .executeTakeFirst() 232 - 233 - res.json({ 234 - did: agent.assertDid, 235 - profile: await bskyProfileToProfileView(did, profile, ctx), 236 - status: status ? await statusToStatusView(status, ctx) : undefined, 237 - }) 238 - } catch (err) { 239 - ctx.logger.error({ err }, 'Failed to get user info') 240 - res.status(500).json({ error: 'Failed to get user info' }) 241 - } 242 - }), 243 - ) 244 - 245 - // Get statuses 246 - router.get( 247 - '/statuses', 248 - handler(async (req, res) => { 249 - try { 250 - // Fetch data stored in our SQLite 251 - const statuses = await ctx.db 252 - .selectFrom('status') 253 - .selectAll() 254 - .orderBy('indexedAt', 'desc') 255 - .limit(30) 256 - .execute() 257 - 258 - res.json({ 259 - statuses: await Promise.all( 260 - statuses.map((status) => statusToStatusView(status, ctx)), 261 - ), 262 - }) 263 - } catch (err) { 264 - ctx.logger.error({ err }, 'Failed to get statuses') 265 - res.status(500).json({ error: 'Failed to get statuses' }) 266 - } 267 - }), 268 - ) 269 - 270 - // Create status 271 - router.post( 272 - '/status', 273 - handler(async (req, res) => { 274 - // If the user is signed in, get an agent which communicates with their server 275 - const agent = await getSessionAgent(req, res, ctx) 276 - if (!agent) { 277 - res.status(401).json({ error: 'Session required' }) 278 - return 279 - } 280 - 281 - // Construct & validate their status record 282 - const rkey = TID.nextStr() 283 - const record = { 284 - $type: 'xyz.statusphere.status', 285 - status: req.body?.status, 286 - createdAt: new Date().toISOString(), 287 - } 288 - if (!XyzStatusphereStatus.validateRecord(record).success) { 289 - res.status(400).json({ error: 'Invalid status' }) 290 - return 291 - } 292 - 293 - let uri 294 - try { 295 - // Write the status record to the user's repository 296 - const response = await agent.com.atproto.repo.putRecord({ 297 - repo: agent.assertDid, 298 - collection: 'xyz.statusphere.status', 299 - rkey, 300 - record, 301 - validate: false, 302 - }) 303 - uri = response.data.uri 304 - } catch (err) { 305 - ctx.logger.warn({ err }, 'failed to write record') 306 - res.status(500).json({ error: 'Failed to write record' }) 307 - return 308 - } 309 - 310 - try { 311 - // Optimistically update our SQLite 312 - // This isn't strictly necessary because the write event will be 313 - // handled in #/firehose/ingestor.ts, but it ensures that future reads 314 - // will be up-to-date after this method finishes. 315 - await ctx.db 316 - .insertInto('status') 317 - .values({ 318 - uri, 319 - authorDid: agent.assertDid, 320 - status: record.status, 321 - createdAt: record.createdAt, 322 - indexedAt: new Date().toISOString(), 323 - }) 324 - .execute() 325 - 326 - res.json({ 327 - success: true, 328 - uri, 329 - status: await statusToStatusView(record.status, ctx), 330 - }) 331 - } catch (err) { 332 - ctx.logger.warn( 333 - { err }, 334 - 'failed to update computed view; ignoring as it should be caught by the firehose', 335 - ) 336 - res.json({ 337 - success: true, 338 - uri, 339 - status: await statusToStatusView(record.status, ctx), 340 - warning: 'Database not updated', 341 - }) 342 - } 343 - }), 344 - ) 345 - 346 - return router 347 - }
+48
packages/appview/src/session.ts
··· 1 + import { IncomingMessage, ServerResponse } from 'node:http' 2 + import { Agent } from '@atproto/api' 3 + import { Request, Response } from 'express' 4 + import { getIronSession, SessionOptions } from 'iron-session' 5 + 6 + import { AppContext } from '#/context' 7 + import { env } from '#/lib/env' 8 + 9 + type Session = { did: string } 10 + 11 + // Common session options 12 + const sessionOptions: SessionOptions = { 13 + cookieName: 'sid', 14 + password: env.COOKIE_SECRET, 15 + cookieOptions: { 16 + secure: env.NODE_ENV === 'production', 17 + httpOnly: true, 18 + sameSite: true, 19 + path: '/', 20 + // Don't set domain explicitly - let browser determine it 21 + domain: undefined, 22 + }, 23 + } 24 + 25 + export async function getSessionAgent( 26 + req: IncomingMessage | Request, 27 + res: ServerResponse<IncomingMessage> | Response, 28 + ctx: AppContext, 29 + ) { 30 + const session = await getIronSession<Session>(req, res, sessionOptions) 31 + 32 + if (!session.did) { 33 + return null 34 + } 35 + 36 + try { 37 + const oauthSession = await ctx.oauthClient.restore(session.did) 38 + return oauthSession ? new Agent(oauthSession) : null 39 + } catch (err) { 40 + ctx.logger.warn({ err }, 'oauth restore failed') 41 + session.destroy() 42 + return null 43 + } 44 + } 45 + 46 + export async function getSession(req: Request, res: Response) { 47 + return getIronSession<Session>(req, res, sessionOptions) 48 + }
+1
packages/client/package.json
··· 14 14 }, 15 15 "dependencies": { 16 16 "@atproto/api": "^0.14.7", 17 + "@atproto/xrpc": "^0.6.9", 17 18 "@statusphere/lexicon": "workspace:*", 18 19 "@tailwindcss/vite": "^4.0.9", 19 20 "@tanstack/react-query": "^5.66.11",
+2 -2
packages/client/src/components/StatusForm.tsx
··· 46 46 47 47 // Use React Query mutation for creating a status 48 48 const mutation = useMutation({ 49 - mutationFn: (emoji: string) => api.createStatus(emoji), 49 + mutationFn: (emoji: string) => api.createStatus({ status: emoji }), 50 50 onMutate: async (emoji) => { 51 51 // Cancel any outgoing refetches so they don't overwrite our optimistic updates 52 52 await queryClient.cancelQueries({ queryKey: ['statuses'] }) ··· 65 65 const optimisticStatus = { 66 66 uri: `optimistic-${Date.now()}`, 67 67 profile: { 68 - did: user.did, 68 + did: user.profile.did, 69 69 handle: user.profile.handle, 70 70 }, 71 71 status: emoji,
+8 -1
packages/client/src/components/StatusList.tsx
··· 1 + import { useEffect } from 'react' 1 2 import { useQuery } from '@tanstack/react-query' 2 3 3 4 import api from '#/services/api' ··· 7 8 const { data, isPending, isError, error } = useQuery({ 8 9 queryKey: ['statuses'], 9 10 queryFn: async () => { 10 - const data = await api.getStatuses() 11 + const { data } = await api.getStatuses({ limit: 30 }) 11 12 return data 12 13 }, 13 14 placeholderData: (previousData) => previousData, // Use previous data while refetching 14 15 refetchInterval: 30e3, // Refetch every 30 seconds 15 16 }) 17 + 18 + useEffect(() => { 19 + if (error) { 20 + console.error(error) 21 + } 22 + }, [error]) 16 23 17 24 // Destructure data 18 25 const statuses = data?.statuses || []
+13 -13
packages/client/src/hooks/useAuth.tsx
··· 1 1 import { createContext, ReactNode, useContext, useState } from 'react' 2 2 import { useQuery, useQueryClient } from '@tanstack/react-query' 3 + import { XRPCError } from '@atproto/xrpc' 4 + import { XyzStatusphereGetUser } from '@statusphere/lexicon' 3 5 4 - import api, { User } from '#/services/api' 6 + import api from '#/services/api' 5 7 6 8 interface AuthContextType { 7 - user: User | null 9 + user: XyzStatusphereGetUser.OutputSchema | null 8 10 loading: boolean 9 11 error: string | null 10 12 login: (handle: string) => Promise<{ redirectUrl: string }> ··· 39 41 } 40 42 41 43 try { 42 - const userData = await api.getCurrentUser() 44 + const { data: userData } = await api.getCurrentUser({}) 43 45 44 46 // Clean up URL if needed 45 47 if (window.location.search && userData) { ··· 52 54 53 55 return userData 54 56 } catch (apiErr) { 57 + if ( 58 + apiErr instanceof XRPCError && 59 + apiErr.error === 'AuthenticationRequired' 60 + ) { 61 + return null 62 + } 63 + 55 64 console.error('🚫 API error during auth check:', apiErr) 56 65 57 66 // If it's a network error, provide a more helpful message ··· 75 84 setError(null) 76 85 77 86 try { 78 - // Add a small artificial delay for UX purposes 79 - const loginPromise = api.login(handle) 80 - 81 - // Ensure the loading state shows for at least 800ms for better UX 82 - const result = await Promise.all([ 83 - loginPromise, 84 - new Promise((resolve) => setTimeout(resolve, 800)), 85 - ]).then(([loginResult]) => loginResult) 86 - 87 - return result 87 + return await api.login(handle) 88 88 } catch (err) { 89 89 const message = err instanceof Error ? err.message : 'Login failed' 90 90 setError(message)
+1 -1
packages/client/src/pages/OAuthCallbackPage.tsx
··· 37 37 .join(', '), 38 38 ) 39 39 40 - const user = await api.getCurrentUser() 40 + const user = await api.getCurrentUser({}) 41 41 console.log('Current user check result:', user) 42 42 43 43 if (user) {
+31 -105
packages/client/src/services/api.ts
··· 1 - import { AppBskyActorDefs, XyzStatusphereDefs } from '@statusphere/lexicon' 1 + import * as Lexicon from '@statusphere/lexicon' 2 + import type { 3 + XyzStatusphereGetStatuses, 4 + XyzStatusphereGetUser, 5 + XyzStatusphereSendStatus, 6 + } from '@statusphere/lexicon' 2 7 3 - // Use '/api' prefix consistently for all API calls 4 - const API_URL = '/api' 8 + class StatusphereAgent extends Lexicon.AtpBaseClient { 9 + constructor() { 10 + super(StatusphereAgent.fetchHandler) 11 + } 5 12 6 - // Helper function for logging API actions 7 - function logApiCall( 8 - method: string, 9 - endpoint: string, 10 - status?: number, 11 - error?: any, 12 - ) { 13 - const statusStr = status ? `[${status}]` : '' 14 - const errorStr = error 15 - ? ` - Error: ${error.message || JSON.stringify(error)}` 16 - : '' 17 - console.log(`🔄 API ${method} ${endpoint} ${statusStr}${errorStr}`) 13 + private static fetchHandler: Lexicon.AtpBaseClient['fetchHandler'] = async ( 14 + path, 15 + options, 16 + ) => { 17 + return await fetch(path, { 18 + ...options, 19 + headers: { 20 + 'Content-Type': 'application/json', 21 + }, 22 + credentials: 'include', 23 + }) 24 + } 18 25 } 19 26 20 - export interface User { 21 - did: string 22 - profile: AppBskyActorDefs.ProfileView 23 - status?: XyzStatusphereDefs.StatusView 24 - } 27 + const agent = new StatusphereAgent() 25 28 26 29 // API service 27 30 export const api = { 28 31 // Login 29 32 async login(handle: string) { 30 - const url = `${API_URL}/login` 31 - logApiCall('POST', url) 32 - 33 - const response = await fetch(url, { 33 + const response = await fetch('/oauth/initiate', { 34 34 method: 'POST', 35 35 headers: { 36 36 'Content-Type': 'application/json', ··· 49 49 50 50 // Logout 51 51 async logout() { 52 - const url = `${API_URL}/logout` 53 - logApiCall('POST', url) 54 - const response = await fetch(url, { 52 + const response = await fetch('/oauth/logout', { 55 53 method: 'POST', 56 54 credentials: 'include', 57 55 }) ··· 64 62 }, 65 63 66 64 // Get current user 67 - async getCurrentUser() { 68 - const url = `${API_URL}/user` 69 - logApiCall('GET', url) 70 - try { 71 - const headers = { 72 - Accept: 'application/json', 73 - } 74 - 75 - const response = await fetch(url, { 76 - credentials: 'include', // This is crucial for sending cookies 77 - headers, 78 - cache: 'no-cache', // Don't cache this request 79 - }) 80 - 81 - logApiCall('GET', '/user', response.status) 82 - 83 - if (!response.ok) { 84 - if (response.status === 401) { 85 - return null 86 - } 87 - 88 - // Try to get error details 89 - let errorText = '' 90 - try { 91 - const errorData = await response.text() 92 - errorText = errorData 93 - } catch (e) { 94 - // Ignore error reading error 95 - } 96 - 97 - throw new Error( 98 - `Failed to get user: ${response.status} ${response.statusText} ${errorText}`, 99 - ) 100 - } 101 - 102 - return response.json() 103 - } catch (error) { 104 - logApiCall('GET', '/user', undefined, error) 105 - if ( 106 - error instanceof TypeError && 107 - error.message.includes('Failed to fetch') 108 - ) { 109 - console.error('Network error - Unable to connect to API server') 110 - } 111 - throw error 112 - } 65 + async getCurrentUser(params: XyzStatusphereGetUser.QueryParams) { 66 + return agent.xyz.statusphere.getUser(params) 113 67 }, 114 68 115 69 // Get statuses 116 - async getStatuses() { 117 - const url = `${API_URL}/statuses` 118 - logApiCall('GET', url) 119 - const response = await fetch(url, { 120 - credentials: 'include', 121 - }) 122 - 123 - if (!response.ok) { 124 - throw new Error('Failed to get statuses') 125 - } 126 - 127 - return response.json() as Promise<{ 128 - statuses: XyzStatusphereDefs.StatusView[] 129 - }> 70 + async getStatuses(params: XyzStatusphereGetStatuses.QueryParams) { 71 + return agent.xyz.statusphere.getStatuses(params) 130 72 }, 131 73 132 74 // Create status 133 - async createStatus(status: string) { 134 - const url = `${API_URL}/status` 135 - logApiCall('POST', url) 136 - const response = await fetch(url, { 137 - method: 'POST', 138 - headers: { 139 - 'Content-Type': 'application/json', 140 - }, 141 - credentials: 'include', 142 - body: JSON.stringify({ status }), 143 - }) 144 - 145 - if (!response.ok) { 146 - const error = await response.json() 147 - throw new Error(error.error || 'Failed to create status') 148 - } 149 - 150 - return response.json() 75 + async createStatus(params: XyzStatusphereSendStatus.InputSchema) { 76 + return agent.xyz.statusphere.sendStatus(params) 151 77 }, 152 78 } 153 79
+1 -1
packages/client/vite.config.ts
··· 18 18 host: '127.0.0.1', 19 19 port: 3000, 20 20 proxy: { 21 - '/api': { 21 + '^/(xrpc|oauth|client-metadata\.json)/.*': { 22 22 target: 'http://localhost:3001', 23 23 changeOrigin: true, 24 24 },
+8
packages/lexicon/package.json
··· 5 5 "author": "", 6 6 "license": "MIT", 7 7 "main": "dist/index.js", 8 + "module": "dist/index.mjs", 8 9 "types": "dist/index.d.ts", 10 + "exports": { 11 + ".": { 12 + "types": "./dist/index.d.ts", 13 + "import": "./dist/index.mjs", 14 + "require": "./dist/index.js" 15 + } 16 + }, 9 17 "private": true, 10 18 "scripts": { 11 19 "build": "pnpm lexgen && tsup",
+32
packages/lexicon/src/index.ts
··· 21 21 import * as ComAtprotoRepoStrongRef from './types/com/atproto/repo/strongRef.js' 22 22 import * as ComAtprotoRepoUploadBlob from './types/com/atproto/repo/uploadBlob.js' 23 23 import * as XyzStatusphereDefs from './types/xyz/statusphere/defs.js' 24 + import * as XyzStatusphereGetStatuses from './types/xyz/statusphere/getStatuses.js' 25 + import * as XyzStatusphereGetUser from './types/xyz/statusphere/getUser.js' 26 + import * as XyzStatusphereSendStatus from './types/xyz/statusphere/sendStatus.js' 24 27 import * as XyzStatusphereStatus from './types/xyz/statusphere/status.js' 25 28 import { OmitKey, Un$Typed } from './util.js' 26 29 27 30 export * as XyzStatusphereDefs from './types/xyz/statusphere/defs.js' 31 + export * as XyzStatusphereGetStatuses from './types/xyz/statusphere/getStatuses.js' 32 + export * as XyzStatusphereGetUser from './types/xyz/statusphere/getUser.js' 33 + export * as XyzStatusphereSendStatus from './types/xyz/statusphere/sendStatus.js' 28 34 export * as XyzStatusphereStatus from './types/xyz/statusphere/status.js' 29 35 export * as ComAtprotoLabelDefs from './types/com/atproto/label/defs.js' 30 36 export * as ComAtprotoRepoApplyWrites from './types/com/atproto/repo/applyWrites.js' ··· 77 83 constructor(client: XrpcClient) { 78 84 this._client = client 79 85 this.status = new StatusRecord(client) 86 + } 87 + 88 + getStatuses( 89 + params?: XyzStatusphereGetStatuses.QueryParams, 90 + opts?: XyzStatusphereGetStatuses.CallOptions, 91 + ): Promise<XyzStatusphereGetStatuses.Response> { 92 + return this._client.call( 93 + 'xyz.statusphere.getStatuses', 94 + params, 95 + undefined, 96 + opts, 97 + ) 98 + } 99 + 100 + getUser( 101 + params?: XyzStatusphereGetUser.QueryParams, 102 + opts?: XyzStatusphereGetUser.CallOptions, 103 + ): Promise<XyzStatusphereGetUser.Response> { 104 + return this._client.call('xyz.statusphere.getUser', params, undefined, opts) 105 + } 106 + 107 + sendStatus( 108 + data?: XyzStatusphereSendStatus.InputSchema, 109 + opts?: XyzStatusphereSendStatus.CallOptions, 110 + ): Promise<XyzStatusphereSendStatus.Response> { 111 + return this._client.call('xyz.statusphere.sendStatus', opts?.qp, data, opts) 80 112 } 81 113 } 82 114
+115
packages/lexicon/src/lexicons.ts
··· 55 55 }, 56 56 }, 57 57 }, 58 + XyzStatusphereGetStatuses: { 59 + lexicon: 1, 60 + id: 'xyz.statusphere.getStatuses', 61 + defs: { 62 + main: { 63 + type: 'query', 64 + description: 'Get a list of the most recent statuses on the network.', 65 + parameters: { 66 + type: 'params', 67 + properties: { 68 + limit: { 69 + type: 'integer', 70 + minimum: 1, 71 + maximum: 100, 72 + default: 50, 73 + }, 74 + cursor: { 75 + type: 'string', 76 + }, 77 + }, 78 + }, 79 + output: { 80 + encoding: 'application/json', 81 + schema: { 82 + type: 'object', 83 + required: ['statuses'], 84 + properties: { 85 + cursor: { 86 + type: 'string', 87 + }, 88 + statuses: { 89 + type: 'array', 90 + items: { 91 + type: 'ref', 92 + ref: 'lex:xyz.statusphere.defs#statusView', 93 + }, 94 + }, 95 + }, 96 + }, 97 + }, 98 + }, 99 + }, 100 + }, 101 + XyzStatusphereGetUser: { 102 + lexicon: 1, 103 + id: 'xyz.statusphere.getUser', 104 + defs: { 105 + main: { 106 + type: 'query', 107 + description: "Get the current user's profile and status.", 108 + parameters: { 109 + type: 'params', 110 + properties: {}, 111 + }, 112 + output: { 113 + encoding: 'application/json', 114 + schema: { 115 + type: 'object', 116 + required: ['profile'], 117 + properties: { 118 + profile: { 119 + type: 'ref', 120 + ref: 'lex:app.bsky.actor.defs#profileView', 121 + }, 122 + status: { 123 + type: 'ref', 124 + ref: 'lex:xyz.statusphere.defs#statusView', 125 + }, 126 + }, 127 + }, 128 + }, 129 + }, 130 + }, 131 + }, 132 + XyzStatusphereSendStatus: { 133 + lexicon: 1, 134 + id: 'xyz.statusphere.sendStatus', 135 + defs: { 136 + main: { 137 + type: 'procedure', 138 + description: 'Send a status into the ATmosphere.', 139 + input: { 140 + encoding: 'application/json', 141 + schema: { 142 + type: 'object', 143 + required: ['status'], 144 + properties: { 145 + status: { 146 + type: 'string', 147 + minLength: 1, 148 + maxGraphemes: 1, 149 + maxLength: 32, 150 + }, 151 + }, 152 + }, 153 + }, 154 + output: { 155 + encoding: 'application/json', 156 + schema: { 157 + type: 'object', 158 + required: ['status'], 159 + properties: { 160 + status: { 161 + type: 'ref', 162 + ref: 'lex:xyz.statusphere.defs#statusView', 163 + }, 164 + }, 165 + }, 166 + }, 167 + }, 168 + }, 169 + }, 58 170 XyzStatusphereStatus: { 59 171 lexicon: 1, 60 172 id: 'xyz.statusphere.status', ··· 1169 1281 1170 1282 export const ids = { 1171 1283 XyzStatusphereDefs: 'xyz.statusphere.defs', 1284 + XyzStatusphereGetStatuses: 'xyz.statusphere.getStatuses', 1285 + XyzStatusphereGetUser: 'xyz.statusphere.getUser', 1286 + XyzStatusphereSendStatus: 'xyz.statusphere.sendStatus', 1172 1287 XyzStatusphereStatus: 'xyz.statusphere.status', 1173 1288 ComAtprotoLabelDefs: 'com.atproto.label.defs', 1174 1289 ComAtprotoRepoApplyWrites: 'com.atproto.repo.applyWrites',
+41
packages/lexicon/src/types/xyz/statusphere/getStatuses.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HeadersMap, XRPCError } from '@atproto/xrpc' 6 + import { CID } from 'multiformats/cid' 7 + 8 + import { validate as _validate } from '../../../lexicons' 9 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../util' 10 + import type * as XyzStatusphereDefs from './defs.js' 11 + 12 + const is$typed = _is$typed, 13 + validate = _validate 14 + const id = 'xyz.statusphere.getStatuses' 15 + 16 + export interface QueryParams { 17 + limit?: number 18 + cursor?: string 19 + } 20 + 21 + export type InputSchema = undefined 22 + 23 + export interface OutputSchema { 24 + cursor?: string 25 + statuses: XyzStatusphereDefs.StatusView[] 26 + } 27 + 28 + export interface CallOptions { 29 + signal?: AbortSignal 30 + headers?: HeadersMap 31 + } 32 + 33 + export interface Response { 34 + success: boolean 35 + headers: HeadersMap 36 + data: OutputSchema 37 + } 38 + 39 + export function toKnownErr(e: any) { 40 + return e 41 + }
+39
packages/lexicon/src/types/xyz/statusphere/getUser.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HeadersMap, XRPCError } from '@atproto/xrpc' 6 + import { CID } from 'multiformats/cid' 7 + 8 + import { validate as _validate } from '../../../lexicons' 9 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../util' 10 + import type * as AppBskyActorDefs from '../../app/bsky/actor/defs.js' 11 + import type * as XyzStatusphereDefs from './defs.js' 12 + 13 + const is$typed = _is$typed, 14 + validate = _validate 15 + const id = 'xyz.statusphere.getUser' 16 + 17 + export interface QueryParams {} 18 + 19 + export type InputSchema = undefined 20 + 21 + export interface OutputSchema { 22 + profile: AppBskyActorDefs.ProfileView 23 + status?: XyzStatusphereDefs.StatusView 24 + } 25 + 26 + export interface CallOptions { 27 + signal?: AbortSignal 28 + headers?: HeadersMap 29 + } 30 + 31 + export interface Response { 32 + success: boolean 33 + headers: HeadersMap 34 + data: OutputSchema 35 + } 36 + 37 + export function toKnownErr(e: any) { 38 + return e 39 + }
+41
packages/lexicon/src/types/xyz/statusphere/sendStatus.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { BlobRef, ValidationResult } from '@atproto/lexicon' 5 + import { HeadersMap, XRPCError } from '@atproto/xrpc' 6 + import { CID } from 'multiformats/cid' 7 + 8 + import { validate as _validate } from '../../../lexicons' 9 + import { is$typed as _is$typed, $Typed, OmitKey } from '../../../util' 10 + import type * as XyzStatusphereDefs from './defs.js' 11 + 12 + const is$typed = _is$typed, 13 + validate = _validate 14 + const id = 'xyz.statusphere.sendStatus' 15 + 16 + export interface QueryParams {} 17 + 18 + export interface InputSchema { 19 + status: string 20 + } 21 + 22 + export interface OutputSchema { 23 + status: XyzStatusphereDefs.StatusView 24 + } 25 + 26 + export interface CallOptions { 27 + signal?: AbortSignal 28 + headers?: HeadersMap 29 + qp?: QueryParams 30 + encoding?: 'application/json' 31 + } 32 + 33 + export interface Response { 34 + success: boolean 35 + headers: HeadersMap 36 + data: OutputSchema 37 + } 38 + 39 + export function toKnownErr(e: any) { 40 + return e 41 + }
+55
pnpm-lock.yaml
··· 62 62 better-sqlite3: 63 63 specifier: ^11.8.1 64 64 version: 11.8.1 65 + compression: 66 + specifier: ^1.8.0 67 + version: 1.8.0 65 68 cors: 66 69 specifier: ^2.8.5 67 70 version: 2.8.5 ··· 87 90 specifier: ^9.6.0 88 91 version: 9.6.0 89 92 devDependencies: 93 + '@atproto/lex-cli': 94 + specifier: ^0.6.1 95 + version: 0.6.1 90 96 '@types/better-sqlite3': 91 97 specifier: ^7.6.12 92 98 version: 7.6.12 99 + '@types/compression': 100 + specifier: ^1.7.5 101 + version: 1.7.5 93 102 '@types/cors': 94 103 specifier: ^2.8.17 95 104 version: 2.8.17 ··· 123 132 '@atproto/api': 124 133 specifier: ^0.14.7 125 134 version: 0.14.7 135 + '@atproto/xrpc': 136 + specifier: ^0.6.9 137 + version: 0.6.9 126 138 '@statusphere/lexicon': 127 139 specifier: workspace:* 128 140 version: link:../lexicon ··· 959 971 '@types/body-parser@1.19.5': 960 972 resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} 961 973 974 + '@types/compression@1.7.5': 975 + resolution: {integrity: sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==} 976 + 962 977 '@types/connect@3.4.38': 963 978 resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} 964 979 ··· 1250 1265 commander@9.5.0: 1251 1266 resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} 1252 1267 engines: {node: ^12.20.0 || >=14} 1268 + 1269 + compressible@2.0.18: 1270 + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} 1271 + engines: {node: '>= 0.6'} 1272 + 1273 + compression@1.8.0: 1274 + resolution: {integrity: sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==} 1275 + engines: {node: '>= 0.8.0'} 1253 1276 1254 1277 concat-map@0.0.1: 1255 1278 resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} ··· 1995 2018 resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} 1996 2019 engines: {node: '>= 0.6'} 1997 2020 2021 + negotiator@0.6.4: 2022 + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} 2023 + engines: {node: '>= 0.6'} 2024 + 1998 2025 node-abi@3.74.0: 1999 2026 resolution: {integrity: sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==} 2000 2027 engines: {node: '>=10'} ··· 2026 2053 resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} 2027 2054 engines: {node: '>= 0.8'} 2028 2055 2056 + on-headers@1.0.2: 2057 + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} 2058 + engines: {node: '>= 0.8'} 2059 + 2029 2060 once@1.4.0: 2030 2061 resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 2031 2062 ··· 3569 3600 '@types/connect': 3.4.38 3570 3601 '@types/node': 22.13.8 3571 3602 3603 + '@types/compression@1.7.5': 3604 + dependencies: 3605 + '@types/express': 5.0.0 3606 + 3572 3607 '@types/connect@3.4.38': 3573 3608 dependencies: 3574 3609 '@types/node': 22.13.8 ··· 3919 3954 commander@4.1.1: {} 3920 3955 3921 3956 commander@9.5.0: {} 3957 + 3958 + compressible@2.0.18: 3959 + dependencies: 3960 + mime-db: 1.52.0 3961 + 3962 + compression@1.8.0: 3963 + dependencies: 3964 + bytes: 3.1.2 3965 + compressible: 2.0.18 3966 + debug: 2.6.9 3967 + negotiator: 0.6.4 3968 + on-headers: 1.0.2 3969 + safe-buffer: 5.2.1 3970 + vary: 1.1.2 3971 + transitivePeerDependencies: 3972 + - supports-color 3922 3973 3923 3974 concat-map@0.0.1: {} 3924 3975 ··· 4604 4655 4605 4656 negotiator@0.6.3: {} 4606 4657 4658 + negotiator@0.6.4: {} 4659 + 4607 4660 node-abi@3.74.0: 4608 4661 dependencies: 4609 4662 semver: 7.7.1 ··· 4626 4679 on-finished@2.4.1: 4627 4680 dependencies: 4628 4681 ee-first: 1.1.1 4682 + 4683 + on-headers@1.0.2: {} 4629 4684 4630 4685 once@1.4.0: 4631 4686 dependencies: