@atproto/bsky 0.0.249 → 0.0.251
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.
- package/CHANGELOG.md +30 -0
- package/dist/api/age-assurance/const.d.ts.map +1 -1
- package/dist/api/age-assurance/const.js +19 -0
- package/dist/api/age-assurance/const.js.map +1 -1
- package/dist/api/age-assurance/kws/age-verified.d.ts +20 -0
- package/dist/api/age-assurance/kws/age-verified.d.ts.map +1 -1
- package/dist/api/age-assurance/kws/age-verified.js +6 -0
- package/dist/api/age-assurance/kws/age-verified.js.map +1 -1
- package/dist/api/app/bsky/feed/getFeed.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getFeed.js +4 -0
- package/dist/api/app/bsky/feed/getFeed.js.map +1 -1
- package/dist/api/app/bsky/feed/searchPosts.js +5 -0
- package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
- package/dist/api/app/bsky/feed/searchPostsV2.js +5 -0
- package/dist/api/app/bsky/feed/searchPostsV2.js.map +1 -1
- package/dist/api/app/bsky/graph/muteActor.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/muteActor.js +3 -0
- package/dist/api/app/bsky/graph/muteActor.js.map +1 -1
- package/dist/api/kws/types.d.ts +33 -0
- package/dist/api/kws/types.d.ts.map +1 -1
- package/dist/api/kws/types.js +3 -0
- package/dist/api/kws/types.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -1
- package/dist/data-plane/index.d.ts +0 -1
- package/dist/data-plane/index.d.ts.map +1 -1
- package/dist/data-plane/index.js +0 -1
- package/dist/data-plane/index.js.map +1 -1
- package/dist/data-plane/server/bsync-subscription.d.ts +47 -0
- package/dist/data-plane/server/bsync-subscription.d.ts.map +1 -0
- package/dist/data-plane/server/bsync-subscription.js +461 -0
- package/dist/data-plane/server/bsync-subscription.js.map +1 -0
- package/dist/data-plane/server/index.d.ts +1 -0
- package/dist/data-plane/server/index.d.ts.map +1 -1
- package/dist/data-plane/server/index.js +1 -0
- package/dist/data-plane/server/index.js.map +1 -1
- package/dist/hydration/feed.d.ts +1 -2
- package/dist/hydration/feed.d.ts.map +1 -1
- package/dist/hydration/feed.js.map +1 -1
- package/dist/lexicons/app/bsky/ageassurance/defs.defs.d.ts +4 -0
- package/dist/lexicons/app/bsky/ageassurance/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/ageassurance/defs.defs.js +3 -0
- package/dist/lexicons/app/bsky/ageassurance/defs.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/convo/defs.defs.js +1 -1
- package/dist/lexicons/chat/bsky/convo/defs.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/group/editGroup.defs.d.ts +4 -4
- package/dist/lexicons/chat/bsky/group/editGroup.defs.js +2 -2
- package/dist/lexicons/chat/bsky/group/editGroup.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/moderation/defs.defs.js +1 -1
- package/dist/lexicons/chat/bsky/moderation/defs.defs.js.map +1 -1
- package/package.json +10 -10
- package/src/api/age-assurance/const.ts +19 -0
- package/src/api/age-assurance/kws/age-verified.test.ts +98 -0
- package/src/api/age-assurance/kws/age-verified.ts +6 -0
- package/src/api/app/bsky/feed/getFeed.ts +5 -0
- package/src/api/app/bsky/feed/searchPosts.ts +7 -0
- package/src/api/app/bsky/feed/searchPostsV2.ts +7 -0
- package/src/api/app/bsky/graph/muteActor.ts +3 -0
- package/src/api/kws/types.ts +11 -0
- package/src/api/kws/util.test.ts +50 -0
- package/src/config.ts +9 -0
- package/src/data-plane/index.ts +0 -1
- package/src/data-plane/{bsync/index.ts → server/bsync-subscription.ts} +245 -121
- package/src/data-plane/server/index.ts +1 -0
- package/src/hydration/feed.ts +1 -2
- package/tests/__snapshots__/feed-generation.test.ts.snap +62 -15
- package/tests/data-plane/thread-mutes.test.ts +2 -0
- package/tests/feed-generation.test.ts +55 -9
- package/tests/stash.test.ts +6 -1
- package/tests/views/age-assurance-v2.test.ts +3 -0
- package/tests/views/age-assurance.test.ts +8 -0
- package/tests/views/bookmarks.test.ts +19 -0
- package/tests/views/drafts.test.ts +25 -2
- package/tests/views/mute-lists.test.ts +2 -0
- package/tests/views/mutes.test.ts +1 -0
- package/tests/views/notifications.test.ts +16 -0
- package/tests/views/post-search.test.ts +81 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/data-plane/bsync/index.d.ts +0 -11
- package/dist/data-plane/bsync/index.d.ts.map +0 -1
- package/dist/data-plane/bsync/index.js +0 -376
- package/dist/data-plane/bsync/index.js.map +0 -1
|
@@ -1,59 +1,164 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
|
-
import events from 'node:events'
|
|
3
|
-
import http from 'node:http'
|
|
4
|
-
import { ConnectRouter } from '@connectrpc/connect'
|
|
5
|
-
import { expressConnectMiddleware } from '@connectrpc/connect-express'
|
|
6
|
-
import express from 'express'
|
|
7
|
-
// eslint-disable-next-line import/default
|
|
8
|
-
import httpTerminator from 'http-terminator'
|
|
9
|
-
import { TID } from '@atproto/common'
|
|
10
2
|
import { lexParse } from '@atproto/lex'
|
|
11
3
|
import { AtUri } from '@atproto/syntax'
|
|
4
|
+
import { BsyncClient, authWithApiKey, createBsyncClient } from '../../bsync.js'
|
|
5
|
+
import { ServerConfig } from '../../config.js'
|
|
12
6
|
import { app } from '../../lexicons/index.js'
|
|
13
|
-
import {
|
|
14
|
-
import { Service } from '../../proto/bsync_connect.js'
|
|
7
|
+
import { subLogger as log } from '../../logger.js'
|
|
15
8
|
import {
|
|
16
9
|
Method,
|
|
10
|
+
MuteOperation,
|
|
17
11
|
MuteOperation_Type,
|
|
18
|
-
|
|
12
|
+
NotifOperation,
|
|
13
|
+
Operation,
|
|
19
14
|
} from '../../proto/bsync_pb.js'
|
|
20
15
|
import { Namespaces } from '../../stash.js'
|
|
21
|
-
import { Database } from '
|
|
22
|
-
import { countAll, excluded } from '
|
|
16
|
+
import { Database } from './db/index.js'
|
|
17
|
+
import { countAll, excluded } from './db/util.js'
|
|
23
18
|
|
|
24
|
-
export
|
|
25
|
-
|
|
19
|
+
export type BsyncCursors = {
|
|
20
|
+
op?: string
|
|
21
|
+
mute?: string
|
|
22
|
+
notif?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class BsyncSubscription {
|
|
26
|
+
private ac: AbortController | undefined
|
|
27
|
+
private pendingScans = new Set<Promise<void>>()
|
|
28
|
+
private bsyncClient: BsyncClient
|
|
29
|
+
private db: Database
|
|
30
|
+
// Latest cursor processed for each stream, used to wait until caught up.
|
|
31
|
+
private cursors: BsyncCursors = {}
|
|
32
|
+
|
|
33
|
+
constructor(public opts: { db: Database; config: ServerConfig }) {
|
|
34
|
+
const { config, db } = opts
|
|
35
|
+
|
|
36
|
+
this.db = db
|
|
37
|
+
this.bsyncClient = createBsyncClient({
|
|
38
|
+
baseUrl: config.bsyncUrl,
|
|
39
|
+
httpVersion: config.bsyncHttpVersion ?? '2',
|
|
40
|
+
nodeOptions: { rejectUnauthorized: !config.bsyncIgnoreBadTls },
|
|
41
|
+
interceptors: config.bsyncApiKey
|
|
42
|
+
? [authWithApiKey(config.bsyncApiKey)]
|
|
43
|
+
: [],
|
|
44
|
+
})
|
|
45
|
+
}
|
|
26
46
|
|
|
27
|
-
|
|
28
|
-
this.
|
|
47
|
+
get running() {
|
|
48
|
+
return !!this.ac
|
|
29
49
|
}
|
|
30
50
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
51
|
+
start() {
|
|
52
|
+
if (this.ac) return
|
|
53
|
+
this.ac = new AbortController()
|
|
54
|
+
this.scanMuteOperations()
|
|
55
|
+
this.scanNotifOperations()
|
|
56
|
+
this.scanOperations()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Waits until the subscription has processed every operation that existed in
|
|
61
|
+
* bsync at the time `targets` was read (one cursor per stream). Returns as
|
|
62
|
+
* soon as the cursors catch up - it does not wait for the bsync long-poll
|
|
63
|
+
* timeout. Intended for tests/dev.
|
|
64
|
+
*/
|
|
65
|
+
async processAll(targets: BsyncCursors, timeout = 5000) {
|
|
66
|
+
if (!this.ac) return
|
|
67
|
+
const start = Date.now()
|
|
68
|
+
while (!this.isCaughtUp(targets)) {
|
|
69
|
+
if (Date.now() - start > timeout) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`bsync subscription was not caught up within ${timeout}ms`,
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
await wait(5)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private isCaughtUp(targets: BsyncCursors) {
|
|
79
|
+
return (
|
|
80
|
+
gteCursor(this.cursors.op, targets.op) &&
|
|
81
|
+
gteCursor(this.cursors.mute, targets.mute) &&
|
|
82
|
+
gteCursor(this.cursors.notif, targets.notif)
|
|
83
|
+
)
|
|
38
84
|
}
|
|
39
85
|
|
|
40
86
|
async destroy() {
|
|
41
|
-
|
|
87
|
+
// Aborting cancels any in-flight long-poll scan immediately, so we don't
|
|
88
|
+
// have to wait for the bsync long-poll timeout to elapse.
|
|
89
|
+
this.ac?.abort()
|
|
90
|
+
this.ac = undefined
|
|
91
|
+
// Wait for the ongoing scans to unwind cleanly.
|
|
92
|
+
await Promise.all(this.pendingScans)
|
|
42
93
|
}
|
|
43
94
|
|
|
44
|
-
|
|
45
|
-
|
|
95
|
+
/**
|
|
96
|
+
* starts a scanner in the background, killing it when this instance is
|
|
97
|
+
* destroyed, and storing its promise in `this.pendingScans` to allow
|
|
98
|
+
* awaiting.
|
|
99
|
+
*/
|
|
100
|
+
private startScanning(
|
|
101
|
+
scanner: (
|
|
102
|
+
cursor: undefined | string,
|
|
103
|
+
signal: AbortSignal,
|
|
104
|
+
) => Promise<string | undefined>,
|
|
105
|
+
) {
|
|
106
|
+
if (!this.ac) return
|
|
107
|
+
const signal = this.ac.signal
|
|
108
|
+
|
|
109
|
+
const scanPromise = this.runScanner(scanner, signal)
|
|
110
|
+
|
|
111
|
+
this.pendingScans.add(scanPromise)
|
|
112
|
+
void scanPromise
|
|
113
|
+
.catch(() => {})
|
|
114
|
+
.finally(() => {
|
|
115
|
+
this.pendingScans.delete(scanPromise)
|
|
116
|
+
})
|
|
46
117
|
}
|
|
47
|
-
}
|
|
48
118
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
119
|
+
private async runScanner(
|
|
120
|
+
scanner: (
|
|
121
|
+
cursor: undefined | string,
|
|
122
|
+
signal: AbortSignal,
|
|
123
|
+
) => Promise<string | undefined>,
|
|
124
|
+
signal: AbortSignal,
|
|
125
|
+
) {
|
|
126
|
+
let cursor: undefined | string
|
|
127
|
+
|
|
128
|
+
while (!signal.aborted) {
|
|
129
|
+
try {
|
|
130
|
+
cursor = await scanner(cursor, signal)
|
|
131
|
+
} catch (err) {
|
|
132
|
+
if (signal.aborted) break
|
|
133
|
+
|
|
134
|
+
log.error({ err }, 'error in bsync scan')
|
|
135
|
+
|
|
136
|
+
// retry in a bit, but bail out early if we're shutting down
|
|
137
|
+
await wait(100, signal)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private scanMuteOperations() {
|
|
143
|
+
this.startScanning(async (cursor, signal) => {
|
|
144
|
+
const res = await this.bsyncClient.scanMuteOperations(
|
|
145
|
+
{ cursor },
|
|
146
|
+
{ signal },
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
await this.processMuteOperations(res.operations)
|
|
150
|
+
this.cursors.mute = res.cursor
|
|
151
|
+
|
|
152
|
+
return res.cursor
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private async processMuteOperations(operations: MuteOperation[]) {
|
|
157
|
+
for (const op of operations) {
|
|
158
|
+
const { type, actorDid, subject } = op
|
|
53
159
|
if (type === MuteOperation_Type.ADD) {
|
|
54
160
|
if (subject.startsWith('did:')) {
|
|
55
|
-
|
|
56
|
-
await db.db
|
|
161
|
+
await this.db.db
|
|
57
162
|
.insertInto('mute')
|
|
58
163
|
.values({
|
|
59
164
|
mutedByDid: actorDid,
|
|
@@ -65,7 +170,7 @@ const createRoutes = (db: Database) => (router: ConnectRouter) =>
|
|
|
65
170
|
} else {
|
|
66
171
|
const uri = new AtUri(subject)
|
|
67
172
|
if (uri.collection === app.bsky.graph.list.$type) {
|
|
68
|
-
await db.db
|
|
173
|
+
await this.db.db
|
|
69
174
|
.insertInto('list_mute')
|
|
70
175
|
.values({
|
|
71
176
|
mutedByDid: actorDid,
|
|
@@ -75,7 +180,7 @@ const createRoutes = (db: Database) => (router: ConnectRouter) =>
|
|
|
75
180
|
.onConflict((oc) => oc.doNothing())
|
|
76
181
|
.execute()
|
|
77
182
|
} else {
|
|
78
|
-
await db.db
|
|
183
|
+
await this.db.db
|
|
79
184
|
.insertInto('thread_mute')
|
|
80
185
|
.values({
|
|
81
186
|
mutedByDid: actorDid,
|
|
@@ -88,7 +193,7 @@ const createRoutes = (db: Database) => (router: ConnectRouter) =>
|
|
|
88
193
|
}
|
|
89
194
|
} else if (type === MuteOperation_Type.REMOVE) {
|
|
90
195
|
if (subject.startsWith('did:')) {
|
|
91
|
-
await db.db
|
|
196
|
+
await this.db.db
|
|
92
197
|
.deleteFrom('mute')
|
|
93
198
|
.where('mutedByDid', '=', actorDid)
|
|
94
199
|
.where('subjectDid', '=', subject)
|
|
@@ -96,13 +201,13 @@ const createRoutes = (db: Database) => (router: ConnectRouter) =>
|
|
|
96
201
|
} else {
|
|
97
202
|
const uri = new AtUri(subject)
|
|
98
203
|
if (uri.collection === app.bsky.graph.list.$type) {
|
|
99
|
-
await db.db
|
|
204
|
+
await this.db.db
|
|
100
205
|
.deleteFrom('list_mute')
|
|
101
206
|
.where('mutedByDid', '=', actorDid)
|
|
102
207
|
.where('listUri', '=', subject)
|
|
103
208
|
.execute()
|
|
104
209
|
} else {
|
|
105
|
-
await db.db
|
|
210
|
+
await this.db.db
|
|
106
211
|
.deleteFrom('thread_mute')
|
|
107
212
|
.where('mutedByDid', '=', actorDid)
|
|
108
213
|
.where('rootUri', '=', subject)
|
|
@@ -110,27 +215,37 @@ const createRoutes = (db: Database) => (router: ConnectRouter) =>
|
|
|
110
215
|
}
|
|
111
216
|
}
|
|
112
217
|
} else if (type === MuteOperation_Type.CLEAR) {
|
|
113
|
-
await db.db
|
|
218
|
+
await this.db.db
|
|
114
219
|
.deleteFrom('mute')
|
|
115
220
|
.where('mutedByDid', '=', actorDid)
|
|
116
221
|
.execute()
|
|
117
|
-
await db.db
|
|
222
|
+
await this.db.db
|
|
118
223
|
.deleteFrom('list_mute')
|
|
119
224
|
.where('mutedByDid', '=', actorDid)
|
|
120
225
|
.execute()
|
|
121
226
|
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private scanNotifOperations() {
|
|
231
|
+
this.startScanning(async (cursor, signal) => {
|
|
232
|
+
const res = await this.bsyncClient.scanNotifOperations(
|
|
233
|
+
{ cursor },
|
|
234
|
+
{ signal },
|
|
235
|
+
)
|
|
122
236
|
|
|
123
|
-
|
|
124
|
-
|
|
237
|
+
await this.processNotifOperations(res.operations)
|
|
238
|
+
this.cursors.notif = res.cursor
|
|
125
239
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
240
|
+
return res.cursor
|
|
241
|
+
})
|
|
242
|
+
}
|
|
129
243
|
|
|
130
|
-
|
|
131
|
-
|
|
244
|
+
private async processNotifOperations(operations: NotifOperation[]) {
|
|
245
|
+
for (const op of operations) {
|
|
246
|
+
const { actorDid, priority } = op
|
|
132
247
|
if (priority !== undefined) {
|
|
133
|
-
await db.db
|
|
248
|
+
await this.db.db
|
|
134
249
|
.insertInto('actor_state')
|
|
135
250
|
.values({
|
|
136
251
|
did: actorDid,
|
|
@@ -142,79 +257,70 @@ const createRoutes = (db: Database) => (router: ConnectRouter) =>
|
|
|
142
257
|
)
|
|
143
258
|
.execute()
|
|
144
259
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private scanOperations() {
|
|
264
|
+
this.startScanning(async (cursor, signal) => {
|
|
265
|
+
const res = await this.bsyncClient.scanOperations({ cursor }, { signal })
|
|
266
|
+
|
|
267
|
+
await this.processOperations(res.operations)
|
|
268
|
+
this.cursors.op = res.cursor
|
|
269
|
+
|
|
270
|
+
return res.cursor
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private async processOperations(operations: Operation[]) {
|
|
275
|
+
for (const op of operations) {
|
|
276
|
+
const { namespace } = op
|
|
160
277
|
|
|
161
278
|
const now = new Date().toISOString()
|
|
162
279
|
|
|
163
280
|
// Index all items into private_data.
|
|
164
|
-
await handleGenericOperation(db,
|
|
281
|
+
await handleGenericOperation(this.db, op, now)
|
|
165
282
|
|
|
166
|
-
// Maintain bespoke indexes for certain namespaces.
|
|
283
|
+
// Maintain bespoke indexes for certain namespaces. A failure here must
|
|
284
|
+
// not stall the stream, so log and move on (matching bsync behavior).
|
|
167
285
|
try {
|
|
168
286
|
if (
|
|
169
287
|
namespace ===
|
|
170
288
|
Namespaces.AppBskyNotificationDefsSubjectActivitySubscription.$type
|
|
171
289
|
) {
|
|
172
|
-
await handleSubjectActivitySubscriptionOperation(db,
|
|
290
|
+
await handleSubjectActivitySubscriptionOperation(this.db, op, now)
|
|
173
291
|
} else if (
|
|
174
292
|
namespace === Namespaces.AppBskyUnspeccedDefsAgeAssuranceEvent.$type
|
|
175
293
|
) {
|
|
176
|
-
await handleAgeAssuranceEventOperation(db,
|
|
294
|
+
await handleAgeAssuranceEventOperation(this.db, op, now)
|
|
177
295
|
} else if (
|
|
178
296
|
namespace === Namespaces.AppBskyAgeassuranceDefsEvent.$type
|
|
179
297
|
) {
|
|
180
|
-
await handleAgeAssuranceV2EventOperation(db,
|
|
298
|
+
await handleAgeAssuranceV2EventOperation(this.db, op, now)
|
|
181
299
|
} else if (namespace === Namespaces.AppBskyBookmarkDefsBookmark.$type) {
|
|
182
|
-
await handleBookmarkOperation(db,
|
|
300
|
+
await handleBookmarkOperation(this.db, op, now)
|
|
183
301
|
} else if (namespace === Namespaces.AppBskyDraftDefsDraftWithId.$type) {
|
|
184
|
-
await handleDraftOperation(db,
|
|
302
|
+
await handleDraftOperation(this.db, op, now)
|
|
185
303
|
}
|
|
186
304
|
} catch (err) {
|
|
187
|
-
|
|
305
|
+
log.warn({ err, namespace }, 'bsync put operation indexing failed')
|
|
188
306
|
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
189
310
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
key,
|
|
196
|
-
method,
|
|
197
|
-
payload,
|
|
198
|
-
},
|
|
199
|
-
}
|
|
200
|
-
},
|
|
201
|
-
|
|
202
|
-
async scanOperations() {
|
|
203
|
-
throw new Error('not implemented')
|
|
204
|
-
},
|
|
205
|
-
|
|
206
|
-
async ping() {
|
|
207
|
-
return {}
|
|
208
|
-
},
|
|
209
|
-
})
|
|
311
|
+
type HandleOperation = (
|
|
312
|
+
db: Database,
|
|
313
|
+
op: Operation,
|
|
314
|
+
now: string,
|
|
315
|
+
) => Promise<void>
|
|
210
316
|
|
|
211
317
|
// upsert into or remove from private_data
|
|
212
|
-
const handleGenericOperation = async (
|
|
318
|
+
const handleGenericOperation: HandleOperation = async (
|
|
213
319
|
db: Database,
|
|
214
|
-
|
|
320
|
+
op: Operation,
|
|
215
321
|
now: string,
|
|
216
322
|
) => {
|
|
217
|
-
const { actorDid, namespace, key, method, payload } =
|
|
323
|
+
const { actorDid, namespace, key, method, payload } = op
|
|
218
324
|
if (method === Method.CREATE || method === Method.UPDATE) {
|
|
219
325
|
await db.db
|
|
220
326
|
.insertInto('private_data')
|
|
@@ -245,31 +351,33 @@ const handleGenericOperation = async (
|
|
|
245
351
|
}
|
|
246
352
|
}
|
|
247
353
|
|
|
248
|
-
const handleSubjectActivitySubscriptionOperation = async (
|
|
354
|
+
const handleSubjectActivitySubscriptionOperation: HandleOperation = async (
|
|
249
355
|
db: Database,
|
|
250
|
-
|
|
356
|
+
op: Operation,
|
|
251
357
|
now: string,
|
|
252
358
|
) => {
|
|
253
|
-
const { actorDid, key, method, payload } =
|
|
359
|
+
const { actorDid, key, method, payload } = op
|
|
254
360
|
|
|
255
361
|
if (method === Method.DELETE) {
|
|
256
|
-
|
|
362
|
+
await db.db
|
|
257
363
|
.deleteFrom('activity_subscription')
|
|
258
364
|
.where('creator', '=', actorDid)
|
|
259
365
|
.where('key', '=', key)
|
|
260
366
|
.execute()
|
|
367
|
+
return
|
|
261
368
|
}
|
|
262
369
|
|
|
263
|
-
const json = Buffer.from(payload).toString('utf8')
|
|
264
370
|
const parsed =
|
|
265
|
-
lexParse<app.bsky.notification.defs.SubjectActivitySubscription>(
|
|
371
|
+
lexParse<app.bsky.notification.defs.SubjectActivitySubscription>(
|
|
372
|
+
Buffer.from(payload).toString('utf8'),
|
|
373
|
+
)
|
|
266
374
|
const {
|
|
267
375
|
subject,
|
|
268
376
|
activitySubscription: { post, reply },
|
|
269
377
|
} = parsed
|
|
270
378
|
|
|
271
379
|
if (method === Method.CREATE) {
|
|
272
|
-
|
|
380
|
+
await db.db
|
|
273
381
|
.insertInto('activity_subscription')
|
|
274
382
|
.values({
|
|
275
383
|
creator: actorDid,
|
|
@@ -280,9 +388,10 @@ const handleSubjectActivitySubscriptionOperation = async (
|
|
|
280
388
|
reply,
|
|
281
389
|
})
|
|
282
390
|
.execute()
|
|
391
|
+
return
|
|
283
392
|
}
|
|
284
393
|
|
|
285
|
-
|
|
394
|
+
await db.db
|
|
286
395
|
.updateTable('activity_subscription')
|
|
287
396
|
.where('creator', '=', actorDid)
|
|
288
397
|
.where('key', '=', key)
|
|
@@ -294,18 +403,16 @@ const handleSubjectActivitySubscriptionOperation = async (
|
|
|
294
403
|
.execute()
|
|
295
404
|
}
|
|
296
405
|
|
|
297
|
-
const handleAgeAssuranceEventOperation = async (
|
|
406
|
+
const handleAgeAssuranceEventOperation: HandleOperation = async (
|
|
298
407
|
db: Database,
|
|
299
|
-
|
|
300
|
-
_now: string,
|
|
408
|
+
op: Operation,
|
|
301
409
|
) => {
|
|
302
|
-
const { actorDid, method, payload } =
|
|
410
|
+
const { actorDid, method, payload } = op
|
|
303
411
|
if (method !== Method.CREATE) return
|
|
304
412
|
|
|
305
413
|
const parsed = lexParse<app.bsky.unspecced.defs.AgeAssuranceEvent>(
|
|
306
414
|
Buffer.from(payload).toString('utf8'),
|
|
307
415
|
)
|
|
308
|
-
|
|
309
416
|
const { status, createdAt } = parsed
|
|
310
417
|
|
|
311
418
|
const update = {
|
|
@@ -313,25 +420,23 @@ const handleAgeAssuranceEventOperation = async (
|
|
|
313
420
|
ageAssuranceLastInitiatedAt: status === 'pending' ? createdAt : undefined,
|
|
314
421
|
}
|
|
315
422
|
|
|
316
|
-
|
|
423
|
+
await db.db
|
|
317
424
|
.updateTable('actor')
|
|
318
425
|
.set(update)
|
|
319
426
|
.where('did', '=', actorDid)
|
|
320
427
|
.execute()
|
|
321
428
|
}
|
|
322
429
|
|
|
323
|
-
const handleAgeAssuranceV2EventOperation = async (
|
|
430
|
+
const handleAgeAssuranceV2EventOperation: HandleOperation = async (
|
|
324
431
|
db: Database,
|
|
325
|
-
|
|
326
|
-
_now: string,
|
|
432
|
+
op: Operation,
|
|
327
433
|
) => {
|
|
328
|
-
const { actorDid, method, payload } =
|
|
434
|
+
const { actorDid, method, payload } = op
|
|
329
435
|
if (method !== Method.CREATE) return
|
|
330
436
|
|
|
331
437
|
const parsed = lexParse<app.bsky.ageassurance.defs.Event>(
|
|
332
438
|
Buffer.from(payload).toString('utf8'),
|
|
333
439
|
)
|
|
334
|
-
|
|
335
440
|
const { status, createdAt, access, countryCode, regionCode } = parsed
|
|
336
441
|
|
|
337
442
|
const update = {
|
|
@@ -342,19 +447,19 @@ const handleAgeAssuranceV2EventOperation = async (
|
|
|
342
447
|
ageAssuranceRegionCode: regionCode,
|
|
343
448
|
}
|
|
344
449
|
|
|
345
|
-
|
|
450
|
+
await db.db
|
|
346
451
|
.updateTable('actor')
|
|
347
452
|
.set(update)
|
|
348
453
|
.where('did', '=', actorDid)
|
|
349
454
|
.execute()
|
|
350
455
|
}
|
|
351
456
|
|
|
352
|
-
const handleBookmarkOperation = async (
|
|
457
|
+
const handleBookmarkOperation: HandleOperation = async (
|
|
353
458
|
db: Database,
|
|
354
|
-
|
|
459
|
+
op: Operation,
|
|
355
460
|
now: string,
|
|
356
461
|
) => {
|
|
357
|
-
const { actorDid, key, method, payload } =
|
|
462
|
+
const { actorDid, key, method, payload } = op
|
|
358
463
|
|
|
359
464
|
const updateAgg = (uri: string, dbTxn: Database) => {
|
|
360
465
|
return dbTxn.db
|
|
@@ -420,12 +525,12 @@ const handleBookmarkOperation = async (
|
|
|
420
525
|
}
|
|
421
526
|
}
|
|
422
527
|
|
|
423
|
-
const handleDraftOperation = async (
|
|
528
|
+
const handleDraftOperation: HandleOperation = async (
|
|
424
529
|
db: Database,
|
|
425
|
-
|
|
530
|
+
op: Operation,
|
|
426
531
|
now: string,
|
|
427
532
|
) => {
|
|
428
|
-
const { actorDid, key, method, payload } =
|
|
533
|
+
const { actorDid, key, method, payload } = op
|
|
429
534
|
|
|
430
535
|
if (method === Method.CREATE) {
|
|
431
536
|
const payloadString = Buffer.from(payload).toString('utf8')
|
|
@@ -464,3 +569,22 @@ const handleDraftOperation = async (
|
|
|
464
569
|
.execute()
|
|
465
570
|
}
|
|
466
571
|
}
|
|
572
|
+
|
|
573
|
+
// compares numeric (bigint) cursor ids; an undefined target is always satisfied
|
|
574
|
+
const gteCursor = (current: string | undefined, target: string | undefined) => {
|
|
575
|
+
if (!target) return true
|
|
576
|
+
return BigInt(current || '0') >= BigInt(target)
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// resolves after `ms`, or early if `signal` aborts
|
|
580
|
+
const wait = (ms: number, signal?: AbortSignal) =>
|
|
581
|
+
new Promise<void>((resolve) => {
|
|
582
|
+
if (signal?.aborted) return resolve()
|
|
583
|
+
const done = () => {
|
|
584
|
+
clearTimeout(timer)
|
|
585
|
+
signal?.removeEventListener('abort', done)
|
|
586
|
+
resolve()
|
|
587
|
+
}
|
|
588
|
+
const timer = setTimeout(done, ms)
|
|
589
|
+
signal?.addEventListener('abort', done, { once: true })
|
|
590
|
+
})
|
package/src/hydration/feed.ts
CHANGED
|
@@ -100,8 +100,7 @@ export type FeedItem = {
|
|
|
100
100
|
post: ItemRef
|
|
101
101
|
repost?: ItemRef
|
|
102
102
|
/**
|
|
103
|
-
* If true, overrides the `reason` with `app.bsky.feed.defs#reasonPin`.
|
|
104
|
-
* only in author feeds.
|
|
103
|
+
* If true, overrides the `reason` with `app.bsky.feed.defs#reasonPin`.
|
|
105
104
|
*/
|
|
106
105
|
authorPinned?: boolean
|
|
107
106
|
}
|