import { dedupeStrs, mapDefined } from '@atproto/common' import { AtUriString } from '@atproto/syntax' import { Server } from '@atproto/xrpc-server' import { AppContext } from '../../../../context.js' import { HydrateCtx, HydrationState, Hydrator, } from '../../../../hydration/hydrator.js' import { app } from '../../../../lexicons/index.js' import { createPipeline } from '../../../../pipeline.js' import { uriToDid as creatorFromUri } from '../../../../util/uris.js' import { Views } from '../../../../views/index.js' import { resHeaders } from '../../../util.js' export default function (server: Server, ctx: AppContext) { const getPosts = createPipeline(skeleton, hydration, noBlocks, presentation) server.add(app.bsky.feed.getPosts, { auth: ctx.authVerifier.standardOptionalParameterized({ lxmCheck: (method) => { if (!method) return false return ( method === app.bsky.feed.getPosts.$lxm || method.startsWith('chat.bsky.') ) }, }), opts: { // @TODO remove after grace period has passed, behavior is non-standard. // temporarily added for compat w/ previous version of xrpc-server to avoid breakage of a few specified parties. paramsParseLoose: true, }, handler: async ({ params, auth, req }) => { const viewer = auth.credentials.iss const labelers = ctx.reqLabelers(req) const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer }) const results = await getPosts({ ...params, hydrateCtx }, ctx) return { encoding: 'application/json', body: results, headers: resHeaders({ labelers: hydrateCtx.labelers }), } }, }) } const skeleton = async (inputs: { params: Params }): Promise => { return { posts: dedupeStrs(inputs.params.uris) } } const hydration = async (inputs: { ctx: Context params: Params skeleton: Skeleton }) => { const { ctx, params, skeleton } = inputs return ctx.hydrator.hydratePosts( skeleton.posts.map((uri) => ({ uri })), params.hydrateCtx, ) } const noBlocks = (inputs: { ctx: Context skeleton: Skeleton hydration: HydrationState }) => { const { ctx, skeleton, hydration } = inputs skeleton.posts = skeleton.posts.filter((uri) => { const creator = creatorFromUri(uri) return !ctx.views.viewerBlockExists(creator, hydration) }) return skeleton } const presentation = (inputs: { ctx: Context params: Params skeleton: Skeleton hydration: HydrationState }) => { const { ctx, skeleton, hydration } = inputs const posts = mapDefined(skeleton.posts, (uri) => ctx.views.post(uri, hydration), ) return { posts } } type Context = { hydrator: Hydrator views: Views } type Params = app.bsky.feed.getPosts.$Params & { hydrateCtx: HydrateCtx } type Skeleton = { posts: AtUriString[] }