@atproto/bsky 0.0.14 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/api/app/bsky/feed/searchPosts.d.ts +3 -0
  3. package/dist/api/com/atproto/temp/fetchLabels.d.ts +3 -0
  4. package/dist/config.d.ts +2 -2
  5. package/dist/context.d.ts +1 -1
  6. package/dist/index.js +716 -443
  7. package/dist/index.js.map +3 -3
  8. package/dist/lexicon/index.d.ts +7 -0
  9. package/dist/lexicon/lexicons.d.ts +60 -0
  10. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +2 -0
  11. package/dist/lexicon/types/com/atproto/temp/fetchLabels.d.ts +33 -0
  12. package/package.json +8 -9
  13. package/src/api/app/bsky/actor/getSuggestions.ts +45 -21
  14. package/src/api/app/bsky/feed/getPostThread.ts +16 -4
  15. package/src/api/app/bsky/feed/searchPosts.ts +127 -0
  16. package/src/api/com/atproto/admin/reverseModerationAction.ts +3 -3
  17. package/src/api/com/atproto/admin/takeModerationAction.ts +2 -2
  18. package/src/api/com/atproto/admin/util.ts +3 -1
  19. package/src/api/com/atproto/temp/fetchLabels.ts +30 -0
  20. package/src/api/index.ts +4 -0
  21. package/src/config.ts +6 -6
  22. package/src/context.ts +11 -9
  23. package/src/db/periodic-moderation-action-reversal.ts +1 -9
  24. package/src/lexicon/index.ts +22 -0
  25. package/src/lexicon/lexicons.ts +189 -129
  26. package/src/lexicon/types/app/bsky/actor/defs.ts +2 -2
  27. package/src/lexicon/types/app/bsky/actor/searchActors.ts +2 -2
  28. package/src/lexicon/types/app/bsky/actor/searchActorsTypeahead.ts +2 -2
  29. package/src/lexicon/types/app/bsky/feed/searchPosts.ts +3 -3
  30. package/src/lexicon/types/app/bsky/graph/defs.ts +2 -2
  31. package/src/lexicon/types/app/bsky/unspecced/searchActorsSkeleton.ts +4 -4
  32. package/src/lexicon/types/app/bsky/unspecced/searchPostsSkeleton.ts +3 -3
  33. package/src/lexicon/types/com/atproto/admin/defs.ts +5 -3
  34. package/src/lexicon/types/com/atproto/admin/disableAccountInvites.ts +1 -1
  35. package/src/lexicon/types/com/atproto/admin/enableAccountInvites.ts +1 -1
  36. package/src/lexicon/types/com/atproto/admin/getModerationReports.ts +3 -3
  37. package/src/lexicon/types/com/atproto/admin/takeModerationAction.ts +1 -1
  38. package/src/lexicon/types/com/atproto/label/defs.ts +9 -9
  39. package/src/lexicon/types/com/atproto/label/queryLabels.ts +2 -2
  40. package/src/lexicon/types/com/atproto/repo/applyWrites.ts +1 -1
  41. package/src/lexicon/types/com/atproto/repo/createRecord.ts +2 -2
  42. package/src/lexicon/types/com/atproto/repo/deleteRecord.ts +2 -2
  43. package/src/lexicon/types/com/atproto/repo/listRecords.ts +1 -1
  44. package/src/lexicon/types/com/atproto/repo/putRecord.ts +3 -3
  45. package/src/lexicon/types/com/atproto/sync/listBlobs.ts +1 -1
  46. package/src/lexicon/types/com/atproto/sync/subscribeRepos.ts +4 -4
  47. package/src/lexicon/types/com/atproto/temp/fetchLabels.ts +47 -0
  48. package/tests/admin/get-repo.test.ts +33 -0
  49. package/tests/views/suggestions.test.ts +15 -7
@@ -65,6 +65,7 @@ import * as ComAtprotoSyncListRepos from './types/com/atproto/sync/listRepos';
65
65
  import * as ComAtprotoSyncNotifyOfUpdate from './types/com/atproto/sync/notifyOfUpdate';
