@atproto/bsky 0.0.15 → 0.0.17

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 (236) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/api/com/atproto/moderation/util.d.ts +4 -3
  3. package/dist/cache/read-through.d.ts +30 -0
  4. package/dist/config.d.ts +18 -0
  5. package/dist/context.d.ts +21 -6
  6. package/dist/daemon/config.d.ts +15 -0
  7. package/dist/daemon/context.d.ts +15 -0
  8. package/dist/daemon/index.d.ts +23 -0
  9. package/dist/daemon/logger.d.ts +3 -0
  10. package/dist/daemon/notifications.d.ts +18 -0
  11. package/dist/daemon/services.d.ts +11 -0
  12. package/dist/db/database-schema.d.ts +1 -2
  13. package/dist/db/index.js +41 -1
  14. package/dist/db/index.js.map +3 -3
  15. package/dist/db/migrations/20231003T202833377Z-create-moderation-subject-status.d.ts +3 -0
  16. package/dist/db/migrations/20231205T000257238Z-remove-did-cache.d.ts +3 -0
  17. package/dist/db/migrations/index.d.ts +2 -0
  18. package/dist/db/pagination.d.ts +2 -1
  19. package/dist/db/{periodic-moderation-action-reversal.d.ts → periodic-moderation-event-reversal.d.ts} +3 -5
  20. package/dist/db/tables/moderation.d.ts +24 -34
  21. package/dist/did-cache.d.ts +10 -7
  22. package/dist/feed-gen/types.d.ts +1 -1
  23. package/dist/index.d.ts +6 -1
  24. package/dist/index.js +4370 -2758
  25. package/dist/index.js.map +3 -3
  26. package/dist/indexer/context.d.ts +2 -0
  27. package/dist/indexer/index.d.ts +1 -0
  28. package/dist/lexicon/index.d.ts +23 -18
  29. package/dist/lexicon/lexicons.d.ts +561 -412
  30. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +1 -7
  31. package/dist/lexicon/types/app/bsky/graph/defs.d.ts +1 -0
  32. package/dist/lexicon/types/com/atproto/admin/defs.d.ts +114 -48
  33. package/dist/lexicon/types/com/atproto/admin/{resolveModerationReports.d.ts → deleteAccount.d.ts} +2 -13
  34. package/dist/lexicon/types/com/atproto/admin/{takeModerationAction.d.ts → emitModerationEvent.d.ts} +5 -6
  35. package/dist/lexicon/types/com/atproto/admin/{getModerationAction.d.ts → getModerationEvent.d.ts} +1 -1
  36. package/dist/lexicon/types/com/atproto/admin/{getModerationActions.d.ts → queryModerationEvents.d.ts} +5 -1
  37. package/dist/lexicon/types/com/atproto/admin/{getModerationReports.d.ts → queryModerationStatuses.d.ts} +12 -6
  38. package/dist/lexicon/types/com/atproto/admin/sendEmail.d.ts +1 -0
  39. package/dist/lexicon/types/com/atproto/{admin/getModerationReport.d.ts → temp/importRepo.d.ts} +10 -7
  40. package/dist/lexicon/types/com/atproto/temp/pushBlob.d.ts +25 -0
  41. package/dist/lexicon/types/com/atproto/{admin/reverseModerationAction.d.ts → temp/transferAccount.d.ts} +11 -5
  42. package/dist/logger.d.ts +1 -0
  43. package/dist/migrate-moderation-data.d.ts +1 -0
  44. package/dist/redis.d.ts +10 -1
  45. package/dist/services/actor/index.d.ts +18 -4
  46. package/dist/services/actor/views.d.ts +6 -8
  47. package/dist/services/feed/index.d.ts +7 -4
  48. package/dist/services/feed/util.d.ts +9 -1
  49. package/dist/services/feed/views.d.ts +11 -21
  50. package/dist/services/graph/index.d.ts +5 -29
  51. package/dist/services/graph/types.d.ts +1 -0
  52. package/dist/services/index.d.ts +3 -7
  53. package/dist/services/label/index.d.ts +10 -4
  54. package/dist/services/moderation/index.d.ts +134 -72
  55. package/dist/services/moderation/pagination.d.ts +36 -0
  56. package/dist/services/moderation/status.d.ts +13 -0
  57. package/dist/services/moderation/types.d.ts +35 -0
  58. package/dist/services/moderation/views.d.ts +18 -14
  59. package/dist/services/types.d.ts +3 -0
  60. package/dist/services/util/notification.d.ts +5 -0
  61. package/dist/services/util/post.d.ts +6 -6
  62. package/dist/util/debug.d.ts +1 -1
  63. package/dist/util/retry.d.ts +1 -6
  64. package/package.json +11 -11
  65. package/src/api/app/bsky/feed/getActorFeeds.ts +2 -1
  66. package/src/api/app/bsky/feed/getActorLikes.ts +1 -3
  67. package/src/api/app/bsky/feed/getAuthorFeed.ts +1 -3
  68. package/src/api/app/bsky/feed/getFeed.ts +9 -9
  69. package/src/api/app/bsky/feed/getFeedGenerator.ts +3 -0
  70. package/src/api/app/bsky/feed/getFeedGenerators.ts +2 -1
  71. package/src/api/app/bsky/feed/getListFeed.ts +1 -3
  72. package/src/api/app/bsky/feed/getPostThread.ts +15 -54
  73. package/src/api/app/bsky/feed/getPosts.ts +21 -18
  74. package/src/api/app/bsky/feed/getSuggestedFeeds.ts +2 -1
  75. package/src/api/app/bsky/feed/getTimeline.ts +1 -3
  76. package/src/api/app/bsky/feed/searchPosts.ts +20 -17
  77. package/src/api/app/bsky/graph/getList.ts +6 -3
  78. package/src/api/app/bsky/graph/getListBlocks.ts +3 -2
  79. package/src/api/app/bsky/graph/getListMutes.ts +2 -1
  80. package/src/api/app/bsky/graph/getLists.ts +2 -1
  81. package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +3 -1
  82. package/src/api/blob-resolver.ts +6 -11
  83. package/src/api/com/atproto/admin/emitModerationEvent.ts +220 -0
  84. package/src/api/com/atproto/admin/{getModerationActions.ts → getModerationEvent.ts} +5 -11
  85. package/src/api/com/atproto/admin/getRecord.ts +1 -0
  86. package/src/api/com/atproto/admin/{getModerationReports.ts → queryModerationEvents.ts} +13 -16
  87. package/src/api/com/atproto/admin/queryModerationStatuses.ts +55 -0
  88. package/src/api/com/atproto/moderation/createReport.ts +9 -7
  89. package/src/api/com/atproto/moderation/util.ts +38 -20
  90. package/src/api/index.ts +8 -14
  91. package/src/auth.ts +29 -21
  92. package/src/auto-moderator/index.ts +26 -19
  93. package/src/cache/read-through.ts +151 -0
  94. package/src/config.ts +90 -1
  95. package/src/context.ts +11 -7
  96. package/src/daemon/config.ts +60 -0
  97. package/src/daemon/context.ts +27 -0
  98. package/src/daemon/index.ts +78 -0
  99. package/src/daemon/logger.ts +6 -0
  100. package/src/daemon/notifications.ts +54 -0
  101. package/src/daemon/services.ts +22 -0
  102. package/src/db/database-schema.ts +0 -2
  103. package/src/db/migrations/20231003T202833377Z-create-moderation-subject-status.ts +123 -0
  104. package/src/db/migrations/20231205T000257238Z-remove-did-cache.ts +14 -0
  105. package/src/db/migrations/index.ts +2 -0
  106. package/src/db/pagination.ts +26 -3
  107. package/src/db/{periodic-moderation-action-reversal.ts → periodic-moderation-event-reversal.ts} +50 -46
  108. package/src/db/tables/moderation.ts +35 -52
  109. package/src/did-cache.ts +33 -56
  110. package/src/feed-gen/bsky-team.ts +1 -1
  111. package/src/feed-gen/hot-classic.ts +1 -1
  112. package/src/feed-gen/index.ts +0 -4
  113. package/src/feed-gen/mutuals.ts +6 -2
  114. package/src/feed-gen/types.ts +1 -1
  115. package/src/index.ts +57 -17
  116. package/src/indexer/context.ts +5 -0
  117. package/src/indexer/index.ts +10 -7
  118. package/src/lexicon/index.ts +80 -67
  119. package/src/lexicon/lexicons.ts +698 -507
  120. package/src/lexicon/types/app/bsky/feed/defs.ts +1 -18
  121. package/src/lexicon/types/app/bsky/graph/defs.ts +1 -0
  122. package/src/lexicon/types/com/atproto/admin/defs.ts +276 -84
  123. package/src/lexicon/types/com/atproto/admin/{resolveModerationReports.ts → deleteAccount.ts} +2 -13
  124. package/src/lexicon/types/com/atproto/admin/{takeModerationAction.ts → emitModerationEvent.ts} +13 -11
  125. package/src/lexicon/types/com/atproto/admin/{getModerationReport.ts → getModerationEvent.ts} +1 -1
  126. package/src/lexicon/types/com/atproto/admin/{getModerationActions.ts → queryModerationEvents.ts} +8 -1
  127. package/src/lexicon/types/com/atproto/admin/{getModerationReports.ts → queryModerationStatuses.ts} +21 -14
  128. package/src/lexicon/types/com/atproto/admin/sendEmail.ts +1 -0
  129. package/src/lexicon/types/com/atproto/{admin/getModerationAction.ts → temp/importRepo.ts} +11 -7
  130. package/src/lexicon/types/com/atproto/temp/pushBlob.ts +39 -0
  131. package/src/lexicon/types/com/atproto/{admin/reverseModerationAction.ts → temp/transferAccount.ts} +18 -5
  132. package/src/logger.ts +2 -0
  133. package/src/migrate-moderation-data.ts +414 -0
  134. package/src/redis.ts +43 -3
  135. package/src/services/actor/index.ts +55 -7
  136. package/src/services/actor/views.ts +18 -21
  137. package/src/services/feed/index.ts +52 -19
  138. package/src/services/feed/util.ts +47 -19
  139. package/src/services/feed/views.ts +87 -13
  140. package/src/services/graph/index.ts +21 -3
  141. package/src/services/graph/types.ts +1 -0
  142. package/src/services/index.ts +14 -14
  143. package/src/services/indexing/index.ts +7 -10
  144. package/src/services/indexing/plugins/block.ts +2 -3
  145. package/src/services/indexing/plugins/feed-generator.ts +2 -3
  146. package/src/services/indexing/plugins/follow.ts +2 -3
  147. package/src/services/indexing/plugins/like.ts +2 -3
  148. package/src/services/indexing/plugins/list-block.ts +2 -3
  149. package/src/services/indexing/plugins/list-item.ts +2 -3
  150. package/src/services/indexing/plugins/list.ts +2 -3
  151. package/src/services/indexing/plugins/post.ts +16 -4
  152. package/src/services/indexing/plugins/repost.ts +2 -3
  153. package/src/services/indexing/plugins/thread-gate.ts +2 -3
  154. package/src/services/label/index.ts +68 -25
  155. package/src/services/moderation/index.ts +380 -395
  156. package/src/services/moderation/pagination.ts +96 -0
  157. package/src/services/moderation/status.ts +241 -0
  158. package/src/services/moderation/types.ts +49 -0
  159. package/src/services/moderation/views.ts +278 -329
  160. package/src/services/types.ts +4 -0
  161. package/src/services/util/notification.ts +70 -0
  162. package/src/util/debug.ts +2 -2
  163. package/src/util/retry.ts +1 -44
  164. package/tests/__snapshots__/feed-generation.test.ts.snap +322 -6
  165. package/tests/__snapshots__/indexing.test.ts.snap +0 -6
  166. package/tests/admin/__snapshots__/get-record.test.ts.snap +30 -132
  167. package/tests/admin/__snapshots__/get-repo.test.ts.snap +14 -60
  168. package/tests/admin/__snapshots__/moderation-events.test.ts.snap +146 -0
  169. package/tests/admin/__snapshots__/moderation-statuses.test.ts.snap +64 -0
  170. package/tests/admin/__snapshots__/moderation.test.ts.snap +0 -125
  171. package/tests/admin/get-record.test.ts +5 -9
  172. package/tests/admin/get-repo.test.ts +10 -12
  173. package/tests/admin/moderation-events.test.ts +221 -0
  174. package/tests/admin/moderation-statuses.test.ts +145 -0
  175. package/tests/admin/moderation.test.ts +512 -860
  176. package/tests/admin/repo-search.test.ts +3 -3
  177. package/tests/algos/hot-classic.test.ts +1 -2
  178. package/tests/auth.test.ts +1 -1
  179. package/tests/auto-moderator/fuzzy-matcher.test.ts +2 -1
  180. package/tests/auto-moderator/labeler.test.ts +19 -20
  181. package/tests/auto-moderator/takedowns.test.ts +61 -28
  182. package/tests/blob-resolver.test.ts +4 -2
  183. package/tests/daemon.test.ts +191 -0
  184. package/tests/did-cache.test.ts +20 -5
  185. package/tests/feed-generation.test.ts +57 -9
  186. package/tests/handle-invalidation.test.ts +1 -5
  187. package/tests/indexing.test.ts +20 -13
  188. package/tests/redis-cache.test.ts +231 -0
  189. package/tests/seeds/basic.ts +3 -0
  190. package/tests/subscription/repo.test.ts +4 -7
  191. package/tests/views/__snapshots__/block-lists.test.ts.snap +3 -9
  192. package/tests/views/__snapshots__/blocks.test.ts.snap +0 -9
  193. package/tests/views/__snapshots__/mute-lists.test.ts.snap +5 -5
  194. package/tests/views/__snapshots__/mutes.test.ts.snap +0 -3
  195. package/tests/views/__snapshots__/thread.test.ts.snap +0 -30
  196. package/tests/views/actor-search.test.ts +2 -3
  197. package/tests/views/author-feed.test.ts +42 -36
  198. package/tests/views/follows.test.ts +40 -35
  199. package/tests/views/list-feed.test.ts +17 -9
  200. package/tests/views/notifications.test.ts +13 -9
  201. package/tests/views/profile.test.ts +20 -19
  202. package/tests/views/thread.test.ts +117 -94
  203. package/tests/views/threadgating.test.ts +89 -19
  204. package/tests/views/timeline.test.ts +21 -13
  205. package/dist/api/com/atproto/admin/resolveModerationReports.d.ts +0 -3
  206. package/dist/api/com/atproto/admin/reverseModerationAction.d.ts +0 -3
  207. package/dist/api/com/atproto/admin/takeModerationAction.d.ts +0 -3
  208. package/dist/db/tables/did-cache.d.ts +0 -10
  209. package/dist/feed-gen/best-of-follows.d.ts +0 -29
  210. package/dist/feed-gen/whats-hot.d.ts +0 -29
  211. package/dist/feed-gen/with-friends.d.ts +0 -3
  212. package/dist/label-cache.d.ts +0 -19
  213. package/src/api/com/atproto/admin/getModerationAction.ts +0 -44
  214. package/src/api/com/atproto/admin/getModerationReport.ts +0 -43
  215. package/src/api/com/atproto/admin/resolveModerationReports.ts +0 -24
  216. package/src/api/com/atproto/admin/reverseModerationAction.ts +0 -115
  217. package/src/api/com/atproto/admin/takeModerationAction.ts +0 -156
  218. package/src/db/tables/did-cache.ts +0 -13
  219. package/src/feed-gen/best-of-follows.ts +0 -74
  220. package/src/feed-gen/whats-hot.ts +0 -101
  221. package/src/feed-gen/with-friends.ts +0 -39
  222. package/src/label-cache.ts +0 -90
  223. package/tests/admin/__snapshots__/get-moderation-action.test.ts.snap +0 -172
  224. package/tests/admin/__snapshots__/get-moderation-actions.test.ts.snap +0 -178
  225. package/tests/admin/__snapshots__/get-moderation-report.test.ts.snap +0 -177
  226. package/tests/admin/__snapshots__/get-moderation-reports.test.ts.snap +0 -307
  227. package/tests/admin/get-moderation-action.test.ts +0 -100
  228. package/tests/admin/get-moderation-actions.test.ts +0 -164
  229. package/tests/admin/get-moderation-report.test.ts +0 -100
  230. package/tests/admin/get-moderation-reports.test.ts +0 -332
  231. package/tests/algos/whats-hot.test.ts +0 -118
  232. package/tests/algos/with-friends.test.ts +0 -145
  233. /package/dist/api/com/atproto/admin/{getModerationAction.d.ts → emitModerationEvent.d.ts} +0 -0
  234. /package/dist/api/com/atproto/admin/{getModerationActions.d.ts → getModerationEvent.d.ts} +0 -0
  235. /package/dist/api/com/atproto/admin/{getModerationReport.d.ts → queryModerationEvents.d.ts} +0 -0
  236. /package/dist/api/com/atproto/admin/{getModerationReports.d.ts → queryModerationStatuses.d.ts} +0 -0
@@ -1,25 +1,29 @@
1
- import { Database, PrimaryDatabase } from '../db'
2
1
  import { ImageUriBuilder } from '../image/uri'
3
2
  import { ActorService } from './actor'
4
3
  import { FeedService } from './feed'
5
4
  import { GraphService } from './graph'
6
5
  import { ModerationService } from './moderation'
7
- import { LabelService } from './label'
6
+ import { LabelCacheOpts, LabelService } from './label'
8
7
  import { ImageInvalidator } from '../image/invalidator'
9
- import { LabelCache } from '../label-cache'
8
+ import { FromDb, FromDbPrimary } from './types'
10
9
 
11
10
  export function createServices(resources: {
12
11
  imgUriBuilder: ImageUriBuilder
13
12
  imgInvalidator: ImageInvalidator
14
- labelCache: LabelCache
13
+ labelCacheOpts: LabelCacheOpts
15
14
  }): Services {
16
- const { imgUriBuilder, imgInvalidator, labelCache } = resources
15
+ const { imgUriBuilder, imgInvalidator, labelCacheOpts } = resources
16
+ const label = LabelService.creator(labelCacheOpts)
17
+ const graph = GraphService.creator(imgUriBuilder)
18
+ const actor = ActorService.creator(imgUriBuilder, graph, label)
19
+ const moderation = ModerationService.creator(imgUriBuilder, imgInvalidator)
20
+ const feed = FeedService.creator(imgUriBuilder, actor, label, graph)
17
21
  return {
18
- actor: ActorService.creator(imgUriBuilder, labelCache),
19
- feed: FeedService.creator(imgUriBuilder, labelCache),
20
- graph: GraphService.creator(imgUriBuilder),
21
- moderation: ModerationService.creator(imgUriBuilder, imgInvalidator),
22
- label: LabelService.creator(labelCache),
22
+ actor,
23
+ feed,
24
+ moderation,
25
+ graph,
26
+ label,
23
27
  }
24
28
  }
25
29
 
@@ -30,7 +34,3 @@ export type Services = {
30
34
  moderation: FromDbPrimary<ModerationService>
31
35
  label: FromDb<LabelService>
32
36
  }
33
-
34
- type FromDb<T> = (db: Database) => T
35
-
36
- type FromDbPrimary<T> = (db: PrimaryDatabase) => T
@@ -7,6 +7,7 @@ import {
7
7
  verifyRepo,
8
8
  Commit,
9
9
  VerifiedRepo,
10
+ getAndParseRecord,
10
11
  } from '@atproto/repo'
11
12
  import { AtUri } from '@atproto/syntax'
12
13
  import { IdResolver, getPds } from '@atproto/identity'
@@ -201,10 +202,11 @@ export class IndexingService {
201
202
  if (op.op === 'delete') {
202
203
  await this.deleteRecord(uri)
203
204
  } else {
205
+ const parsed = await getAndParseRecord(blocks, cid)
204
206
  await this.indexRecord(
205
207
  uri,
206
208
  cid,
207
- op.value,
209
+ parsed.record,
208
210
  op.op === 'create' ? WriteOpAction.Create : WriteOpAction.Update,
209
211
  now,
210
212
  )
@@ -389,19 +391,15 @@ type UriAndCid = {
389
391
  cid: CID
390
392
  }
391
393
 
392
- type RecordDescript = UriAndCid & {
393
- value: unknown
394
- }
395
-
396
394
  type IndexOp =
397
395
  | ({
398
396
  op: 'create' | 'update'
399
- } & RecordDescript)
397
+ } & UriAndCid)
400
398
  | ({ op: 'delete' } & UriAndCid)
401
399
 
402
400
  const findDiffFromCheckout = (
403
401
  curr: Record<string, UriAndCid>,
404
- checkout: Record<string, RecordDescript>,
402
+ checkout: Record<string, UriAndCid>,
405
403
  ): IndexOp[] => {
406
404
  const ops: IndexOp[] = []
407
405
  for (const uri of Object.keys(checkout)) {
@@ -428,14 +426,13 @@ const findDiffFromCheckout = (
428
426
  const formatCheckout = (
429
427
  did: string,
430
428
  verifiedRepo: VerifiedRepo,
431
- ): Record<string, RecordDescript> => {
432
- const records: Record<string, RecordDescript> = {}
429
+ ): Record<string, UriAndCid> => {
430
+ const records: Record<string, UriAndCid> = {}
433
431
  for (const create of verifiedRepo.creates) {
434
432
  const uri = AtUri.make(did, create.collection, create.rkey)
435
433
  records[uri.toString()] = {
436
434
  uri,
437
435
  cid: create.cid,
438
- value: create.record,
439
436
  }
440
437
  }
441
438
  return records
@@ -1,6 +1,5 @@
1
1
  import { Selectable } from 'kysely'
2
- import { AtUri } from '@atproto/syntax'
3
- import { toSimplifiedISOSafe } from '@atproto/common'
2
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
4
3
  import { CID } from 'multiformats/cid'
5
4
  import * as Block from '../../../lexicon/types/app/bsky/graph/block'
6
5
  import * as lex from '../../../lexicon/lexicons'
@@ -27,7 +26,7 @@ const insertFn = async (
27
26
  cid: cid.toString(),
28
27
  creator: uri.host,
29
28
  subjectDid: obj.subject,
30
- createdAt: toSimplifiedISOSafe(obj.createdAt),
29
+ createdAt: normalizeDatetimeAlways(obj.createdAt),
31
30
  indexedAt: timestamp,
32
31
  })
33
32
  .onConflict((oc) => oc.doNothing())
@@ -1,6 +1,5 @@
1
1
  import { Selectable } from 'kysely'
2
- import { AtUri } from '@atproto/syntax'
3
- import { toSimplifiedISOSafe } from '@atproto/common'
2
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
4
3
  import { CID } from 'multiformats/cid'
5
4
  import * as FeedGenerator from '../../../lexicon/types/app/bsky/feed/generator'
6
5
  import * as lex from '../../../lexicon/lexicons'
@@ -33,7 +32,7 @@ const insertFn = async (
33
32
  ? JSON.stringify(obj.descriptionFacets)
34
33
  : undefined,
35
34
  avatarCid: obj.avatar?.ref.toString(),
36
- createdAt: toSimplifiedISOSafe(obj.createdAt),
35
+ createdAt: normalizeDatetimeAlways(obj.createdAt),
37
36
  indexedAt: timestamp,
38
37
  })
39
38
  .onConflict((oc) => oc.doNothing())
@@ -1,6 +1,5 @@
1
1
  import { Selectable } from 'kysely'
2
- import { AtUri } from '@atproto/syntax'
3
- import { toSimplifiedISOSafe } from '@atproto/common'
2
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
4
3
  import { CID } from 'multiformats/cid'
5
4
  import * as Follow from '../../../lexicon/types/app/bsky/graph/follow'
6
5
  import * as lex from '../../../lexicon/lexicons'
@@ -28,7 +27,7 @@ const insertFn = async (
28
27
  cid: cid.toString(),
29
28
  creator: uri.host,
30
29
  subjectDid: obj.subject,
31
- createdAt: toSimplifiedISOSafe(obj.createdAt),
30
+ createdAt: normalizeDatetimeAlways(obj.createdAt),
32
31
  indexedAt: timestamp,
33
32
  })
34
33
  .onConflict((oc) => oc.doNothing())
@@ -1,6 +1,5 @@
1
1
  import { Selectable } from 'kysely'
2
- import { AtUri } from '@atproto/syntax'
3
- import { toSimplifiedISOSafe } from '@atproto/common'
2
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
4
3
  import { CID } from 'multiformats/cid'
5
4
  import * as Like from '../../../lexicon/types/app/bsky/feed/like'
6
5
  import * as lex from '../../../lexicon/lexicons'
@@ -29,7 +28,7 @@ const insertFn = async (
29
28
  creator: uri.host,
30
29
  subject: obj.subject.uri,
31
30
  subjectCid: obj.subject.cid,
32
- createdAt: toSimplifiedISOSafe(obj.createdAt),
31
+ createdAt: normalizeDatetimeAlways(obj.createdAt),
33
32
  indexedAt: timestamp,
34
33
  })
