@atproto/bsky 0.0.16 → 0.0.18

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 (110) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cache/read-through.d.ts +30 -0
  3. package/dist/config.d.ts +18 -0
  4. package/dist/context.d.ts +6 -6
  5. package/dist/daemon/config.d.ts +15 -0
  6. package/dist/daemon/context.d.ts +15 -0
  7. package/dist/daemon/index.d.ts +23 -0
  8. package/dist/daemon/logger.d.ts +3 -0
  9. package/dist/daemon/notifications.d.ts +18 -0
  10. package/dist/daemon/services.d.ts +11 -0
  11. package/dist/db/database-schema.d.ts +1 -2
  12. package/dist/db/index.js +16 -1
  13. package/dist/db/index.js.map +3 -3
  14. package/dist/db/migrations/20231205T000257238Z-remove-did-cache.d.ts +3 -0
  15. package/dist/db/migrations/index.d.ts +1 -0
  16. package/dist/did-cache.d.ts +10 -7
  17. package/dist/index.d.ts +4 -0
  18. package/dist/index.js +1921 -938
  19. package/dist/index.js.map +3 -3
  20. package/dist/indexer/context.d.ts +2 -0
  21. package/dist/indexer/index.d.ts +1 -0
  22. package/dist/lexicon/index.d.ts +12 -0
  23. package/dist/lexicon/lexicons.d.ts +134 -0
  24. package/dist/lexicon/types/com/atproto/admin/deleteAccount.d.ts +25 -0
  25. package/dist/lexicon/types/com/atproto/temp/importRepo.d.ts +32 -0
  26. package/dist/lexicon/types/com/atproto/temp/pushBlob.d.ts +25 -0
  27. package/dist/lexicon/types/com/atproto/temp/transferAccount.d.ts +42 -0
  28. package/dist/logger.d.ts +1 -0
  29. package/dist/redis.d.ts +10 -1
  30. package/dist/services/actor/index.d.ts +18 -4
  31. package/dist/services/actor/views.d.ts +5 -7
  32. package/dist/services/feed/index.d.ts +6 -4
  33. package/dist/services/feed/views.d.ts +5 -4
  34. package/dist/services/index.d.ts +3 -7
  35. package/dist/services/label/index.d.ts +10 -4
  36. package/dist/services/moderation/index.d.ts +0 -1
  37. package/dist/services/types.d.ts +3 -0
  38. package/dist/services/util/notification.d.ts +5 -0
  39. package/dist/services/util/post.d.ts +6 -6
  40. package/dist/util/retry.d.ts +1 -6
  41. package/package.json +6 -6
  42. package/src/api/app/bsky/actor/searchActorsTypeahead.ts +1 -1
  43. package/src/cache/read-through.ts +151 -0
  44. package/src/config.ts +90 -1
  45. package/src/context.ts +7 -7
  46. package/src/daemon/config.ts +60 -0
  47. package/src/daemon/context.ts +27 -0
  48. package/src/daemon/index.ts +78 -0
  49. package/src/daemon/logger.ts +6 -0
  50. package/src/daemon/notifications.ts +54 -0
  51. package/src/daemon/services.ts +22 -0
  52. package/src/db/database-schema.ts +0 -2
  53. package/src/db/migrations/20231205T000257238Z-remove-did-cache.ts +14 -0
  54. package/src/db/migrations/index.ts +1 -0
  55. package/src/did-cache.ts +33 -56
  56. package/src/feed-gen/index.ts +0 -4
  57. package/src/index.ts +55 -16
  58. package/src/indexer/context.ts +5 -0
  59. package/src/indexer/index.ts +10 -7
  60. package/src/lexicon/index.ts +50 -0
  61. package/src/lexicon/lexicons.ts +156 -0
  62. package/src/lexicon/types/com/atproto/admin/deleteAccount.ts +38 -0
  63. package/src/lexicon/types/com/atproto/temp/importRepo.ts +45 -0
  64. package/src/lexicon/types/com/atproto/temp/pushBlob.ts +39 -0
  65. package/src/lexicon/types/com/atproto/temp/transferAccount.ts +62 -0
  66. package/src/logger.ts +2 -0
  67. package/src/redis.ts +43 -3
  68. package/src/services/actor/index.ts +55 -7
  69. package/src/services/actor/views.ts +16 -13
  70. package/src/services/feed/index.ts +27 -13
  71. package/src/services/feed/views.ts +20 -10
  72. package/src/services/index.ts +14 -14
  73. package/src/services/indexing/index.ts +7 -10
  74. package/src/services/indexing/plugins/post.ts +13 -0
  75. package/src/services/label/index.ts +66 -22
  76. package/src/services/moderation/index.ts +1 -1
  77. package/src/services/moderation/status.ts +1 -4
  78. package/src/services/types.ts +4 -0
  79. package/src/services/util/notification.ts +70 -0
  80. package/src/util/retry.ts +1 -44
  81. package/tests/admin/get-repo.test.ts +5 -3
  82. package/tests/admin/moderation.test.ts +2 -2
  83. package/tests/admin/repo-search.test.ts +1 -0
  84. package/tests/algos/hot-classic.test.ts +1 -2
  85. package/tests/auth.test.ts +1 -1
  86. package/tests/auto-moderator/labeler.test.ts +19 -20
  87. package/tests/auto-moderator/takedowns.test.ts +16 -10
  88. package/tests/blob-resolver.test.ts +4 -2
  89. package/tests/daemon.test.ts +191 -0
  90. package/tests/did-cache.test.ts +20 -5
  91. package/tests/handle-invalidation.test.ts +1 -5
  92. package/tests/indexing.test.ts +20 -13
  93. package/tests/redis-cache.test.ts +231 -0
  94. package/tests/seeds/basic.ts +3 -0
  95. package/tests/subscription/repo.test.ts +4 -7
  96. package/tests/views/profile.test.ts +0 -1
  97. package/tests/views/thread.test.ts +73 -78
  98. package/tests/views/threadgating.test.ts +38 -0
  99. package/dist/db/tables/did-cache.d.ts +0 -10
  100. package/dist/feed-gen/best-of-follows.d.ts +0 -29
  101. package/dist/feed-gen/whats-hot.d.ts +0 -29
  102. package/dist/feed-gen/with-friends.d.ts +0 -3
  103. package/dist/label-cache.d.ts +0 -19
  104. package/src/db/tables/did-cache.ts +0 -13
  105. package/src/feed-gen/best-of-follows.ts +0 -77
  106. package/src/feed-gen/whats-hot.ts +0 -101
  107. package/src/feed-gen/with-friends.ts +0 -43
  108. package/src/label-cache.ts +0 -90
  109. package/tests/algos/whats-hot.test.ts +0 -118
  110. package/tests/algos/with-friends.test.ts +0 -145
