@atproto/bsky 0.0.81 → 0.0.83
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 +22 -0
- package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js +1 -3
- package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js.map +1 -1
- package/dist/auth-verifier.d.ts +6 -0
- package/dist/auth-verifier.d.ts.map +1 -1
- package/dist/auth-verifier.js +80 -1
- package/dist/auth-verifier.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/client.d.ts.map +1 -1
- package/dist/data-plane/client.js +2 -1
- package/dist/data-plane/client.js.map +1 -1
- package/dist/data-plane/server/db/migrations/20240829T211238293Z-simplify-actor-sync.d.ts +4 -0
- package/dist/data-plane/server/db/migrations/20240829T211238293Z-simplify-actor-sync.d.ts.map +1 -0
- package/dist/data-plane/server/db/migrations/20240829T211238293Z-simplify-actor-sync.js +26 -0
- package/dist/data-plane/server/db/migrations/20240829T211238293Z-simplify-actor-sync.js.map +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
- package/dist/data-plane/server/db/migrations/index.js +2 -1
- package/dist/data-plane/server/db/migrations/index.js.map +1 -1
- package/dist/data-plane/server/db/tables/actor-sync.d.ts +0 -3
- package/dist/data-plane/server/db/tables/actor-sync.d.ts.map +1 -1
- package/dist/data-plane/server/db/tables/actor-sync.js.map +1 -1
- package/dist/data-plane/server/indexing/index.d.ts +2 -7
- package/dist/data-plane/server/indexing/index.d.ts.map +1 -1
- package/dist/data-plane/server/indexing/index.js +4 -21
- package/dist/data-plane/server/indexing/index.js.map +1 -1
- package/dist/data-plane/server/subscription.d.ts +26 -0
- package/dist/data-plane/server/subscription.d.ts.map +1 -0
- package/dist/data-plane/server/subscription.js +115 -0
- package/dist/data-plane/server/subscription.js.map +1 -0
- package/dist/feature-gates.d.ts +5 -1
- package/dist/feature-gates.d.ts.map +1 -1
- package/dist/feature-gates.js +5 -1
- package/dist/feature-gates.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +35 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +35 -0
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +14 -0
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.js +9 -1
- package/dist/lexicon/types/app/bsky/actor/defs.js.map +1 -1
- package/package.json +10 -8
- package/src/api/app/bsky/graph/getSuggestedFollowsByActor.ts +1 -8
- package/src/auth-verifier.ts +77 -0
- package/src/config.ts +8 -0
- package/src/data-plane/client.ts +4 -1
- package/src/data-plane/server/db/migrations/20240829T211238293Z-simplify-actor-sync.ts +23 -0
- package/src/data-plane/server/db/migrations/index.ts +1 -0
- package/src/data-plane/server/db/tables/actor-sync.ts +0 -3
- package/src/data-plane/server/indexing/index.ts +4 -25
- package/src/data-plane/server/subscription.ts +104 -0
- package/src/feature-gates.ts +5 -1
- package/src/index.ts +5 -1
- package/src/lexicon/lexicons.ts +37 -0
- package/src/lexicon/types/app/bsky/actor/defs.ts +23 -0
- package/tests/data-plane/indexing.test.ts +1 -1
- package/tests/data-plane/{subscription/repo.test.ts → subscription.test.ts} +4 -9
- package/tests/entryway-auth.test.ts +174 -0
- package/tests/views/actor-search.test.ts +1 -1
- package/dist/data-plane/server/subscription/index.d.ts +0 -33
- package/dist/data-plane/server/subscription/index.d.ts.map +0 -1
- package/dist/data-plane/server/subscription/index.js +0 -341
- package/dist/data-plane/server/subscription/index.js.map +0 -1
- package/dist/data-plane/server/subscription/util.d.ts +0 -65
- package/dist/data-plane/server/subscription/util.d.ts.map +0 -1
- package/dist/data-plane/server/subscription/util.js +0 -215
- package/dist/data-plane/server/subscription/util.js.map +0 -1
- package/src/data-plane/server/subscription/index.ts +0 -352
- package/src/data-plane/server/subscription/util.ts +0 -156
- package/tests/data-plane/subscription/util.test.ts +0 -185
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import PQueue from 'p-queue'
|
|
3
|
-
import { OutputSchema as RepoMessage } from '../../../lexicon/types/com/atproto/sync/subscribeRepos'
|
|
4
|
-
import * as message from '../../../lexicon/types/com/atproto/sync/subscribeRepos'
|
|
5
|
-
|
|
6
|
-
// A queue with arbitrarily many partitions, each processing work sequentially.
|
|
7
|
-
// Partitions are created lazily and taken out of memory when they go idle.
|
|
8
|
-
export class PartitionedQueue {
|
|
9
|
-
main: PQueue
|
|
10
|
-
partitions = new Map<string, PQueue>()
|
|
11
|
-
|
|
12
|
-
constructor(opts: { concurrency: number }) {
|
|
13
|
-
this.main = new PQueue({ concurrency: opts.concurrency })
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async add(partitionId: string, task: () => Promise<void>) {
|
|
17
|
-
if (this.main.isPaused) return
|
|
18
|
-
return this.main.add(() => {
|
|
19
|
-
return this.getPartition(partitionId).add(task)
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async destroy() {
|
|
24
|
-
this.main.pause()
|
|
25
|
-
this.main.clear()
|
|
26
|
-
this.partitions.forEach((p) => p.clear())
|
|
27
|
-
await this.main.onIdle() // All in-flight work completes
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
private getPartition(partitionId: string) {
|
|
31
|
-
let partition = this.partitions.get(partitionId)
|
|
32
|
-
if (!partition) {
|
|
33
|
-
partition = new PQueue({ concurrency: 1 })
|
|
34
|
-
partition.once('idle', () => this.partitions.delete(partitionId))
|
|
35
|
-
this.partitions.set(partitionId, partition)
|
|
36
|
-
}
|
|
37
|
-
return partition
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export class LatestQueue {
|
|
42
|
-
queue = new PQueue({ concurrency: 1 })
|
|
43
|
-
|
|
44
|
-
async add(task: () => Promise<void>) {
|
|
45
|
-
if (this.queue.isPaused) return
|
|
46
|
-
this.queue.clear() // Only queue the latest task, invalidate any previous ones
|
|
47
|
-
return this.queue.add(task)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async destroy() {
|
|
51
|
-
this.queue.pause()
|
|
52
|
-
this.queue.clear()
|
|
53
|
-
await this.queue.onIdle() // All in-flight work completes
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Add items to a list, and mark those items as
|
|
59
|
-
* completed. Upon item completion, get list of consecutive
|
|
60
|
-
* items completed at the head of the list. Example:
|
|
61
|
-
*
|
|
62
|
-
* const consecutive = new ConsecutiveList<number>()
|
|
63
|
-
* const item1 = consecutive.push(1)
|
|
64
|
-
* const item2 = consecutive.push(2)
|
|
65
|
-
* const item3 = consecutive.push(3)
|
|
66
|
-
* item2.complete() // []
|
|
67
|
-
* item1.complete() // [1, 2]
|
|
68
|
-
* item3.complete() // [3]
|
|
69
|
-
*
|
|
70
|
-
*/
|
|
71
|
-
export class ConsecutiveList<T> {
|
|
72
|
-
list: ConsecutiveItem<T>[] = []
|
|
73
|
-
|
|
74
|
-
push(value: T) {
|
|
75
|
-
const item = new ConsecutiveItem<T>(this, value)
|
|
76
|
-
this.list.push(item)
|
|
77
|
-
return item
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
complete(): T[] {
|
|
81
|
-
let i = 0
|
|
82
|
-
while (this.list[i]?.isComplete) {
|
|
83
|
-
i += 1
|
|
84
|
-
}
|
|
85
|
-
return this.list.splice(0, i).map((item) => item.value)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export class ConsecutiveItem<T> {
|
|
90
|
-
isComplete = false
|
|
91
|
-
constructor(
|
|
92
|
-
private consecutive: ConsecutiveList<T>,
|
|
93
|
-
public value: T,
|
|
94
|
-
) {}
|
|
95
|
-
|
|
96
|
-
complete() {
|
|
97
|
-
this.isComplete = true
|
|
98
|
-
return this.consecutive.complete()
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export class PerfectMap<K, V> extends Map<K, V> {
|
|
103
|
-
get(key: K): V {
|
|
104
|
-
const val = super.get(key)
|
|
105
|
-
assert(val !== undefined, `Key not found in PerfectMap: ${key}`)
|
|
106
|
-
return val
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// These are the message types that have a sequence number and a repo
|
|
111
|
-
export type ProcessableMessage =
|
|
112
|
-
| message.Commit
|
|
113
|
-
| message.Handle
|
|
114
|
-
| message.Identity
|
|
115
|
-
| message.Migrate
|
|
116
|
-
| message.Tombstone
|
|
117
|
-
|
|
118
|
-
export function loggableMessage(msg: RepoMessage) {
|
|
119
|
-
if (message.isCommit(msg)) {
|
|
120
|
-
const { seq, rebase, prev, repo, commit, time, tooBig, blobs } = msg
|
|
121
|
-
return {
|
|
122
|
-
$type: msg.$type,
|
|
123
|
-
seq,
|
|
124
|
-
rebase,
|
|
125
|
-
prev: prev?.toString(),
|
|
126
|
-
repo,
|
|
127
|
-
commit: commit.toString(),
|
|
128
|
-
time,
|
|
129
|
-
tooBig,
|
|
130
|
-
hasBlobs: blobs.length > 0,
|
|
131
|
-
}
|
|
132
|
-
} else if (message.isHandle(msg)) {
|
|
133
|
-
return msg
|
|
134
|
-
} else if (message.isIdentity(msg)) {
|
|
135
|
-
return msg
|
|
136
|
-
} else if (message.isAccount(msg)) {
|
|
137
|
-
return msg
|
|
138
|
-
} else if (message.isMigrate(msg)) {
|
|
139
|
-
return msg
|
|
140
|
-
} else if (message.isTombstone(msg)) {
|
|
141
|
-
return msg
|
|
142
|
-
} else if (message.isInfo(msg)) {
|
|
143
|
-
return msg
|
|
144
|
-
}
|
|
145
|
-
return msg
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export function jitter(maxMs) {
|
|
149
|
-
return Math.round((Math.random() - 0.5) * maxMs * 2)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export function strToInt(str: string) {
|
|
153
|
-
const int = parseInt(str, 10)
|
|
154
|
-
assert(!isNaN(int), 'string could not be parsed to an integer')
|
|
155
|
-
return int
|
|
156
|
-
}
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import { wait } from '@atproto/common'
|
|
2
|
-
import { randomStr } from '@atproto/crypto'
|
|
3
|
-
import {
|
|
4
|
-
ConsecutiveList,
|
|
5
|
-
LatestQueue,
|
|
6
|
-
PartitionedQueue,
|
|
7
|
-
} from '../../../src/data-plane/server/subscription/util'
|
|
8
|
-
|
|
9
|
-
describe('subscription utils', () => {
|
|
10
|
-
describe('ConsecutiveList', () => {
|
|
11
|
-
it('tracks consecutive complete items.', () => {
|
|
12
|
-
const consecutive = new ConsecutiveList<number>()
|
|
13
|
-
// add items
|
|
14
|
-
const item1 = consecutive.push(1)
|
|
15
|
-
const item2 = consecutive.push(2)
|
|
16
|
-
const item3 = consecutive.push(3)
|
|
17
|
-
expect(item1.isComplete).toEqual(false)
|
|
18
|
-
expect(item2.isComplete).toEqual(false)
|
|
19
|
-
expect(item3.isComplete).toEqual(false)
|
|
20
|
-
// complete items out of order
|
|
21
|
-
expect(consecutive.list.length).toBe(3)
|
|
22
|
-
expect(item2.complete()).toEqual([])
|
|
23
|
-
expect(item2.isComplete).toEqual(true)
|
|
24
|
-
expect(consecutive.list.length).toBe(3)
|
|
25
|
-
expect(item1.complete()).toEqual([1, 2])
|
|
26
|
-
expect(item1.isComplete).toEqual(true)
|
|
27
|
-
expect(consecutive.list.length).toBe(1)
|
|
28
|
-
expect(item3.complete()).toEqual([3])
|
|
29
|
-
expect(consecutive.list.length).toBe(0)
|
|
30
|
-
expect(item3.isComplete).toEqual(true)
|
|
31
|
-
})
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
describe('LatestQueue', () => {
|
|
35
|
-
it('only performs most recently queued item.', async () => {
|
|
36
|
-
const latest = new LatestQueue()
|
|
37
|
-
const complete: number[] = []
|
|
38
|
-
latest.add(async () => {
|
|
39
|
-
await wait(1)
|
|
40
|
-
complete.push(1)
|
|
41
|
-
})
|
|
42
|
-
latest.add(async () => {
|
|
43
|
-
await wait(1)
|
|
44
|
-
complete.push(2)
|
|
45
|
-
})
|
|
46
|
-
latest.add(async () => {
|
|
47
|
-
await wait(1)
|
|
48
|
-
complete.push(3)
|
|
49
|
-
})
|
|
50
|
-
latest.add(async () => {
|
|
51
|
-
await wait(1)
|
|
52
|
-
complete.push(4)
|
|
53
|
-
})
|
|
54
|
-
await latest.queue.onIdle()
|
|
55
|
-
expect(complete).toEqual([1, 4]) // skip 2, 3
|
|
56
|
-
latest.add(async () => {
|
|
57
|
-
await wait(1)
|
|
58
|
-
complete.push(5)
|
|
59
|
-
})
|
|
60
|
-
latest.add(async () => {
|
|
61
|
-
await wait(1)
|
|
62
|
-
complete.push(6)
|
|
63
|
-
})
|
|
64
|
-
await latest.queue.onIdle()
|
|
65
|
-
expect(complete).toEqual([1, 4, 5, 6])
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('stops processing queued messages on destroy.', async () => {
|
|
69
|
-
const latest = new LatestQueue()
|
|
70
|
-
const complete: number[] = []
|
|
71
|
-
latest.add(async () => {
|
|
72
|
-
await wait(1)
|
|
73
|
-
complete.push(1)
|
|
74
|
-
})
|
|
75
|
-
latest.add(async () => {
|
|
76
|
-
await wait(1)
|
|
77
|
-
complete.push(2)
|
|
78
|
-
})
|
|
79
|
-
const destroyed = latest.destroy()
|
|
80
|
-
latest.add(async () => {
|
|
81
|
-
await wait(1)
|
|
82
|
-
complete.push(3)
|
|
83
|
-
})
|
|
84
|
-
await destroyed
|
|
85
|
-
expect(complete).toEqual([1]) // 2 was cleared, 3 was after destroy
|
|
86
|
-
// show that waiting on destroyed above was already enough to reflect all complete items
|
|
87
|
-
await latest.queue.onIdle()
|
|
88
|
-
expect(complete).toEqual([1])
|
|
89
|
-
})
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
describe('PartitionedQueue', () => {
|
|
93
|
-
it('performs work in parallel across partitions, serial within a partition.', async () => {
|
|
94
|
-
const partitioned = new PartitionedQueue({ concurrency: Infinity })
|
|
95
|
-
const complete: number[] = []
|
|
96
|
-
// partition 1 items start slow but get faster: slow should still complete first.
|
|
97
|
-
partitioned.add('1', async () => {
|
|
98
|
-
await wait(30)
|
|
99
|
-
complete.push(11)
|
|
100
|
-
})
|
|
101
|
-
partitioned.add('1', async () => {
|
|
102
|
-
await wait(20)
|
|
103
|
-
complete.push(12)
|
|
104
|
-
})
|
|
105
|
-
partitioned.add('1', async () => {
|
|
106
|
-
await wait(1)
|
|
107
|
-
complete.push(13)
|
|
108
|
-
})
|
|
109
|
-
expect(partitioned.partitions.size).toEqual(1)
|
|
110
|
-
// partition 2 items complete quickly except the last, which is slowest of all events.
|
|
111
|
-
partitioned.add('2', async () => {
|
|
112
|
-
await wait(1)
|
|
113
|
-
complete.push(21)
|
|
114
|
-
})
|
|
115
|
-
partitioned.add('2', async () => {
|
|
116
|
-
await wait(1)
|
|
117
|
-
complete.push(22)
|
|
118
|
-
})
|
|
119
|
-
partitioned.add('2', async () => {
|
|
120
|
-
await wait(1)
|
|
121
|
-
complete.push(23)
|
|
122
|
-
})
|
|
123
|
-
partitioned.add('2', async () => {
|
|
124
|
-
await wait(60)
|
|
125
|
-
complete.push(24)
|
|
126
|
-
})
|
|
127
|
-
expect(partitioned.partitions.size).toEqual(2)
|
|
128
|
-
await partitioned.main.onIdle()
|
|
129
|
-
expect(complete).toEqual([21, 22, 23, 11, 12, 13, 24])
|
|
130
|
-
expect(partitioned.partitions.size).toEqual(0)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('limits overall concurrency.', async () => {
|
|
134
|
-
const partitioned = new PartitionedQueue({ concurrency: 1 })
|
|
135
|
-
const complete: number[] = []
|
|
136
|
-
// if concurrency were not constrained, partition 1 would complete all items
|
|
137
|
-
// before any items from partition 2. since it is constrained, the work is complete in the order added.
|
|
138
|
-
partitioned.add('1', async () => {
|
|
139
|
-
await wait(1)
|
|
140
|
-
complete.push(11)
|
|
141
|
-
})
|
|
142
|
-
partitioned.add('2', async () => {
|
|
143
|
-
await wait(10)
|
|
144
|
-
complete.push(21)
|
|
145
|
-
})
|
|
146
|
-
partitioned.add('1', async () => {
|
|
147
|
-
await wait(1)
|
|
148
|
-
complete.push(12)
|
|
149
|
-
})
|
|
150
|
-
partitioned.add('2', async () => {
|
|
151
|
-
await wait(10)
|
|
152
|
-
complete.push(22)
|
|
153
|
-
})
|
|
154
|
-
// only partition 1 exists so far due to the concurrency
|
|
155
|
-
expect(partitioned.partitions.size).toEqual(1)
|
|
156
|
-
await partitioned.main.onIdle()
|
|
157
|
-
expect(complete).toEqual([11, 21, 12, 22])
|
|
158
|
-
expect(partitioned.partitions.size).toEqual(0)
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
it('settles with many items.', async () => {
|
|
162
|
-
const partitioned = new PartitionedQueue({ concurrency: 100 })
|
|
163
|
-
const complete: { partition: string; id: number }[] = []
|
|
164
|
-
const partitions = new Set<string>()
|
|
165
|
-
for (let i = 0; i < 500; ++i) {
|
|
166
|
-
const partition = randomStr(1, 'base16').slice(0, 1)
|
|
167
|
-
partitions.add(partition)
|
|
168
|
-
partitioned.add(partition, async () => {
|
|
169
|
-
await wait((i % 2) * 2)
|
|
170
|
-
complete.push({ partition, id: i })
|
|
171
|
-
})
|
|
172
|
-
}
|
|
173
|
-
expect(partitioned.partitions.size).toEqual(partitions.size)
|
|
174
|
-
await partitioned.main.onIdle()
|
|
175
|
-
expect(complete.length).toEqual(500)
|
|
176
|
-
for (const partition of partitions) {
|
|
177
|
-
const ids = complete
|
|
178
|
-
.filter((item) => item.partition === partition)
|
|
179
|
-
.map((item) => item.id)
|
|
180
|
-
expect(ids).toEqual([...ids].sort((a, b) => a - b))
|
|
181
|
-
}
|
|
182
|
-
expect(partitioned.partitions.size).toEqual(0)
|
|
183
|
-
})
|
|
184
|
-
})
|
|
185
|
-
})
|