+1
-1
README.md
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+1
-1
packages/appview/src/lib/hydrate.ts
-347
packages/appview/src/routes.ts
-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
+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
+1
packages/client/package.json
+2
-2
packages/client/src/components/StatusForm.tsx
+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
+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
+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
+1
-1
packages/client/src/pages/OAuthCallbackPage.tsx
+31
-105
packages/client/src/services/api.ts
+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
+1
-1
packages/client/vite.config.ts
+8
packages/lexicon/package.json
+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
+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
+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
+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
+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
+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
+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: