@atproto/bsky 0.0.37 → 0.0.39

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 (149) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/api/app/bsky/feed/getAuthorFeed.d.ts +2 -3
  3. package/dist/api/app/bsky/feed/getListFeed.d.ts +2 -2
  4. package/dist/api/app/bsky/feed/getTimeline.d.ts +4 -2
  5. package/dist/api/app/bsky/labeler/getServices.d.ts +3 -0
  6. package/dist/api/util.d.ts +9 -2
  7. package/dist/auth-verifier.d.ts +1 -1
  8. package/dist/context.d.ts +3 -0
  9. package/dist/data-plane/server/db/database-schema.d.ts +2 -2
  10. package/dist/data-plane/server/db/migrations/20240226T225725627Z-labelers.d.ts +3 -0
  11. package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
  12. package/dist/data-plane/server/db/tables/labeler.d.ts +13 -0
  13. package/dist/data-plane/server/indexing/index.d.ts +2 -0
  14. package/dist/data-plane/server/indexing/plugins/labeler.d.ts +10 -0
  15. package/dist/data-plane/server/util.d.ts +6 -6
  16. package/dist/hydration/actor.d.ts +3 -0
  17. package/dist/hydration/hydrator.d.ts +27 -22
  18. package/dist/hydration/label.d.ts +23 -9
  19. package/dist/index.js +4100 -4645
  20. package/dist/index.js.map +3 -3
  21. package/dist/lexicon/index.d.ts +7 -27
  22. package/dist/lexicon/lexicons.d.ts +516 -1463
  23. package/dist/lexicon/types/app/bsky/actor/defs.d.ts +23 -1
  24. package/dist/lexicon/types/app/bsky/embed/record.d.ts +2 -1
  25. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +1 -0
  26. package/dist/lexicon/types/app/bsky/graph/defs.d.ts +3 -0
  27. package/dist/lexicon/types/app/bsky/labeler/defs.d.ts +41 -0
  28. package/dist/lexicon/types/{com/atproto/admin/searchRepos.d.ts → app/bsky/labeler/getServices.d.ts} +7 -7
  29. package/dist/lexicon/types/app/bsky/labeler/service.d.ts +14 -0
  30. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +0 -304
  31. package/dist/lexicon/types/com/atproto/label/defs.d.ts +23 -0
  32. package/dist/lexicon/types/com/atproto/server/describeServer.d.ts +7 -0
  33. package/dist/proto/bsky_connect.d.ts +7 -1
  34. package/dist/proto/bsky_pb.d.ts +25 -0
  35. package/dist/util.d.ts +7 -0
  36. package/dist/views/index.d.ts +3 -0
  37. package/dist/views/types.d.ts +2 -1
  38. package/package.json +14 -13
  39. package/proto/bsky.proto +12 -0
  40. package/src/api/app/bsky/actor/getProfile.ts +21 -17
  41. package/src/api/app/bsky/actor/getProfiles.ts +16 -7
  42. package/src/api/app/bsky/actor/getSuggestions.ts +18 -13
  43. package/src/api/app/bsky/actor/searchActors.ts +9 -5
  44. package/src/api/app/bsky/actor/searchActorsTypeahead.ts +12 -5
  45. package/src/api/app/bsky/feed/getActorFeeds.ts +16 -6
  46. package/src/api/app/bsky/feed/getActorLikes.ts +18 -8
  47. package/src/api/app/bsky/feed/getAuthorFeed.ts +18 -19
  48. package/src/api/app/bsky/feed/getFeed.ts +14 -7
  49. package/src/api/app/bsky/feed/getFeedGenerator.ts +8 -2
  50. package/src/api/app/bsky/feed/getFeedGenerators.ts +16 -5
  51. package/src/api/app/bsky/feed/getLikes.ts +13 -6
  52. package/src/api/app/bsky/feed/getListFeed.ts +13 -7
  53. package/src/api/app/bsky/feed/getPostThread.ts +15 -8
  54. package/src/api/app/bsky/feed/getPosts.ts +14 -5
  55. package/src/api/app/bsky/feed/getRepostedBy.ts +13 -6
  56. package/src/api/app/bsky/feed/getSuggestedFeeds.ts +8 -2
  57. package/src/api/app/bsky/feed/getTimeline.ts +14 -8
  58. package/src/api/app/bsky/feed/searchPosts.ts +9 -5
  59. package/src/api/app/bsky/graph/getBlocks.ts +10 -9
  60. package/src/api/app/bsky/graph/getFollowers.ts +23 -15
  61. package/src/api/app/bsky/graph/getFollows.ts +23 -15
  62. package/src/api/app/bsky/graph/getList.ts +14 -8
  63. package/src/api/app/bsky/graph/getListBlocks.ts +10 -7
  64. package/src/api/app/bsky/graph/getListMutes.ts +10 -7
  65. package/src/api/app/bsky/graph/getLists.ts +9 -7
  66. package/src/api/app/bsky/graph/getMutes.ts +10 -8
  67. package/src/api/app/bsky/graph/getSuggestedFollowsByActor.ts +10 -7
  68. package/src/api/app/bsky/graph/muteActor.ts +1 -1
  69. package/src/api/app/bsky/labeler/getServices.ts +46 -0
  70. package/src/api/app/bsky/notification/listNotifications.ts +12 -8
  71. package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +6 -3
  72. package/src/api/com/atproto/admin/getAccountInfos.ts +10 -3
  73. package/src/api/index.ts +2 -0
  74. package/src/api/util.ts +19 -4
  75. package/src/auth-verifier.ts +2 -2
  76. package/src/context.ts +20 -0
  77. package/src/data-plane/server/db/database-schema.ts +4 -4
  78. package/src/data-plane/server/db/migrations/20240226T225725627Z-labelers.ts +27 -0
  79. package/src/data-plane/server/db/migrations/index.ts +1 -0
  80. package/src/data-plane/server/db/tables/labeler.ts +16 -0
  81. package/src/data-plane/server/indexing/index.ts +4 -0
  82. package/src/data-plane/server/indexing/plugins/labeler.ts +77 -0
  83. package/src/data-plane/server/routes/interactions.ts +17 -1
  84. package/src/data-plane/server/routes/labels.ts +4 -2
  85. package/src/data-plane/server/routes/profile.ts +15 -1
  86. package/src/data-plane/server/routes/records.ts +1 -0
  87. package/src/hydration/actor.ts +6 -0
  88. package/src/hydration/hydrator.ts +171 -97
  89. package/src/hydration/label.ts +106 -20
  90. package/src/index.ts +1 -3
  91. package/src/lexicon/index.ts +22 -137
  92. package/src/lexicon/lexicons.ts +552 -1635
  93. package/src/lexicon/types/app/bsky/actor/defs.ts +57 -1
  94. package/src/lexicon/types/app/bsky/embed/record.ts +2 -0
  95. package/src/lexicon/types/app/bsky/feed/defs.ts +1 -0
  96. package/src/lexicon/types/app/bsky/graph/defs.ts +3 -0
  97. package/src/lexicon/types/app/bsky/labeler/defs.ts +93 -0
  98. package/src/lexicon/types/{com/atproto/admin/searchRepos.ts → app/bsky/labeler/getServices.ts} +8 -8
  99. package/src/lexicon/types/app/bsky/labeler/service.ts +31 -0
  100. package/src/lexicon/types/com/atproto/admin/defs.ts +0 -694
  101. package/src/lexicon/types/com/atproto/label/defs.ts +78 -0
  102. package/src/lexicon/types/com/atproto/server/describeServer.ts +18 -0
  103. package/src/proto/bsky_connect.ts +11 -0
  104. package/src/proto/bsky_pb.ts +146 -0
  105. package/src/util.ts +44 -0
  106. package/src/views/index.ts +77 -8
  107. package/src/views/types.ts +6 -3
  108. package/tests/__snapshots__/feed-generation.test.ts.snap +12 -45
  109. package/tests/_util.ts +21 -0
  110. package/tests/data-plane/__snapshots__/indexing.test.ts.snap +20 -8
  111. package/tests/label-hydration.test.ts +162 -0
  112. package/tests/views/__snapshots__/author-feed.test.ts.snap +0 -46
  113. package/tests/views/__snapshots__/block-lists.test.ts.snap +7 -17
  114. package/tests/views/__snapshots__/blocks.test.ts.snap +0 -9
  115. package/tests/views/__snapshots__/labeler-service.test.ts.snap +156 -0
  116. package/tests/views/__snapshots__/list-feed.test.ts.snap +0 -20
  117. package/tests/views/__snapshots__/mute-lists.test.ts.snap +10 -18
  118. package/tests/views/__snapshots__/mutes.test.ts.snap +0 -4
  119. package/tests/views/__snapshots__/notifications.test.ts.snap +0 -9
  120. package/tests/views/__snapshots__/posts.test.ts.snap +0 -7
  121. package/tests/views/__snapshots__/profile.test.ts.snap +40 -6
  122. package/tests/views/__snapshots__/thread.test.ts.snap +0 -38
  123. package/tests/views/__snapshots__/threadgating.test.ts.snap +2 -0
  124. package/tests/views/__snapshots__/timeline.test.ts.snap +0 -145
  125. package/tests/views/labeler-service.test.ts +156 -0
  126. package/tests/views/takedown-labels.test.ts +133 -0
  127. package/tests/views/timeline.test.ts +7 -2
  128. package/dist/data-plane/server/db/tables/moderation.d.ts +0 -42
  129. package/dist/lexicon/types/com/atproto/admin/createCommunicationTemplate.d.ts +0 -37
  130. package/dist/lexicon/types/com/atproto/admin/deleteCommunicationTemplate.d.ts +0 -25
  131. package/dist/lexicon/types/com/atproto/admin/emitModerationEvent.d.ts +0 -45
  132. package/dist/lexicon/types/com/atproto/admin/getModerationEvent.d.ts +0 -29
  133. package/dist/lexicon/types/com/atproto/admin/getRecord.d.ts +0 -31
  134. package/dist/lexicon/types/com/atproto/admin/getRepo.d.ts +0 -30
  135. package/dist/lexicon/types/com/atproto/admin/listCommunicationTemplates.d.ts +0 -31
  136. package/dist/lexicon/types/com/atproto/admin/queryModerationEvents.d.ts +0 -48
  137. package/dist/lexicon/types/com/atproto/admin/queryModerationStatuses.d.ts +0 -50
  138. package/dist/lexicon/types/com/atproto/admin/updateCommunicationTemplate.d.ts +0 -39
  139. package/src/data-plane/server/db/tables/moderation.ts +0 -59
  140. package/src/lexicon/types/com/atproto/admin/createCommunicationTemplate.ts +0 -54
  141. package/src/lexicon/types/com/atproto/admin/deleteCommunicationTemplate.ts +0 -38
  142. package/src/lexicon/types/com/atproto/admin/emitModerationEvent.ts +0 -67
  143. package/src/lexicon/types/com/atproto/admin/getModerationEvent.ts +0 -41
  144. package/src/lexicon/types/com/atproto/admin/getRecord.ts +0 -43
  145. package/src/lexicon/types/com/atproto/admin/getRepo.ts +0 -42
  146. package/src/lexicon/types/com/atproto/admin/listCommunicationTemplates.ts +0 -44
  147. package/src/lexicon/types/com/atproto/admin/queryModerationEvents.ts +0 -73
  148. package/src/lexicon/types/com/atproto/admin/queryModerationStatuses.ts +0 -74
  149. package/src/lexicon/types/com/atproto/admin/updateCommunicationTemplate.ts +0 -57