35
34
  .onConflict((oc) => oc.doNothing())
@@ -1,6 +1,5 @@
1
1
  import { Selectable } from 'kysely'
2
- import { AtUri } from '@atproto/syntax'
3
- import { toSimplifiedISOSafe } from '@atproto/common'
2
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
4
3
  import { CID } from 'multiformats/cid'
5
4
  import * as ListBlock from '../../../lexicon/types/app/bsky/graph/listblock'
6
5
  import * as lex from '../../../lexicon/lexicons'
@@ -27,7 +26,7 @@ const insertFn = async (
27
26
  cid: cid.toString(),
28
27
  creator: uri.host,
29
28
  subjectUri: obj.subject,
30
- createdAt: toSimplifiedISOSafe(obj.createdAt),
29
+ createdAt: normalizeDatetimeAlways(obj.createdAt),
31
30
  indexedAt: timestamp,
32
31
  })
33
32
  .onConflict((oc) => oc.doNothing())
@@ -1,6 +1,5 @@
1
1
  import { Selectable } from 'kysely'
2
- import { AtUri } from '@atproto/syntax'
3
- import { toSimplifiedISOSafe } from '@atproto/common'
2
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
4
3
  import { CID } from 'multiformats/cid'
5
4
  import * as ListItem from '../../../lexicon/types/app/bsky/graph/listitem'