@@ -820,6 +820,29 @@ export const schemaDict = {
820
820
  },
821
821
  },
822
822
  },
823
+ ComAtprotoAdminDeleteAccount: {
824
+ lexicon: 1,
825
+ id: 'com.atproto.admin.deleteAccount',
826
+ defs: {
827
+ main: {
828
+ type: 'procedure',
829
+ description: 'Delete a user account as an administrator.',
830
+ input: {
831
+ encoding: 'application/json',
832
+ schema: {
833
+ type: 'object',
834
+ required: ['did'],
835
+ properties: {
836
+ did: {
837
+ type: 'string',
838
+ format: 'did',
839
+ },
840
+ },
841
+ },
842
+ },
843
+ },
844
+ },
845
+ },
823
846
  ComAtprotoAdminDisableAccountInvites: {
824
847
  lexicon: 1,
825
848
  id: 'com.atproto.admin.disableAccountInvites',
@@ -3979,6 +4002,135 @@ export const schemaDict = {
3979
4002
  },
3980
4003
  },
3981
4004
  },
4005
+ ComAtprotoTempImportRepo: {
4006
+ lexicon: 1,
4007
+ id: 'com.atproto.temp.importRepo',
4008
+ defs: {
4009
+ main: {
4010
+ type: 'procedure',
4011
+ description:
4012
+ "Gets the did's repo, optionally catching up from a specific revision.",
4013
+ parameters: {
4014
+ type: 'params',
4015
+ required: ['did'],
4016
+ properties: {
4017
+ did: {
4018
+ type: 'string',
4019
+ format: 'did',
4020
+ description: 'The DID of the repo.',
4021
+ },
4022
+ },
4023
+ },
4024
+ input: {
4025
+ encoding: 'application/vnd.ipld.car',
4026
+ },
4027
+ output: {
4028
+ encoding: 'text/plain',
4029
+ },
4030
+ },
4031
+ },
4032
+ },
4033
+ ComAtprotoTempPushBlob: {
4034
+ lexicon: 1,
4035
+ id: 'com.atproto.temp.pushBlob',
4036
+ defs: {
4037
+ main: {
4038
+ type: 'procedure',
4039
+ description:
4040
+ "Gets the did's repo, optionally catching up from a specific revision.",
4041
+ parameters: {
4042
+ type: 'params',
4043
+ required: ['did'],
4044
+ properties: {
4045
+ did: {
4046
+ type: 'string',
4047
+ format: 'did',
4048
+ description: 'The DID of the repo.',
4049
+ },
4050
+ },
4051
+ },
4052
+ input: {
4053
+ encoding: '*/*',
4054
+ },
4055
+ },
4056
+ },
4057
+ },
4058
+ ComAtprotoTempTransferAccount: {
4059
+ lexicon: 1,
4060
+ id: 'com.atproto.temp.transferAccount',
4061
+ defs: {
4062
+ main: {
4063
+ type: 'procedure',
4064
+ description: 'Transfer an account.',
4065
+ input: {
4066
+ encoding: 'application/json',
4067
+ schema: {
4068
+ type: 'object',
4069
+ required: ['handle', 'did', 'plcOp'],
4070
+ properties: {
4071
+ handle: {
4072
+ type: 'string',
4073
+ format: 'handle',
4074
+ },
4075
+ did: {
4076
+ type: 'string',
4077
+ format: 'did',
4078
+ },
4079
+ plcOp: {
4080
+ type: 'unknown',
4081
+ },
4082
+ },
4083
+ },
4084
+ },
4085
+ output: {
4086
+ encoding: 'application/json',
4087
+ schema: {
4088
+ type: 'object',
4089
+ required: ['accessJwt', 'refreshJwt', 'handle', 'did'],
4090
+ properties: {
4091
+ accessJwt: {
4092
+ type: 'string',
4093
+ },
4094
+ refreshJwt: {
4095
+ type: 'string',
4096
+ },
4097
+ handle: {
4098
+ type: 'string',
4099
+ format: 'handle',
4100
+ },
4101
+ did: {
4102
+ type: 'string',
4103
+ format: 'did',
4104
+ },
4105
+ },
4106
+ },
4107
+ },
4108
+ errors: [
4109
+ {
4110
+ name: 'InvalidHandle',
4111
+ },
4112
+ {
4113
+ name: 'InvalidPassword',
4114
+ },
4115
+ {
4116
+ name: 'InvalidInviteCode',
4117
+ },
4118
+ {
4119
+ name: 'HandleNotAvailable',
4120
+ },
4121
+ {
4122
+ name: 'UnsupportedDomain',
4123
+ },
4124
+ {
4125
+ name: 'UnresolvableDid',
4126
+ },
4127
+ {
4128
+ name: 'IncompatibleDidDoc',
4129
+ },
4130
+ ],
4131
+ },
4132
+ },
4133
+ },
3982
4134
  AppBskyActorDefs: {
3983
4135
  lexicon: 1,
3984
4136
  id: 'app.bsky.actor.defs',
@@ -7671,6 +7823,7 @@ export const schemas: LexiconDoc[] = Object.values(schemaDict) as LexiconDoc[]
7671
7823
  export const lexicons: Lexicons = new Lexicons(schemas)
7672
7824
  export const ids = {
7673
7825
  ComAtprotoAdminDefs: 'com.atproto.admin.defs',
7826
+ ComAtprotoAdminDeleteAccount: 'com.atproto.admin.deleteAccount',
7674
7827
  ComAtprotoAdminDisableAccountInvites:
7675
7828
  'com.atproto.admin.disableAccountInvites',
7676
7829
  ComAtprotoAdminDisableInviteCodes: 'com.atproto.admin.disableInviteCodes',
@@ -7746,6 +7899,9 @@ export const ids = {
7746
7899
  ComAtprotoSyncRequestCrawl: 'com.atproto.sync.requestCrawl',
7747
7900
  ComAtprotoSyncSubscribeRepos: 'com.atproto.sync.subscribeRepos',
7748
7901
  ComAtprotoTempFetchLabels: 'com.atproto.temp.fetchLabels',
7902
+ ComAtprotoTempImportRepo: 'com.atproto.temp.importRepo',
7903
+ ComAtprotoTempPushBlob: 'com.atproto.temp.pushBlob',
7904
+ ComAtprotoTempTransferAccount: 'com.atproto.temp.transferAccount',
7749
7905
  AppBskyActorDefs: 'app.bsky.actor.defs',
7750
7906
  AppBskyActorGetPreferences: 'app.bsky.actor.getPreferences',
7751
7907
  AppBskyActorGetProfile: 'app.bsky.actor.getProfile',
@@ -0,0 +1,38 @@
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 } from '@atproto/xrpc-server'
10
+
11
+ export interface QueryParams {}
12
+
13
+ export interface InputSchema {
14
+ did: string
15
+ [k: string]: unknown
16
+ }
17
+
18
+ export interface HandlerInput {
19
+ encoding: 'application/json'
20
+ body: InputSchema
21
+ }
22
+
23
+ export interface HandlerError {
24
+ status: number
25
+ message?: string
26
+ }
27
+
28
+ export type HandlerOutput = HandlerError | void
29
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
30
+ auth: HA
31
+ params: QueryParams
32
+ input: HandlerInput
33
+ req: express.Request
34
+ res: express.Response
35
+ }
36
+ export type Handler<HA extends HandlerAuth = never> = (
37
+ ctx: HandlerReqCtx<HA>,
38
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -0,0 +1,45 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import stream from 'stream'
6
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
7
+ import { lexicons } from '../../../../lexicons'
8
+ import { isObj, hasProp } from '../../../../util'
9
+ import { CID } from 'multiformats/cid'
10
+ import { HandlerAuth } from '@atproto/xrpc-server'
11
+
12
+ export interface QueryParams {
13
+ /** The DID of the repo. */
14
+ did: string
15
+ }
16
+
17
+ export type InputSchema = string | Uint8Array
18
+
19
+ export interface HandlerInput {
20
+ encoding: 'application/vnd.ipld.car'
21
+ body: stream.Readable
22
+ }
23
+
24
+ export interface HandlerSuccess {
25
+ encoding: 'text/plain'
26
+ body: Uint8Array | stream.Readable
27
+ headers?: { [key: string]: string }
28
+ }
29
+
30
+ export interface HandlerError {
31
+ status: number
32
+ message?: string
33
+ }
34
+
35
+ export type HandlerOutput = HandlerError | HandlerSuccess
36
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
37
+ auth: HA
38
+ params: QueryParams
39
+ input: HandlerInput
40
+ req: express.Request
41
+ res: express.Response
42
+ }
43
+ export type Handler<HA extends HandlerAuth = never> = (
44
+ ctx: HandlerReqCtx<HA>,
45
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -0,0 +1,39 @@
1
+ /**
2
+ * GENERATED CODE - DO NOT MODIFY
3
+ */
4
+ import express from 'express'
5
+ import stream from 'stream'
6
+ import { ValidationResult, BlobRef } from '@atproto/lexicon'
7
+ import { lexicons } from '../../../../lexicons'
8
+ import { isObj, hasProp } from '../../../../util'
9
+ import { CID } from 'multiformats/cid'
10
+ import { HandlerAuth } from '@atproto/xrpc-server'
11
+
12
+ export interface QueryParams {
13
+ /** The DID of the repo. */
14
+ did: string
15
+ }
16
+
17
+ export type InputSchema = string | Uint8Array
18
+
19
+ export interface HandlerInput {
20
+ encoding: '*/*'
21
+ body: stream.Readable
22
+ }
23
+
24
+ export interface HandlerError {
25
+ status: number
26
+ message?: string
27
+ }
28
+
29
+ export type HandlerOutput = HandlerError | void
30
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
31
+ auth: HA
32
+ params: QueryParams
33
+ input: HandlerInput
34
+ req: express.Request
35
+ res: express.Response
36
+ }
37
+ export type Handler<HA extends HandlerAuth = never> = (
38
+ ctx: HandlerReqCtx<HA>,
39
+ ) => Promise<HandlerOutput> | HandlerOutput
@@ -0,0 +1,62 @@
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 } from '@atproto/xrpc-server'
10
+
11
+ export interface QueryParams {}
12
+
13
+ export interface InputSchema {
14
+ handle: string
15
+ did: string
16
+ plcOp: {}
17
+ [k: string]: unknown
18
+ }
19
+
20
+ export interface OutputSchema {
21
+ accessJwt: string
22
+ refreshJwt: string
23
+ handle: string
24
+ did: string
25
+ [k: string]: unknown
26
+ }
27
+
28
+ export interface HandlerInput {
29
+ encoding: 'application/json'
30
+ body: InputSchema
31
+ }
32
+
33
+ export interface HandlerSuccess {
34
+ encoding: 'application/json'
35
+ body: OutputSchema
36
+ headers?: { [key: string]: string }
37
+ }
38
+
39
+ export interface HandlerError {
40
+ status: number
41
+ message?: string
42
+ error?:
43
+ | 'InvalidHandle'
44
+ | 'InvalidPassword'
45
+ | 'InvalidInviteCode'
46
+ | 'HandleNotAvailable'
47
+ | 'UnsupportedDomain'
48
+ | 'UnresolvableDid'
49
+ | 'IncompatibleDidDoc'
50
+ }
51
+
52
+ export type HandlerOutput = HandlerError | HandlerSuccess
53
+ export type HandlerReqCtx<HA extends HandlerAuth = never> = {
54
+ auth: HA
55
+ params: QueryParams
56
+ input: HandlerInput
57
+ req: express.Request
58
+ res: express.Response
59
+ }
60
+ export type Handler<HA extends HandlerAuth = never> = (
61
+ ctx: HandlerReqCtx<HA>,
62
+ ) => Promise<HandlerOutput> | HandlerOutput
package/src/logger.ts CHANGED
@@ -3,6 +3,8 @@ import { subsystemLogger } from '@atproto/common'
3
3
 
4
4
  export const dbLogger: ReturnType<typeof subsystemLogger> =
5
5
  subsystemLogger('bsky:db')
6
+ export const cacheLogger: ReturnType<typeof subsystemLogger> =
7
+ subsystemLogger('bsky:cache')
6
8
  export const subLogger: ReturnType<typeof subsystemLogger> =
7
9
  subsystemLogger('bsky:sub')
8
10
  export const labelerLogger: ReturnType<typeof subsystemLogger> =
package/src/redis.ts CHANGED
@@ -11,12 +11,16 @@ export class Redis {
11
11
  name: opts.sentinel,
12
12
  sentinels: opts.hosts.map((h) => addressParts(h, 26379)),
13
13
  password: opts.password,
14
+ db: opts.db,
15
+ commandTimeout: opts.commandTimeout,
14
16
  })
15
17
  } else if ('host' in opts) {
16
18
  assert(opts.host)
17
19
  this.driver = new RedisDriver({
18
20
  ...addressParts(opts.host),
19
21
  password: opts.password,
22
+ db: opts.db,
23
+ commandTimeout: opts.commandTimeout,
20
24
  })
21
25
  } else {
22
26
  assert(opts.driver)
@@ -25,6 +29,10 @@ export class Redis {
25
29
  this.namespace = opts.namespace
26
30
  }
27
31
 
32
+ withNamespace(namespace: string): Redis {
33
+ return new Redis({ driver: this.driver, namespace })
34
+ }
35
+
28
36
  async readStreams(
29
37
  streams: StreamRef[],
30
38
  opts: { count: number; blockMs?: number },
@@ -97,8 +105,38 @@ export class Redis {
97
105
  return await this.driver.get(this.ns(key))
98
106
  }
99
107
 
100
- async set(key: string, val: string | number) {
101
- await this.driver.set(this.ns(key), val)
108
+ async set(key: string, val: string | number, ttlMs?: number) {
109
+ if (ttlMs !== undefined) {
110
+ await this.driver.set(this.ns(key), val, 'PX', ttlMs)
111
+ } else {
112
+ await this.driver.set(this.ns(key), val)
113
+ }
114
+ }
115
+
116
+ async getMulti(keys: string[]) {
117
+ const namespaced = keys.map((k) => this.ns(k))
118
+ const got = await this.driver.mget(...namespaced)
119
+ const results = {}
120
+ for (let i = 0; i < keys.length; i++) {
121
+ const key = keys[i]
122
+ results[key] = got[i]
123
+ }
124
+ return results
125
+ }
126
+
127
+ async setMulti(vals: Record<string, string | number>, ttlMs?: number) {
128
+ if (Object.keys(vals).length === 0) {
129
+ return
130
+ }
131
+ let builder = this.driver.multi({ pipeline: true })
132
+ for (const key of Object.keys(vals)) {
133
+ if (ttlMs !== undefined) {
134
+ builder = builder.set(this.ns(key), vals[key], 'PX', ttlMs)
135
+ } else {
136
+ builder = builder.set(this.ns(key), vals[key])
137
+ }
138
+ }
139
+ await builder.exec()
102
140
  }
103
141
 
104
142
  async del(key: string) {
@@ -152,9 +190,11 @@ export type RedisOptions = (
152
190
  ) & {
153
191
  password?: string
154
192
  namespace?: string
193
+ db?: number
194
+ commandTimeout?: number
155
195
  }
156
196
 
157
- function addressParts(
197
+ export function addressParts(
158
198
  addr: string,
159
199
  defaultPort = 6379,
160
200
  ): { host: string; port: number } {
@@ -1,27 +1,37 @@
1
1
  import { sql } from 'kysely'
2
+ import { wait } from '@atproto/common'
2
3
  import { Database } from '../../db'
3
4
  import { notSoftDeletedClause } from '../../db/util'
4
5
  import { ActorViews } from './views'
5
6
  import { ImageUriBuilder } from '../../image/uri'
6
7
  import { Actor } from '../../db/tables/actor'
7
- import { LabelCache } from '../../label-cache'
8
8
  import { TimeCidKeyset, paginate } from '../../db/pagination'
9
9
  import { SearchKeyset, getUserSearchQuery } from '../util/search'
10
+ import { FromDb } from '../types'
11
+ import { GraphService } from '../graph'
12
+ import { LabelService } from '../label'
10
13
 
11
14
  export * from './types'
12
15
 
13
16
  export class ActorService {
17
+ views: ActorViews
18
+
14
19
  constructor(
15
20
  public db: Database,
16
21
  public imgUriBuilder: ImageUriBuilder,
17
- public labelCache: LabelCache,
18
- ) {}
19
-
20
- static creator(imgUriBuilder: ImageUriBuilder, labelCache: LabelCache) {
21
- return (db: Database) => new ActorService(db, imgUriBuilder, labelCache)
22
+ private graph: FromDb<GraphService>,
23
+ private label: FromDb<LabelService>,
24
+ ) {
25
+ this.views = new ActorViews(this.db, this.imgUriBuilder, graph, label)
22
26
  }
23
27
 
24
- views = new ActorViews(this.db, this.imgUriBuilder, this.labelCache)
28
+ static creator(
29
+ imgUriBuilder: ImageUriBuilder,
30
+ graph: FromDb<GraphService>,
31
+ label: FromDb<LabelService>,
32
+ ) {
33
+ return (db: Database) => new ActorService(db, imgUriBuilder, graph, label)
34
+ }
25
35
 
26
36
  async getActorDid(handleOrDid: string): Promise<string | null> {
27
37
  if (handleOrDid.startsWith('did:')) {
@@ -144,6 +154,44 @@ export class ActorService {
144
154
  .executeTakeFirst()
145
155
  return res?.repoRev ?? null
146
156
  }
157
+
158
+ async *all(
159
+ opts: {
160
+ batchSize?: number
161
+ forever?: boolean
162
+ cooldownMs?: number
163
+ startFromDid?: string
164
+ } = {},
165
+ ) {
166
+ const {
167
+ cooldownMs = 1000,
168
+ batchSize = 1000,
169
+ forever = false,
170
+ startFromDid,
171
+ } = opts
172
+ const baseQuery = this.db.db
173
+ .selectFrom('actor')
174
+ .selectAll()
175
+ .orderBy('did')
176
+ .limit(batchSize)
177
+ while (true) {
178
+ let cursor = startFromDid
179
+ do {
180
+ const actors = cursor
181
+ ? await baseQuery.where('did', '>', cursor).execute()
182
+ : await baseQuery.execute()
183
+ for (const actor of actors) {
184
+ yield actor
185
+ }
186
+ cursor = actors.at(-1)?.did
187
+ } while (cursor)
188
+ if (forever) {
189
+ await wait(cooldownMs)
190
+ } else {
191
+ return
192
+ }
193
+ }
194
+ }
147
195
  }
148
196
 
149
197
  type ActorResult = Actor
@@ -11,7 +11,6 @@ import { Actor } from '../../db/tables/actor'
11
11
  import { ImageUriBuilder } from '../../image/uri'
12
12
  import { LabelService, Labels, getSelfLabels } from '../label'
13
13
  import { BlockAndMuteState, GraphService } from '../graph'
14
- import { LabelCache } from '../../label-cache'
15
14
  import {
16
15
  ActorInfoMap,
17
16
  ProfileDetailHydrationState,
@@ -21,17 +20,24 @@ import {
21
20
  toMapByDid,
22
21
  } from './types'
23
22
  import { ListInfoMap } from '../graph/types'
23
+ import { FromDb } from '../types'
24
24
 
25
25
  export class ActorViews {
26
+ services: {
27
+ label: LabelService
28
+ graph: GraphService
29
+ }
30
+
26
31
  constructor(
27
32
  private db: Database,
28
33
  private imgUriBuilder: ImageUriBuilder,
29
- private labelCache: LabelCache,
30
- ) {}
31
-
32
- services = {
33
- label: LabelService.creator(this.labelCache)(this.db),
34
- graph: GraphService.creator(this.imgUriBuilder)(this.db),
34
+ private graph: FromDb<GraphService>,
35
+ private label: FromDb<LabelService>,
36
+ ) {
37
+ this.services = {
38
+ label: label(db),
39
+ graph: graph(db),
40
+ }
35
41
  }
36
42
 
37
43
  async profiles(
@@ -51,7 +57,7 @@ export class ActorViews {
51
57
  async profilesBasic(
52
58
  results: (ActorResult | string)[],
53
59
  viewer: string | null,
54
- opts?: { omitLabels?: boolean; includeSoftDeleted?: boolean },
60
+ opts?: { includeSoftDeleted?: boolean },
55
61
  ): Promise<ActorInfoMap> {
56
62
  if (results.length === 0) return {}
57
63
  const dids = results.map((res) => (typeof res === 'string' ? res : res.did))
@@ -59,7 +65,7 @@ export class ActorViews {
59
65
  viewer,
60
66
  includeSoftDeleted: opts?.includeSoftDeleted,
61
67
  })
62
- return this.profileBasicPresentation(dids, hydrated, viewer, opts)
68
+ return this.profileBasicPresentation(dids, hydrated, viewer)
63
69
  }
64
70
 
65
71
  async profilesList(
@@ -349,9 +355,6 @@ export class ActorViews {
349
355
  dids: string[],
350
356
  state: ProfileHydrationState,
351
357
  viewer: string | null,
352
- opts?: {
353
- omitLabels?: boolean
354
- },
355
358
  ): ProfileViewMap {
356
359
  const result = this.profilePresentation(dids, state, viewer)
357
360
  return Object.values(result).reduce((acc, prof) => {
@@ -361,7 +364,7 @@ export class ActorViews {
361
364
  displayName: prof.displayName,
362
365
  avatar: prof.avatar,
363
366
  viewer: prof.viewer,
364
- labels: opts?.omitLabels ? undefined : prof.labels,
367
+ labels: prof.labels,
365
368
  }
366
369
  acc[prof.did] = profileBasic
367
370
  return acc
@@ -1,6 +1,7 @@
1
1
  import { sql } from 'kysely'
2
2
  import { AtUri } from '@atproto/syntax'
3
3
  import { jsonStringToLex } from '@atproto/lexicon'
4
+ import { mapDefined } from '@atproto/common'
4
5
  import { Database } from '../../db'
5
6
  import { countAll, noMatch, notSoftDeletedClause } from '../../db/util'
6
7
  import { ImageUriBuilder } from '../../image/uri'
@@ -42,29 +43,42 @@ import {
42
43
  RelationshipPair,
43
44
  } from '../graph'
44
45
  import { FeedViews } from './views'
45
- import { LabelCache } from '../../label-cache'
46
46
  import { threadgateToPostUri, postToThreadgateUri } from './util'
47
- import { mapDefined } from '@atproto/common'
47
+ import { FromDb } from '../types'
48
48
 
49
49
  export * from './types'
50
50
 
51
51
  export class FeedService {
52
+ views: FeedViews
53
+ services: {
54
+ label: LabelService
55
+ actor: ActorService
56
+ graph: GraphService
57
+ }
58
+
52
59
  constructor(
53
60
  public db: Database,
54
61
  public imgUriBuilder: ImageUriBuilder,
55
- public labelCache: LabelCache,
56
- ) {}
57
-
58
- views = new FeedViews(this.db, this.imgUriBuilder, this.labelCache)
59
-
60
- services = {
61
- label: LabelService.creator(this.labelCache)(this.db),
62
- actor: ActorService.creator(this.imgUriBuilder, this.labelCache)(this.db),
63
- graph: GraphService.creator(this.imgUriBuilder)(this.db),
62
+ private actor: FromDb<ActorService>,
63
+ private label: FromDb<LabelService>,
64
+ private graph: FromDb<GraphService>,
65
+ ) {
66
+ this.views = new FeedViews(this.db, this.imgUriBuilder, actor, graph)
67
+ this.services = {
68
+ label: label(this.db),
69
+ actor: actor(this.db),
70
+ graph: graph(this.db),
71
+ }
64
72
  }
65
73
 
66
- static creator(imgUriBuilder: ImageUriBuilder, labelCache: LabelCache) {
67
- return (db: Database) => new FeedService(db, imgUriBuilder, labelCache)
74
+ static creator(
75
+ imgUriBuilder: ImageUriBuilder,
76
+ actor: FromDb<ActorService>,
77
+ label: FromDb<LabelService>,
78
+ graph: FromDb<GraphService>,
79
+ ) {
80
+ return (db: Database) =>
81
+ new FeedService(db, imgUriBuilder, actor, label, graph)
68
82
  }
69
83
 
70
84
  selectPostQb() {