@@ -11,9 +11,13 @@ import {
11
11
  createPipeline,
12
12
  } from '../../../../pipeline'
13
13
  import { didFromUri } from '../../../../hydration/util'
14
- import { Hydrator, mergeStates } from '../../../../hydration/hydrator'
14
+ import {
15
+ HydrateCtx,
16
+ Hydrator,
17
+ mergeStates,
18
+ } from '../../../../hydration/hydrator'
15
19
  import { Views } from '../../../../views'
16
- import { clearlyBadCursor } from '../../../util'
20
+ import { clearlyBadCursor, resHeaders } from '../../../util'
17
21
 
18
22
  export default function (server: Server, ctx: AppContext) {
19
23
  const getFollowers = createPipeline(
@@ -24,17 +28,17 @@ export default function (server: Server, ctx: AppContext) {
24
28
  )
25
29
  server.app.bsky.graph.getFollowers({
26
30
  auth: ctx.authVerifier.optionalStandardOrRole,
27
- handler: async ({ params, auth }) => {
28
- const { viewer, canViewTakedowns } = ctx.authVerifier.parseCreds(auth)
31
+ handler: async ({ params, auth, req }) => {
32
+ const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth)
33
+ const labelers = ctx.reqLabelers(req)
34
+ const hydrateCtx = { labelers, viewer, includeTakedowns }
29
35
 
30
- const result = await getFollowers(
31
- { ...params, viewer, canViewTakedowns },
32
- ctx,
33
- )
36
+ const result = await getFollowers({ ...params, hydrateCtx }, ctx)
34
37
 
35
38
  return {
36
39
  encoding: 'application/json',
37
40
  body: result,
41
+ headers: resHeaders({ labelers }),
38
42
  }
39
43
  },
40
44
  })
@@ -65,7 +69,6 @@ const hydration = async (
65
69
  input: HydrationFnInput<Context, Params, SkeletonState>,
66
70
  ) => {
67
71
  const { ctx, params, skeleton } = input
68
- const { viewer } = params
69
72
  const { followUris, subjectDid } = skeleton
70
73
  const followState = await ctx.hydrator.hydrateFollows(followUris)
71
74
  const dids = [subjectDid]
@@ -76,13 +79,16 @@ const hydration = async (
76
79
  }
77
80
  }
78
81
  }
79
- const profileState = await ctx.hydrator.hydrateProfiles(dids, viewer)
82
+ const profileState = await ctx.hydrator.hydrateProfiles(
83
+ dids,
84
+ params.hydrateCtx,
85
+ )
80
86
  return mergeStates(followState, profileState)
81
87
  }