6
5
  import * as lex from '../../../lexicon/lexicons'
@@ -35,7 +34,7 @@ const insertFn = async (
35
34
  creator: uri.host,
36
35
  subjectDid: obj.subject,
37
36
  listUri: obj.list,
38
- createdAt: toSimplifiedISOSafe(obj.createdAt),
37
+ createdAt: normalizeDatetimeAlways(obj.createdAt),
39
38
  indexedAt: timestamp,
40
39
  })
41
40
  .onConflict((oc) => oc.doNothing())
@@ -1,6 +1,5 @@
1
1
  import { Selectable } from 'kysely'
2
- import { AtUri } from '@atproto/syntax'
3
- import { toSimplifiedISOSafe } from '@atproto/common'
2
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
4
3
  import { CID } from 'multiformats/cid'
5
4
  import * as List from '../../../lexicon/types/app/bsky/graph/list'
6
5
  import * as lex from '../../../lexicon/lexicons'
@@ -33,7 +32,7 @@ const insertFn = async (
33
32
  ? JSON.stringify(obj.descriptionFacets)
34
33
  : undefined,
35
34
  avatarCid: obj.avatar?.ref.toString(),
36
- createdAt: toSimplifiedISOSafe(obj.createdAt),
35
+ createdAt: normalizeDatetimeAlways(obj.createdAt),
37
36
  indexedAt: timestamp,
38
37
  })
39
38
  .onConflict((oc) => oc.doNothing())
@@ -1,7 +1,6 @@
1
1
  import { Insertable, Selectable, sql } from 'kysely'
2
2
  import { CID } from 'multiformats/cid'
