@atproto/ozone 0.1.45 → 0.1.47

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 (93) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +4 -0
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/api/moderation/getRecord.d.ts.map +1 -1
  6. package/dist/api/moderation/getRecord.js +5 -4
  7. package/dist/api/moderation/getRecord.js.map +1 -1
  8. package/dist/api/moderation/getRecords.d.ts +4 -0
  9. package/dist/api/moderation/getRecords.d.ts.map +1 -0
  10. package/dist/api/moderation/getRecords.js +37 -0
  11. package/dist/api/moderation/getRecords.js.map +1 -0
  12. package/dist/api/moderation/getRepo.d.ts.map +1 -1
  13. package/dist/api/moderation/getRepo.js +5 -4
  14. package/dist/api/moderation/getRepo.js.map +1 -1
  15. package/dist/api/moderation/getRepos.d.ts +4 -0
  16. package/dist/api/moderation/getRepos.d.ts.map +1 -0
  17. package/dist/api/moderation/getRepos.js +36 -0
  18. package/dist/api/moderation/getRepos.js.map +1 -0
  19. package/dist/api/util.d.ts +1 -1
  20. package/dist/api/util.d.ts.map +1 -1
  21. package/dist/api/util.js +13 -9
  22. package/dist/api/util.js.map +1 -1
  23. package/dist/db/migrations/20241001T205730722Z-subject-status-review-state-index.d.ts +4 -0
  24. package/dist/db/migrations/20241001T205730722Z-subject-status-review-state-index.d.ts.map +1 -0
  25. package/dist/db/migrations/20241001T205730722Z-subject-status-review-state-index.js +18 -0
  26. package/dist/db/migrations/20241001T205730722Z-subject-status-review-state-index.js.map +1 -0
  27. package/dist/db/migrations/index.d.ts +1 -0
  28. package/dist/db/migrations/index.d.ts.map +1 -1
  29. package/dist/db/migrations/index.js +2 -1
  30. package/dist/db/migrations/index.js.map +1 -1
  31. package/dist/lexicon/index.d.ts +4 -0
  32. package/dist/lexicon/index.d.ts.map +1 -1
  33. package/dist/lexicon/index.js +8 -0
  34. package/dist/lexicon/index.js.map +1 -1
  35. package/dist/lexicon/lexicons.d.ts +118 -0
  36. package/dist/lexicon/lexicons.d.ts.map +1 -1
  37. package/dist/lexicon/lexicons.js +135 -3
  38. package/dist/lexicon/lexicons.js.map +1 -1
  39. package/dist/lexicon/types/app/bsky/actor/defs.d.ts +2 -0
  40. package/dist/lexicon/types/app/bsky/actor/defs.d.ts.map +1 -1
  41. package/dist/lexicon/types/app/bsky/actor/defs.js.map +1 -1
  42. package/dist/lexicon/types/app/bsky/actor/profile.d.ts +1 -0
  43. package/dist/lexicon/types/app/bsky/actor/profile.d.ts.map +1 -1
  44. package/dist/lexicon/types/app/bsky/actor/profile.js.map +1 -1
  45. package/dist/lexicon/types/app/bsky/feed/defs.d.ts +13 -2
  46. package/dist/lexicon/types/app/bsky/feed/defs.d.ts.map +1 -1
  47. package/dist/lexicon/types/app/bsky/feed/defs.js +21 -1
  48. package/dist/lexicon/types/app/bsky/feed/defs.js.map +1 -1
  49. package/dist/lexicon/types/app/bsky/feed/getAuthorFeed.d.ts +1 -0
  50. package/dist/lexicon/types/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
  51. package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.d.ts +2 -0
  52. package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.d.ts.map +1 -1
  53. package/dist/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.d.ts +2 -0
  54. package/dist/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.d.ts.map +1 -1
  55. package/dist/lexicon/types/com/atproto/repo/getRecord.d.ts +1 -0
  56. package/dist/lexicon/types/com/atproto/repo/getRecord.d.ts.map +1 -1
  57. package/dist/lexicon/types/tools/ozone/moderation/getRecords.d.ts +39 -0
  58. package/dist/lexicon/types/tools/ozone/moderation/getRecords.d.ts.map +1 -0
  59. package/dist/lexicon/types/tools/ozone/moderation/getRecords.js +3 -0
  60. package/dist/lexicon/types/tools/ozone/moderation/getRecords.js.map +1 -0
  61. package/dist/lexicon/types/tools/ozone/moderation/getRepos.d.ts +39 -0
  62. package/dist/lexicon/types/tools/ozone/moderation/getRepos.d.ts.map +1 -0
  63. package/dist/lexicon/types/tools/ozone/moderation/getRepos.js +3 -0
  64. package/dist/lexicon/types/tools/ozone/moderation/getRepos.js.map +1 -0
  65. package/dist/mod-service/views.d.ts +5 -5
  66. package/dist/mod-service/views.d.ts.map +1 -1
  67. package/dist/mod-service/views.js +75 -48
  68. package/dist/mod-service/views.js.map +1 -1
  69. package/package.json +9 -9
  70. package/src/api/index.ts +4 -0
  71. package/src/api/moderation/getRecord.ts +7 -5
  72. package/src/api/moderation/getRecords.ts +50 -0
  73. package/src/api/moderation/getRepo.ts +7 -5
  74. package/src/api/moderation/getRepos.ts +41 -0
  75. package/src/api/util.ts +16 -9
  76. package/src/db/migrations/20241001T205730722Z-subject-status-review-state-index.ts +15 -0
  77. package/src/db/migrations/index.ts +1 -0
  78. package/src/lexicon/index.ts +24 -0
  79. package/src/lexicon/lexicons.ts +137 -3
  80. package/src/lexicon/types/app/bsky/actor/defs.ts +2 -0
  81. package/src/lexicon/types/app/bsky/actor/profile.ts +1 -0
  82. package/src/lexicon/types/app/bsky/feed/defs.ts +38 -2
  83. package/src/lexicon/types/app/bsky/feed/getAuthorFeed.ts +1 -0
  84. package/src/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.ts +2 -0
  85. package/src/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.ts +2 -0
  86. package/src/lexicon/types/com/atproto/repo/getRecord.ts +1 -0
  87. package/src/lexicon/types/tools/ozone/moderation/getRecords.ts +50 -0
  88. package/src/lexicon/types/tools/ozone/moderation/getRepos.ts +50 -0
  89. package/src/mod-service/views.ts +93 -52
  90. package/tests/__snapshots__/get-records.test.ts.snap +153 -0
  91. package/tests/__snapshots__/get-repos.test.ts.snap +108 -0
  92. package/tests/get-records.test.ts +87 -0
  93. package/tests/get-repos.test.ts +87 -0
@@ -1686,6 +1686,11 @@ export const schemaDict = {
1686
1686
  },
1687
1687
  },
1688
1688
  },
1689
+ errors: [
1690
+ {
1691
+ name: 'RecordNotFound',
1692
+ },
1693
+ ],
1689
1694
  },
1690
1695
  },
1691
1696
  },
@@ -4190,6 +4195,10 @@ export const schemaDict = {
4190
4195
  ref: 'lex:com.atproto.label.defs#label',
4191
4196
  },
4192
4197
  },
4198
+ pinnedPost: {
4199
+ type: 'ref',
4200
+ ref: 'lex:com.atproto.repo.strongRef',
4201
+ },
4193
4202
  },
4194
4203
  },
4195
4204
  profileAssociated: {
@@ -4812,6 +4821,10 @@ export const schemaDict = {
4812
4821
  type: 'ref',
4813
4822
  ref: 'lex:com.atproto.repo.strongRef',
4814
4823
  },
4824
+ pinnedPost: {
4825
+ type: 'ref',
4826
+ ref: 'lex:com.atproto.repo.strongRef',
4827
+ },
4815
4828
  createdAt: {
4816
4829
  type: 'string',
4817
4830
  format: 'datetime',
@@ -5468,6 +5481,9 @@ export const schemaDict = {
5468
5481
  embeddingDisabled: {
5469
5482
  type: 'boolean',
5470
5483
  },
5484
+ pinned: {
5485
+ type: 'boolean',
5486
+ },
5471
5487
  },
5472
5488
  },
5473
5489
  feedViewPost: {
@@ -5484,7 +5500,10 @@ export const schemaDict = {
5484
5500
  },
5485
5501
  reason: {
5486
5502
  type: 'union',
5487
- refs: ['lex:app.bsky.feed.defs#reasonRepost'],
5503
+ refs: [
5504
+ 'lex:app.bsky.feed.defs#reasonRepost',
5505
+ 'lex:app.bsky.feed.defs#reasonPin',
5506
+ ],
5488
5507
  },
5489
5508
  feedContext: {
5490
5509
  type: 'string',
@@ -5536,6 +5555,10 @@ export const schemaDict = {
5536
5555
  },
5537
5556
  },
5538
5557
  },
5558
+ reasonPin: {
5559
+ type: 'object',
5560
+ properties: {},
5561
+ },
5539
5562
  threadViewPost: {
5540
5563
  type: 'object',
5541
5564
  required: ['post'],
@@ -5693,7 +5716,10 @@ export const schemaDict = {
5693
5716
  },
5694
5717
  reason: {
5695
5718
  type: 'union',
5696
- refs: ['lex:app.bsky.feed.defs#skeletonReasonRepost'],
5719
+ refs: [
5720
+ 'lex:app.bsky.feed.defs#skeletonReasonRepost',
5721
+ 'lex:app.bsky.feed.defs#skeletonReasonPin',
5722
+ ],
5697
5723
  },
5698
5724
  feedContext: {
5699
5725
  type: 'string',
@@ -5713,6 +5739,10 @@ export const schemaDict = {
5713
5739
  },
5714
5740
  },
5715
5741
  },
5742
+ skeletonReasonPin: {
5743
+ type: 'object',
5744
+ properties: {},
5745
+ },
5716
5746
  threadgateView: {
5717
5747
  type: 'object',
5718
5748
  properties: {
@@ -6078,6 +6108,10 @@ export const schemaDict = {
6078
6108
  ],
6079
6109
  default: 'posts_with_replies',
6080
6110
  },
6111
+ includePins: {
6112
+ type: 'boolean',
6113
+ default: false,
6114
+ },
6081
6115
  },
6082
6116
  },
6083
6117
  output: {
@@ -7162,7 +7196,7 @@ export const schemaDict = {
7162
7196
  type: 'record',
7163
7197
  key: 'tid',
7164
7198
  description:
7165
- "Record defining interaction gating rules for a thread (aka, reply controls). The record key (rkey) of the threadgate record must match the record key of the thread's root post, and that record must be in the same repository..",
7199
+ "Record defining interaction gating rules for a thread (aka, reply controls). The record key (rkey) of the threadgate record must match the record key of the thread's root post, and that record must be in the same repository.",
7166
7200
  record: {
7167
7201
  type: 'object',
7168
7202
  required: ['post', 'createdAt'],
@@ -8236,6 +8270,12 @@ export const schemaDict = {
8236
8270
  ref: 'lex:app.bsky.actor.defs#profileView',
8237
8271
  },
8238
8272
  },
8273
+ isFallback: {
8274
+ type: 'boolean',
8275
+ description:
8276
+ 'If true, response has fallen-back to generic results, and is not scoped using relativeToDid',
8277
+ default: false,
8278
+ },
8239
8279
  },
8240
8280
  },
8241
8281
  },
@@ -9192,6 +9232,12 @@ export const schemaDict = {
9192
9232
  ref: 'lex:app.bsky.unspecced.defs#skeletonSearchActor',
9193
9233
  },
9194
9234
  },
9235
+ relativeToDid: {
9236
+ type: 'string',
9237
+ format: 'did',
9238
+ description:
9239
+ 'DID of the account these suggestions are relative to. If this is returned undefined, suggestions are based on the viewer.',
9240
+ },
9195
9241
  },
9196
9242
  },
9197
9243
  },
@@ -11602,6 +11648,49 @@ export const schemaDict = {
11602
11648
  },
11603
11649
  },
11604
11650
  },
11651
+ ToolsOzoneModerationGetRecords: {
11652
+ lexicon: 1,
11653
+ id: 'tools.ozone.moderation.getRecords',
11654
+ defs: {
11655
+ main: {
11656
+ type: 'query',
11657
+ description: 'Get details about some records.',
11658
+ parameters: {
11659
+ type: 'params',
11660
+ required: ['uris'],
11661
+ properties: {
11662
+ uris: {
11663
+ type: 'array',
11664
+ maxLength: 100,
11665
+ items: {
11666
+ type: 'string',
11667
+ format: 'at-uri',
11668
+ },
11669
+ },
11670
+ },
11671
+ },
11672
+ output: {
11673
+ encoding: 'application/json',
11674
+ schema: {
11675
+ type: 'object',
11676
+ required: ['records'],
11677
+ properties: {
11678
+ records: {
11679
+ type: 'array',
11680
+ items: {
11681
+ type: 'union',
11682
+ refs: [
11683
+ 'lex:tools.ozone.moderation.defs#recordViewDetail',
11684
+ 'lex:tools.ozone.moderation.defs#recordViewNotFound',
11685
+ ],
11686
+ },
11687
+ },
11688
+ },
11689
+ },
11690
+ },
11691
+ },
11692
+ },
11693
+ },
11605
11694
  ToolsOzoneModerationGetRepo: {
11606
11695
  lexicon: 1,
11607
11696
  id: 'tools.ozone.moderation.getRepo',
@@ -11634,6 +11723,49 @@ export const schemaDict = {
11634
11723
  },
11635
11724
  },
11636
11725
  },
11726
+ ToolsOzoneModerationGetRepos: {
11727
+ lexicon: 1,
11728
+ id: 'tools.ozone.moderation.getRepos',
11729
+ defs: {
11730
+ main: {
11731
+ type: 'query',
11732
+ description: 'Get details about some repositories.',
11733
+ parameters: {
11734
+ type: 'params',
11735
+ required: ['dids'],
11736
+ properties: {
11737
+ dids: {
11738
+ type: 'array',
11739
+ maxLength: 100,
11740
+ items: {
11741
+ type: 'string',
11742
+ format: 'did',
11743
+ },
11744
+ },
11745
+ },
11746
+ },
11747
+ output: {
11748
+ encoding: 'application/json',
11749
+ schema: {
11750
+ type: 'object',
11751
+ required: ['repos'],
11752
+ properties: {
11753
+ repos: {
11754
+ type: 'array',
11755
+ items: {
11756
+ type: 'union',
11757
+ refs: [
11758
+ 'lex:tools.ozone.moderation.defs#repoViewDetail',
11759
+ 'lex:tools.ozone.moderation.defs#repoViewNotFound',
11760
+ ],
11761
+ },
11762
+ },
11763
+ },
11764
+ },
11765
+ },
11766
+ },
11767
+ },
11768
+ },
11637
11769
  ToolsOzoneModerationQueryEvents: {
11638
11770
  lexicon: 1,
11639
11771
  id: 'tools.ozone.moderation.queryEvents',
@@ -12450,7 +12582,9 @@ export const ids = {
12450
12582
  ToolsOzoneModerationEmitEvent: 'tools.ozone.moderation.emitEvent',
12451
12583
  ToolsOzoneModerationGetEvent: 'tools.ozone.moderation.getEvent',
12452
12584
  ToolsOzoneModerationGetRecord: 'tools.ozone.moderation.getRecord',
12585
+ ToolsOzoneModerationGetRecords: 'tools.ozone.moderation.getRecords',
12453
12586
  ToolsOzoneModerationGetRepo: 'tools.ozone.moderation.getRepo',
12587
+ ToolsOzoneModerationGetRepos: 'tools.ozone.moderation.getRepos',
12454
12588
  ToolsOzoneModerationQueryEvents: 'tools.ozone.moderation.queryEvents',
12455
12589
  ToolsOzoneModerationQueryStatuses: 'tools.ozone.moderation.queryStatuses',
12456
12590
  ToolsOzoneModerationSearchRepos: 'tools.ozone.moderation.searchRepos',
@@ -7,6 +7,7 @@ import { isObj, hasProp } from '../../../../util'
7
7
  import { CID } from 'multiformats/cid'
8
8
  import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs'
9
9
  import * as AppBskyGraphDefs from '../graph/defs'
10
+ import * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef'
10
11
 
11
12
  export interface ProfileViewBasic {
12
13
  did: string
@@ -74,6 +75,7 @@ export interface ProfileViewDetailed {
74
75
  createdAt?: string
75
76
  viewer?: ViewerState
76
77
  labels?: ComAtprotoLabelDefs.Label[]
78
+ pinnedPost?: ComAtprotoRepoStrongRef.Main
77
79
  [k: string]: unknown
78
80
  }
79
81
 
@@ -20,6 +20,7 @@ export interface Record {
20
20
  | ComAtprotoLabelDefs.SelfLabels
21
21
  | { $type: string; [k: string]: unknown }
22
22
  joinedViaStarterPack?: ComAtprotoRepoStrongRef.Main
23
+ pinnedPost?: ComAtprotoRepoStrongRef.Main
23
24
  createdAt?: string
24
25
  [k: string]: unknown
25
26
  }
@@ -55,6 +55,7 @@ export interface ViewerState {
55
55
  threadMuted?: boolean
56
56
  replyDisabled?: boolean
57
57
  embeddingDisabled?: boolean
58
+ pinned?: boolean
58
59
  [k: string]: unknown
59
60
  }
60
61
 
@@ -73,7 +74,7 @@ export function validateViewerState(v: unknown): ValidationResult {
73
74
  export interface FeedViewPost {
74
75
  post: PostView
75
76
  reply?: ReplyRef
76
- reason?: ReasonRepost | { $type: string; [k: string]: unknown }
77
+ reason?: ReasonRepost | ReasonPin | { $type: string; [k: string]: unknown }
77
78
  /** Context provided by feed generator that may be passed back alongside interactions. */
78
79
  feedContext?: string
79
80
  [k: string]: unknown
@@ -134,6 +135,22 @@ export function validateReasonRepost(v: unknown): ValidationResult {
134
135
  return lexicons.validate('app.bsky.feed.defs#reasonRepost', v)
135
136
  }
136
137
 
138
+ export interface ReasonPin {
139
+ [k: string]: unknown
140
+ }
141
+
142
+ export function isReasonPin(v: unknown): v is ReasonPin {
143
+ return (
144
+ isObj(v) &&
145
+ hasProp(v, '$type') &&
146
+ v.$type === 'app.bsky.feed.defs#reasonPin'
147
+ )
148
+ }
149
+
150
+ export function validateReasonPin(v: unknown): ValidationResult {
151
+ return lexicons.validate('app.bsky.feed.defs#reasonPin', v)
152
+ }
153
+
137
154
  export interface ThreadViewPost {
138
155
  post: PostView
139
156
  parent?:
@@ -265,7 +282,10 @@ export function validateGeneratorViewerState(v: unknown): ValidationResult {
265
282
 
266
283
  export interface SkeletonFeedPost {
267
284
  post: string
268
- reason?: SkeletonReasonRepost | { $type: string; [k: string]: unknown }
285
+ reason?:
286
+ | SkeletonReasonRepost
287
+ | SkeletonReasonPin
288
+ | { $type: string; [k: string]: unknown }
269
289
  /** Context that will be passed through to client and may be passed to feed generator back alongside interactions. */
270
290
  feedContext?: string
271
291
  [k: string]: unknown
@@ -300,6 +320,22 @@ export function validateSkeletonReasonRepost(v: unknown): ValidationResult {
300
320
  return lexicons.validate('app.bsky.feed.defs#skeletonReasonRepost', v)
301
321
  }
302
322
 
323
+ export interface SkeletonReasonPin {
324
+ [k: string]: unknown
325
+ }
326
+
327
+ export function isSkeletonReasonPin(v: unknown): v is SkeletonReasonPin {
328
+ return (
329
+ isObj(v) &&
330
+ hasProp(v, '$type') &&
331
+ v.$type === 'app.bsky.feed.defs#skeletonReasonPin'
332
+ )
333
+ }
334
+
335
+ export function validateSkeletonReasonPin(v: unknown): ValidationResult {
336
+ return lexicons.validate('app.bsky.feed.defs#skeletonReasonPin', v)
337
+ }
338
+
303
339
  export interface ThreadgateView {
304
340
  uri?: string
305
341
  cid?: string
@@ -20,6 +20,7 @@ export interface QueryParams {
20
20
  | 'posts_with_media'
21
21
  | 'posts_and_author_threads'
22
22
  | (string & {})
23
+ includePins: boolean
23
24
  }
24
25
 
25
26
  export type InputSchema = undefined
@@ -17,6 +17,8 @@ export type InputSchema = undefined
17
17
 
18
18
  export interface OutputSchema {
19
19
  suggestions: AppBskyActorDefs.ProfileView[]
20
+ /** If true, response has fallen-back to generic results, and is not scoped using relativeToDid */
21
+ isFallback?: boolean
20
22
  [k: string]: unknown
21
23
  }
22
24
 
@@ -23,6 +23,8 @@ export type InputSchema = undefined
23
23
  export interface OutputSchema {
24
24
  cursor?: string
25
25
  actors: AppBskyUnspeccedDefs.SkeletonSearchActor[]
26
+ /** DID of the account these suggestions are relative to. If this is returned undefined, suggestions are based on the viewer. */
27
+ relativeToDid?: string
26
28
  [k: string]: unknown
27
29
  }
28
30
 
@@ -39,6 +39,7 @@ export interface HandlerSuccess {
39
39
  export interface HandlerError {
40
40
  status: number
41
41
  message?: string
42
+ error?: 'RecordNotFound'
42
43
  }
43
44
 
44
45
  export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
@@ -0,0 +1,50 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
6
+ import { lexicons } from '../../../../lexicons'
7
+ import { isObj, hasProp } from '../../../../util'
8
+ import { CID } from 'multiformats/cid'
9
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+ import * as ToolsOzoneModerationDefs from './defs'
11
+
12
+ export interface QueryParams {
13
+ uris: string[]
14
+ }
15
+
16
+ export type InputSchema = undefined
17
+
18
+ export interface OutputSchema {
19
+ records: (
20
+ | ToolsOzoneModerationDefs.RecordViewDetail
21
+ | ToolsOzoneModerationDefs.RecordViewNotFound
22
+ | { $type: string; [k: string]: unknown }
23
+ )[]
24
+ [k: string]: unknown
25
+ }
26
+
27
+ export type HandlerInput = undefined
28
+
29
+ export interface HandlerSuccess {
30
+ encoding: 'application/json'
31
+ body: OutputSchema
32
+ headers?: { [key: string]: string }
33
+ }
34
+
35
+ export interface HandlerError {
36
+ status: number
37
+ message?: string
38
+ }
39
+
40
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
41
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
42
+ auth: HA
43
+ params: QueryParams
44
+ input: HandlerInput
45
+ req: express.Request
46
+ res: express.Response
47
+ }
48
+ export type Handler<HA extends HandlerAuth = never> = (
49
+ ctx: HandlerReqCtx<HA>,
50
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -0,0 +1,50 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
6
+ import { lexicons } from '../../../../lexicons'
7
+ import { isObj, hasProp } from '../../../../util'
8
+ import { CID } from 'multiformats/cid'
9
+ import { HandlerAuth, HandlerPipeThrough } from '@atproto/xrpc-server'
10
+ import * as ToolsOzoneModerationDefs from './defs'
11
+
12
+ export interface QueryParams {
13
+ dids: string[]
14
+ }
15
+
16
+ export type InputSchema = undefined
17
+
18
+ export interface OutputSchema {
19
+ repos: (
20
+ | ToolsOzoneModerationDefs.RepoViewDetail
21
+ | ToolsOzoneModerationDefs.RepoViewNotFound
22
+ | { $type: string; [k: string]: unknown }
23
+ )[]
24
+ [k: string]: unknown
25
+ }
26
+
27
+ export type HandlerInput = undefined
28
+
29
+ export interface HandlerSuccess {
30
+ encoding: 'application/json'
31
+ body: OutputSchema
32
+ headers?: { [key: string]: string }
33
+ }
34
+
35
+ export interface HandlerError {
36
+ status: number
37
+ message?: string
38
+ }
39
+
40
+ export type HandlerOutput = HandlerError | HandlerSuccess | HandlerPipeThrough
41
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
42
+ auth: HA
43
+ params: QueryParams
44
+ input: HandlerInput
45
+ req: express.Request
46
+ res: express.Response
47
+ }
48
+ export type Handler<HA extends HandlerAuth = never> = (
49
+ ctx: HandlerReqCtx<HA>,
50
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -223,25 +223,33 @@ export class ModerationViews {
223
223
  }
224
224
  }
225
225
 
226
- async repoDetail(
227
- did: string,
226
+ async repoDetails(
227
+ dids: string[],
228
228
  labelers?: ParsedLabelers,
229
- ): Promise<RepoViewDetail | undefined> {
229
+ ): Promise<Map<string, RepoView>> {
230
+ const results = new Map<string, RepoView>()
230
231
  const [repos, localLabels, externalLabels] = await Promise.all([
231
- this.repos([did]),
232
- this.labels(did),
233
- this.getExternalLabels([did], labelers),
232
+ this.repos(dids),
233
+ this.labels(dids),
234
+ this.getExternalLabels(dids, labelers),
234
235
  ])
235
- const repo = repos.get(did)
236
- if (!repo) return
237
236
 
238
- return {
239
- ...repo,
240
- moderation: {
241
- ...repo.moderation,
242
- },
243
- labels: [...localLabels, ...externalLabels],
244
- }
237
+ repos.forEach((repo, did) => {
238
+ const labels = [
239
+ ...(localLabels.get(did) || []),
240
+ ...(externalLabels.get(did) || []),
241
+ ]
242
+ const repoView = {
243
+ ...repo,
244
+ labels,
245
+ moderation: {
246
+ ...repo.moderation,
247
+ },
248
+ }
249
+ results.set(did, repoView)
250
+ })
251
+
252
+ return results
245
253
  }
246
254
 
247
255
  async fetchRecords(
@@ -308,47 +316,59 @@ export class ModerationViews {
308
316
  }, new Map<string, RecordView>())
309
317
  }
310
318
 
311
- async recordDetail(
312
- subject: RecordSubject,
319
+ async recordDetails(
320
+ subjects: RecordSubject[],
313
321
  labelers?: ParsedLabelers,
314
- ): Promise<RecordViewDetail | undefined> {
315
- const [records, subjectStatusesResult] = await Promise.all([
316
- this.records([subject]),
317
- this.getSubjectStatus([subject.uri]),
318
- ])
319
- const record = records.get(subject.uri)
320
- if (!record) return undefined
322
+ ): Promise<Map<string, RecordViewDetail>> {
323
+ const subjectUris = subjects.map((s) => s.uri)
324
+ const [records, subjectStatusesResult, localLabels, externalLabels] =
325
+ await Promise.all([
326
+ this.records(subjects),
327
+ this.getSubjectStatus(subjectUris),
328
+ this.labels(subjectUris),
329
+ this.getExternalLabels(subjectUris, labelers),
330
+ ])
331
+
332
+ const results = new Map<string, RecordViewDetail>()
333
+
334
+ await Promise.all(
335
+ Array.from(records.entries()).map(async ([uri, record]) => {
336
+ const selfLabels = getSelfLabels({
337
+ uri: record.uri,
338
+ cid: record.cid,
339
+ record: record.value,
340
+ })
321
341
 
322
- const status = subjectStatusesResult.get(subject.uri)
342
+ const status = subjectStatusesResult.get(uri)
343
+ const blobs = await this.blob(findBlobRefs(record.value))
323
344
 
324
- const [blobs, labels, externalLabels, subjectStatus] = await Promise.all([
325
- this.blob(findBlobRefs(record.value)),
326
- this.labels(record.uri),
327
- this.getExternalLabels([record.uri], labelers),
328
- status ? this.formatSubjectStatus(status) : Promise.resolve(undefined),
329
- ])
330
- const selfLabels = getSelfLabels({
331
- uri: record.uri,
332
- cid: record.cid,
333
- record: record.value,
334
- })
345
+ results.set(uri, {
346
+ ...record,
347
+ blobs,
348
+ moderation: {
349
+ ...record.moderation,
350
+ subjectStatus: status
351
+ ? this.formatSubjectStatus(status)
352
+ : undefined,
353
+ },
354
+ labels: [
355
+ ...(localLabels.get(uri) || []),
356
+ ...selfLabels,
357
+ ...(externalLabels.get(uri) || []),
358
+ ],
359
+ })
360
+ }),
361
+ )
335
362
 
336
- return {
337
- ...record,
338
- blobs,
339
- moderation: {
340
- ...record.moderation,
341
- subjectStatus,
342
- },
343
- labels: [...labels, ...selfLabels, ...externalLabels],
344
- }
363
+ return results
345
364
  }
346
365
 
347
366
  async getExternalLabels(
348
367
  subjects: string[],
349
368
  labelers?: ParsedLabelers,
350
- ): Promise<Label[]> {
351
- if (!labelers?.dids.length && !labelers?.redact.size) return []
369
+ ): Promise<Map<string, Label[]>> {
370
+ const results = new Map<string, Label[]>()
371
+ if (!labelers?.dids.length && !labelers?.redact.size) return results
352
372
  try {
353
373
  const {
354
374
  data: { labels },
@@ -356,13 +376,20 @@ export class ModerationViews {
356
376
  uriPatterns: subjects,
357
377
  sources: labelers.dids,
358
378
  })
359
- return labels
379
+ labels.forEach((label) => {
380
+ if (!results.has(label.uri)) {
381
+ results.set(label.uri, [label])
382
+ return
383
+ }
384
+ results.get(label.uri)?.push(label)
385
+ })
386
+ return results
360
387
  } catch (err) {
361
388
  httpLogger.error(
362
389
  { err, subjects, labelers },
363
390
  'failed to resolve labels from appview',
364
391
  )
365
- return []
392
+ return results
366
393
  }
367
394
  }
368
395
 
@@ -452,14 +479,28 @@ export class ModerationViews {
452
479
  })
453
480
  }
454
481
 
455
- async labels(subject: string, includeNeg?: boolean): Promise<Label[]> {
482
+ async labels(
483
+ subjects: string[],
484
+ includeNeg?: boolean,
485
+ ): Promise<Map<string, Label[]>> {
486
+ const labels = new Map<string, Label[]>()
456
487
  const res = await this.db.db
457
488
  .selectFrom('label')
458
- .where('label.uri', '=', subject)
489
+ .where('label.uri', 'in', subjects)
459
490
  .if(!includeNeg, (qb) => qb.where('neg', '=', false))
460
491
  .selectAll()
461
492
  .execute()
462
- return Promise.all(res.map((l) => this.formatLabelAndEnsureSig(l)))
493
+
494
+ await Promise.all(
495
+ res.map(async (labelRow) => {
496
+ const signedLabel = await this.formatLabelAndEnsureSig(labelRow)
497
+ if (!labels.has(labelRow.uri)) {
498
+ labels.set(labelRow.uri, [])
499
+ }
500
+ labels.get(labelRow.uri)?.push(signedLabel)
501
+ }),
502
+ )
503
+ return labels
463
504
  }
464
505
 
465
506
  async formatLabelAndEnsureSig(row: LabelRow) {