@atproto/api 0.20.22 → 0.20.25
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 +26 -0
- package/dist/client/lexicons.d.ts +28 -12
- package/dist/client/lexicons.d.ts.map +1 -1
- package/dist/client/lexicons.js +14 -6
- package/dist/client/lexicons.js.map +1 -1
- package/dist/client/types/app/bsky/ageassurance/defs.d.ts +2 -0
- package/dist/client/types/app/bsky/ageassurance/defs.d.ts.map +1 -1
- package/dist/client/types/app/bsky/ageassurance/defs.js.map +1 -1
- package/package.json +22 -16
- package/definitions/labels.json +0 -170
- package/docs/moderation.md +0 -260
- package/jest.config.cjs +0 -23
- package/jest.d.ts +0 -20
- package/jest.setup.ts +0 -97
- package/scripts/code/labels.mjs +0 -77
- package/scripts/generate-code.mjs +0 -4
- package/src/age-assurance.test.ts +0 -218
- package/src/age-assurance.ts +0 -137
- package/src/agent.ts +0 -1645
- package/src/atp-agent.ts +0 -570
- package/src/bsky-agent.ts +0 -14
- package/src/const.ts +0 -1
- package/src/index.ts +0 -44
- package/src/mocker.ts +0 -220
- package/src/moderation/decision.ts +0 -389
- package/src/moderation/index.ts +0 -69
- package/src/moderation/mutewords.ts +0 -170
- package/src/moderation/subjects/account.ts +0 -44
- package/src/moderation/subjects/feed-generator.ts +0 -24
- package/src/moderation/subjects/notification.ts +0 -25
- package/src/moderation/subjects/post.ts +0 -433
- package/src/moderation/subjects/profile.ts +0 -26
- package/src/moderation/subjects/status.ts +0 -27
- package/src/moderation/subjects/user-list.ts +0 -48
- package/src/moderation/types.ts +0 -191
- package/src/moderation/ui.ts +0 -21
- package/src/moderation/util.ts +0 -111
- package/src/predicate.ts +0 -52
- package/src/rich-text/detection.ts +0 -146
- package/src/rich-text/rich-text.ts +0 -418
- package/src/rich-text/sanitization.ts +0 -42
- package/src/rich-text/unicode.ts +0 -47
- package/src/rich-text/util.ts +0 -15
- package/src/session-manager.ts +0 -5
- package/src/types.ts +0 -165
- package/src/util.ts +0 -96
- package/tests/atp-agent.test.ts +0 -3863
- package/tests/dispatcher.test.ts +0 -527
- package/tests/errors.test.ts +0 -29
- package/tests/moderation-behaviors.test.ts +0 -951
- package/tests/moderation-custom-labels.test.ts +0 -334
- package/tests/moderation-mutewords.test.ts +0 -1299
- package/tests/moderation-prefs.test.ts +0 -402
- package/tests/moderation-quoteposts.test.ts +0 -277
- package/tests/moderation.test.ts +0 -739
- package/tests/rich-text-detection.test.ts +0 -456
- package/tests/rich-text-sanitization.test.ts +0 -216
- package/tests/rich-text.test.ts +0 -705
- package/tests/util/echo-server.ts +0 -37
- package/tests/util/moderation-behavior.ts +0 -268
- package/tsconfig.build.json +0 -9
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -10
package/tests/dispatcher.test.ts
DELETED
|
@@ -1,527 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert'
|
|
2
|
-
import { AddressInfo } from 'node:net'
|
|
3
|
-
import { getPdsEndpoint, isValidDidDoc } from '@atproto/common-web'
|
|
4
|
-
import { TestNetworkNoAppView } from '@atproto/dev-env'
|
|
5
|
-
import {
|
|
6
|
-
AtpAgent,
|
|
7
|
-
AtpSessionData,
|
|
8
|
-
AtpSessionEvent,
|
|
9
|
-
BSKY_LABELER_DID,
|
|
10
|
-
} from '../src/index.js'
|
|
11
|
-
import { createHeaderEchoServer } from './util/echo-server.js'
|
|
12
|
-
|
|
13
|
-
const getPdsEndpointUrl = (...args: Parameters<typeof getPdsEndpoint>) => {
|
|
14
|
-
const endpoint = getPdsEndpoint(...args)
|
|
15
|
-
return endpoint ? new URL(endpoint) : endpoint
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
describe('AtpAgent', () => {
|
|
19
|
-
let network: TestNetworkNoAppView
|
|
20
|
-
|
|
21
|
-
beforeAll(async () => {
|
|
22
|
-
network = await TestNetworkNoAppView.create({
|
|
23
|
-
dbPostgresSchema: 'api_agent',
|
|
24
|
-
pds: {
|
|
25
|
-
enableDidDocWithSession: true,
|
|
26
|
-
},
|
|
27
|
-
})
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
afterAll(async () => {
|
|
31
|
-
await network?.close()
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('clones correctly', () => {
|
|
35
|
-
const persistSession = (_evt: AtpSessionEvent, _sess?: AtpSessionData) => {}
|
|
36
|
-
const agent = new AtpAgent({ service: network.pds.url, persistSession })
|
|
37
|
-
const agent2 = agent.clone()
|
|
38
|
-
expect(agent2 instanceof AtpAgent).toBeTruthy()
|
|
39
|
-
expect(agent.serviceUrl).toEqual(agent2.serviceUrl)
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('creates a new session on account creation.', async () => {
|
|
43
|
-
const events: string[] = []
|
|
44
|
-
const sessions: (AtpSessionData | undefined)[] = []
|
|
45
|
-
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
46
|
-
events.push(evt)
|
|
47
|
-
sessions.push(sess)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const agent = new AtpAgent({ service: network.pds.url, persistSession })
|
|
51
|
-
|
|
52
|
-
const res = await agent.createAccount({
|
|
53
|
-
handle: 'user1.test',
|
|
54
|
-
email: 'user1@test.com',
|
|
55
|
-
password: 'password',
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
expect(agent.hasSession).toEqual(true)
|
|
59
|
-
expect(agent.session?.accessJwt).toEqual(res.data.accessJwt)
|
|
60
|
-
expect(agent.session?.refreshJwt).toEqual(res.data.refreshJwt)
|
|
61
|
-
expect(agent.session?.handle).toEqual(res.data.handle)
|
|
62
|
-
expect(agent.session?.did).toEqual(res.data.did)
|
|
63
|
-
expect(agent.session?.email).toEqual('user1@test.com')
|
|
64
|
-
expect(agent.session?.emailConfirmed).toEqual(false)
|
|
65
|
-
assert(isValidDidDoc(res.data.didDoc))
|
|
66
|
-
expect(agent.pdsUrl).toEqual(getPdsEndpointUrl(res.data.didDoc))
|
|
67
|
-
|
|
68
|
-
const { data: sessionInfo } = await agent.com.atproto.server.getSession({})
|
|
69
|
-
expect(sessionInfo).toMatchObject({
|
|
70
|
-
did: res.data.did,
|
|
71
|
-
handle: res.data.handle,
|
|
72
|
-
email: 'user1@test.com',
|
|
73
|
-
emailConfirmed: false,
|
|
74
|
-
})
|
|
75
|
-
expect(isValidDidDoc(sessionInfo.didDoc)).toBe(true)
|
|
76
|
-
|
|
77
|
-
expect(events.length).toEqual(1)
|
|
78
|
-
expect(events[0]).toEqual('create')
|
|
79
|
-
expect(sessions.length).toEqual(1)
|
|
80
|
-
expect(sessions[0]?.accessJwt).toEqual(agent.session?.accessJwt)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it('creates a new session on login.', async () => {
|
|
84
|
-
const events: string[] = []
|
|
85
|
-
const sessions: (AtpSessionData | undefined)[] = []
|
|
86
|
-
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
87
|
-
events.push(evt)
|
|
88
|
-
sessions.push(sess)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const agent1 = new AtpAgent({ service: network.pds.url, persistSession })
|
|
92
|
-
|
|
93
|
-
const email = 'user2@test.com'
|
|
94
|
-
await agent1.createAccount({
|
|
95
|
-
handle: 'user2.test',
|
|
96
|
-
email,
|
|
97
|
-
password: 'password',
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
const agent2 = new AtpAgent({ service: network.pds.url, persistSession })
|
|
101
|
-
const res1 = await agent2.login({
|
|
102
|
-
identifier: 'user2.test',
|
|
103
|
-
password: 'password',
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
expect(agent2.hasSession).toEqual(true)
|
|
107
|
-
expect(agent2.session?.accessJwt).toEqual(res1.data.accessJwt)
|
|
108
|
-
expect(agent2.session?.refreshJwt).toEqual(res1.data.refreshJwt)
|
|
109
|
-
expect(agent2.session?.handle).toEqual(res1.data.handle)
|
|
110
|
-
expect(agent2.session?.did).toEqual(res1.data.did)
|
|
111
|
-
expect(agent2.session?.email).toEqual('user2@test.com')
|
|
112
|
-
expect(agent2.session?.emailConfirmed).toEqual(false)
|
|
113
|
-
assert(isValidDidDoc(res1.data.didDoc))
|
|
114
|
-
expect(agent2.pdsUrl).toEqual(getPdsEndpointUrl(res1.data.didDoc))
|
|
115
|
-
|
|
116
|
-
const { data: sessionInfo } = await agent2.com.atproto.server.getSession({})
|
|
117
|
-
expect(sessionInfo).toMatchObject({
|
|
118
|
-
did: res1.data.did,
|
|
119
|
-
handle: res1.data.handle,
|
|
120
|
-
email,
|
|
121
|
-
emailConfirmed: false,
|
|
122
|
-
})
|
|
123
|
-
expect(isValidDidDoc(sessionInfo.didDoc)).toBe(true)
|
|
124
|
-
|
|
125
|
-
expect(events.length).toEqual(2)
|
|
126
|
-
expect(events[0]).toEqual('create')
|
|
127
|
-
expect(events[1]).toEqual('create')
|
|
128
|
-
expect(sessions.length).toEqual(2)
|
|
129
|
-
expect(sessions[0]?.accessJwt).toEqual(agent1.session?.accessJwt)
|
|
130
|
-
expect(sessions[1]?.accessJwt).toEqual(agent2.session?.accessJwt)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('resumes an existing session.', async () => {
|
|
134
|
-
const events: string[] = []
|
|
135
|
-
const sessions: (AtpSessionData | undefined)[] = []
|
|
136
|
-
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
137
|
-
events.push(evt)
|
|
138
|
-
sessions.push(sess)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const agent1 = new AtpAgent({ service: network.pds.url, persistSession })
|
|
142
|
-
|
|
143
|
-
await agent1.createAccount({
|
|
144
|
-
handle: 'user3.test',
|
|
145
|
-
email: 'user3@test.com',
|
|
146
|
-
password: 'password',
|
|
147
|
-
})
|
|
148
|
-
if (!agent1.session) {
|
|
149
|
-
throw new Error('No session created')
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const agent2 = new AtpAgent({ service: network.pds.url, persistSession })
|
|
153
|
-
const res1 = await agent2.resumeSession(agent1.session)
|
|
154
|
-
|
|
155
|
-
expect(agent2.hasSession).toEqual(true)
|
|
156
|
-
expect(agent2.session?.handle).toEqual(res1.data.handle)
|
|
157
|
-
expect(agent2.session?.did).toEqual(res1.data.did)
|
|
158
|
-
assert(isValidDidDoc(res1.data.didDoc))
|
|
159
|
-
expect(agent2.pdsUrl).toEqual(getPdsEndpointUrl(res1.data.didDoc))
|
|
160
|
-
|
|
161
|
-
const { data: sessionInfo } = await agent2.com.atproto.server.getSession({})
|
|
162
|
-
expect(sessionInfo).toMatchObject({
|
|
163
|
-
did: res1.data.did,
|
|
164
|
-
handle: res1.data.handle,
|
|
165
|
-
email: res1.data.email,
|
|
166
|
-
emailConfirmed: false,
|
|
167
|
-
})
|
|
168
|
-
expect(isValidDidDoc(sessionInfo.didDoc)).toBe(true)
|
|
169
|
-
|
|
170
|
-
expect(events.length).toEqual(2)
|
|
171
|
-
expect(events[0]).toEqual('create')
|
|
172
|
-
expect(events[1]).toEqual('update')
|
|
173
|
-
expect(sessions.length).toEqual(2)
|
|
174
|
-
expect(sessions[0]?.accessJwt).toEqual(agent1.session?.accessJwt)
|
|
175
|
-
expect(sessions[1]?.accessJwt).toEqual(agent2.session?.accessJwt)
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
it('refreshes existing session.', async () => {
|
|
179
|
-
const events: string[] = []
|
|
180
|
-
const sessions: (AtpSessionData | undefined)[] = []
|
|
181
|
-
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
182
|
-
events.push(evt)
|
|
183
|
-
sessions.push(sess)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const agent = new AtpAgent({ service: network.pds.url, persistSession })
|
|
187
|
-
|
|
188
|
-
// create an account and a session with it
|
|
189
|
-
await agent.createAccount({
|
|
190
|
-
handle: 'user4.test',
|
|
191
|
-
email: 'user4@test.com',
|
|
192
|
-
password: 'password',
|
|
193
|
-
})
|
|
194
|
-
if (!agent.session?.refreshJwt) {
|
|
195
|
-
throw new Error('No session created')
|
|
196
|
-
}
|
|
197
|
-
const session1 = agent.session
|
|
198
|
-
const origAccessJwt = session1.accessJwt
|
|
199
|
-
|
|
200
|
-
// wait 1 second so that a token refresh will issue a new access token
|
|
201
|
-
// (if the timestamp, which has 1 second resolution, is the same -- then the access token won't change)
|
|
202
|
-
await new Promise((r) => setTimeout(r, 1000))
|
|
203
|
-
|
|
204
|
-
// patch the fetch handler to fake an expired token error on the next request
|
|
205
|
-
agent.sessionManager.setFetch(
|
|
206
|
-
async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
207
|
-
const req = new Request(input, init)
|
|
208
|
-
if (
|
|
209
|
-
req.headers.get('authorization') === `Bearer ${origAccessJwt}` &&
|
|
210
|
-
!req.url.includes('com.atproto.server.refreshSession')
|
|
211
|
-
) {
|
|
212
|
-
return new Response(JSON.stringify({ error: 'ExpiredToken' }), {
|
|
213
|
-
status: 400,
|
|
214
|
-
headers: { 'Content-Type': 'application/json' },
|
|
215
|
-
})
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return globalThis.fetch(req)
|
|
219
|
-
},
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
// put the agent through the auth flow
|
|
223
|
-
const res1 = await createPost(agent)
|
|
224
|
-
|
|
225
|
-
expect(res1.success).toEqual(true)
|
|
226
|
-
expect(agent.hasSession).toEqual(true)
|
|
227
|
-
expect(agent.session?.accessJwt).not.toEqual(session1.accessJwt)
|
|
228
|
-
expect(agent.session?.refreshJwt).not.toEqual(session1.refreshJwt)
|
|
229
|
-
expect(agent.session?.handle).toEqual(session1.handle)
|
|
230
|
-
expect(agent.session?.did).toEqual(session1.did)
|
|
231
|
-
expect(agent.session?.email).toEqual(session1.email)
|
|
232
|
-
expect(agent.session?.emailConfirmed).toEqual(session1.emailConfirmed)
|
|
233
|
-
|
|
234
|
-
expect(events.length).toEqual(2)
|
|
235
|
-
expect(events[0]).toEqual('create')
|
|
236
|
-
expect(events[1]).toEqual('update')
|
|
237
|
-
expect(sessions.length).toEqual(2)
|
|
238
|
-
expect(sessions[0]?.accessJwt).toEqual(origAccessJwt)
|
|
239
|
-
expect(sessions[1]?.accessJwt).toEqual(agent.session?.accessJwt)
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
it('dedupes session refreshes.', async () => {
|
|
243
|
-
const events: string[] = []
|
|
244
|
-
const sessions: (AtpSessionData | undefined)[] = []
|
|
245
|
-
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
246
|
-
events.push(evt)
|
|
247
|
-
sessions.push(sess)
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const agent = new AtpAgent({ service: network.pds.url, persistSession })
|
|
251
|
-
|
|
252
|
-
// create an account and a session with it
|
|
253
|
-
await agent.createAccount({
|
|
254
|
-
handle: 'user5.test',
|
|
255
|
-
email: 'user5@test.com',
|
|
256
|
-
password: 'password',
|
|
257
|
-
})
|
|
258
|
-
if (!agent.session) {
|
|
259
|
-
throw new Error('No session created')
|
|
260
|
-
}
|
|
261
|
-
const session1 = agent.session
|
|
262
|
-
const origAccessJwt = session1.accessJwt
|
|
263
|
-
|
|
264
|
-
// wait 1 second so that a token refresh will issue a new access token
|
|
265
|
-
// (if the timestamp, which has 1 second resolution, is the same -- then the access token won't change)
|
|
266
|
-
await new Promise((r) => setTimeout(r, 1000))
|
|
267
|
-
|
|
268
|
-
// patch the fetch handler to fake an expired token error on the next request
|
|
269
|
-
let expiredCalls = 0
|
|
270
|
-
let refreshCalls = 0
|
|
271
|
-
|
|
272
|
-
agent.sessionManager.setFetch(
|
|
273
|
-
async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
274
|
-
const req = new Request(input, init)
|
|
275
|
-
if (req.headers.get('authorization') === `Bearer ${origAccessJwt}`) {
|
|
276
|
-
expiredCalls++
|
|
277
|
-
return new Response(JSON.stringify({ error: 'ExpiredToken' }), {
|
|
278
|
-
status: 400,
|
|
279
|
-
headers: { 'Content-Type': 'application/json' },
|
|
280
|
-
})
|
|
281
|
-
}
|
|
282
|
-
if (req.url.includes('com.atproto.server.refreshSession')) {
|
|
283
|
-
refreshCalls++
|
|
284
|
-
}
|
|
285
|
-
return globalThis.fetch(req)
|
|
286
|
-
},
|
|
287
|
-
)
|
|
288
|
-
|
|
289
|
-
// put the agent through the auth flow
|
|
290
|
-
const [res1, res2, res3] = await Promise.all([
|
|
291
|
-
createPost(agent),
|
|
292
|
-
createPost(agent),
|
|
293
|
-
createPost(agent),
|
|
294
|
-
])
|
|
295
|
-
|
|
296
|
-
expect(expiredCalls).toEqual(3)
|
|
297
|
-
expect(refreshCalls).toEqual(1)
|
|
298
|
-
expect(res1.success).toEqual(true)
|
|
299
|
-
expect(res2.success).toEqual(true)
|
|
300
|
-
expect(res3.success).toEqual(true)
|
|
301
|
-
expect(agent.hasSession).toEqual(true)
|
|
302
|
-
expect(agent.session?.accessJwt).not.toEqual(session1.accessJwt)
|
|
303
|
-
expect(agent.session?.refreshJwt).not.toEqual(session1.refreshJwt)
|
|
304
|
-
expect(agent.session?.handle).toEqual(session1.handle)
|
|
305
|
-
expect(agent.session?.did).toEqual(session1.did)
|
|
306
|
-
expect(agent.session?.email).toEqual(session1.email)
|
|
307
|
-
expect(agent.session?.emailConfirmed).toEqual(session1.emailConfirmed)
|
|
308
|
-
|
|
309
|
-
expect(events.length).toEqual(2)
|
|
310
|
-
expect(events[0]).toEqual('create')
|
|
311
|
-
expect(events[1]).toEqual('update')
|
|
312
|
-
expect(sessions.length).toEqual(2)
|
|
313
|
-
expect(sessions[0]?.accessJwt).toEqual(origAccessJwt)
|
|
314
|
-
expect(sessions[1]?.accessJwt).toEqual(agent.session?.accessJwt)
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
it('persists an empty session on login and resumeSession failures', async () => {
|
|
318
|
-
const events: string[] = []
|
|
319
|
-
const sessions: (AtpSessionData | undefined)[] = []
|
|
320
|
-
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
321
|
-
events.push(evt)
|
|
322
|
-
sessions.push(sess)
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const agent = new AtpAgent({ service: network.pds.url, persistSession })
|
|
326
|
-
|
|
327
|
-
try {
|
|
328
|
-
await agent.login({
|
|
329
|
-
identifier: 'baduser.test',
|
|
330
|
-
password: 'password',
|
|
331
|
-
})
|
|
332
|
-
} catch (_e: any) {
|
|
333
|
-
// ignore
|
|
334
|
-
}
|
|
335
|
-
expect(agent.hasSession).toEqual(false)
|
|
336
|
-
|
|
337
|
-
try {
|
|
338
|
-
await agent.resumeSession({
|
|
339
|
-
accessJwt: 'bad',
|
|
340
|
-
refreshJwt: 'bad',
|
|
341
|
-
did: 'bad',
|
|
342
|
-
handle: 'bad',
|
|
343
|
-
active: true,
|
|
344
|
-
})
|
|
345
|
-
} catch (_e: any) {
|
|
346
|
-
// ignore
|
|
347
|
-
}
|
|
348
|
-
expect(agent.hasSession).toEqual(false)
|
|
349
|
-
|
|
350
|
-
expect(events.length).toEqual(2)
|
|
351
|
-
expect(events[0]).toEqual('create-failed')
|
|
352
|
-
expect(events[1]).toEqual('expired')
|
|
353
|
-
expect(sessions.length).toEqual(2)
|
|
354
|
-
expect(typeof sessions[0]).toEqual('undefined')
|
|
355
|
-
expect(typeof sessions[1]).toEqual('undefined')
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
it('does not modify the session on a failed refresh', async () => {
|
|
359
|
-
const events: { event: string; session: AtpSessionData | undefined }[] = []
|
|
360
|
-
const persistSession = (
|
|
361
|
-
event: AtpSessionEvent,
|
|
362
|
-
session?: AtpSessionData,
|
|
363
|
-
) => {
|
|
364
|
-
events.push({ event, session: session && { ...session } })
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
const agent = new AtpAgent({ service: network.pds.url, persistSession })
|
|
368
|
-
|
|
369
|
-
// create an account and a session with it
|
|
370
|
-
await agent.createAccount({
|
|
371
|
-
handle: 'user6.test',
|
|
372
|
-
email: 'user6@test.com',
|
|
373
|
-
password: 'password',
|
|
374
|
-
})
|
|
375
|
-
if (!agent.session) {
|
|
376
|
-
throw new Error('No session created')
|
|
377
|
-
}
|
|
378
|
-
const session1 = agent.session
|
|
379
|
-
const origAccessJwt = session1.accessJwt
|
|
380
|
-
|
|
381
|
-
// patch the fetch handler to fake an expired token error on the next request
|
|
382
|
-
agent.sessionManager.setFetch(
|
|
383
|
-
async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
384
|
-
const req = new Request(input, init)
|
|
385
|
-
if (req.headers.get('authorization') === `Bearer ${origAccessJwt}`) {
|
|
386
|
-
return new Response(JSON.stringify({ error: 'ExpiredToken' }), {
|
|
387
|
-
status: 400,
|
|
388
|
-
headers: { 'Content-Type': 'application/json' },
|
|
389
|
-
})
|
|
390
|
-
}
|
|
391
|
-
if (req.url.includes('com.atproto.server.refreshSession')) {
|
|
392
|
-
return new Response(undefined, { status: 500 })
|
|
393
|
-
}
|
|
394
|
-
return globalThis.fetch(req)
|
|
395
|
-
},
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
// put the agent through the auth flow
|
|
399
|
-
try {
|
|
400
|
-
await agent.app.bsky.feed.getTimeline()
|
|
401
|
-
throw new Error('Should have failed')
|
|
402
|
-
} catch (e: any) {
|
|
403
|
-
// the original error passes through
|
|
404
|
-
expect(e.status).toEqual(400)
|
|
405
|
-
expect(e.error).toEqual('ExpiredToken')
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// still has session because it wasn't invalidated
|
|
409
|
-
expect(agent.hasSession).toEqual(true)
|
|
410
|
-
|
|
411
|
-
expect(events.length).toEqual(2)
|
|
412
|
-
|
|
413
|
-
expect(events[0].event).toEqual('create')
|
|
414
|
-
expect(events[0].session?.accessJwt).toEqual(origAccessJwt)
|
|
415
|
-
expect(events[1].event).toEqual('network-error')
|
|
416
|
-
expect(events[1].session?.accessJwt).toEqual(origAccessJwt)
|
|
417
|
-
})
|
|
418
|
-
|
|
419
|
-
describe('createAccount', () => {
|
|
420
|
-
it('persists an empty session on failure', async () => {
|
|
421
|
-
const events: string[] = []
|
|
422
|
-
const sessions: (AtpSessionData | undefined)[] = []
|
|
423
|
-
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
424
|
-
events.push(evt)
|
|
425
|
-
sessions.push(sess)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const agent = new AtpAgent({ service: network.pds.url, persistSession })
|
|
429
|
-
|
|
430
|
-
await expect(
|
|
431
|
-
agent.createAccount({
|
|
432
|
-
handle: '',
|
|
433
|
-
email: '',
|
|
434
|
-
password: 'password',
|
|
435
|
-
}),
|
|
436
|
-
).rejects.toThrow()
|
|
437
|
-
|
|
438
|
-
expect(agent.hasSession).toEqual(false)
|
|
439
|
-
expect(agent.session).toEqual(undefined)
|
|
440
|
-
expect(events.length).toEqual(1)
|
|
441
|
-
expect(events[0]).toEqual('create-failed')
|
|
442
|
-
expect(sessions.length).toEqual(1)
|
|
443
|
-
expect(sessions[0]).toEqual(undefined)
|
|
444
|
-
})
|
|
445
|
-
})
|
|
446
|
-
|
|
447
|
-
describe('App labelers header', () => {
|
|
448
|
-
it('adds the labelers header as expected', async () => {
|
|
449
|
-
await using server = await createHeaderEchoServer()
|
|
450
|
-
const port = (server.address() as AddressInfo).port
|
|
451
|
-
const agent = new AtpAgent({ service: `http://localhost:${port}` })
|
|
452
|
-
const agent2 = new AtpAgent({ service: `http://localhost:${port}` })
|
|
453
|
-
|
|
454
|
-
const res1 = await agent.com.atproto.server.describeServer()
|
|
455
|
-
expect(res1.data['atproto-accept-labelers']).toEqual(
|
|
456
|
-
`${BSKY_LABELER_DID};redact`,
|
|
457
|
-
)
|
|
458
|
-
|
|
459
|
-
AtpAgent.configure({ appLabelers: ['did:plc:test1', 'did:plc:test2'] })
|
|
460
|
-
const res2 = await agent.com.atproto.server.describeServer()
|
|
461
|
-
expect(res2.data['atproto-accept-labelers']).toEqual(
|
|
462
|
-
'did:plc:test1;redact, did:plc:test2;redact',
|
|
463
|
-
)
|
|
464
|
-
const res3 = await agent2.com.atproto.server.describeServer()
|
|
465
|
-
expect(res3.data['atproto-accept-labelers']).toEqual(
|
|
466
|
-
'did:plc:test1;redact, did:plc:test2;redact',
|
|
467
|
-
)
|
|
468
|
-
AtpAgent.configure({ appLabelers: [BSKY_LABELER_DID] })
|
|
469
|
-
})
|
|
470
|
-
})
|
|
471
|
-
|
|
472
|
-
describe('configureLabelers', () => {
|
|
473
|
-
it('adds the labelers header as expected', async () => {
|
|
474
|
-
await using server = await createHeaderEchoServer()
|
|
475
|
-
|
|
476
|
-
const port = (server.address() as AddressInfo).port
|
|
477
|
-
const agent = new AtpAgent({ service: `http://localhost:${port}` })
|
|
478
|
-
|
|
479
|
-
agent.configureLabelers(['did:plc:test1'])
|
|
480
|
-
const res1 = await agent.com.atproto.server.describeServer()
|
|
481
|
-
expect(res1.data['atproto-accept-labelers']).toEqual(
|
|
482
|
-
`${BSKY_LABELER_DID};redact, did:plc:test1`,
|
|
483
|
-
)
|
|
484
|
-
|
|
485
|
-
agent.configureLabelers(['did:plc:test1', 'did:plc:test2'])
|
|
486
|
-
const res2 = await agent.com.atproto.server.describeServer()
|
|
487
|
-
expect(res2.data['atproto-accept-labelers']).toEqual(
|
|
488
|
-
`${BSKY_LABELER_DID};redact, did:plc:test1, did:plc:test2`,
|
|
489
|
-
)
|
|
490
|
-
})
|
|
491
|
-
})
|
|
492
|
-
|
|
493
|
-
describe('configureProxy', () => {
|
|
494
|
-
it('adds the proxy header as expected', async () => {
|
|
495
|
-
await using server = await createHeaderEchoServer()
|
|
496
|
-
const port = (server.address() as AddressInfo).port
|
|
497
|
-
const agent = new AtpAgent({ service: `http://localhost:${port}` })
|
|
498
|
-
|
|
499
|
-
const res1 = await agent.com.atproto.server.describeServer()
|
|
500
|
-
expect(res1.data['atproto-proxy']).toBeFalsy()
|
|
501
|
-
|
|
502
|
-
agent.configureProxy('did:plc:test1#atproto_labeler')
|
|
503
|
-
const res2 = await agent.com.atproto.server.describeServer()
|
|
504
|
-
expect(res2.data['atproto-proxy']).toEqual(
|
|
505
|
-
'did:plc:test1#atproto_labeler',
|
|
506
|
-
)
|
|
507
|
-
|
|
508
|
-
const res3 = await agent
|
|
509
|
-
.withProxy('atproto_labeler', 'did:plc:test2')
|
|
510
|
-
.com.atproto.server.describeServer()
|
|
511
|
-
expect(res3.data['atproto-proxy']).toEqual(
|
|
512
|
-
'did:plc:test2#atproto_labeler',
|
|
513
|
-
)
|
|
514
|
-
})
|
|
515
|
-
})
|
|
516
|
-
})
|
|
517
|
-
|
|
518
|
-
const createPost = async (agent: AtpAgent) => {
|
|
519
|
-
return agent.com.atproto.repo.createRecord({
|
|
520
|
-
repo: agent.accountDid,
|
|
521
|
-
collection: 'app.bsky.feed.post',
|
|
522
|
-
record: {
|
|
523
|
-
text: 'hello there',
|
|
524
|
-
createdAt: new Date().toISOString(),
|
|
525
|
-
},
|
|
526
|
-
})
|
|
527
|
-
}
|
package/tests/errors.test.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { AtpAgent, ComAtprotoServerCreateAccount } from '@atproto/api'
|
|
2
|
-
import { TestNetworkNoAppView } from '@atproto/dev-env'
|
|
3
|
-
|
|
4
|
-
describe('errors', () => {
|
|
5
|
-
let network: TestNetworkNoAppView
|
|
6
|
-
let client: AtpAgent
|
|
7
|
-
|
|
8
|
-
beforeAll(async () => {
|
|
9
|
-
network = await TestNetworkNoAppView.create({
|
|
10
|
-
dbPostgresSchema: 'known_errors',
|
|
11
|
-
})
|
|
12
|
-
client = network.pds.getAgent()
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
afterAll(async () => {
|
|
16
|
-
await network?.close()
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('constructs the correct error instance', async () => {
|
|
20
|
-
const res = client.com.atproto.server.createAccount({
|
|
21
|
-
handle: 'admin.blah',
|
|
22
|
-
email: 'admin@test.com',
|
|
23
|
-
password: 'password',
|
|
24
|
-
})
|
|
25
|
-
await expect(res).rejects.toThrow(
|
|
26
|
-
ComAtprotoServerCreateAccount.UnsupportedDomainError,
|
|
27
|
-
)
|
|
28
|
-
})
|
|
29
|
-
})
|