@atproto/bsky 0.0.224 → 0.0.226

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 (207) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/api/app/bsky/actor/getProfile.d.ts.map +1 -1
  3. package/dist/api/app/bsky/actor/getProfile.js +2 -1
  4. package/dist/api/app/bsky/actor/getProfile.js.map +1 -1
  5. package/dist/api/app/bsky/actor/getProfiles.d.ts.map +1 -1
  6. package/dist/api/app/bsky/actor/getProfiles.js +2 -1
  7. package/dist/api/app/bsky/actor/getProfiles.js.map +1 -1
  8. package/dist/api/app/bsky/actor/searchActors.d.ts.map +1 -1
  9. package/dist/api/app/bsky/actor/searchActors.js +2 -1
  10. package/dist/api/app/bsky/actor/searchActors.js.map +1 -1
  11. package/dist/api/app/bsky/bookmark/getBookmarks.d.ts.map +1 -1
  12. package/dist/api/app/bsky/bookmark/getBookmarks.js +1 -1
  13. package/dist/api/app/bsky/bookmark/getBookmarks.js.map +1 -1
  14. package/dist/api/app/bsky/contact/getMatches.d.ts.map +1 -1
  15. package/dist/api/app/bsky/contact/getMatches.js +1 -1
  16. package/dist/api/app/bsky/contact/getMatches.js.map +1 -1
  17. package/dist/api/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
  18. package/dist/api/app/bsky/feed/getAuthorFeed.js +2 -1
  19. package/dist/api/app/bsky/feed/getAuthorFeed.js.map +1 -1
  20. package/dist/api/app/bsky/feed/getLikes.d.ts.map +1 -1
  21. package/dist/api/app/bsky/feed/getLikes.js +2 -1
  22. package/dist/api/app/bsky/feed/getLikes.js.map +1 -1
  23. package/dist/api/app/bsky/feed/getPostThread.d.ts.map +1 -1
  24. package/dist/api/app/bsky/feed/getPostThread.js +2 -1
  25. package/dist/api/app/bsky/feed/getPostThread.js.map +1 -1
  26. package/dist/api/app/bsky/feed/getQuotes.d.ts.map +1 -1
  27. package/dist/api/app/bsky/feed/getQuotes.js +2 -1
  28. package/dist/api/app/bsky/feed/getQuotes.js.map +1 -1
  29. package/dist/api/app/bsky/feed/getRepostedBy.d.ts.map +1 -1
  30. package/dist/api/app/bsky/feed/getRepostedBy.js +2 -1
  31. package/dist/api/app/bsky/feed/getRepostedBy.js.map +1 -1
  32. package/dist/api/app/bsky/feed/getTimeline.d.ts +2 -4
  33. package/dist/api/app/bsky/feed/getTimeline.d.ts.map +1 -1
  34. package/dist/api/app/bsky/feed/getTimeline.js +1 -1
  35. package/dist/api/app/bsky/feed/getTimeline.js.map +1 -1
  36. package/dist/api/app/bsky/feed/searchPosts.d.ts.map +1 -1
  37. package/dist/api/app/bsky/feed/searchPosts.js +2 -1
  38. package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
  39. package/dist/api/app/bsky/graph/getActorStarterPacks.d.ts.map +1 -1
  40. package/dist/api/app/bsky/graph/getActorStarterPacks.js +2 -1
  41. package/dist/api/app/bsky/graph/getActorStarterPacks.js.map +1 -1
  42. package/dist/api/app/bsky/graph/getBlocks.d.ts.map +1 -1
  43. package/dist/api/app/bsky/graph/getBlocks.js +1 -1
  44. package/dist/api/app/bsky/graph/getBlocks.js.map +1 -1
  45. package/dist/api/app/bsky/graph/getFollowers.d.ts.map +1 -1
  46. package/dist/api/app/bsky/graph/getFollowers.js +2 -1
  47. package/dist/api/app/bsky/graph/getFollowers.js.map +1 -1
  48. package/dist/api/app/bsky/graph/getFollows.d.ts.map +1 -1
  49. package/dist/api/app/bsky/graph/getFollows.js +2 -1
  50. package/dist/api/app/bsky/graph/getFollows.js.map +1 -1
  51. package/dist/api/app/bsky/graph/getKnownFollowers.d.ts.map +1 -1
  52. package/dist/api/app/bsky/graph/getKnownFollowers.js +1 -1
  53. package/dist/api/app/bsky/graph/getKnownFollowers.js.map +1 -1
  54. package/dist/api/app/bsky/graph/getList.d.ts.map +1 -1
  55. package/dist/api/app/bsky/graph/getList.js +2 -1
  56. package/dist/api/app/bsky/graph/getList.js.map +1 -1
  57. package/dist/api/app/bsky/graph/getListBlocks.d.ts.map +1 -1
  58. package/dist/api/app/bsky/graph/getListBlocks.js +1 -1
  59. package/dist/api/app/bsky/graph/getListBlocks.js.map +1 -1
  60. package/dist/api/app/bsky/graph/getListMutes.d.ts.map +1 -1
  61. package/dist/api/app/bsky/graph/getListMutes.js +1 -1
  62. package/dist/api/app/bsky/graph/getListMutes.js.map +1 -1
  63. package/dist/api/app/bsky/graph/getLists.d.ts.map +1 -1
  64. package/dist/api/app/bsky/graph/getLists.js +2 -1
  65. package/dist/api/app/bsky/graph/getLists.js.map +1 -1
  66. package/dist/api/app/bsky/graph/getListsWithMembership.js +1 -1
  67. package/dist/api/app/bsky/graph/getListsWithMembership.js.map +1 -1
  68. package/dist/api/app/bsky/graph/getMutes.d.ts.map +1 -1
  69. package/dist/api/app/bsky/graph/getMutes.js +1 -1
  70. package/dist/api/app/bsky/graph/getMutes.js.map +1 -1
  71. package/dist/api/app/bsky/graph/getStarterPack.d.ts.map +1 -1
  72. package/dist/api/app/bsky/graph/getStarterPack.js +2 -1
  73. package/dist/api/app/bsky/graph/getStarterPack.js.map +1 -1
  74. package/dist/api/app/bsky/graph/getStarterPacks.d.ts.map +1 -1
  75. package/dist/api/app/bsky/graph/getStarterPacks.js +2 -1
  76. package/dist/api/app/bsky/graph/getStarterPacks.js.map +1 -1
  77. package/dist/api/app/bsky/graph/getStarterPacksWithMembership.js +1 -1
  78. package/dist/api/app/bsky/graph/getStarterPacksWithMembership.js.map +1 -1
  79. package/dist/api/app/bsky/graph/searchStarterPacks.d.ts.map +1 -1
  80. package/dist/api/app/bsky/graph/searchStarterPacks.js +2 -1
  81. package/dist/api/app/bsky/graph/searchStarterPacks.js.map +1 -1
  82. package/dist/api/app/bsky/notification/listActivitySubscriptions.js +1 -1
  83. package/dist/api/app/bsky/notification/listActivitySubscriptions.js.map +1 -1
  84. package/dist/api/app/bsky/notification/listNotifications.d.ts.map +1 -1
  85. package/dist/api/app/bsky/notification/listNotifications.js +1 -1
  86. package/dist/api/app/bsky/notification/listNotifications.js.map +1 -1
  87. package/dist/api/app/bsky/unspecced/getPostThreadOtherV2.d.ts.map +1 -1
  88. package/dist/api/app/bsky/unspecced/getPostThreadOtherV2.js +2 -1
  89. package/dist/api/app/bsky/unspecced/getPostThreadOtherV2.js.map +1 -1
  90. package/dist/api/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -1
  91. package/dist/api/app/bsky/unspecced/getPostThreadV2.js +2 -1
  92. package/dist/api/app/bsky/unspecced/getPostThreadV2.js.map +1 -1
  93. package/dist/auth-verifier.d.ts +1 -0
  94. package/dist/auth-verifier.d.ts.map +1 -1
  95. package/dist/auth-verifier.js +1 -0
  96. package/dist/auth-verifier.js.map +1 -1
  97. package/dist/data-plane/client/index.d.ts.map +1 -1
  98. package/dist/data-plane/client/index.js +2 -0
  99. package/dist/data-plane/client/index.js.map +1 -1
  100. package/dist/data-plane/client/util.d.ts +2 -1
  101. package/dist/data-plane/client/util.d.ts.map +1 -1
  102. package/dist/data-plane/client/util.js +6 -1
  103. package/dist/data-plane/client/util.js.map +1 -1
  104. package/dist/data-plane/client/util.test.d.ts +2 -0
  105. package/dist/data-plane/client/util.test.d.ts.map +1 -0
  106. package/dist/data-plane/client/util.test.js +33 -0
  107. package/dist/data-plane/client/util.test.js.map +1 -0
  108. package/dist/data-plane/server/indexing/plugins/feed-generator.js +2 -1
  109. package/dist/data-plane/server/indexing/plugins/feed-generator.js.map +1 -1
  110. package/dist/data-plane/server/indexing/plugins/list.js +2 -1
  111. package/dist/data-plane/server/indexing/plugins/list.js.map +1 -1
  112. package/dist/data-plane/server/indexing/plugins/post.js +3 -3
  113. package/dist/data-plane/server/indexing/plugins/post.js.map +1 -1
  114. package/dist/data-plane/server/indexing/plugins/profile.js +3 -2
  115. package/dist/data-plane/server/indexing/plugins/profile.js.map +1 -1
  116. package/dist/hydration/hydrator.d.ts +5 -0
  117. package/dist/hydration/hydrator.d.ts.map +1 -1
  118. package/dist/hydration/hydrator.js +8 -0
  119. package/dist/hydration/hydrator.js.map +1 -1
  120. package/dist/hydration/util.d.ts +13 -7
  121. package/dist/hydration/util.d.ts.map +1 -1
  122. package/dist/hydration/util.js +30 -10
  123. package/dist/hydration/util.js.map +1 -1
  124. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts +1 -0
  125. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts.map +1 -1
  126. package/dist/lexicons/app/bsky/actor/defs.defs.js +1 -0
  127. package/dist/lexicons/app/bsky/actor/defs.defs.js.map +1 -1
  128. package/dist/lexicons/app/bsky/actor/profile.defs.d.ts.map +1 -1
  129. package/dist/lexicons/app/bsky/actor/profile.defs.js +2 -10
  130. package/dist/lexicons/app/bsky/actor/profile.defs.js.map +1 -1
  131. package/dist/lexicons/app/bsky/embed/external.defs.d.ts.map +1 -1
  132. package/dist/lexicons/app/bsky/embed/external.defs.js +1 -1
  133. package/dist/lexicons/app/bsky/embed/external.defs.js.map +1 -1
  134. package/dist/lexicons/app/bsky/embed/images.defs.d.ts +3 -0
  135. package/dist/lexicons/app/bsky/embed/images.defs.d.ts.map +1 -1
  136. package/dist/lexicons/app/bsky/embed/images.defs.js +1 -5
  137. package/dist/lexicons/app/bsky/embed/images.defs.js.map +1 -1
  138. package/dist/lexicons/app/bsky/embed/video.defs.d.ts.map +1 -1
  139. package/dist/lexicons/app/bsky/embed/video.defs.js +2 -6
  140. package/dist/lexicons/app/bsky/embed/video.defs.js.map +1 -1
  141. package/dist/lexicons/app/bsky/feed/generator.defs.d.ts.map +1 -1
  142. package/dist/lexicons/app/bsky/feed/generator.defs.js +1 -5
  143. package/dist/lexicons/app/bsky/feed/generator.defs.js.map +1 -1
  144. package/dist/lexicons/app/bsky/graph/list.defs.d.ts.map +1 -1
  145. package/dist/lexicons/app/bsky/graph/list.defs.js +1 -5
  146. package/dist/lexicons/app/bsky/graph/list.defs.js.map +1 -1
  147. package/dist/lexicons/app/bsky/video/defs.defs.js +1 -1
  148. package/dist/lexicons/app/bsky/video/defs.defs.js.map +1 -1
  149. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts +2 -6
  150. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.d.ts.map +1 -1
  151. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js +1 -1
  152. package/dist/lexicons/com/atproto/repo/uploadBlob.defs.js.map +1 -1
  153. package/dist/views/index.d.ts.map +1 -1
  154. package/dist/views/index.js +13 -8
  155. package/dist/views/index.js.map +1 -1
  156. package/dist/views/util.d.ts +1 -2
  157. package/dist/views/util.d.ts.map +1 -1
  158. package/dist/views/util.js +1 -5
  159. package/dist/views/util.js.map +1 -1
  160. package/package.json +10 -10
  161. package/src/api/app/bsky/actor/getProfile.ts +3 -1
  162. package/src/api/app/bsky/actor/getProfiles.ts +3 -1
  163. package/src/api/app/bsky/actor/searchActors.ts +3 -1
  164. package/src/api/app/bsky/bookmark/getBookmarks.ts +3 -6
  165. package/src/api/app/bsky/contact/getMatches.ts +3 -6
  166. package/src/api/app/bsky/feed/getAuthorFeed.ts +3 -1
  167. package/src/api/app/bsky/feed/getLikes.ts +3 -1
  168. package/src/api/app/bsky/feed/getPostThread.ts +2 -1
  169. package/src/api/app/bsky/feed/getQuotes.ts +3 -1
  170. package/src/api/app/bsky/feed/getRepostedBy.ts +3 -1
  171. package/src/api/app/bsky/feed/getTimeline.ts +3 -6
  172. package/src/api/app/bsky/feed/searchPosts.ts +3 -1
  173. package/src/api/app/bsky/graph/getActorStarterPacks.ts +3 -1
  174. package/src/api/app/bsky/graph/getBlocks.ts +3 -6
  175. package/src/api/app/bsky/graph/getFollowers.ts +3 -1
  176. package/src/api/app/bsky/graph/getFollows.ts +3 -1
  177. package/src/api/app/bsky/graph/getKnownFollowers.ts +3 -6
  178. package/src/api/app/bsky/graph/getList.ts +3 -1
  179. package/src/api/app/bsky/graph/getListBlocks.ts +3 -6
  180. package/src/api/app/bsky/graph/getListMutes.ts +3 -6
  181. package/src/api/app/bsky/graph/getLists.ts +3 -1
  182. package/src/api/app/bsky/graph/getListsWithMembership.ts +3 -3
  183. package/src/api/app/bsky/graph/getMutes.ts +3 -6
  184. package/src/api/app/bsky/graph/getStarterPack.ts +3 -1
  185. package/src/api/app/bsky/graph/getStarterPacks.ts +3 -1
  186. package/src/api/app/bsky/graph/getStarterPacksWithMembership.ts +3 -3
  187. package/src/api/app/bsky/graph/searchStarterPacks.ts +3 -1
  188. package/src/api/app/bsky/notification/listActivitySubscriptions.ts +3 -3
  189. package/src/api/app/bsky/notification/listNotifications.ts +3 -6
  190. package/src/api/app/bsky/unspecced/getPostThreadOtherV2.ts +2 -1
  191. package/src/api/app/bsky/unspecced/getPostThreadV2.ts +2 -1
  192. package/src/auth-verifier.ts +1 -0
  193. package/src/data-plane/client/index.ts +2 -0
  194. package/src/data-plane/client/util.test.ts +39 -0
  195. package/src/data-plane/client/util.ts +9 -1
  196. package/src/data-plane/server/indexing/plugins/feed-generator.ts +2 -2
  197. package/src/data-plane/server/indexing/plugins/list.ts +2 -2
  198. package/src/data-plane/server/indexing/plugins/post.ts +4 -4
  199. package/src/data-plane/server/indexing/plugins/profile.ts +3 -3
  200. package/src/hydration/hydrator.ts +8 -0
  201. package/src/hydration/util.ts +40 -21
  202. package/src/views/index.ts +19 -15
  203. package/src/views/util.ts +1 -5
  204. package/tests/views/__snapshots__/profile.test.ts.snap +3 -0
  205. package/tests/views/blocks.test.ts +69 -0
  206. package/tests/views/profile.test.ts +77 -0
  207. package/tsconfig.build.tsbuildinfo +1 -1
