@atproto/dev-env 0.0.0-spaces-alpha-20260818163953
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 +3424 -0
- package/LICENSE.txt +7 -0
- package/README.md +39 -0
- package/bin.js +7 -0
- package/dist/bin-multi-pds.d.ts +2 -0
- package/dist/bin-multi-pds.d.ts.map +1 -0
- package/dist/bin-multi-pds.js +130 -0
- package/dist/bin-multi-pds.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +56 -0
- package/dist/bin.js.map +1 -0
- package/dist/bsky.d.ts +27 -0
- package/dist/bsky.d.ts.map +1 -0
- package/dist/bsky.js +165 -0
- package/dist/bsky.js.map +1 -0
- package/dist/bsync.d.ts +22 -0
- package/dist/bsync.d.ts.map +1 -0
- package/dist/bsync.js +54 -0
- package/dist/bsync.js.map +1 -0
- package/dist/const.d.ts +4 -0
- package/dist/const.d.ts.map +1 -0
- package/dist/const.js +4 -0
- package/dist/const.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +10 -0
- package/dist/env.js.map +1 -0
- package/dist/feed-gen.d.ts +15 -0
- package/dist/feed-gen.d.ts.map +1 -0
- package/dist/feed-gen.js +73 -0
- package/dist/feed-gen.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/introspect.d.ts +22 -0
- package/dist/introspect.d.ts.map +1 -0
- package/dist/introspect.js +65 -0
- package/dist/introspect.js.map +1 -0
- package/dist/mock/data.d.ts +3 -0
- package/dist/mock/data.d.ts.map +1 -0
- package/dist/mock/data.js +46 -0
- package/dist/mock/data.js.map +1 -0
- package/dist/mock/img/blur-hash-avatar-b64.d.ts +3 -0
- package/dist/mock/img/blur-hash-avatar-b64.d.ts.map +1 -0
- package/dist/mock/img/blur-hash-avatar-b64.js +2 -0
- package/dist/mock/img/blur-hash-avatar-b64.js.map +1 -0
- package/dist/mock/img/labeled-img-b64.d.ts +3 -0
- package/dist/mock/img/labeled-img-b64.d.ts.map +1 -0
- package/dist/mock/img/labeled-img-b64.js +2 -0
- package/dist/mock/img/labeled-img-b64.js.map +1 -0
- package/dist/mock/index.d.ts +3 -0
- package/dist/mock/index.d.ts.map +1 -0
- package/dist/mock/index.js +574 -0
- package/dist/mock/index.js.map +1 -0
- package/dist/moderator-client.d.ts +58 -0
- package/dist/moderator-client.d.ts.map +1 -0
- package/dist/moderator-client.js +110 -0
- package/dist/moderator-client.js.map +1 -0
- package/dist/network-no-appview.d.ts +21 -0
- package/dist/network-no-appview.d.ts.map +1 -0
- package/dist/network-no-appview.js +74 -0
- package/dist/network-no-appview.js.map +1 -0
- package/dist/network.d.ts +32 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +190 -0
- package/dist/network.js.map +1 -0
- package/dist/ozone.d.ts +31 -0
- package/dist/ozone.d.ts.map +1 -0
- package/dist/ozone.js +254 -0
- package/dist/ozone.js.map +1 -0
- package/dist/pds.d.ts +23 -0
- package/dist/pds.d.ts.map +1 -0
- package/dist/pds.js +97 -0
- package/dist/pds.js.map +1 -0
- package/dist/plc.d.ts +14 -0
- package/dist/plc.d.ts.map +1 -0
- package/dist/plc.js +28 -0
- package/dist/plc.js.map +1 -0
- package/dist/seed/author-feed.d.ts +4 -0
- package/dist/seed/author-feed.d.ts.map +1 -0
- package/dist/seed/author-feed.js +50 -0
- package/dist/seed/author-feed.js.map +1 -0
- package/dist/seed/basic.d.ts +16 -0
- package/dist/seed/basic.d.ts.map +1 -0
- package/dist/seed/basic.js +113 -0
- package/dist/seed/basic.js.map +1 -0
- package/dist/seed/client.d.ts +151 -0
- package/dist/seed/client.d.ts.map +1 -0
- package/dist/seed/client.js +365 -0
- package/dist/seed/client.js.map +1 -0
- package/dist/seed/follows.d.ts +4 -0
- package/dist/seed/follows.d.ts.map +1 -0
- package/dist/seed/follows.js +55 -0
- package/dist/seed/follows.js.map +1 -0
- package/dist/seed/index.d.ts +11 -0
- package/dist/seed/index.d.ts.map +1 -0
- package/dist/seed/index.js +11 -0
- package/dist/seed/index.js.map +1 -0
- package/dist/seed/likes.d.ts +4 -0
- package/dist/seed/likes.d.ts.map +1 -0
- package/dist/seed/likes.js +37 -0
- package/dist/seed/likes.js.map +1 -0
- package/dist/seed/quotes.d.ts +4 -0
- package/dist/seed/quotes.d.ts.map +1 -0
- package/dist/seed/quotes.js +21 -0
- package/dist/seed/quotes.js.map +1 -0
- package/dist/seed/reposts.d.ts +4 -0
- package/dist/seed/reposts.d.ts.map +1 -0
- package/dist/seed/reposts.js +17 -0
- package/dist/seed/reposts.js.map +1 -0
- package/dist/seed/thread-v2.d.ts +227 -0
- package/dist/seed/thread-v2.d.ts.map +1 -0
- package/dist/seed/thread-v2.js +755 -0
- package/dist/seed/thread-v2.js.map +1 -0
- package/dist/seed/users-bulk.d.ts +3 -0
- package/dist/seed/users-bulk.d.ts.map +1 -0
- package/dist/seed/users-bulk.js +224 -0
- package/dist/seed/users-bulk.js.map +1 -0
- package/dist/seed/users.d.ts +3 -0
- package/dist/seed/users.d.ts.map +1 -0
- package/dist/seed/users.js +45 -0
- package/dist/seed/users.js.map +1 -0
- package/dist/seed/verifications.d.ts +5 -0
- package/dist/seed/verifications.d.ts.map +1 -0
- package/dist/seed/verifications.js +164 -0
- package/dist/seed/verifications.js.map +1 -0
- package/dist/service-profile-lexicon.d.ts +11 -0
- package/dist/service-profile-lexicon.d.ts.map +1 -0
- package/dist/service-profile-lexicon.js +115 -0
- package/dist/service-profile-lexicon.js.map +1 -0
- package/dist/service-profile-ozone.d.ts +18 -0
- package/dist/service-profile-ozone.d.ts.map +1 -0
- package/dist/service-profile-ozone.js +333 -0
- package/dist/service-profile-ozone.js.map +1 -0
- package/dist/service-profile.d.ts +24 -0
- package/dist/service-profile.d.ts.map +1 -0
- package/dist/service-profile.js +61 -0
- package/dist/service-profile.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +14 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +86 -0
- package/dist/util.js.map +1 -0
- package/package.json +69 -0
- package/src/bin-multi-pds.ts +166 -0
- package/src/bin.ts +64 -0
- package/src/bsky.ts +198 -0
- package/src/bsync.ts +66 -0
- package/src/const.ts +3 -0
- package/src/env.ts +9 -0
- package/src/feed-gen.ts +96 -0
- package/src/index.ts +17 -0
- package/src/introspect.ts +91 -0
- package/src/mock/data.ts +46 -0
- package/src/mock/img/blur-hash-avatar-b64.ts +1 -0
- package/src/mock/img/labeled-img-b64.ts +1 -0
- package/src/mock/index.ts +702 -0
- package/src/moderator-client.ts +267 -0
- package/src/network-no-appview.ts +89 -0
- package/src/network.ts +243 -0
- package/src/ozone.ts +287 -0
- package/src/pds.ts +118 -0
- package/src/plc.ts +33 -0
- package/src/seed/author-feed.ts +98 -0
- package/src/seed/basic.ts +189 -0
- package/src/seed/client.ts +676 -0
- package/src/seed/follows.ts +57 -0
- package/src/seed/index.ts +11 -0
- package/src/seed/likes.ts +43 -0
- package/src/seed/quotes.ts +53 -0
- package/src/seed/reposts.ts +18 -0
- package/src/seed/thread-v2.ts +925 -0
- package/src/seed/users-bulk.ts +229 -0
- package/src/seed/users.ts +64 -0
- package/src/seed/verifications.ts +254 -0
- package/src/service-profile-lexicon.ts +127 -0
- package/src/service-profile-ozone.ts +375 -0
- package/src/service-profile.ts +95 -0
- package/src/types.ts +68 -0
- package/src/util.ts +118 -0
- package/tsconfig.build.json +25 -0
- package/tsconfig.json +4 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import type { AtpAgent } from '@atproto/api'
|
|
2
|
+
import { Secp256k1Keypair } from '@atproto/crypto'
|
|
3
|
+
import type { TestPds } from './pds.js'
|
|
4
|
+
import {
|
|
5
|
+
type ServiceMigrationOptions,
|
|
6
|
+
ServiceProfile,
|
|
7
|
+
type ServiceUserDetails,
|
|
8
|
+
} from './service-profile.js'
|
|
9
|
+
|
|
10
|
+
export class OzoneServiceProfile extends ServiceProfile {
|
|
11
|
+
static async create(
|
|
12
|
+
pds: TestPds,
|
|
13
|
+
ozoneUrl: string,
|
|
14
|
+
userDetails = {
|
|
15
|
+
email: 'mod-authority@test.com',
|
|
16
|
+
handle: 'mod-authority.test',
|
|
17
|
+
password: 'hunter2',
|
|
18
|
+
},
|
|
19
|
+
) {
|
|
20
|
+
const agent = pds.getAgent()
|
|
21
|
+
await agent.createAccount(userDetails)
|
|
22
|
+
|
|
23
|
+
const key = await Secp256k1Keypair.create({ exportable: true })
|
|
24
|
+
|
|
25
|
+
return new OzoneServiceProfile(pds, agent, userDetails, ozoneUrl, key)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected constructor(
|
|
29
|
+
pds: TestPds,
|
|
30
|
+
agent: AtpAgent,
|
|
31
|
+
userDetails: ServiceUserDetails,
|
|
32
|
+
readonly ozoneUrl: string,
|
|
33
|
+
readonly key: Secp256k1Keypair,
|
|
34
|
+
) {
|
|
35
|
+
super(pds, agent, userDetails)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async createAppPasswordForVerification() {
|
|
39
|
+
const { data } = await this.agent.com.atproto.server.createAppPassword({
|
|
40
|
+
name: 'ozone-verifier',
|
|
41
|
+
})
|
|
42
|
+
return data.password
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async migrateTo(pds: TestPds, options: ServiceMigrationOptions = {}) {
|
|
46
|
+
await super.migrateTo(pds, {
|
|
47
|
+
...options,
|
|
48
|
+
services: {
|
|
49
|
+
...options.services,
|
|
50
|
+
atproto_labeler: {
|
|
51
|
+
type: 'AtprotoLabeler',
|
|
52
|
+
endpoint: this.ozoneUrl,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
verificationMethods: {
|
|
56
|
+
...options.verificationMethods,
|
|
57
|
+
atproto_label: this.key.did(),
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async createRecords() {
|
|
63
|
+
await this.agent.app.bsky.actor.profile.create(
|
|
64
|
+
{ repo: this.did },
|
|
65
|
+
{
|
|
66
|
+
displayName: 'Dev-env Moderation',
|
|
67
|
+
description: `The pretend version of mod.bsky.app`,
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
await this.agent.app.bsky.labeler.service.create(
|
|
72
|
+
{ repo: this.did, rkey: 'self' },
|
|
73
|
+
{
|
|
74
|
+
policies: {
|
|
75
|
+
labelValues: [
|
|
76
|
+
'!hide',
|
|
77
|
+
'!warn',
|
|
78
|
+
'porn',
|
|
79
|
+
'sexual',
|
|
80
|
+
'nudity',
|
|
81
|
+
'sexual-figurative',
|
|
82
|
+
'graphic-media',
|
|
83
|
+
'self-harm',
|
|
84
|
+
'sensitive',
|
|
85
|
+
'extremist',
|
|
86
|
+
'intolerant',
|
|
87
|
+
'threat',
|
|
88
|
+
'rude',
|
|
89
|
+
'illicit',
|
|
90
|
+
'security',
|
|
91
|
+
'unsafe-link',
|
|
92
|
+
'impersonation',
|
|
93
|
+
'misinformation',
|
|
94
|
+
'scam',
|
|
95
|
+
'engagement-farming',
|
|
96
|
+
'spam',
|
|
97
|
+
'rumor',
|
|
98
|
+
'misleading',
|
|
99
|
+
'inauthentic',
|
|
100
|
+
],
|
|
101
|
+
labelValueDefinitions: [
|
|
102
|
+
{
|
|
103
|
+
identifier: 'spam',
|
|
104
|
+
blurs: 'content',
|
|
105
|
+
severity: 'inform',
|
|
106
|
+
defaultSetting: 'hide',
|
|
107
|
+
adultOnly: false,
|
|
108
|
+
locales: [
|
|
109
|
+
{
|
|
110
|
+
lang: 'en',
|
|
111
|
+
name: 'Spam',
|
|
112
|
+
description:
|
|
113
|
+
'Unwanted, repeated, or unrelated actions that bother users.',
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
identifier: 'impersonation',
|
|
119
|
+
blurs: 'none',
|
|
120
|
+
severity: 'inform',
|
|
121
|
+
defaultSetting: 'hide',
|
|
122
|
+
adultOnly: false,
|
|
123
|
+
locales: [
|
|
124
|
+
{
|
|
125
|
+
lang: 'en',
|
|
126
|
+
name: 'Impersonation',
|
|
127
|
+
description:
|
|
128
|
+
'Pretending to be someone else without permission.',
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
identifier: 'scam',
|
|
134
|
+
blurs: 'content',
|
|
135
|
+
severity: 'alert',
|
|
136
|
+
defaultSetting: 'hide',
|
|
137
|
+
adultOnly: false,
|
|
138
|
+
locales: [
|
|
139
|
+
{
|
|
140
|
+
lang: 'en',
|
|
141
|
+
name: 'Scam',
|
|
142
|
+
description: 'Scams, phishing & fraud.',
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
identifier: 'intolerant',
|
|
148
|
+
blurs: 'content',
|
|
149
|
+
severity: 'alert',
|
|
150
|
+
defaultSetting: 'warn',
|
|
151
|
+
adultOnly: false,
|
|
152
|
+
locales: [
|
|
153
|
+
{
|
|
154
|
+
lang: 'en',
|
|
155
|
+
name: 'Intolerance',
|
|
156
|
+
description: 'Discrimination against protected groups.',
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
identifier: 'self-harm',
|
|
162
|
+
blurs: 'content',
|
|
163
|
+
severity: 'alert',
|
|
164
|
+
defaultSetting: 'warn',
|
|
165
|
+
adultOnly: false,
|
|
166
|
+
locales: [
|
|
167
|
+
{
|
|
168
|
+
lang: 'en',
|
|
169
|
+
name: 'Self-Harm',
|
|
170
|
+
description:
|
|
171
|
+
'Promotes self-harm, including graphic images, glorifying discussions, or triggering stories.',
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
identifier: 'security',
|
|
177
|
+
blurs: 'content',
|
|
178
|
+
severity: 'alert',
|
|
179
|
+
defaultSetting: 'hide',
|
|
180
|
+
adultOnly: false,
|
|
181
|
+
locales: [
|
|
182
|
+
{
|
|
183
|
+
lang: 'en',
|
|
184
|
+
name: 'Security Concerns',
|
|
185
|
+
description:
|
|
186
|
+
'May be unsafe and could harm your device, steal your info, or get your account hacked.',
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
identifier: 'misleading',
|
|
192
|
+
blurs: 'content',
|
|
193
|
+
severity: 'alert',
|
|
194
|
+
defaultSetting: 'warn',
|
|
195
|
+
adultOnly: false,
|
|
196
|
+
locales: [
|
|
197
|
+
{
|
|
198
|
+
lang: 'en',
|
|
199
|
+
name: 'Misleading',
|
|
200
|
+
description:
|
|
201
|
+
'Altered images/videos, deceptive links, or false statements.',
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
identifier: 'threat',
|
|
207
|
+
blurs: 'content',
|
|
208
|
+
severity: 'inform',
|
|
209
|
+
defaultSetting: 'hide',
|
|
210
|
+
adultOnly: false,
|
|
211
|
+
locales: [
|
|
212
|
+
{
|
|
213
|
+
lang: 'en',
|
|
214
|
+
name: 'Threats',
|
|
215
|
+
description:
|
|
216
|
+
'Promotes violence or harm towards others, including threats, incitement, or advocacy of harm.',
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
identifier: 'unsafe-link',
|
|
222
|
+
blurs: 'content',
|
|
223
|
+
severity: 'alert',
|
|
224
|
+
defaultSetting: 'hide',
|
|
225
|
+
adultOnly: false,
|
|
226
|
+
locales: [
|
|
227
|
+
{
|
|
228
|
+
lang: 'en',
|
|
229
|
+
name: 'Unsafe link',
|
|
230
|
+
description:
|
|
231
|
+
'Links to harmful sites with malware, phishing, or violating content that risk security and privacy.',
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
identifier: 'illicit',
|
|
237
|
+
blurs: 'content',
|
|
238
|
+
severity: 'alert',
|
|
239
|
+
defaultSetting: 'hide',
|
|
240
|
+
adultOnly: false,
|
|
241
|
+
locales: [
|
|
242
|
+
{
|
|
243
|
+
lang: 'en',
|
|
244
|
+
name: 'Illicit',
|
|
245
|
+
description:
|
|
246
|
+
'Promoting or selling potentially illicit goods, services, or activities.',
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
identifier: 'misinformation',
|
|
252
|
+
blurs: 'content',
|
|
253
|
+
severity: 'inform',
|
|
254
|
+
defaultSetting: 'warn',
|
|
255
|
+
adultOnly: false,
|
|
256
|
+
locales: [
|
|
257
|
+
{
|
|
258
|
+
lang: 'en',
|
|
259
|
+
name: 'Misinformation',
|
|
260
|
+
description:
|
|
261
|
+
'Spreading false or misleading info, including unverified claims and harmful conspiracy theories.',
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
identifier: 'rumor',
|
|
267
|
+
blurs: 'content',
|
|
268
|
+
severity: 'inform',
|
|
269
|
+
defaultSetting: 'warn',
|
|
270
|
+
adultOnly: false,
|
|
271
|
+
locales: [
|
|
272
|
+
{
|
|
273
|
+
lang: 'en',
|
|
274
|
+
name: 'Rumor',
|
|
275
|
+
description:
|
|
276
|
+
'Approach with caution, as these claims lack evidence from credible sources.',
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
identifier: 'rude',
|
|
282
|
+
blurs: 'content',
|
|
283
|
+
severity: 'inform',
|
|
284
|
+
defaultSetting: 'hide',
|
|
285
|
+
adultOnly: false,
|
|
286
|
+
locales: [
|
|
287
|
+
{
|
|
288
|
+
lang: 'en',
|
|
289
|
+
name: 'Rude',
|
|
290
|
+
description:
|
|
291
|
+
'Rude or impolite, including crude language and disrespectful comments, without constructive purpose.',
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
identifier: 'extremist',
|
|
297
|
+
blurs: 'content',
|
|
298
|
+
severity: 'alert',
|
|
299
|
+
defaultSetting: 'hide',
|
|
300
|
+
adultOnly: false,
|
|
301
|
+
locales: [
|
|
302
|
+
{
|
|
303
|
+
lang: 'en',
|
|
304
|
+
name: 'Extremist',
|
|
305
|
+
description:
|
|
306
|
+
'Radical views advocating violence, hate, or discrimination against individuals or groups.',
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
identifier: 'sensitive',
|
|
312
|
+
blurs: 'content',
|
|
313
|
+
severity: 'alert',
|
|
314
|
+
defaultSetting: 'warn',
|
|
315
|
+
adultOnly: false,
|
|
316
|
+
locales: [
|
|
317
|
+
{
|
|
318
|
+
lang: 'en',
|
|
319
|
+
name: 'Sensitive',
|
|
320
|
+
description:
|
|
321
|
+
'May be upsetting, covering topics like substance abuse or mental health issues, cautioning sensitive viewers.',
|
|
322
|
+
},
|
|
323
|
+
],
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
identifier: 'engagement-farming',
|
|
327
|
+
blurs: 'content',
|
|
328
|
+
severity: 'alert',
|
|
329
|
+
defaultSetting: 'hide',
|
|
330
|
+
adultOnly: false,
|
|
331
|
+
locales: [
|
|
332
|
+
{
|
|
333
|
+
lang: 'en',
|
|
334
|
+
name: 'Engagement Farming',
|
|
335
|
+
description:
|
|
336
|
+
'Insincere content or bulk actions aimed at gaining followers, including frequent follows, posts, and likes.',
|
|
337
|
+
},
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
identifier: 'inauthentic',
|
|
342
|
+
blurs: 'content',
|
|
343
|
+
severity: 'alert',
|
|
344
|
+
defaultSetting: 'hide',
|
|
345
|
+
adultOnly: false,
|
|
346
|
+
locales: [
|
|
347
|
+
{
|
|
348
|
+
lang: 'en',
|
|
349
|
+
name: 'Inauthentic Account',
|
|
350
|
+
description: 'Bot or a person pretending to be someone else.',
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
identifier: 'sexual-figurative',
|
|
356
|
+
blurs: 'media',
|
|
357
|
+
severity: 'none',
|
|
358
|
+
defaultSetting: 'show',
|
|
359
|
+
adultOnly: true,
|
|
360
|
+
locales: [
|
|
361
|
+
{
|
|
362
|
+
lang: 'en',
|
|
363
|
+
name: 'Sexually Suggestive (Cartoon)',
|
|
364
|
+
description:
|
|
365
|
+
'Art with explicit or suggestive sexual themes, including provocative imagery or partial nudity.',
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
},
|
|
369
|
+
],
|
|
370
|
+
},
|
|
371
|
+
createdAt: new Date().toISOString(),
|
|
372
|
+
},
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { AtpAgent } from '@atproto/api'
|
|
2
|
+
import type { DidString } from '@atproto/lex'
|
|
3
|
+
import type { TestPds } from './pds.js'
|
|
4
|
+
|
|
5
|
+
export type ServiceUserDetails = {
|
|
6
|
+
email: string
|
|
7
|
+
handle: string
|
|
8
|
+
password: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type ServiceMigrationOptions = {
|
|
12
|
+
services?: Record<string, unknown>
|
|
13
|
+
verificationMethods?: Record<string, unknown>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class ServiceProfile {
|
|
17
|
+
protected constructor(
|
|
18
|
+
protected pds: TestPds,
|
|
19
|
+
/** @note assumes the session is already authenticated */
|
|
20
|
+
protected agent: AtpAgent,
|
|
21
|
+
protected userDetails: ServiceUserDetails,
|
|
22
|
+
) {}
|
|
23
|
+
|
|
24
|
+
get did(): DidString {
|
|
25
|
+
return this.agent.assertDid
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async migrateTo(newPds: TestPds, options: ServiceMigrationOptions = {}) {
|
|
29
|
+
const newAgent = newPds.getAgent()
|
|
30
|
+
|
|
31
|
+
const newPdsDesc = await newAgent.com.atproto.server.describeServer()
|
|
32
|
+
const serviceAuth = await this.agent.com.atproto.server.getServiceAuth({
|
|
33
|
+
aud: newPdsDesc.data.did,
|
|
34
|
+
lxm: 'com.atproto.server.createAccount',
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const inviteCode = newPds.ctx.cfg.invites.required
|
|
38
|
+
? await newAgent.com.atproto.server
|
|
39
|
+
.createInviteCode(
|
|
40
|
+
{ useCount: 1 },
|
|
41
|
+
{
|
|
42
|
+
encoding: 'application/json',
|
|
43
|
+
headers: newPds.adminAuthHeaders(),
|
|
44
|
+
},
|
|
45
|
+
)
|
|
46
|
+
.then((res) => res.data.code)
|
|
47
|
+
: undefined
|
|
48
|
+
|
|
49
|
+
await newAgent.createAccount(
|
|
50
|
+
{
|
|
51
|
+
...this.userDetails,
|
|
52
|
+
inviteCode,
|
|
53
|
+
did: this.did,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
encoding: 'application/json',
|
|
57
|
+
headers: { authorization: `Bearer ${serviceAuth.data.token}` },
|
|
58
|
+
},
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
// The session manager will use the "didDoc" in the result of
|
|
62
|
+
// "createAccount" in order to setup the pdsUrl. However, since are in the
|
|
63
|
+
// process of migrating, that didDoc references the old PDS. In order to
|
|
64
|
+
// avoid calling the old PDS, let's clear the pdsUrl, which will result in
|
|
65
|
+
// the (new) serviceUrl being used.
|
|
66
|
+
newAgent.sessionManager.pdsUrl = undefined
|
|
67
|
+
|
|
68
|
+
const newDidCredentialsRes =
|
|
69
|
+
await newAgent.com.atproto.identity.getRecommendedDidCredentials()
|
|
70
|
+
|
|
71
|
+
await this.agent.com.atproto.identity.requestPlcOperationSignature()
|
|
72
|
+
const { token } = await this.pds.ctx.accountManager.db.db
|
|
73
|
+
.selectFrom('email_token')
|
|
74
|
+
.select('token')
|
|
75
|
+
.where('did', '=', this.did)
|
|
76
|
+
.where('purpose', '=', 'plc_operation')
|
|
77
|
+
.executeTakeFirstOrThrow()
|
|
78
|
+
|
|
79
|
+
const op = { ...newDidCredentialsRes.data, token }
|
|
80
|
+
Object.assign((op.services ??= {}), options.services)
|
|
81
|
+
Object.assign((op.verificationMethods ??= {}), options.verificationMethods)
|
|
82
|
+
|
|
83
|
+
const signedPlcOperation =
|
|
84
|
+
await this.agent.com.atproto.identity.signPlcOperation(op)
|
|
85
|
+
|
|
86
|
+
await newAgent.com.atproto.identity.submitPlcOperation({
|
|
87
|
+
operation: signedPlcOperation.data.operation,
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
await newAgent.com.atproto.server.activateAccount()
|
|
91
|
+
|
|
92
|
+
this.pds = newPds
|
|
93
|
+
this.agent = newAgent
|
|
94
|
+
}
|
|
95
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type * as bsky from '@atproto/bsky'
|
|
2
|
+
import type * as bsync from '@atproto/bsync'
|
|
3
|
+
import type { ExportableKeypair, Keypair } from '@atproto/crypto'
|
|
4
|
+
import type * as ozone from '@atproto/ozone'
|
|
5
|
+
import type * as pds from '@atproto/pds'
|
|
6
|
+
|
|
7
|
+
export type IntrospectConfig = {
|
|
8
|
+
port?: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type PlcConfig = {
|
|
12
|
+
port?: number
|
|
13
|
+
version?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type PdsConfig = Partial<pds.ServerEnvironment> & {
|
|
17
|
+
didPlcUrl: string
|
|
18
|
+
migration?: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type BskyConfig = Partial<bsky.ServerConfig> & {
|
|
22
|
+
bsyncUrl: string
|
|
23
|
+
plcUrl: string
|
|
24
|
+
repoProvider: string
|
|
25
|
+
dbPostgresUrl: string
|
|
26
|
+
dbPostgresSchema: string
|
|
27
|
+
redisHost: string
|
|
28
|
+
pdsPort: number
|
|
29
|
+
migration?: string
|
|
30
|
+
privateKey?: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type BsyncConfig = Partial<bsync.ServerEnvironment> & {
|
|
34
|
+
dbUrl: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type OzoneConfig = Partial<ozone.OzoneEnvironment> & {
|
|
38
|
+
plcUrl: string
|
|
39
|
+
appviewUrl: string
|
|
40
|
+
appviewDid: string
|
|
41
|
+
dbPostgresUrl: string
|
|
42
|
+
migration?: string
|
|
43
|
+
signingKey?: ExportableKeypair
|
|
44
|
+
imgInvalidator?: ozone.ImageInvalidator
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type TestServerParams = {
|
|
48
|
+
dbPostgresUrl: string
|
|
49
|
+
dbPostgresSchema: string
|
|
50
|
+
pds: Partial<PdsConfig>
|
|
51
|
+
plc: Partial<PlcConfig>
|
|
52
|
+
bsky: Partial<BskyConfig>
|
|
53
|
+
ozone: Partial<OzoneConfig>
|
|
54
|
+
introspect: Partial<IntrospectConfig>
|
|
55
|
+
extraPdses: number
|
|
56
|
+
/**
|
|
57
|
+
* Create a lexicon-authority account and point every PDS at it, so lexicon
|
|
58
|
+
* documents (permission sets, `space` declarations) resolve. Opt-in: it costs
|
|
59
|
+
* an account creation plus a record write per document, which most tests don't
|
|
60
|
+
* need. `TestNetwork` always does this.
|
|
61
|
+
*/
|
|
62
|
+
lexiconAuthority: boolean
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type DidAndKey = {
|
|
66
|
+
did: string
|
|
67
|
+
key: Keypair
|
|
68
|
+
}
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import * as plc from '@did-plc/lib'
|
|
2
|
+
import { request } from 'undici'
|
|
3
|
+
import { Secp256k1Keypair } from '@atproto/crypto'
|
|
4
|
+
import type { IdResolver } from '@atproto/identity'
|
|
5
|
+
import type { TestBsky } from './bsky.js'
|
|
6
|
+
import type { TestPds } from './pds.js'
|
|
7
|
+
import type { DidAndKey } from './types.js'
|
|
8
|
+
|
|
9
|
+
export const mockNetworkUtilities = (
|
|
10
|
+
pds: TestPds | TestPds[],
|
|
11
|
+
bsky?: TestBsky,
|
|
12
|
+
) => {
|
|
13
|
+
const pdses = Array.isArray(pds) ? pds : [pds]
|
|
14
|
+
for (const p of pdses) {
|
|
15
|
+
mockResolvers(p.ctx.idResolver, pdses)
|
|
16
|
+
}
|
|
17
|
+
if (bsky) {
|
|
18
|
+
mockResolvers(bsky.ctx.idResolver, pdses)
|
|
19
|
+
mockResolvers(bsky.dataplane.idResolver, pdses)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const mockResolvers = (
|
|
24
|
+
idResolver: IdResolver,
|
|
25
|
+
pds: TestPds | TestPds[],
|
|
26
|
+
// handles resolve against the matching pds by default, but may point
|
|
27
|
+
// elsewhere e.g. an entryway
|
|
28
|
+
handleResolveUrl?: string,
|
|
29
|
+
) => {
|
|
30
|
+
const pdses = Array.isArray(pds) ? pds : [pds]
|
|
31
|
+
|
|
32
|
+
// Map pds public url to its local url when resolving from plc
|
|
33
|
+
const origResolveDid = idResolver.did.resolveNoCache
|
|
34
|
+
idResolver.did.resolveNoCache = async (did: string) => {
|
|
35
|
+
const result = await (origResolveDid.call(
|
|
36
|
+
idResolver.did,
|
|
37
|
+
did,
|
|
38
|
+
) as ReturnType<typeof origResolveDid>)
|
|
39
|
+
const service = result?.service?.find((svc) => svc.id === '#atproto_pds')
|
|
40
|
+
if (typeof service?.serviceEndpoint === 'string') {
|
|
41
|
+
for (const p of pdses) {
|
|
42
|
+
service.serviceEndpoint = service.serviceEndpoint.replace(
|
|
43
|
+
p.ctx.cfg.service.publicUrl,
|
|
44
|
+
`http://localhost:${p.port}`,
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return result
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const origResolveHandleDns = idResolver.handle.resolveDns
|
|
52
|
+
idResolver.handle.resolve = async (handle: string) => {
|
|
53
|
+
// Handle domains across PDSes are disjoint suffixes (.test, .test2, ...);
|
|
54
|
+
// first match wins.
|
|
55
|
+
const match = pdses.find((p) =>
|
|
56
|
+
p.ctx.cfg.identity.serviceHandleDomains.some((d) => handle.endsWith(d)),
|
|
57
|
+
)
|
|
58
|
+
if (!match) {
|
|
59
|
+
return origResolveHandleDns.call(idResolver.handle, handle)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const url = new URL(
|
|
63
|
+
`/.well-known/atproto-did`,
|
|
64
|
+
handleResolveUrl ?? match.url,
|
|
65
|
+
)
|
|
66
|
+
try {
|
|
67
|
+
const res = await request(url, { headers: { host: handle } })
|
|
68
|
+
if (res.statusCode !== 200) {
|
|
69
|
+
await res.body.dump()
|
|
70
|
+
return undefined
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return res.body.text()
|
|
74
|
+
} catch (err) {
|
|
75
|
+
return undefined
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const mockMailer = (pds: TestPds) => {
|
|
81
|
+
const mailer = pds.ctx.mailer
|
|
82
|
+
const _origSendMail = mailer.transporter.sendMail
|
|
83
|
+
mailer.transporter.sendMail = async (opts) => {
|
|
84
|
+
const result = await _origSendMail.call(mailer.transporter, opts)
|
|
85
|
+
console.log(`✉️ Email: ${JSON.stringify(result, null, 2)}`)
|
|
86
|
+
return result
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const usedLockIds = new Set()
|
|
91
|
+
export const uniqueLockId = () => {
|
|
92
|
+
let lockId: number
|
|
93
|
+
do {
|
|
94
|
+
lockId = 1000 + Math.ceil(1000 * Math.random())
|
|
95
|
+
} while (usedLockIds.has(lockId))
|
|
96
|
+
usedLockIds.add(lockId)
|
|
97
|
+
return lockId
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const createDidAndKey = async (opts: {
|
|
101
|
+
plcUrl: string
|
|
102
|
+
handle: string
|
|
103
|
+
pds: string
|
|
104
|
+
}): Promise<DidAndKey> => {
|
|
105
|
+
const { plcUrl, handle, pds } = opts
|
|
106
|
+
const key = await Secp256k1Keypair.create({ exportable: true })
|
|
107
|
+
const did = await new plc.Client(plcUrl).createDid({
|
|
108
|
+
signingKey: key.did(),
|
|
109
|
+
rotationKeys: [key.did()],
|
|
110
|
+
handle,
|
|
111
|
+
pds,
|
|
112
|
+
signer: key,
|
|
113
|
+
})
|
|
114
|
+
return {
|
|
115
|
+
key,
|
|
116
|
+
did,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["../../tsconfig/node.tsconfig.json"],
|
|
3
|
+
"include": ["./src"],
|
|
4
|
+
"references": [
|
|
5
|
+
{ "path": "../api/tsconfig.build.json" },
|
|
6
|
+
{ "path": "../bsky/tsconfig.build.json" },
|
|
7
|
+
{ "path": "../bsync/tsconfig.build.json" },
|
|
8
|
+
{ "path": "../common-web/tsconfig.build.json" },
|
|
9
|
+
{ "path": "../crypto/tsconfig.build.json" },
|
|
10
|
+
{ "path": "../identity/tsconfig.build.json" },
|
|
11
|
+
{ "path": "../lex/lex/tsconfig.build.json" },
|
|
12
|
+
{ "path": "../lexicon/tsconfig.build.json" },
|
|
13
|
+
{ "path": "../ozone/tsconfig.build.json" },
|
|
14
|
+
{ "path": "../pds/tsconfig.build.json" },
|
|
15
|
+
{ "path": "../sync/tsconfig.build.json" },
|
|
16
|
+
{ "path": "../syntax/tsconfig.build.json" },
|
|
17
|
+
{ "path": "../xrpc-server/tsconfig.build.json" },
|
|
18
|
+
],
|
|
19
|
+
"compilerOptions": {
|
|
20
|
+
"rootDir": "./src",
|
|
21
|
+
"outDir": "./dist",
|
|
22
|
+
// @TODO Change to true and fix implicit any errors
|
|
23
|
+
"noImplicitAny": false,
|
|
24
|
+
},
|
|
25
|
+
}
|
package/tsconfig.json
ADDED