82
88
 
83
89
  const noBlocks = (input: RulesFnInput<Context, Params, SkeletonState>) => {
84
90
  const { skeleton, params, hydration, ctx } = input
85
- const { viewer } = params
91
+ const viewer = params.hydrateCtx.viewer
86
92
  skeleton.followUris = skeleton.followUris.filter((followUri) => {
87
93
  const followerDid = didFromUri(followUri)
88
94
  return (
@@ -102,13 +108,16 @@ const presentation = (
102
108
  ctx.views.actorIsTakendown(did, hydration)
103
109
 
104
110
  const subject = ctx.views.profile(subjectDid, hydration)
105
- if (!subject || (!params.canViewTakedowns && isTakendown(subjectDid))) {
111
+ if (
112
+ !subject ||
113
+ (!params.hydrateCtx.includeTakedowns && isTakendown(subjectDid))
114
+ ) {
106
115
  throw new InvalidRequestError(`Actor not found: ${params.actor}`)
107
116
  }
108
117
 
109
118
  const followers = mapDefined(followUris, (followUri) => {
110
119
  const followerDid = didFromUri(followUri)
111
- if (!params.canViewTakedowns && isTakendown(followerDid)) {
120
+ if (!params.hydrateCtx.includeTakedowns && isTakendown(followerDid)) {
112
121
  return
113
122
  }
114
123
  return ctx.views.profile(didFromUri(followUri), hydration)
@@ -123,8 +132,7 @@ type Context = {
123
132
  }
124
133
 
125
134
  type Params = QueryParams & {
126
- viewer: string | null
127
- canViewTakedowns: boolean
135
+ hydrateCtx: HydrateCtx
128
136
  }
129
137
 
130
138
  type SkeletonState = {
@@ -10,26 +10,30 @@ import {
10
10
  SkeletonFnInput,
11
11
  createPipeline,
12
12
  } from '../../../../pipeline'
13
- import { Hydrator, mergeStates } from '../../../../hydration/hydrator'
13
+ import {
14
+ HydrateCtx,
15
+ Hydrator,
16
+ mergeStates,
17
+ } from '../../../../hydration/hydrator'
14
18
  import { Views } from '../../../../views'
15
- import { clearlyBadCursor } from '../../../util'
19
+ import { clearlyBadCursor, resHeaders } from '../../../util'
16
20
 
17
21
  export default function (server: Server, ctx: AppContext) {
18
22
  const getFollows = createPipeline(skeleton, hydration, noBlocks, presentation)
19
23
  server.app.bsky.graph.getFollows({
20
24
  auth: ctx.authVerifier.optionalStandardOrRole,
21
- handler: async ({ params, auth }) => {
22
- const { viewer, canViewTakedowns } = ctx.authVerifier.parseCreds(auth)
25
+ handler: async ({ params, auth, req }) => {
26
+ const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth)
27
+ const labelers = ctx.reqLabelers(req)
28
+ const hydrateCtx = { labelers, viewer, includeTakedowns }
23
29
 
24
30
  // @TODO ensure canViewTakedowns gets threaded through and applied properly
25
- const result = await getFollows(
26
- { ...params, viewer, canViewTakedowns },
27
- ctx,
28
- )
31
+ const result = await getFollows({ ...params, hydrateCtx }, ctx)
29
32
 
30
33
  return {
31
34
  encoding: 'application/json',
32
35
  body: result,
36
+ headers: resHeaders({ labelers }),
33
37
  }
34
38
  },
35
39
  })
@@ -60,7 +64,6 @@ const hydration = async (
60
64
  input: HydrationFnInput<Context, Params, SkeletonState>,
61
65
  ) => {
62
66
  const { ctx, params, skeleton } = input
63
- const { viewer } = params
64
67
  const { followUris, subjectDid } = skeleton
65
68
  const followState = await ctx.hydrator.hydrateFollows(followUris)
66
69
  const dids = [subjectDid]
@@ -71,13 +74,16 @@ const hydration = async (
71
74
  }
72
75
  }
73
76
  }
74
- const profileState = await ctx.hydrator.hydrateProfiles(dids, viewer)
77
+ const profileState = await ctx.hydrator.hydrateProfiles(
78
+ dids,
79
+ params.hydrateCtx,
80
+ )
75
81
  return mergeStates(followState, profileState)
76
82
  }
77
83
 
78
84
  const noBlocks = (input: RulesFnInput<Context, Params, SkeletonState>) => {
79
85
  const { skeleton, params, hydration, ctx } = input
80
- const { viewer } = params
86
+ const viewer = params.hydrateCtx.viewer
81
87
  skeleton.followUris = skeleton.followUris.filter((followUri) => {
82
88
  const follow = hydration.follows?.get(followUri)
83
89
  if (!follow) return false
@@ -99,14 +105,17 @@ const presentation = (
99
105
  ctx.views.actorIsTakendown(did, hydration)
100
106
 
101
107
  const subject = ctx.views.profile(subjectDid, hydration)
102
- if (!subject || (!params.canViewTakedowns && isTakendown(subjectDid))) {
108
+ if (
109
+ !subject ||
110
+ (!params.hydrateCtx.includeTakedowns && isTakendown(subjectDid))
111
+ ) {
103
112
  throw new InvalidRequestError(`Actor not found: ${params.actor}`)
104
113
  }
105
114
 
106
115
  const follows = mapDefined(followUris, (followUri) => {
107
116
  const followDid = hydration.follows?.get(followUri)?.record.subject
108
117
  if (!followDid) return
109
- if (!params.canViewTakedowns && isTakendown(followDid)) {
118
+ if (!params.hydrateCtx.includeTakedowns && isTakendown(followDid)) {
110
119
  return
111
120
  }
112
121
  return ctx.views.profile(followDid, hydration)
@@ -121,8 +130,7 @@ type Context = {
121
130
  }
122
131
 
123
132
  type Params = QueryParams & {
124
- viewer: string | null
125
- canViewTakedowns: boolean
133
+ hydrateCtx: HydrateCtx
126
134
  }
127
135
 
128
136
  type SkeletonState = {
@@ -10,21 +10,28 @@ import {
10
10
  PresentationFnInput,
11
11
  SkeletonFnInput,
12
12
  } from '../../../../pipeline'
13
- import { Hydrator, mergeStates } from '../../../../hydration/hydrator'
13
+ import {
14
+ HydrateCtx,
15
+ Hydrator,
16
+ mergeStates,
17
+ } from '../../../../hydration/hydrator'
14
18
  import { Views } from '../../../../views'
15
- import { clearlyBadCursor } from '../../../util'
19
+ import { clearlyBadCursor, resHeaders } from '../../../util'
16
20
  import { ListItemInfo } from '../../../../proto/bsky_pb'
17
21
 
18
22
  export default function (server: Server, ctx: AppContext) {
19
23
  const getList = createPipeline(skeleton, hydration, noRules, presentation)
20
24
  server.app.bsky.graph.getList({
21
25
  auth: ctx.authVerifier.standardOptional,
22
- handler: async ({ params, auth }) => {
26
+ handler: async ({ params, auth, req }) => {
23
27
  const viewer = auth.credentials.iss
24
- const result = await getList({ ...params, viewer }, ctx)
28
+ const labelers = ctx.reqLabelers(req)
29
+ const hydrateCtx = { labelers, viewer }
30
+ const result = await getList({ ...params, hydrateCtx }, ctx)
25
31
  return {
26
32
  encoding: 'application/json',
27
33
  body: result,
34
+ headers: resHeaders({ labelers }),
28
35
  }
29
36
  },
30
37
  })
@@ -53,13 +60,12 @@ const hydration = async (
53
60
  input: HydrationFnInput<Context, Params, SkeletonState>,
54
61
  ) => {
55
62
  const { ctx, params, skeleton } = input
56
- const { viewer } = params
57
63
  const { listUri, listitems } = skeleton
58
64
  const [listState, profileState] = await Promise.all([
59
- ctx.hydrator.hydrateLists([listUri], viewer),
65
+ ctx.hydrator.hydrateLists([listUri], params.hydrateCtx),
60
66
  ctx.hydrator.hydrateProfiles(
61
67
  listitems.map(({ did }) => did),
62
- viewer,
68
+ params.hydrateCtx,
63
69
  ),
64
70
  ])
65
71
  return mergeStates(listState, profileState)
@@ -88,7 +94,7 @@ type Context = {
88
94
  }
89
95
 
90
96
  type Params = QueryParams & {
91
- viewer: string | null
97
+ hydrateCtx: HydrateCtx
92
98
  }
93
99
 
94
100
  type SkeletonState = {
@@ -9,9 +9,9 @@ import {
9
9
  PresentationFnInput,
10
10
  SkeletonFnInput,
11
11
  } from '../../../../pipeline'
12
- import { Hydrator } from '../../../../hydration/hydrator'
12
+ import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator'
13
13
  import { Views } from '../../../../views'
14
- import { clearlyBadCursor } from '../../../util'
14
+ import { clearlyBadCursor, resHeaders } from '../../../util'
15
15
 
16
16
  export default function (server: Server, ctx: AppContext) {
17
17
  const getListBlocks = createPipeline(
@@ -22,12 +22,15 @@ export default function (server: Server, ctx: AppContext) {
22
22
  )
23
23
  server.app.bsky.graph.getListBlocks({
24
24
  auth: ctx.authVerifier.standard,
25
- handler: async ({ params, auth }) => {
25
+ handler: async ({ params, auth, req }) => {
26
26
  const viewer = auth.credentials.iss
27
- const result = await getListBlocks({ ...params, viewer }, ctx)
27
+ const labelers = ctx.reqLabelers(req)
28
+ const hydrateCtx = { labelers, viewer }
29
+ const result = await getListBlocks({ ...params, hydrateCtx }, ctx)
28
30
  return {
29
31
  encoding: 'application/json',
30
32
  body: result,
33
+ headers: resHeaders({ labelers }),
31
34
  }
32
35
  },
33
36
  })
@@ -42,7 +45,7 @@ const skeleton = async (
42
45
  }
43
46
  const { listUris, cursor } =
44
47
  await ctx.hydrator.dataplane.getBlocklistSubscriptions({
45
- actorDid: params.viewer,
48
+ actorDid: params.hydrateCtx.viewer,
46
49
  cursor: params.cursor,
47
50
  limit: params.limit,
48
51
  })
@@ -53,7 +56,7 @@ const hydration = async (
53
56
  input: HydrationFnInput<Context, Params, SkeletonState>,
54
57
  ) => {
55
58
  const { ctx, params, skeleton } = input
56
- return await ctx.hydrator.hydrateLists(skeleton.listUris, params.viewer)
59
+ return await ctx.hydrator.hydrateLists(skeleton.listUris, params.hydrateCtx)
57
60
  }
58
61
 
59
62
  const presentation = (
@@ -71,7 +74,7 @@ type Context = {
71
74
  }
72
75
 
73
76
  type Params = QueryParams & {
74
- viewer: string
77
+ hydrateCtx: HydrateCtx & { viewer: string }
75
78
  }
76
79
 
77
80
  type SkeletonState = {
@@ -9,9 +9,9 @@ import {
9
9
  PresentationFnInput,
10
10
  SkeletonFnInput,
11
11
  } from '../../../../pipeline'
12
- import { Hydrator } from '../../../../hydration/hydrator'
12
+ import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator'
13
13
  import { Views } from '../../../../views'
14
- import { clearlyBadCursor } from '../../../util'
14
+ import { clearlyBadCursor, resHeaders } from '../../../util'
15
15
 
16
16
  export default function (server: Server, ctx: AppContext) {
17
17
  const getListMutes = createPipeline(
@@ -22,12 +22,15 @@ export default function (server: Server, ctx: AppContext) {
22
22
  )
23
23
  server.app.bsky.graph.getListMutes({
24
24
  auth: ctx.authVerifier.standard,
25
- handler: async ({ params, auth }) => {
25
+ handler: async ({ params, auth, req }) => {
26
26
  const viewer = auth.credentials.iss
27
- const result = await getListMutes({ ...params, viewer }, ctx)
27
+ const labelers = ctx.reqLabelers(req)
28
+ const hydrateCtx = { labelers, viewer }
29
+ const result = await getListMutes({ ...params, hydrateCtx }, ctx)
28
30
  return {
29
31
  encoding: 'application/json',
30
32
  body: result,
33
+ headers: resHeaders({ labelers }),
31
34
  }
32
35
  },
33
36
  })
@@ -42,7 +45,7 @@ const skeleton = async (
42
45
  }
43
46
  const { listUris, cursor } =
44
47
  await ctx.hydrator.dataplane.getMutelistSubscriptions({
45
- actorDid: params.viewer,
48
+ actorDid: params.hydrateCtx.viewer,
46
49
  cursor: params.cursor,
47
50
  limit: params.limit,
48
51
  })
@@ -53,7 +56,7 @@ const hydration = async (
53
56
  input: HydrationFnInput<Context, Params, SkeletonState>,
54
57
  ) => {
55
58
  const { ctx, params, skeleton } = input
56
- return await ctx.hydrator.hydrateLists(skeleton.listUris, params.viewer)
59
+ return await ctx.hydrator.hydrateLists(skeleton.listUris, params.hydrateCtx)
57
60
  }
58
61
 
59
62
  const presentation = (
@@ -71,7 +74,7 @@ type Context = {
71
74
  }
72
75
 
73
76
  type Params = QueryParams & {
74
- viewer: string
77
+ hydrateCtx: HydrateCtx & { viewer: string }
75
78
  }
76
79
 
77
80
  type SkeletonState = {
@@ -9,21 +9,24 @@ import {
9
9
  PresentationFnInput,
10
10
  SkeletonFnInput,
11
11
  } from '../../../../pipeline'
12
- import { Hydrator } from '../../../../hydration/hydrator'
12
+ import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator'
13
13
  import { Views } from '../../../../views'
14
- import { clearlyBadCursor } from '../../../util'
14
+ import { clearlyBadCursor, resHeaders } from '../../../util'
15
15
 
16
16
  export default function (server: Server, ctx: AppContext) {
17
17
  const getLists = createPipeline(skeleton, hydration, noRules, presentation)
18
18
  server.app.bsky.graph.getLists({
19
19
  auth: ctx.authVerifier.standardOptional,
20
- handler: async ({ params, auth }) => {
20
+ handler: async ({ params, auth, req }) => {
21
21
  const viewer = auth.credentials.iss
22
- const result = await getLists({ ...params, viewer }, ctx)
22
+ const labelers = ctx.reqLabelers(req)
23
+ const hydrateCtx = { labelers, viewer }
24
+ const result = await getLists({ ...params, hydrateCtx }, ctx)
23
25
 
24
26
  return {
25
27
  encoding: 'application/json',
26
28
  body: result,
29
+ headers: resHeaders({ labelers }),
27
30
  }
28
31
  },
29
32
  })
@@ -48,9 +51,8 @@ const hydration = async (
48
51
  input: HydrationFnInput<Context, Params, SkeletonState>,
49
52
  ) => {
50
53
  const { ctx, params, skeleton } = input
51
- const { viewer } = params
52
54
  const { listUris } = skeleton
53
- return ctx.hydrator.hydrateLists(listUris, viewer)
55
+ return ctx.hydrator.hydrateLists(listUris, params.hydrateCtx)
54
56
  }
55
57
 
56
58
  const presentation = (
@@ -70,7 +72,7 @@ type Context = {
70
72
  }
71
73
 
72
74
  type Params = QueryParams & {
73
- viewer: string | null
75
+ hydrateCtx: HydrateCtx
74
76
  }
75
77
 
76
78
  type SkeletonState = {
@@ -2,7 +2,7 @@ import { mapDefined } from '@atproto/common'
2
2
  import { Server } from '../../../../lexicon'
3
3
  import { QueryParams } from '../../../../lexicon/types/app/bsky/graph/getMutes'
4
4
  import AppContext from '../../../../context'
5
- import { Hydrator } from '../../../../hydration/hydrator'
5
+ import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator'
6
6
  import { Views } from '../../../../views'
7
7
  import {
8
8
  HydrationFnInput,
@@ -11,18 +11,21 @@ import {
11
11
  createPipeline,
12
12
  noRules,
13
13
  } from '../../../../pipeline'
14
- import { clearlyBadCursor } from '../../../util'
14
+ import { clearlyBadCursor, resHeaders } from '../../../util'
15
15
 
16
16
  export default function (server: Server, ctx: AppContext) {
17
17
  const getMutes = createPipeline(skeleton, hydration, noRules, presentation)
18
18
  server.app.bsky.graph.getMutes({
19
19
  auth: ctx.authVerifier.standard,
20
- handler: async ({ params, auth }) => {
20
+ handler: async ({ params, auth, req }) => {
21
21
  const viewer = auth.credentials.iss
22
- const result = await getMutes({ ...params, viewer }, ctx)
22
+ const labelers = ctx.reqLabelers(req)
23
+ const hydrateCtx = { labelers, viewer }
24
+ const result = await getMutes({ ...params, hydrateCtx }, ctx)
23
25
  return {
24
26
  encoding: 'application/json',
25
27
  body: result,
28
+ headers: resHeaders({ labelers }),
26
29
  }
27
30
  },
28
31
  })
@@ -34,7 +37,7 @@ const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
34
37
  return { mutedDids: [] }
35
38
  }
36
39
  const { dids, cursor } = await ctx.hydrator.dataplane.getMutes({
37
- actorDid: params.viewer,
40
+ actorDid: params.hydrateCtx.viewer,
38
41
  cursor: params.cursor,
39
42
  limit: params.limit,
40
43
  })
@@ -48,9 +51,8 @@ const hydration = async (
48
51
  input: HydrationFnInput<Context, Params, SkeletonState>,
49
52
  ) => {
50
53
  const { ctx, params, skeleton } = input
51
- const { viewer } = params
52
54
  const { mutedDids } = skeleton
53
- return ctx.hydrator.hydrateProfiles(mutedDids, viewer)
55
+ return ctx.hydrator.hydrateProfiles(mutedDids, params.hydrateCtx)
54
56
  }
55
57
 
56
58
  const presentation = (
@@ -70,7 +72,7 @@ type Context = {
70
72
  }
71
73
 
72
74
  type Params = QueryParams & {
73
- viewer: string
75
+ hydrateCtx: HydrateCtx & { viewer: string }
74
76
  }
75
77
 
76
78
  type SkeletonState = {
@@ -10,8 +10,9 @@ import {
10
10
  SkeletonFnInput,
11
11
  createPipeline,
12
12
  } from '../../../../pipeline'
13
- import { Hydrator } from '../../../../hydration/hydrator'
13
+ import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator'
14
14
  import { Views } from '../../../../views'
15
+ import { resHeaders } from '../../../util'
15
16
 
16
17
  export default function (server: Server, ctx: AppContext) {
17
18
  const getSuggestedFollowsByActor = createPipeline(
@@ -22,15 +23,18 @@ export default function (server: Server, ctx: AppContext) {
22
23
  )
23
24
  server.app.bsky.graph.getSuggestedFollowsByActor({
24
25
  auth: ctx.authVerifier.standard,
25
- handler: async ({ auth, params }) => {
26
+ handler: async ({ auth, params, req }) => {
26
27
  const viewer = auth.credentials.iss
28
+ const labelers = ctx.reqLabelers(req)
29
+ const hydrateCtx = { labelers, viewer }
27
30
  const result = await getSuggestedFollowsByActor(
28
- { ...params, viewer },
31
+ { ...params, hydrateCtx },
29
32
  ctx,
30
33
  )
31
34
  return {
32
35
  encoding: 'application/json',
33
36
  body: result,
37
+ headers: resHeaders({ labelers }),
34
38
  }
35
39
  },
36
40
  })
@@ -43,7 +47,7 @@ const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
43
47
  throw new InvalidRequestError('Actor not found')
44
48
  }
45
49
  const { dids, cursor } = await ctx.hydrator.dataplane.getFollowSuggestions({
46
- actorDid: params.viewer,
50
+ actorDid: params.hydrateCtx.viewer,
47
51
  relativeToDid,
48
52
  })
49
53
  return {
@@ -56,9 +60,8 @@ const hydration = async (
56
60
  input: HydrationFnInput<Context, Params, SkeletonState>,
57
61
  ) => {
58
62
  const { ctx, params, skeleton } = input
59
- const { viewer } = params
60
63
  const { suggestedDids } = skeleton
61
- return ctx.hydrator.hydrateProfilesDetailed(suggestedDids, viewer)
64
+ return ctx.hydrator.hydrateProfilesDetailed(suggestedDids, params.hydrateCtx)
62
65
  }
63
66
 
64
67
  const noBlocksOrMutes = (
@@ -90,7 +93,7 @@ type Context = {
90
93
  }
91
94
 
92
95
  type Params = QueryParams & {
93
- viewer: string
96
+ hydrateCtx: HydrateCtx & { viewer: string }
94
97
  }
95
98
 
96
99
  type SkeletonState = {
@@ -6,7 +6,7 @@ import { MuteOperation_Type } from '../../../../proto/bsync_pb'
6
6
  export default function (server: Server, ctx: AppContext) {
7
7
  server.app.bsky.graph.muteActor({
8
8
  auth: ctx.authVerifier.standard,
9
- handler: async ({ req, auth, input }) => {
9
+ handler: async ({ auth, input }) => {
10
10
  const { actor } = input.body
11
11
  const requester = auth.credentials.iss
12
12
  const [did] = await ctx.hydrator.actor.getDids([actor])
@@ -0,0 +1,46 @@
1
+ import { Server } from '../../../../lexicon'
2
+ import AppContext from '../../../../context'
3
+ import { mapDefined } from '@atproto/common'
4
+ import { resHeaders } from '../../../util'
5
+
6
+ export default function (server: Server, ctx: AppContext) {
7
+ server.app.bsky.labeler.getServices({
8
+ auth: ctx.authVerifier.standardOptional,
9
+ handler: async ({ params, auth, req }) => {
10
+ const { dids, detailed } = params
11
+ const viewer = auth.credentials.iss
12
+ const labelers = ctx.reqLabelers(req)
13
+
14
+ const hydration = await ctx.hydrator.hydrateLabelers(dids, {
15
+ viewer,
16
+ labelers,
17
+ })
18
+
19
+ const views = mapDefined(dids, (did) => {
20
+ if (detailed) {
21
+ const view = ctx.views.labelerDetailed(did, hydration)
22
+ if (!view) return
23
+ return {
24
+ $type: 'app.bsky.labeler.defs#labelerViewDetailed',
25
+ ...view,
26
+ }
27
+ } else {
28
+ const view = ctx.views.labeler(did, hydration)
29
+ if (!view) return
30
+ return {
31
+ $type: 'app.bsky.labeler.defs#labelerView',
32
+ ...view,
33
+ }
34
+ }
35
+ })
36
+
37
+ return {
38
+ encoding: 'application/json',
39
+ body: {
40
+ views,
41
+ },
42
+ headers: resHeaders({ labelers }),
43
+ }
44
+ },
45
+ })
46
+ }
@@ -10,11 +10,11 @@ import {
10
10
  RulesFnInput,
11
11
  SkeletonFnInput,
12
12
  } from '../../../../pipeline'
13
- import { Hydrator } from '../../../../hydration/hydrator'
13
+ import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator'
14
14
  import { Views } from '../../../../views'
15
15
  import { Notification } from '../../../../proto/bsky_pb'
16
16
  import { didFromUri } from '../../../../hydration/util'
17
- import { clearlyBadCursor } from '../../../util'
17
+ import { clearlyBadCursor, resHeaders } from '../../../util'
18
18
 
19
19
  export default function (server: Server, ctx: AppContext) {
20
20
  const listNotifications = createPipeline(
@@ -25,12 +25,15 @@ export default function (server: Server, ctx: AppContext) {
25
25
  )
26
26
  server.app.bsky.notification.listNotifications({
27
27
  auth: ctx.authVerifier.standard,
28
- handler: async ({ params, auth }) => {
28
+ handler: async ({ params, auth, req }) => {
29
29
  const viewer = auth.credentials.iss
30
- const result = await listNotifications({ ...params, viewer }, ctx)
30
+ const labelers = ctx.reqLabelers(req)
31
+ const hydrateCtx = { labelers, viewer }
32
+ const result = await listNotifications({ ...params, hydrateCtx }, ctx)
31
33
  return {
32
34
  encoding: 'application/json',
33
35
  body: result,
36
+ headers: resHeaders({ labelers }),
34
37
  }
35
38
  },
36
39
  })
@@ -43,17 +46,18 @@ const skeleton = async (
43
46
  if (params.seenAt) {
44
47
  throw new InvalidRequestError('The seenAt parameter is unsupported')
45
48
  }
49
+ const viewer = params.hydrateCtx.viewer
46
50
  if (clearlyBadCursor(params.cursor)) {
47
51
  return { notifs: [] }
48
52
  }
49
53
  const [res, lastSeenRes] = await Promise.all([
50
54
  ctx.hydrator.dataplane.getNotifications({
51
- actorDid: params.viewer,
55
+ actorDid: viewer,
52
56
  cursor: params.cursor,
53
57
  limit: params.limit,
54
58
  }),
55
59
  ctx.hydrator.dataplane.getNotificationSeen({
56
- actorDid: params.viewer,
60
+ actorDid: viewer,
57
61
  }),
58
62
  ])
59
63
  // @NOTE for the first page of results if there's no last-seen time, consider top notification unread
@@ -73,7 +77,7 @@ const hydration = async (
73
77
  input: HydrationFnInput<Context, Params, SkeletonState>,
74
78
  ) => {
75
79
  const { skeleton, params, ctx } = input
76
- return ctx.hydrator.hydrateNotifications(skeleton.notifs, params.viewer)
80
+ return ctx.hydrator.hydrateNotifications(skeleton.notifs, params.hydrateCtx)
77
81
  }
78
82
 
79
83
  const noBlockOrMutes = (
@@ -107,7 +111,7 @@ type Context = {
107
111
  }
108
112
 
109
113
  type Params = QueryParams & {
110
- viewer: string
114
+ hydrateCtx: HydrateCtx & { viewer: string }
111
115
  }
112
116
 
113
117
  type SkeletonState = {