@@ -22,12 +22,14 @@ export default function (server: Server, ctx: AppContext) {
22
22
  server.add(app.bsky.graph.getStarterPacks, {
23
23
  auth: ctx.authVerifier.standardOptional,
24
24
  handler: async ({ auth, params, req }) => {
25
- const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth)
25
+ const { viewer, includeTakedowns, skipViewerBlocks } =
26
+ ctx.authVerifier.parseCreds(auth)
26
27
  const labelers = ctx.reqLabelers(req)
27
28
  const hydrateCtx = await ctx.hydrator.createContext({
28
29
  viewer,
29
30
  labelers,
30
31
  includeTakedowns,
32
+ skipViewerBlocks,
31
33
  })
32
34
 
33
35
  const result = await getStarterPacks({ ...params, hydrateCtx }, ctx)
@@ -3,7 +3,7 @@ import { AtUriString, DidString } from '@atproto/syntax'
3
3
  import { InvalidRequestError, Server } from '@atproto/xrpc-server'
4
4
  import { AppContext } from '../../../../context'
5
5
  import {
6
- HydrateCtx,
6
+ HydrateCtxWithViewer,
7
7
  Hydrator,
8
8
  mergeManyStates,
9
9
  } from '../../../../hydration/hydrator'
@@ -35,7 +35,7 @@ export default function (server: Server, ctx: AppContext) {
35
35
  viewer,
36
36
  })
37
37
  const result = await getStarterPacksWithMembership(
38
- { ...params, hydrateCtx: hydrateCtx.copy({ viewer }) },
38
+ { ...params, hydrateCtx },
39
39
  ctx,
40
40
  )
41
41
 
@@ -126,7 +126,7 @@ type Context = {
126
126
  }
127
127
 
128
128
  type Params = app.bsky.graph.getStarterPacksWithMembership.$Params & {
129
- hydrateCtx: HydrateCtx & { viewer: string }
129
+ hydrateCtx: HydrateCtxWithViewer
130
130
  }
131
131
 
132
132
  type SkeletonState = {
@@ -27,12 +27,14 @@ export default function (server: Server, ctx: AppContext) {
27
27
  server.add(app.bsky.graph.searchStarterPacks, {
28
28
  auth: ctx.authVerifier.standardOptional,
29
29
  handler: async ({ auth, params, req }) => {
30
- const { viewer, includeTakedowns } = ctx.authVerifier.parseCreds(auth)
30
+ const { viewer, includeTakedowns, skipViewerBlocks } =
31
+ ctx.authVerifier.parseCreds(auth)
31
32
  const labelers = ctx.reqLabelers(req)
32
33
  const hydrateCtx = await ctx.hydrator.createContext({
33
34
  viewer,
34
35
  labelers,
35
36
  includeTakedowns,
37
+ skipViewerBlocks,
36
38
  })
37
39
  const results = await searchStarterPacks({ ...params, hydrateCtx }, ctx)
38
40
  return {
@@ -2,7 +2,7 @@ import { mapDefined } from '@atproto/common'
2
2
  import { DidString } from '@atproto/syntax'
3
3
  import { Server } from '@atproto/xrpc-server'
4
4
  import { AppContext } from '../../../../context'
5
- import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator'
5
+ import { HydrateCtxWithViewer, Hydrator } from '../../../../hydration/hydrator'
6
6
  import { app } from '../../../../lexicons/index.js'
7
7
  import {
8
8
  HydrationFnInput,
@@ -32,7 +32,7 @@ export default function (server: Server, ctx: AppContext) {
32
32
  })
33
33
 
34
34
  const result = await listActivitySubscriptions(
35
- { ...params, hydrateCtx: hydrateCtx.copy({ viewer }) },
35
+ { ...params, hydrateCtx },
36
36
  ctx,
37
37
  )
38
38
 
@@ -103,7 +103,7 @@ type Context = {
103
103
  }
104
104
 
105
105
  type Params = app.bsky.notification.listActivitySubscriptions.$Params & {
106
- hydrateCtx: HydrateCtx & { viewer: string }
106
+ hydrateCtx: HydrateCtxWithViewer
107
107
  }
108
108
 
109
109
  type SkeletonState = {
@@ -3,7 +3,7 @@ import { AtUriString, DatetimeString, DidString } from '@atproto/syntax'
3
3
  import { InvalidRequestError, Server } from '@atproto/xrpc-server'
4
4
  import { ServerConfig } from '../../../../config'
5
5
  import { AppContext } from '../../../../context'
6
- import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator'
6
+ import { HydrateCtxWithViewer, Hydrator } from '../../../../hydration/hydrator'
7
7
  import { app } from '../../../../lexicons/index.js'
8
8
  import {
9
9
  HydrationFnInput,
@@ -31,10 +31,7 @@ export default function (server: Server, ctx: AppContext) {
31
31
  const viewer = auth.credentials.iss
32
32
  const labelers = ctx.reqLabelers(req)
33
33
  const hydrateCtx = await ctx.hydrator.createContext({ labelers, viewer })
34
- const result = await listNotifications(
35
- { ...params, hydrateCtx: hydrateCtx.copy({ viewer }) },
36
- ctx,
37
- )
34
+ const result = await listNotifications({ ...params, hydrateCtx }, ctx)
38
35
  return {
39
36
  encoding: 'application/json',
40
37
  body: result,
@@ -254,7 +251,7 @@ type Context = {
254
251
  }
255
252
 
256
253
  type Params = app.bsky.notification.listNotifications.$Params & {
257
- hydrateCtx: HydrateCtx & { viewer: string }
254
+ hydrateCtx: HydrateCtxWithViewer
258
255
  }
259
256
 
260
257
  type SkeletonState = {
@@ -35,7 +35,7 @@ export default function (server: Server, ctx: AppContext) {
35
35
  server.add(app.bsky.unspecced.getPostThreadOtherV2, {
36
36
  auth: ctx.authVerifier.optionalStandardOrRole,
37
37
  handler: async ({ params, auth, req }) => {
38
- const { viewer, includeTakedowns, include3pBlocks } =
38
+ const { viewer, includeTakedowns, include3pBlocks, skipViewerBlocks } =
39
39
  ctx.authVerifier.parseCreds(auth)
40
40
  const labelers = ctx.reqLabelers(req)
41
41
  const hydrateCtx = await ctx.hydrator.createContext({
@@ -43,6 +43,7 @@ export default function (server: Server, ctx: AppContext) {
43
43
  viewer,
44
44
  includeTakedowns,
45
45
  include3pBlocks,
46
+ skipViewerBlocks,
46
47
  })
47
48
 
48
49
  return {
@@ -26,7 +26,7 @@ export default function (server: Server, ctx: AppContext) {
26
26
  server.add(app.bsky.unspecced.getPostThreadV2, {
27
27
  auth: ctx.authVerifier.optionalStandardOrRole,
28
28
  handler: async ({ params, auth, req }) => {
29
- const { viewer, includeTakedowns, include3pBlocks } =
29
+ const { viewer, includeTakedowns, include3pBlocks, skipViewerBlocks } =
30
30
  ctx.authVerifier.parseCreds(auth)
31
31
  const labelers = ctx.reqLabelers(req)
32
32
  const features = ctx.featureGatesClient.scope(
@@ -42,6 +42,7 @@ export default function (server: Server, ctx: AppContext) {
42
42
  viewer,
43
43
  includeTakedowns,
44
44
  include3pBlocks,
45
+ skipViewerBlocks,
45
46
  features,
46
47
  })
47
48
 
@@ -411,6 +411,7 @@ export class AuthVerifier {
411
411
  include3pBlocks: includeTakedownsAnd3pBlocks,
412
412
  canPerformTakedown,
413
413
  isModService,
414
+ skipViewerBlocks: isModService && viewer !== null,
414
415
  }
415
416
  }
416
417
  }
@@ -10,6 +10,7 @@ import {
10
10
  import { createGrpcTransport } from '@connectrpc/connect-node'
11
11
  import { Service } from '../../proto/bsky_connect'
12
12
  import { HostList } from './hosts'
13
+ import { callerInterceptor } from './util'
13
14
 
14
15
  export * from './hosts'
15
16
  export * from './util'
@@ -105,6 +106,7 @@ const createBaseClient = (
105
106
  httpVersion,
106
107
  acceptCompression: [],
107
108
  nodeOptions: { rejectUnauthorized },
109
+ interceptors: [callerInterceptor('appview')],
108
110
  })
109
111
  return createPromiseClient(Service, transport)
110
112
  }
@@ -0,0 +1,39 @@
1
+ /// <reference types="jest" />
2
+ import { callerInterceptor } from './util'
3
+
4
+ describe('callerInterceptor', () => {
5
+ it('sets x-atlantis-caller header on the request', async () => {
6
+ const interceptor = callerInterceptor('appview')
7
+ const expectedResponse = { status: 'ok' }
8
+ const next = jest.fn().mockResolvedValue(expectedResponse)
9
+
10
+ const req = { header: new Headers() }
11
+ const handler = interceptor(next)
12
+ const res = await handler(req as any)
13
+
14
+ expect(req.header.get('x-atlantis-caller')).toBe('appview')
15
+ expect(next).toHaveBeenCalledWith(req)
16
+ expect(res).toBe(expectedResponse)
17
+ })
18
+
19
+ it('uses the provided caller value', async () => {
20
+ const interceptor = callerInterceptor('feed-generator')
21
+ const next = jest.fn().mockResolvedValue({})
22
+
23
+ const req = { header: new Headers() }
24
+ await interceptor(next)(req as any)
25
+
26
+ expect(req.header.get('x-atlantis-caller')).toBe('feed-generator')
27
+ })
28
+
29
+ it('does not overwrite other existing headers', async () => {
30
+ const interceptor = callerInterceptor('appview')
31
+ const next = jest.fn().mockResolvedValue({})
32
+
33
+ const req = { header: new Headers({ 'x-other': 'value' }) }
34
+ await interceptor(next)(req as any)
35
+
36
+ expect(req.header.get('x-atlantis-caller')).toBe('appview')
37
+ expect(req.header.get('x-other')).toBe('value')
38
+ })
39
+ })
@@ -1,7 +1,15 @@
1
- import { Code, ConnectError } from '@connectrpc/connect'
1
+ import { Code, ConnectError, Interceptor } from '@connectrpc/connect'
2
2
  import * as ui8 from 'uint8arrays'
3
3
  import { getDidKeyFromMultibase } from '@atproto/identity'
4
4
 
5
+ export const callerInterceptor =
6
+ (caller: string): Interceptor =>
7
+ (next) =>
8
+ (req) => {
9
+ req.header.set('x-atlantis-caller', caller)
10
+ return next(req)
11
+ }
12
+
5
13
  export const isDataplaneError = (
6
14
  err: unknown,
7
15
  code?: Code,
@@ -1,5 +1,5 @@
1
1
  import { Selectable } from 'kysely'
2
- import { Cid } from '@atproto/lex'
2
+ import { Cid, getBlobCidString } from '@atproto/lex'
3
3
  import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
4
4
  import { app } from '../../../../lexicons'
5
5
  import { BackgroundQueue } from '../../background'
@@ -28,7 +28,7 @@ const insertFn = async (
28
28
  descriptionFacets: obj.descriptionFacets
29
29
  ? JSON.stringify(obj.descriptionFacets)
30
30
  : undefined,
31
- avatarCid: obj.avatar?.ref.toString(),
31
+ avatarCid: getBlobCidString(obj.avatar),
32
32
  createdAt: normalizeDatetimeAlways(obj.createdAt),
33
33
  indexedAt: timestamp,
34
34
  })
@@ -1,5 +1,5 @@
1
1
  import { Selectable } from 'kysely'
2
- import { Cid } from '@atproto/lex'
2
+ import { Cid, getBlobCidString } from '@atproto/lex'
3
3
  import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
4
4
  import { app } from '../../../../lexicons'
5
5
  import { BackgroundQueue } from '../../background'
@@ -28,7 +28,7 @@ const insertFn = async (
28
28
  descriptionFacets: obj.descriptionFacets
29
29
  ? JSON.stringify(obj.descriptionFacets)
30
30
  : undefined,
31
- avatarCid: obj.avatar?.ref.toString(),
31
+ avatarCid: getBlobCidString(obj.avatar),
32
32
  createdAt: normalizeDatetimeAlways(obj.createdAt),
33
33
  indexedAt: timestamp,
34
34
  })
@@ -1,5 +1,5 @@
1
1
  import { Insertable, Selectable, sql } from 'kysely'
2
- import { $Typed, Cid, lexParse } from '@atproto/lex'
2
+ import { $Typed, Cid, getBlobCidString, lexParse } from '@atproto/lex'
3
3
  import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
4
4
  import { app } from '../../../../lexicons'
5
5
  import {
@@ -152,7 +152,7 @@ const insertFn = async (
152
152
  const imagesEmbed = images.map((img, i) => ({
153
153
  postUri: uri.toString(),
154
154
  position: i,
155
- imageCid: img.image.ref.toString(),
155
+ imageCid: getBlobCidString(img.image),
156
156
  alt: img.alt,
157
157
  }))
158
158
  embeds.push(imagesEmbed)
@@ -164,7 +164,7 @@ const insertFn = async (
164
164
  uri: external.uri,
165
165
  title: external.title,
166
166
  description: external.description,
167
- thumbCid: external.thumb?.ref.toString() || null,
167
+ thumbCid: getBlobCidString(external.thumb) || null,
168
168
  }
169
169
  embeds.push(externalEmbed)
170
170
  await db.insertInto('post_embed_external').values(externalEmbed).execute()
@@ -231,7 +231,7 @@ const insertFn = async (
231
231
  const { video } = postEmbed
232
232
  const videoEmbed = {
233
233
  postUri: uri.toString(),
234
- videoCid: video.ref.toString(),
234
+ videoCid: getBlobCidString(video),
235
235
  // @NOTE: alt is required for image but not for video on the lexicon.
236
236
  alt: postEmbed.alt ?? null,
237
237
  }
@@ -1,4 +1,4 @@
1
- import { Cid } from '@atproto/lex'
1
+ import { Cid, getBlobCidString } from '@atproto/lex'
2
2
  import { AtUri } from '@atproto/syntax'
3
3
  import { app } from '../../../../lexicons'
4
4
  import { BackgroundQueue } from '../../background'
@@ -24,8 +24,8 @@ const insertFn = async (
24
24
  creator: uri.host,
25
25
  displayName: obj.displayName,
26
26
  description: obj.description,
27
- avatarCid: obj.avatar?.ref.toString(),
28
- bannerCid: obj.banner?.ref.toString(),
27
+ avatarCid: getBlobCidString(obj.avatar),
28
+ bannerCid: getBlobCidString(obj.banner),
29
29
  joinedViaStarterPackUri: obj.joinedViaStarterPack?.uri,
30
30
  createdAt: obj.createdAt ?? new Date().toISOString(),
31
31
  indexedAt: timestamp,
@@ -85,6 +85,7 @@ export class HydrateCtx {
85
85
  includeTakedowns = this.vals.includeTakedowns
86
86
  overrideIncludeTakedownsForActor = this.vals.overrideIncludeTakedownsForActor
87
87
  include3pBlocks = this.vals.include3pBlocks
88
+ skipViewerBlocks = this.vals.skipViewerBlocks
88
89
  includeDebugField = this.vals.includeDebugField
89
90
  features = this.vals.features
90
91
  constructor(private vals: HydrateCtxVals) {}
@@ -98,12 +99,15 @@ export class HydrateCtx {
98
99
  }
99
100
  }
100
101
 
102
+ export type HydrateCtxWithViewer = HydrateCtx & { viewer: string }
103
+
101
104
  export type HydrateCtxVals = {
102
105
  labelers: ParsedLabelers
103
106
  viewer: DidString | null
104
107
  includeTakedowns?: boolean
105
108
  overrideIncludeTakedownsForActor?: boolean
106
109
  include3pBlocks?: boolean
110
+ skipViewerBlocks?: boolean
107
111
  includeDebugField?: boolean
108
112
  features: ScopedFeatureGatesClient
109
113
  }
@@ -1357,6 +1361,7 @@ export class Hydrator {
1357
1361
  viewer: vals.viewer,
1358
1362
  includeTakedowns: vals.includeTakedowns,
1359
1363
  include3pBlocks: vals.include3pBlocks,
1364
+ skipViewerBlocks: vals.skipViewerBlocks,
1360
1365
  includeDebugField,
1361
1366
  // create default anonymous scope
1362
1367
  features: vals.features || this.config.featureGatesClient.scope({}),
@@ -1425,6 +1430,9 @@ const labelSubjectsForDid = (dids: DidString[]) => {
1425
1430
  ...dids.map((did) =>
1426
1431
  AtUri.make(did, app.bsky.actor.profile.$type, 'self').toString(),
1427
1432
  ),
1433
+ ...dids.map((did) =>
1434
+ AtUri.make(did, app.bsky.actor.status.$type, 'self').toString(),
1435
+ ),
1428
1436
  ]
1429
1437
  }
1430
1438
 
@@ -2,19 +2,24 @@ import { Timestamp } from '@bufbuild/protobuf'
2
2
  import {
3
3
  AtUriString,
4
4
  Cid,
5
- Infer,
5
+ InferInput,
6
+ InferOutput,
6
7
  LexParseOptions,
7
8
  LexValue,
8
9
  RecordSchema,
9
10
  Schema,
10
11
  TypedLexMap,
11
12
  ValidateOptions,
12
- lexParse,
13
+ lexParseJsonBytes,
13
14
  parseCidSafe,
14
15
  } from '@atproto/lex'
15
16
  import { AtUri } from '@atproto/syntax'
16
17
  import { Record as RecordEntry } from '../proto/bsky_pb'
17
18
 
19
+ const PARSE_OPTIONS: LexParseOptions & ValidateOptions = {
20
+ strict: false,
21
+ }
22
+
18
23
  export class HydrationMap<K, T> extends Map<K, T | null> implements Merges {
19
24
  merge(map: HydrationMap<K, T>): this {
20
25
  for (const [key, val] of map) {
@@ -28,8 +33,8 @@ export interface Merges {
28
33
  merge<T extends this>(map: T): this
29
34
  }
30
35
 
31
- export type RecordInfo<T extends { $type: string }> = {
32
- record: T & TypedLexMap
36
+ export type RecordInfo<T extends TypedLexMap> = {
37
+ record: T
33
38
  cid: string
34
39
  sortedAt: Date
35
40
  indexedAt: Date
@@ -70,17 +75,32 @@ export function parseRecord<TSchema extends RecordSchema>(
70
75
  recordSchema: TSchema,
71
76
  recordEntry: RecordEntry,
72
77
  includeTakedowns: boolean,
73
- ): RecordInfo<Infer<TSchema>> | undefined {
78
+ ): RecordInfo<InferInput<TSchema>> | undefined {
74
79
  if (!includeTakedowns && recordEntry.takenDown) {
75
80
  return undefined
76
81
  }
77
82
 
78
83
  const cid = recordEntry.cid
79
- if (!cid) return
84
+ if (!cid) {
85
+ return undefined
86
+ }
87
+
88
+ if (recordEntry.record.byteLength === 0) {
89
+ return undefined
90
+ }
80
91
 
81
- const record = parseJsonBytes(recordSchema, recordEntry.record)
92
+ const record = lexParseJsonBytes(recordEntry.record, PARSE_OPTIONS)
82
93
  if (!record) {
83
- return
94
+ return undefined
95
+ }
96
+
97
+ // @NOTE We cannot use parse mode here. We must return the original to ensure
98
+ // that the caller gets the same data as what is stored in the PDS (in case of
99
+ // records). This is important because the receiver of the data should be able
100
+ // to compute the right record CID.
101
+
102
+ if (!recordSchema.$matches(record, PARSE_OPTIONS)) {
103
+ return undefined
84
104
  }
85
105
 
86
106
  return {
@@ -92,23 +112,22 @@ export function parseRecord<TSchema extends RecordSchema>(
92
112
  }
93
113
  }
94
114
 
115
+ /**
116
+ * Decodes binary data containing a JSON representation of a Lex value, and
117
+ * validates it against the provided schema, in parse mode (i.e., allowing
118
+ * coercion & defaults).
119
+ *
120
+ * Returns undefined if the input is empty (from dataplane's empty value
121
+ * convention), or if the validation fails.
122
+ */
95
123
  export const parseJsonBytes = <TSchema extends Schema<LexValue>>(
96
124
  schema: TSchema,
97
125
  bytes: Uint8Array | undefined,
98
- options: LexParseOptions & ValidateOptions = { strict: false },
99
- ): Infer<TSchema> | undefined => {
126
+ ): InferOutput<TSchema> | undefined => {
100
127
  if (!bytes || bytes.byteLength === 0) return undefined
101
-
102
- // @NOTE Buffer.from(bytes) creates a copy of the ArrayBuffer
103
- const jsonBuffer = Buffer.from(
104
- bytes.buffer,
105
- bytes.byteOffset,
106
- bytes.byteLength,
107
- )
108
- const jsonString = jsonBuffer.toString('utf8')
109
-
110
- const value = lexParse(jsonString, options)
111
- return schema.ifMatches(value, options)
128
+ const value = lexParseJsonBytes(bytes, PARSE_OPTIONS)
129
+ const result = schema.safeParse(value, PARSE_OPTIONS)
130
+ return result.success ? result.value : undefined
112
131
  }
113
132
 
114
133
  export const parseString = <T extends string | undefined>(
@@ -1,5 +1,11 @@
1
1
  import { HOUR, MINUTE, mapDefined } from '@atproto/common'
2
- import { $Typed, Un$Typed, Unknown$TypedObject, UriString } from '@atproto/lex'
2
+ import {
3
+ $Typed,
4
+ Un$Typed,
5
+ Unknown$TypedObject,
6
+ UriString,
7
+ getBlobCidString,
8
+ } from '@atproto/lex'
3
9
  import {
4
10
  AtUri,
5
11
  AtUriString,
@@ -104,12 +110,7 @@ import {
104
110
  isSelfLabelsType,
105
111
  isVideoEmbedType,
106
112
  } from './types.js'
107
- import {
108
- VideoUriBuilder,
109
- cidFromBlobJson,
110
- parsePostgate,
111
- parseThreadGate,
112
- } from './util'
113
+ import { VideoUriBuilder, parsePostgate, parseThreadGate } from './util'
113
114
 
114
115
  const notificationDeletedRecord =
115
116
  app.bsky.notification.defs.recordDeleted.$build({})
@@ -169,6 +170,7 @@ export class Views {
169
170
  }
170
171
 
171
172
  viewerBlockExists(did: DidString, state: HydrationState): boolean {
173
+ if (state.ctx?.skipViewerBlocks) return false
172
174
  const viewer = state.profileViewers?.get(did)
173
175
  if (!viewer) return false
174
176
  return !!(
@@ -277,7 +279,7 @@ export class Views {
277
279
  ? this.imgUriBuilder.getPresetUri(
278
280
  'banner',
279
281
  did,
280
- cidFromBlobJson(actor.profile.banner),
282
+ getBlobCidString(actor.profile.banner),
281
283
  )
282
284
  : undefined,
283
285
  followersCount: profileAggs?.followers ?? 0,
@@ -356,7 +358,7 @@ export class Views {
356
358
  ? this.imgUriBuilder.getPresetUri(
357
359
  'avatar',
358
360
  did,
359
- cidFromBlobJson(actor.profile.avatar),
361
+ getBlobCidString(actor.profile.avatar),
360
362
  )
361
363
  : undefined,
362
364
  // associated.feedgens and associated.lists info not necessarily included
@@ -576,6 +578,7 @@ export class Views {
576
578
  }
577
579
 
578
580
  const uri = AtUri.make(did, app.bsky.actor.status.$nsid, 'self').toString()
581
+ const labels = state.labels?.getBySubject(uri)
579
582
 
580
583
  const minDuration = 5 * MINUTE
581
584
  const maxDuration = 4 * HOUR
@@ -602,6 +605,7 @@ export class Views {
602
605
  record.embed && isExternalEmbedType(record.embed)
603
606
  ? this.externalEmbed(did, record.embed)
604
607
  : undefined,
608
+ labels,
605
609
  expiresAt,
606
610
  isActive,
607
611
  }
@@ -672,7 +676,7 @@ export class Views {
672
676
  ? this.imgUriBuilder.getPresetUri(
673
677
  'avatar',
674
678
  creator,
675
- cidFromBlobJson(list.record.avatar),
679
+ getBlobCidString(list.record.avatar),
676
680
  )
677
681
  : undefined,
678
682
  listItemCount: listAgg?.listItems ?? 0,
@@ -916,7 +920,7 @@ export class Views {
916
920
  ? this.imgUriBuilder.getPresetUri(
917
921
  'avatar',
918
922
  creatorDid,
919
- cidFromBlobJson(feedgen.record.avatar),
923
+ getBlobCidString(feedgen.record.avatar),
920
924
  )
921
925
  : undefined,
922
926
  likeCount: aggs?.likes ?? 0,
@@ -2076,12 +2080,12 @@ export class Views {
2076
2080
  thumb: this.imgUriBuilder.getPresetUri(
2077
2081
  'feed_thumbnail',
2078
2082
  did,
2079
- cidFromBlobJson(img.image),
2083
+ getBlobCidString(img.image),
2080
2084
  ),
2081
2085
  fullsize: this.imgUriBuilder.getPresetUri(
2082
2086
  'feed_fullsize',
2083
2087
  did,
2084
- cidFromBlobJson(img.image),
2088
+ getBlobCidString(img.image),
2085
2089
  ),
2086
2090
  alt: img.alt,
2087
2091
  aspectRatio: img.aspectRatio,
@@ -2092,7 +2096,7 @@ export class Views {
2092
2096
  }
2093
2097
 
2094
2098
  videoEmbed(did: DidString, embed: VideoEmbed): $Typed<VideoEmbedView> {
2095
- const cid = cidFromBlobJson(embed.video)
2099
+ const cid = getBlobCidString(embed.video)
2096
2100
  return app.bsky.embed.video.view.$build({
2097
2101
  cid,
2098
2102
  playlist: this.videoUriBuilder.playlist({ did, cid }),
@@ -2117,7 +2121,7 @@ export class Views {
2117
2121
  ? this.imgUriBuilder.getPresetUri(
2118
2122
  'feed_thumbnail',
2119
2123
  did,
2120
- cidFromBlobJson(thumb),
2124
+ getBlobCidString(thumb),
2121
2125
  )
2122
2126
  : undefined,
2123
2127
  },
package/src/views/util.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as util from 'node:util'
2
- import { AtUriString, BlobRef, DidString, UriString } from '@atproto/lex'
2
+ import { AtUriString, DidString, UriString } from '@atproto/lex'
3
3
  import {
4
4
  GateRecord,
5
5
  PostRecord,
@@ -59,10 +59,6 @@ type ParsedThreadGate = {
59
59
  allowListUris?: AtUriString[]
60
60
  }
61
61
 
62
- export const cidFromBlobJson = (json: BlobRef): string => {
63
- return json.ref.toString()
64
- }
65
-
66
62
  export const parsePostgate = ({
67
63
  gate,
68
64
  viewerDid,
@@ -722,6 +722,7 @@ Object {
722
722
  "expiresAt": "1970-01-01T00:00:00.000Z",
723
723
  "isActive": true,
724
724
  "isDisabled": false,
725
+ "labels": Array [],
725
726
  "record": Object {
726
727
  "$type": "app.bsky.actor.status",
727
728
  "createdAt": "1970-01-01T00:00:00.000Z",
@@ -755,6 +756,7 @@ Object {
755
756
  "expiresAt": "1970-01-01T00:00:00.000Z",
756
757
  "isActive": false,
757
758
  "isDisabled": false,
759
+ "labels": Array [],
758
760
  "record": Object {
759
761
  "$type": "app.bsky.actor.status",
760
762
  "createdAt": "1970-01-01T00:00:00.000Z",
@@ -788,6 +790,7 @@ Object {
788
790
  "expiresAt": "1970-01-01T00:00:00.000Z",
789
791
  "isActive": true,
790
792
  "isDisabled": true,
793
+ "labels": Array [],
791
794
  "record": Object {
792
795
  "$type": "app.bsky.actor.status",
793
796
  "createdAt": "1970-01-01T00:00:00.000Z",