3
- import { AtUri } from '@atproto/syntax'
4
- import { toSimplifiedISOSafe } from '@atproto/common'
3
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
5
4
  import { jsonStringToLex } from '@atproto/lexicon'
6
5
  import {
7
6
  Record as PostRecord,
@@ -68,7 +67,7 @@ const insertFn = async (
68
67
  cid: cid.toString(),
69
68
  creator: uri.host,
70
69
  text: obj.text,
71
- createdAt: toSimplifiedISOSafe(obj.createdAt),
70
+ createdAt: normalizeDatetimeAlways(obj.createdAt),
72
71
  replyRoot: obj.reply?.root?.uri || null,
73
72
  replyRootCid: obj.reply?.root?.cid || null,
74
73
  replyParent: obj.reply?.parent?.uri || null,
@@ -113,6 +112,7 @@ const insertFn = async (
113
112
  obj.reply,
114
113
  )
115
114
  if (invalidReplyRoot || violatesThreadGate) {
115
+ Object.assign(insertedPost, { invalidReplyRoot, violatesThreadGate })
116
116
  await db
117
117
  .updateTable('post')
118
118
  .where('uri', '=', post.uri)
@@ -242,6 +242,13 @@ const notifsForInsert = (obj: IndexedPost) => {
242
242
  }
243
243
  }
244
244
 
245
+ if (obj.post.violatesThreadGate) {
246
+ // don't generate reply notifications when post violates threadgate
247
+ return notifs
248
+ }
249
+
250
+ // reply notifications
251
+
245
252
  for (const ancestor of obj.ancestors ?? []) {
246
253
  if (ancestor.uri === obj.post.uri) continue // no need to notify for own post
247
254
  if (ancestor.height < REPLY_NOTIF_DEPTH) {
@@ -354,6 +361,11 @@ const updateAggregates = async (db: DatabaseSchema, postIdx: IndexedPost) => {
354
361
  replyCount: db
355
362
  .selectFrom('post')
356
363
  .where('post.replyParent', '=', postIdx.post.replyParent)
364
+ .where((qb) =>
365
+ qb
366
+ .where('post.violatesThreadGate', 'is', null)
367
+ .orWhere('post.violatesThreadGate', '=', false),
368
+ )
357
369
  .select(countAll.as('count')),
358
370
  })
359
371
  .onConflict((oc) =>
@@ -420,7 +432,7 @@ async function validateReply(
420
432
  const violatesThreadGate = await feedutil.violatesThreadGate(
421
433
  db,
422
434
  creator,
423
- new AtUri(reply.root.uri).host,
435
+ new AtUri(reply.root.uri).hostname,
424
436
  replyRefs.root?.record ?? null,
425
437
  replyRefs.gate?.record ?? null,
426
438
  )
@@ -1,7 +1,6 @@
1
1
  import { Selectable } from 'kysely'
2
2
  import { CID } from 'multiformats/cid'
3
- import { AtUri } from '@atproto/syntax'
4
- import { toSimplifiedISOSafe } from '@atproto/common'
3
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
5
4
  import * as Repost from '../../../lexicon/types/app/bsky/feed/repost'
6
5
  import * as lex from '../../../lexicon/lexicons'
7
6
  import { DatabaseSchema, DatabaseSchemaType } from '../../../db/database-schema'
@@ -27,7 +26,7 @@ const insertFn = async (
27
26
  creator: uri.host,
28
27
  subject: obj.subject.uri,
29
28
  subjectCid: obj.subject.cid,
30
- createdAt: toSimplifiedISOSafe(obj.createdAt),
29
+ createdAt: normalizeDatetimeAlways(obj.createdAt),
31
30
  indexedAt: timestamp,
32
31
  }
33
32
  const [inserted] = await Promise.all([
@@ -1,6 +1,5 @@
1
- import { AtUri } from '@atproto/syntax'
1
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
2
2
  import { InvalidRequestError } from '@atproto/xrpc-server'
3
- import { toSimplifiedISOSafe } from '@atproto/common'
4
3
  import { CID } from 'multiformats/cid'
5
4
  import * as Threadgate from '../../../lexicon/types/app/bsky/feed/threadgate'
6
5
  import * as lex from '../../../lexicon/lexicons'
@@ -33,7 +32,7 @@ const insertFn = async (
33
32
  cid: cid.toString(),
34
33
  creator: uri.host,
35
34
  postUri: obj.post,
36
- createdAt: toSimplifiedISOSafe(obj.createdAt),
35
+ createdAt: normalizeDatetimeAlways(obj.createdAt),
37
36
  indexedAt: timestamp,
38
37
  })
39
38
  .onConflict((oc) => oc.doNothing())
@@ -1,18 +1,38 @@
1
1
  import { sql } from 'kysely'
2
- import { AtUri } from '@atproto/syntax'
3
- import { toSimplifiedISOSafe } from '@atproto/common'
2
+ import { AtUri, normalizeDatetimeAlways } from '@atproto/syntax'
4
3
  import { Database } from '../../db'
5
4
  import { Label, isSelfLabels } from '../../lexicon/types/com/atproto/label/defs'
6
5
  import { ids } from '../../lexicon/lexicons'
7
- import { LabelCache } from '../../label-cache'
6
+ import { ReadThroughCache } from '../../cache/read-through'
7
+ import { Redis } from '../../redis'
8
8
 
9
9
  export type Labels = Record<string, Label[]>
10
10
 
11
+ export type LabelCacheOpts = {
12
+ redis: Redis
13
+ staleTTL: number
14
+ maxTTL: number
15
+ }
16
+
11
17
  export class LabelService {
12
- constructor(public db: Database, public cache: LabelCache | null) {}
18
+ public cache: ReadThroughCache<Label[]> | null
13
19
 
14
- static creator(cache: LabelCache | null) {
15
- return (db: Database) => new LabelService(db, cache)
20
+ constructor(public db: Database, cacheOpts: LabelCacheOpts | null) {
21
+ if (cacheOpts) {
22
+ this.cache = new ReadThroughCache(cacheOpts.redis, {
23
+ ...cacheOpts,
24
+ fetchMethod: async (subject: string) => {
25
+ const res = await fetchLabelsForSubjects(db, [subject])
26
+ return res[subject] ?? []
27
+ },
28
+ fetchManyMethod: (subjects: string[]) =>
29
+ fetchLabelsForSubjects(db, subjects),
30
+ })
31
+ }
32
+ }
33
+
34
+ static creator(cacheOpts: LabelCacheOpts | null) {
35
+ return (db: Database) => new LabelService(db, cacheOpts)
16
36
  }
17
37
 
18
38
  async formatAndCreate(
@@ -73,24 +93,19 @@ export class LabelService {
73
93
  },
74
94
  ): Promise<Labels> {
75
95
  if (subjects.length < 1) return {}
76
- const res =
77
- this.cache === null || opts?.skipCache
78
- ? await this.db.db
79
- .selectFrom('label')
80
- .where('label.uri', 'in', subjects)
81
- .if(!opts?.includeNeg, (qb) => qb.where('neg', '=', false))
82
- .selectAll()
83
- .execute()
84
- : this.cache.forSubjects(subjects, opts?.includeNeg)
85
- return res.reduce((acc, cur) => {
86
- acc[cur.uri] ??= []
87
- acc[cur.uri].push({
88
- ...cur,
89
- cid: cur.cid === '' ? undefined : cur.cid,
90
- neg: cur.neg,
91
- })
92
- return acc
93
- }, {} as Labels)
96
+ const res = this.cache
97
+ ? await this.cache.getMany(subjects, { revalidate: opts?.skipCache })
98
+ : await fetchLabelsForSubjects(this.db, subjects)
99
+
100
+ if (opts?.includeNeg) {
101
+ return res
102
+ }
103
+
104
+ const noNegs: Labels = {}
105
+ for (const [key, val] of Object.entries(res)) {
106
+ noNegs[key] = val.filter((label) => !label.neg)
107
+ }
108
+ return noNegs
94
109
  }
95
110
 
96
111
  // gets labels for any record. when did is present, combine labels for both did & profile record.
@@ -166,9 +181,37 @@ export function getSelfLabels(details: {
166
181
  const src = new AtUri(uri).host // record creator
167
182
  const cts =
168
183
  typeof record.createdAt === 'string'
169
- ? toSimplifiedISOSafe(record.createdAt)
184
+ ? normalizeDatetimeAlways(record.createdAt)
170
185
  : new Date(0).toISOString()
171
186
  return record.labels.values.map(({ val }) => {
172
187
  return { src, uri, cid, val, cts, neg: false }
173
188
  })
174
189
  }
190
+
191
+ const fetchLabelsForSubjects = async (
192
+ db: Database,
193
+ subjects: string[],
194
+ ): Promise<Record<string, Label[]>> => {
195
+ if (subjects.length === 0) {
196
+ return {}
197
+ }
198
+ const res = await db.db
199
+ .selectFrom('label')
200
+ .where('label.uri', 'in', subjects)
201
+ .selectAll()
202
+ .execute()
203
+ const labelMap = res.reduce((acc, cur) => {
204
+ acc[cur.uri] ??= []
205
+ acc[cur.uri].push({
206
+ ...cur,
207
+ cid: cur.cid === '' ? undefined : cur.cid,
208
+ neg: cur.neg,
209
+ })
210
+ return acc
211
+ }, {} as Record<string, Label[]>)
212
+ // ensure we cache negatives
213
+ for (const subject of subjects) {
214
+ labelMap[subject] ??= []
215
+ }
216
+ return labelMap
217
+ }