66
66
  import * as ComAtprotoSyncRequestCrawl from './types/com/atproto/sync/requestCrawl';
67
67
  import * as ComAtprotoSyncSubscribeRepos from './types/com/atproto/sync/subscribeRepos';
68
+ import * as ComAtprotoTempFetchLabels from './types/com/atproto/temp/fetchLabels';
68
69
  import * as AppBskyActorGetPreferences from './types/app/bsky/actor/getPreferences';
69
70
  import * as AppBskyActorGetProfile from './types/app/bsky/actor/getProfile';
70
71
  import * as AppBskyActorGetProfiles from './types/app/bsky/actor/getProfiles';
@@ -149,6 +150,7 @@ export declare class AtprotoNS {
149
150
  repo: RepoNS;
150
151
  server: ServerNS;
151
152
  sync: SyncNS;
153
+ temp: TempNS;
152
154
  constructor(server: Server);
153
155
  }
154
156
  export declare class AdminNS {
@@ -245,6 +247,11 @@ export declare class SyncNS {
245
247
  requestCrawl<AV extends AuthVerifier>(cfg: ConfigOf<AV, ComAtprotoSyncRequestCrawl.Handler<ExtractAuth<AV>>, ComAtprotoSyncRequestCrawl.HandlerReqCtx<ExtractAuth<AV>>>): void;
246
248
  subscribeRepos<AV extends StreamAuthVerifier>(cfg: ConfigOf<AV, ComAtprotoSyncSubscribeRepos.Handler<ExtractAuth<AV>>, ComAtprotoSyncSubscribeRepos.HandlerReqCtx<ExtractAuth<AV>>>): void;
247
249
  }
250
+ export declare class TempNS {
251
+ _server: Server;
252
+ constructor(server: Server);
253
+ fetchLabels<AV extends AuthVerifier>(cfg: ConfigOf<AV, ComAtprotoTempFetchLabels.Handler<ExtractAuth<AV>>, ComAtprotoTempFetchLabels.HandlerReqCtx<ExtractAuth<AV>>>): void;
254
+ }
248
255
  export declare class AppNS {
249
256
  _server: Server;
250
257
  bsky: BskyNS;
@@ -358,6 +358,10 @@ export declare const schemaDict: {
358
358
  inviteNote: {
359
359
  type: string;
360
360
  };
361
+ emailConfirmedAt: {
362
+ type: string;
363
+ format: string;
364
+ };
361
365
  };
362
366
  };
363
367
  accountView: {
@@ -393,6 +397,10 @@ export declare const schemaDict: {
393
397
  invitesDisabled: {
394
398
  type: string;
395
399
  };
400
+ emailConfirmedAt: {
401
+ type: string;
402
+ format: string;
403
+ };
396
404
  inviteNote: {
397
405
  type: string;
398
406
  };
@@ -3585,6 +3593,46 @@ export declare const schemaDict: {
3585
3593
  };
3586
3594
  };
3587
3595
  };
3596
+ ComAtprotoTempFetchLabels: {
3597
+ lexicon: number;
3598
+ id: string;
3599
+ defs: {
3600
+ main: {
3601
+ type: string;
3602
+ description: string;
3603
+ parameters: {
3604
+ type: string;
3605
+ properties: {
3606
+ since: {
3607
+ type: string;
3608
+ };
3609
+ limit: {
3610
+ type: string;
3611
+ minimum: number;
3612
+ maximum: number;
3613
+ default: number;
3614
+ };
3615
+ };
3616
+ };
3617
+ output: {
3618
+ encoding: string;
3619
+ schema: {
3620
+ type: string;
3621
+ required: string[];
3622
+ properties: {
3623
+ labels: {
3624
+ type: string;
3625
+ items: {
3626
+ type: string;
3627
+ ref: string;
3628
+ };
3629
+ };
3630
+ };
3631
+ };
3632
+ };
3633
+ };
3634
+ };
3635
+ };
3588
3636
  AppBskyActorDefs: {
3589
3637
  lexicon: number;
3590
3638
  id: string;
@@ -3889,6 +3937,7 @@ export declare const schemaDict: {
3889
3937
  defs: {
3890
3938
  main: {
3891
3939
  type: string;
3940
+ description: string;
3892
3941
  parameters: {
3893
3942
  type: string;
3894
3943
  required: string[];
@@ -3915,6 +3964,7 @@ export declare const schemaDict: {
3915
3964
  defs: {
3916
3965
  main: {
3917
3966
  type: string;
3967
+ description: string;
3918
3968
  parameters: {
3919
3969
  type: string;
3920
3970
  required: string[];
@@ -3997,6 +4047,7 @@ export declare const schemaDict: {
3997
4047
  defs: {
3998
4048
  main: {
3999
4049
  type: string;
4050
+ description: string;
4000
4051
  key: string;
4001
4052
  record: {
4002
4053
  type: string;
@@ -5188,6 +5239,7 @@ export declare const schemaDict: {
5188
5239
  defs: {
5189
5240
  main: {
5190
5241
  type: string;
5242
+ description: string;
5191
5243
  parameters: {
5192
5244
  type: string;
5193
5245
  required: string[];
@@ -5316,6 +5368,7 @@ export declare const schemaDict: {
5316
5368
  defs: {
5317
5369
  main: {
5318
5370
  type: string;
5371
+ description: string;
5319
5372
  parameters: {
5320
5373
  type: string;
5321
5374
  required: string[];
@@ -5403,6 +5456,7 @@ export declare const schemaDict: {
5403
5456
  defs: {
5404
5457
  main: {
5405
5458
  type: string;
5459
+ description: string;
5406
5460
  parameters: {
5407
5461
  type: string;
5408
5462
  required: string[];
@@ -5551,6 +5605,7 @@ export declare const schemaDict: {
5551
5605
  defs: {
5552
5606
  main: {
5553
5607
  type: string;
5608
+ description: string;
5554
5609
  key: string;
5555
5610
  record: {
5556
5611
  type: string;
@@ -5575,6 +5630,7 @@ export declare const schemaDict: {
5575
5630
  defs: {
5576
5631
  main: {
5577
5632
  type: string;
5633
+ description: string;
5578
5634
  key: string;
5579
5635
  record: {
5580
5636
  type: string;
@@ -5691,6 +5747,7 @@ export declare const schemaDict: {
5691
5747
  id: string;
5692
5748
  defs: {
5693
5749
  main: {
5750
+ description: string;
5694
5751
  type: string;
5695
5752
  key: string;
5696
5753
  record: {
@@ -6609,6 +6666,7 @@ export declare const schemaDict: {
6609
6666
  defs: {
6610
6667
  main: {
6611
6668
  type: string;
6669
+ description: string;
6612
6670
  parameters: {
6613
6671
  type: string;
6614
6672
  properties: {
@@ -6639,6 +6697,7 @@ export declare const schemaDict: {
6639
6697
  defs: {
6640
6698
  main: {
6641
6699
  type: string;
6700
+ description: string;
6642
6701
  parameters: {
6643
6702
  type: string;
6644
6703
  properties: {
@@ -7207,6 +7266,7 @@ export declare const ids: {
7207
7266
  ComAtprotoSyncNotifyOfUpdate: string;
7208
7267
  ComAtprotoSyncRequestCrawl: string;
7209
7268
  ComAtprotoSyncSubscribeRepos: string;
7269
+ ComAtprotoTempFetchLabels: string;
7210
7270
  AppBskyActorDefs: string;
7211
7271
  AppBskyActorGetPreferences: string;
7212
7272
  AppBskyActorGetProfile: string;
@@ -128,6 +128,7 @@ export interface RepoViewDetail {
128
128
  invites?: ComAtprotoServerDefs.InviteCode[];
129
129
  invitesDisabled?: boolean;
130
130
  inviteNote?: string;
131
+ emailConfirmedAt?: string;
131
132
  [k: string]: unknown;
132
133
  }
133
134
  export declare function isRepoViewDetail(v: unknown): v is RepoViewDetail;
@@ -140,6 +141,7 @@ export interface AccountView {
140
141
  invitedBy?: ComAtprotoServerDefs.InviteCode;
141
142
  invites?: ComAtprotoServerDefs.InviteCode[];
142
143
  invitesDisabled?: boolean;
144
+ emailConfirmedAt?: string;
143
145
  inviteNote?: string;
144
146
  [k: string]: unknown;
145
147
  }
@@ -0,0 +1,33 @@
1
+ import express from 'express';
2
+ import { HandlerAuth } from '@atproto/xrpc-server';
3
+ import * as ComAtprotoLabelDefs from '../label/defs';
4
+ export interface QueryParams {
5
+ since?: number;
6
+ limit: number;
7
+ }
8
+ export declare type InputSchema = undefined;
9
+ export interface OutputSchema {
10
+ labels: ComAtprotoLabelDefs.Label[];
11
+ [k: string]: unknown;
12
+ }
13
+ export declare type HandlerInput = undefined;
14
+ export interface HandlerSuccess {
15
+ encoding: 'application/json';
16
+ body: OutputSchema;
17
+ headers?: {
18
+ [key: string]: string;
19
+ };
20
+ }
21
+ export interface HandlerError {
22
+ status: number;
23
+ message?: string;
24
+ }
25
+ export declare type HandlerOutput = HandlerError | HandlerSuccess;
26
+ export declare type HandlerReqCtx<HA extends HandlerAuth = never> = {
27
+ auth: HA;
28
+ params: QueryParams;
29
+ input: HandlerInput;
30
+ req: express.Request;
31
+ res: express.Response;
32
+ };
33
+ export declare type Handler<HA extends HandlerAuth = never> = (ctx: HandlerReqCtx<HA>) => Promise<HandlerOutput> | HandlerOutput;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/bsky",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "license": "MIT",
5
5
  "description": "Reference implementation of app.bsky App View (Bluesky API)",
6
6
  "keywords": [
@@ -32,17 +32,17 @@
32
32
  "pg": "^8.10.0",
33
33
  "pino": "^8.15.0",
34
34
  "pino-http": "^8.2.1",
35
- "sharp": "^0.31.2",
35
+ "sharp": "^0.32.6",
36
36
  "typed-emitter": "^2.1.0",
37
37
  "uint8arrays": "3.0.0",
38
38
  "@atproto/api": "^0.6.23",
39
39
  "@atproto/common": "^0.3.3",
40
- "@atproto/crypto": "^0.2.3",
40
+ "@atproto/crypto": "^0.3.0",
41
41
  "@atproto/syntax": "^0.1.4",
42
- "@atproto/identity": "^0.3.1",
42
+ "@atproto/identity": "^0.3.2",
43
43
  "@atproto/lexicon": "^0.3.0",
44
- "@atproto/repo": "^0.3.4",
45
- "@atproto/xrpc-server": "^0.4.0"
44
+ "@atproto/repo": "^0.3.5",
45
+ "@atproto/xrpc-server": "^0.4.1"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@did-plc/server": "^0.0.1",
@@ -51,12 +51,11 @@
51
51
  "@types/express-serve-static-core": "^4.17.36",
52
52
  "@types/pg": "^8.6.6",
53
53
  "@types/qs": "^6.9.7",
54
- "@types/sharp": "^0.31.0",
55
54
  "axios": "^0.27.2",
56
55
  "@atproto/api": "^0.6.23",
57
- "@atproto/dev-env": "^0.2.14",
56
+ "@atproto/dev-env": "^0.2.15",
58
57
  "@atproto/lex-cli": "^0.2.4",
59
- "@atproto/pds": "^0.3.2",
58
+ "@atproto/pds": "^0.3.3",
60
59
  "@atproto/xrpc": "^0.4.0"
61
60
  },
62
61
  "scripts": {
@@ -42,12 +42,12 @@ const skeleton = async (
42
42
  ctx: Context,
43
43
  ): Promise<SkeletonState> => {
44
44
  const { db } = ctx
45
- const { limit, cursor, viewer } = params
45
+ const { viewer } = params
46
+ const alreadyIncluded = parseCursor(params.cursor)
46
47
  const { ref } = db.db.dynamic
47
- let suggestionsQb = db.db
48
+ const suggestions = await db.db
48
49
  .selectFrom('suggested_follow')
49
50
  .innerJoin('actor', 'actor.did', 'suggested_follow.did')
50
- .innerJoin('profile_agg', 'profile_agg.did', 'actor.did')
51
51
  .where(notSoftDeletedClause(ref('actor')))
52
52
  .where('suggested_follow.did', '!=', viewer ?? '')
53
53
  .whereNotExists((qb) =>
@@ -57,27 +57,30 @@ const skeleton = async (
57
57
  .where('creator', '=', viewer ?? '')
58
58
  .whereRef('subjectDid', '=', ref('actor.did')),
59
59
  )
60
+ .if(alreadyIncluded.length > 0, (qb) =>
61
+ qb.where('suggested_follow.order', 'not in', alreadyIncluded),
62
+ )
60
63
  .selectAll()
61
- .select('profile_agg.postsCount as postsCount')
62
- .limit(limit)
63
64
  .orderBy('suggested_follow.order', 'asc')
65
+ .execute()
64
66
 
65
- if (cursor) {
66
- const cursorRow = await db.db
67
- .selectFrom('suggested_follow')
68
- .where('did', '=', cursor)
69
- .selectAll()
70
- .executeTakeFirst()
71
- if (cursorRow) {
72
- suggestionsQb = suggestionsQb.where(
73
- 'suggested_follow.order',
74
- '>',
75
- cursorRow.order,
76
- )
77
- }
78
- }
79
- const suggestions = await suggestionsQb.execute()
80
- return { params, suggestions, cursor: suggestions.at(-1)?.did }
67
+ // always include first two
68
+ const firstTwo = suggestions.filter(
69
+ (row) => row.order === 1 || row.order === 2,
70
+ )
71
+ const rest = suggestions.filter((row) => row.order !== 1 && row.order !== 2)
72
+ const limited = firstTwo.concat(shuffle(rest)).slice(0, params.limit)
73
+
74
+ // if the result set ends up getting larger, consider using a seed included in the cursor for for the randomized shuffle
75
+ const cursor =
76
+ limited.length > 0
77
+ ? limited
78
+ .map((row) => row.order.toString())
79
+ .concat(alreadyIncluded.map((id) => id.toString()))
80
+ .join(':')
81
+ : undefined
82
+
83
+ return { params, suggestions: limited, cursor }
81
84
  }
82
85
 
83
86
  const hydration = async (state: SkeletonState, ctx: Context) => {
@@ -110,6 +113,27 @@ const presentation = (state: HydrationState) => {
110
113
  return { actors: suggestedActors, cursor }
111
114
  }
112
115
 
116
+ const parseCursor = (cursor?: string): number[] => {
117
+ if (!cursor) {
118
+ return []
119
+ }
120
+ try {
121
+ return cursor
122
+ .split(':')
123
+ .map((id) => parseInt(id, 10))
124
+ .filter((id) => !isNaN(id))
125
+ } catch {
126
+ return []
127
+ }
128
+ }
129
+
130
+ const shuffle = <T>(arr: T[]): T[] => {
131
+ return arr
132
+ .map((value) => ({ value, sort: Math.random() }))
133
+ .sort((a, b) => a.sort - b.sort)
134
+ .map(({ value }) => value)
135
+ }
136
+
113
137
  type Context = {
114
138
  db: Database
115
139
  actorService: ActorService
@@ -255,11 +255,15 @@ const getThreadData = async (
255
255
  .orderBy('sortAt', 'desc')
256
256
  .execute(),
257
257
  ])
258
- const parentsByUri = parents.reduce((acc, parent) => {
259
- return Object.assign(acc, { [parent.postUri]: parent })
258
+ // prevent self-referential loops
259
+ const includedPosts = new Set<string>([uri])
260
+ const parentsByUri = parents.reduce((acc, post) => {
261
+ return Object.assign(acc, { [post.uri]: post })
260
262
  }, {} as Record<string, FeedRow>)
261
263
  const childrenByParentUri = children.reduce((acc, child) => {
262
264
  if (!child.replyParent) return acc
265
+ if (includedPosts.has(child.uri)) return acc
266
+ includedPosts.add(child.uri)
263
267
  acc[child.replyParent] ??= []
264
268
  acc[child.replyParent].push(child)
265
269
  return acc
@@ -269,7 +273,12 @@ const getThreadData = async (
269
273
  return {
270
274
  post,
271
275
  parent: post.replyParent
272
- ? getParentData(parentsByUri, post.replyParent, parentHeight)
276
+ ? getParentData(
277
+ parentsByUri,
278
+ includedPosts,
279
+ post.replyParent,
280
+ parentHeight,
281
+ )
273
282
  : undefined,
274
283
  replies: getChildrenData(childrenByParentUri, uri, depth),
275
284
  }
@@ -277,16 +286,19 @@ const getThreadData = async (
277
286
 
278
287
  const getParentData = (
279
288
  postsByUri: Record<string, FeedRow>,
289
+ includedPosts: Set<string>,
280
290
  uri: string,
281
291
  depth: number,
282
292
  ): PostThread | ParentNotFoundError | undefined => {
283
293
  if (depth < 1) return undefined
294
+ if (includedPosts.has(uri)) return undefined
295
+ includedPosts.add(uri)
284
296
  const post = postsByUri[uri]
285
297
  if (!post) return new ParentNotFoundError(uri)
286
298
  return {
287
299
  post,
288
300
  parent: post.replyParent
289
- ? getParentData(postsByUri, post.replyParent, depth - 1)
301
+ ? getParentData(postsByUri, includedPosts, post.replyParent, depth - 1)
290
302
  : undefined,
291
303
  replies: [],
292
304
  }
@@ -0,0 +1,127 @@
1
+ import AppContext from '../../../../context'
2
+ import { Server } from '../../../../lexicon'
3
+ import { InvalidRequestError } from '@atproto/xrpc-server'
4
+ import AtpAgent from '@atproto/api'
5
+ import { AtUri } from '@atproto/syntax'
6
+ import { mapDefined } from '@atproto/common'
7
+ import { QueryParams } from '../../../../lexicon/types/app/bsky/feed/searchPosts'
8
+ import { Database } from '../../../../db'
9
+ import { FeedHydrationState, FeedService } from '../../../../services/feed'
10
+ import { ActorService } from '../../../../services/actor'
11
+ import { createPipeline } from '../../../../pipeline'
12
+
13
+ export default function (server: Server, ctx: AppContext) {
14
+ const searchPosts = createPipeline(
15
+ skeleton,
16
+ hydration,
17
+ noBlocks,
18
+ presentation,
19
+ )
20
+ server.app.bsky.feed.searchPosts({
21
+ auth: ctx.authOptionalVerifier,
22
+ handler: async ({ auth, params }) => {
23
+ const viewer = auth.credentials.did
24
+ const db = ctx.db.getReplica('search')
25
+ const feedService = ctx.services.feed(db)
26
+ const actorService = ctx.services.actor(db)
27
+ const searchAgent = ctx.searchAgent
28
+ if (!searchAgent) {
29
+ throw new InvalidRequestError('Search not available')
30
+ }
31
+
32
+ const results = await searchPosts(
33
+ { ...params, viewer },
34
+ { db, feedService, actorService, searchAgent },
35
+ )
36
+
37
+ return {
38
+ encoding: 'application/json',
39
+ body: results,
40
+ }
41
+ },
42
+ })
43
+ }
44
+
45
+ const skeleton = async (
46
+ params: Params,
47
+ ctx: Context,
48
+ ): Promise<SkeletonState> => {
49
+ const res = await ctx.searchAgent.api.app.bsky.unspecced.searchPostsSkeleton({
50
+ q: params.q,
51
+ cursor: params.cursor,
52
+ limit: params.limit,
53
+ })
54
+ return {
55
+ params,
56
+ postUris: res.data.posts.map((a) => a.uri),
57
+ cursor: res.data.cursor,
58
+ hitsTotal: res.data.hitsTotal,
59
+ }
60
+ }
61
+
62
+ const hydration = async (
63
+ state: SkeletonState,
64
+ ctx: Context,
65
+ ): Promise<HydrationState> => {
66
+ const { feedService } = ctx
67
+ const { params, postUris } = state
68
+ const uris = new Set<string>(postUris)
69
+ const dids = new Set<string>(postUris.map((uri) => new AtUri(uri).hostname))
70
+ const hydrated = await feedService.feedHydration({
71
+ uris,
72
+ dids,
73
+ viewer: params.viewer,
74
+ })
75
+ return { ...state, ...hydrated }
76
+ }
77
+
78
+ const noBlocks = (state: HydrationState): HydrationState => {
79
+ const { viewer } = state.params
80
+ state.postUris = state.postUris.filter((uri) => {
81
+ const post = state.posts[uri]
82
+ if (!viewer || !post) return true
83
+ return !state.bam.block([viewer, post.creator])
84
+ })
85
+ return state
86
+ }
87
+
88
+ const presentation = (state: HydrationState, ctx: Context) => {
89
+ const { feedService, actorService } = ctx
90
+ const { postUris, profiles, params } = state
91
+ const actors = actorService.views.profileBasicPresentation(
92
+ Object.keys(profiles),
93
+ state,
94
+ { viewer: params.viewer },
95
+ )
96
+
97
+ const postViews = mapDefined(postUris, (uri) =>
98
+ feedService.views.formatPostView(
99
+ uri,
100
+ actors,
101
+ state.posts,
102
+ state.threadgates,
103
+ state.embeds,
104
+ state.labels,
105
+ state.lists,
106
+ ),
107
+ )
108
+ return { posts: postViews, cursor: state.cursor, hitsTotal: state.hitsTotal }
109
+ }
110
+
111
+ type Context = {
112
+ db: Database
113
+ feedService: FeedService
114
+ actorService: ActorService
115
+ searchAgent: AtpAgent
116
+ }
117
+
118
+ type Params = QueryParams & { viewer: string | null }
119
+
120
+ type SkeletonState = {
121
+ params: Params
122
+ postUris: string[]
123
+ hitsTotal?: number
124
+ cursor?: string
125
+ }
126
+
127
+ type HydrationState = SkeletonState & FeedHydrationState
@@ -85,9 +85,9 @@ export default function (server: Server, ctx: AppContext) {
85
85
  return { result, restored }
86
86
  })
87
87
 
88
- if (restored) {
89
- const { did, subjects } = restored
90
- const agent = await ctx.pdsAdminAgent(did)
88
+ if (restored && ctx.moderationPushAgent) {
89
+ const agent = ctx.moderationPushAgent
90
+ const { subjects } = restored
91
91
  const results = await Promise.allSettled(
92
92
  subjects.map((subject) =>
93
93
  retryHttp(() =>
@@ -111,10 +111,10 @@ export default function (server: Server, ctx: AppContext) {
111
111
  return { result, takenDown }
112
112
  })
113
113
 
114
- if (takenDown) {
114
+ if (takenDown && ctx.moderationPushAgent) {
115
+ const agent = ctx.moderationPushAgent
115
116
  const { did, subjects } = takenDown
116
117
  if (did && subjects.length > 0) {
117
- const agent = await ctx.pdsAdminAgent(did)
118
118
  const results = await Promise.allSettled(
119
119
  subjects.map((subject) =>
120
120
  retryHttp(() =>
@@ -9,8 +9,9 @@ export const getPdsAccountInfo = async (
9
9
  ctx: AppContext,
10
10
  did: string,
11
11
  ): Promise<AccountView | null> => {
12
+ const agent = ctx.moderationPushAgent
13
+ if (!agent) return null
12
14
  try {
13
- const agent = await ctx.pdsAdminAgent(did)
14
15
  const res = await agent.api.com.atproto.admin.getAccountInfo({ did })
15
16
  return res.data
16
17
  } catch (err) {
@@ -31,6 +32,7 @@ export const addAccountInfoToRepoViewDetail = (
31
32
  invitesDisabled: accountInfo.invitesDisabled,
32
33
  inviteNote: accountInfo.inviteNote,
33
34
  invites: accountInfo.invites,
35
+ emailConfirmedAt: accountInfo.emailConfirmedAt,
34
36
  }
35
37
  }
36
38
 
@@ -0,0 +1,30 @@
1
+ import { Server } from '../../../../lexicon'
2
+ import AppContext from '../../../../context'
3
+
4
+ export default function (server: Server, ctx: AppContext) {
5
+ server.com.atproto.temp.fetchLabels(async ({ params }) => {
6
+ const { limit } = params
7
+ const db = ctx.db.getReplica()
8
+ const since =
9
+ params.since !== undefined ? new Date(params.since).toISOString() : ''
10
+ const labelRes = await db.db
11
+ .selectFrom('label')
12
+ .selectAll()
13
+ .orderBy('label.cts', 'asc')
14
+ .where('cts', '>', since)
15
+ .limit(limit)
16
+ .execute()
17
+
18
+ const labels = labelRes.map((l) => ({
19
+ ...l,
20
+ cid: l.cid === '' ? undefined : l.cid,
21
+ }))
22
+
23
+ return {
24
+ encoding: 'application/json',
25
+ body: {
26
+ labels,
27
+ },
28
+ }
29
+ })
30
+ }
package/src/api/index.ts CHANGED
@@ -13,6 +13,7 @@ import getLikes from './app/bsky/feed/getLikes'
13
13
  import getListFeed from './app/bsky/feed/getListFeed'
14
14
  import getPostThread from './app/bsky/feed/getPostThread'
15
15
  import getPosts from './app/bsky/feed/getPosts'
16
+ import searchPosts from './app/bsky/feed/searchPosts'
16
17
  import getActorLikes from './app/bsky/feed/getActorLikes'
17
18
  import getProfile from './app/bsky/actor/getProfile'
18
19
  import getProfiles from './app/bsky/actor/getProfiles'
@@ -52,6 +53,7 @@ import getModerationReport from './com/atproto/admin/getModerationReport'
52
53
  import getModerationReports from './com/atproto/admin/getModerationReports'
53
54
  import resolveHandle from './com/atproto/identity/resolveHandle'
54
55
  import getRecord from './com/atproto/repo/getRecord'
56
+ import fetchLabels from './com/atproto/temp/fetchLabels'
55
57
 
56
58
  export * as health from './health'
57
59
 
@@ -74,6 +76,7 @@ export default function (server: Server, ctx: AppContext) {
74
76
  getListFeed(server, ctx)
75
77
  getPostThread(server, ctx)
76
78
  getPosts(server, ctx)
79
+ searchPosts(server, ctx)
77
80
  getActorLikes(server, ctx)
78
81
  getProfile(server, ctx)
79
82
  getProfiles(server, ctx)
@@ -114,5 +117,6 @@ export default function (server: Server, ctx: AppContext) {
114
117
  getModerationReports(server, ctx)
115
118
  resolveHandle(server, ctx)
116
119
  getRecord(server, ctx)
120
+ fetchLabels(server, ctx)
117
121
  return server
118
122
  }