@atproto/api 0.0.7 → 0.1.0
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/README.md +44 -6
- package/dist/agent.d.ts +22 -0
- package/dist/client/index.d.ts +63 -61
- package/dist/client/lexicons.d.ts +168 -22
- package/dist/client/types/app/bsky/actor/getSuggestions.d.ts +0 -7
- package/dist/client/types/com/atproto/account/delete.d.ts +14 -2
- package/dist/{src/client/types/com/atproto/account/delete.d.ts → client/types/com/atproto/account/requestDelete.d.ts} +0 -0
- package/dist/client/types/com/atproto/admin/blob.d.ts +37 -0
- package/dist/client/types/com/atproto/admin/moderationAction.d.ts +13 -2
- package/dist/client/types/com/atproto/admin/record.d.ts +5 -2
- package/dist/client/types/com/atproto/admin/repo.d.ts +2 -2
- package/dist/client/types/com/atproto/admin/takeModerationAction.d.ts +5 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +667 -368
- package/dist/index.js.map +4 -4
- package/dist/types.d.ts +33 -0
- package/package.json +1 -1
- package/src/agent.ts +305 -0
- package/src/client/index.ts +75 -63
- package/src/client/lexicons.ts +219 -49
- package/src/client/types/app/bsky/actor/getSuggestions.ts +0 -18
- package/src/client/types/com/atproto/account/delete.ts +21 -1
- package/src/client/types/com/atproto/account/requestDelete.ts +27 -0
- package/src/client/types/com/atproto/admin/blob.ts +84 -0
- package/src/client/types/com/atproto/admin/moderationAction.ts +29 -10
- package/src/client/types/com/atproto/admin/record.ts +5 -2
- package/src/client/types/com/atproto/admin/repo.ts +2 -2
- package/src/client/types/com/atproto/admin/takeModerationAction.ts +8 -0
- package/src/index.ts +3 -3
- package/src/types.ts +71 -0
- package/tests/_util.ts +26 -0
- package/tests/agent.test.ts +391 -0
- package/tests/errors.test.ts +4 -8
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/src/client/index.d.ts +0 -474
- package/dist/src/client/lexicons.d.ts +0 -3036
- package/dist/src/client/schemas.d.ts +0 -17
- package/dist/src/client/types/app/bsky/actor/createScene.d.ts +0 -32
- package/dist/src/client/types/app/bsky/actor/getProfile.d.ts +0 -36
- package/dist/src/client/types/app/bsky/actor/getSuggestions.d.ts +0 -36
- package/dist/src/client/types/app/bsky/actor/profile.d.ts +0 -15
- package/dist/src/client/types/app/bsky/actor/ref.d.ts +0 -14
- package/dist/src/client/types/app/bsky/actor/search.d.ts +0 -32
- package/dist/src/client/types/app/bsky/actor/searchTypeahead.d.ts +0 -28
- package/dist/src/client/types/app/bsky/actor/updateProfile.d.ts +0 -48
- package/dist/src/client/types/app/bsky/administration/moderationAction.d.ts +0 -16
- package/dist/src/client/types/app/bsky/administration/reverseModerationAction.d.ts +0 -22
- package/dist/src/client/types/app/bsky/administration/takeModerationAction.d.ts +0 -27
- package/dist/src/client/types/app/bsky/badge.d.ts +0 -22
- package/dist/src/client/types/app/bsky/badgeAccept.d.ts +0 -11
- package/dist/src/client/types/app/bsky/badgeOffer.d.ts +0 -11
- package/dist/src/client/types/app/bsky/declaration.d.ts +0 -6
- package/dist/src/client/types/app/bsky/embed/external.d.ts +0 -26
- package/dist/src/client/types/app/bsky/embed/images.d.ts +0 -23
- package/dist/src/client/types/app/bsky/feed/embed.d.ts +0 -36
- package/dist/src/client/types/app/bsky/feed/feedViewPost.d.ts +0 -26
- package/dist/src/client/types/app/bsky/feed/getAuthorFeed.d.ts +0 -22
- package/dist/src/client/types/app/bsky/feed/getPostThread.d.ts +0 -43
- package/dist/src/client/types/app/bsky/feed/getRepostedBy.d.ts +0 -35
- package/dist/src/client/types/app/bsky/feed/getTimeline.d.ts +0 -22
- package/dist/src/client/types/app/bsky/feed/getVotes.d.ts +0 -33
- package/dist/src/client/types/app/bsky/feed/mediaEmbed.d.ts +0 -15
- package/dist/src/client/types/app/bsky/feed/post.d.ts +0 -54
- package/dist/src/client/types/app/bsky/feed/repost.d.ts +0 -6
- package/dist/src/client/types/app/bsky/feed/setVote.d.ts +0 -25
- package/dist/src/client/types/app/bsky/feed/trend.d.ts +0 -6
- package/dist/src/client/types/app/bsky/feed/vote.d.ts +0 -7
- package/dist/src/client/types/app/bsky/follow.d.ts +0 -9
- package/dist/src/client/types/app/bsky/getAuthorFeed.d.ts +0 -56
- package/dist/src/client/types/app/bsky/getBadgeMembers.d.ts +0 -29
- package/dist/src/client/types/app/bsky/getHomeFeed.d.ts +0 -56
- package/dist/src/client/types/app/bsky/getLikedBy.d.ts +0 -29
- package/dist/src/client/types/app/bsky/getNotificationCount.d.ts +0 -16
- package/dist/src/client/types/app/bsky/getNotifications.d.ts +0 -33
- package/dist/src/client/types/app/bsky/getPostThread.d.ts +0 -55
- package/dist/src/client/types/app/bsky/getProfile.d.ts +0 -26
- package/dist/src/client/types/app/bsky/getRepostedBy.d.ts +0 -29
- package/dist/src/client/types/app/bsky/getTimeline.d.ts +0 -56
- package/dist/src/client/types/app/bsky/getUserFollowers.d.ts +0 -31
- package/dist/src/client/types/app/bsky/getUserFollows.d.ts +0 -31
- package/dist/src/client/types/app/bsky/getUsersSearch.d.ts +0 -26
- package/dist/src/client/types/app/bsky/getUsersTypeahead.d.ts +0 -22
- package/dist/src/client/types/app/bsky/graph/assertCreator.d.ts +0 -1
- package/dist/src/client/types/app/bsky/graph/assertMember.d.ts +0 -1
- package/dist/src/client/types/app/bsky/graph/assertion.d.ts +0 -7
- package/dist/src/client/types/app/bsky/graph/confirmation.d.ts +0 -8
- package/dist/src/client/types/app/bsky/graph/follow.d.ts +0 -6
- package/dist/src/client/types/app/bsky/graph/getAssertions.d.ts +0 -43
- package/dist/src/client/types/app/bsky/graph/getFollowers.d.ts +0 -34
- package/dist/src/client/types/app/bsky/graph/getFollows.d.ts +0 -33
- package/dist/src/client/types/app/bsky/graph/getMembers.d.ts +0 -33
- package/dist/src/client/types/app/bsky/graph/getMemberships.d.ts +0 -33
- package/dist/src/client/types/app/bsky/invite.d.ts +0 -10
- package/dist/src/client/types/app/bsky/inviteAccept.d.ts +0 -14
- package/dist/src/client/types/app/bsky/like.d.ts +0 -10
- package/dist/src/client/types/app/bsky/mediaEmbed.d.ts +0 -15
- package/dist/src/client/types/app/bsky/notification/getCount.d.ts +0 -17
- package/dist/src/client/types/app/bsky/notification/list.d.ts +0 -32
- package/dist/src/client/types/app/bsky/notification/updateSeen.d.ts +0 -17
- package/dist/src/client/types/app/bsky/post.d.ts +0 -23
- package/dist/src/client/types/app/bsky/postNotificationsSeen.d.ts +0 -20
- package/dist/src/client/types/app/bsky/profile.d.ts +0 -5
- package/dist/src/client/types/app/bsky/repost.d.ts +0 -10
- package/dist/src/client/types/app/bsky/system/actorScene.d.ts +0 -1
- package/dist/src/client/types/app/bsky/system/actorUser.d.ts +0 -1
- package/dist/src/client/types/app/bsky/system/declRef.d.ts +0 -5
- package/dist/src/client/types/app/bsky/system/declaration.d.ts +0 -4
- package/dist/src/client/types/app/bsky/updateProfile.d.ts +0 -23
- package/dist/src/client/types/com/atproto/account/create.d.ts +0 -41
- package/dist/src/client/types/com/atproto/account/createInviteCode.d.ts +0 -22
- package/dist/src/client/types/com/atproto/account/get.d.ts +0 -12
- package/dist/src/client/types/com/atproto/account/requestPasswordReset.d.ts +0 -17
- package/dist/src/client/types/com/atproto/account/resetPassword.d.ts +0 -24
- package/dist/src/client/types/com/atproto/blob/upload.d.ts +0 -19
- package/dist/src/client/types/com/atproto/createAccount.d.ts +0 -40
- package/dist/src/client/types/com/atproto/createInviteCode.d.ts +0 -20
- package/dist/src/client/types/com/atproto/createSession.d.ts +0 -24
- package/dist/src/client/types/com/atproto/data/uploadFile.d.ts +0 -19
- package/dist/src/client/types/com/atproto/deleteAccount.d.ts +0 -20
- package/dist/src/client/types/com/atproto/deleteSession.d.ts +0 -17
- package/dist/src/client/types/com/atproto/getAccount.d.ts +0 -16
- package/dist/src/client/types/com/atproto/getAccountsConfig.d.ts +0 -17
- package/dist/src/client/types/com/atproto/getSession.d.ts +0 -17
- package/dist/src/client/types/com/atproto/handle/resolve.d.ts +0 -18
- package/dist/src/client/types/com/atproto/refreshSession.d.ts +0 -20
- package/dist/src/client/types/com/atproto/repo/batchWrite.d.ts +0 -39
- package/dist/src/client/types/com/atproto/repo/createRecord.d.ts +0 -26
- package/dist/src/client/types/com/atproto/repo/deleteRecord.d.ts +0 -19
- package/dist/src/client/types/com/atproto/repo/describe.d.ts +0 -22
- package/dist/src/client/types/com/atproto/repo/getRecord.d.ts +0 -23
- package/dist/src/client/types/com/atproto/repo/listRecords.d.ts +0 -30
- package/dist/src/client/types/com/atproto/repo/putRecord.d.ts +0 -27
- package/dist/src/client/types/com/atproto/repo/strongRef.d.ts +0 -5
- package/dist/src/client/types/com/atproto/repoBatchWrite.d.ts +0 -36
- package/dist/src/client/types/com/atproto/repoCreateRecord.d.ts +0 -24
- package/dist/src/client/types/com/atproto/repoDeleteRecord.d.ts +0 -18
- package/dist/src/client/types/com/atproto/repoDescribe.d.ts +0 -21
- package/dist/src/client/types/com/atproto/repoGetRecord.d.ts +0 -22
- package/dist/src/client/types/com/atproto/repoListRecords.d.ts +0 -27
- package/dist/src/client/types/com/atproto/repoPutRecord.d.ts +0 -25
- package/dist/src/client/types/com/atproto/requestAccountPasswordReset.d.ts +0 -19
- package/dist/src/client/types/com/atproto/resetAccountPassword.d.ts +0 -26
- package/dist/src/client/types/com/atproto/resolveHandle.d.ts +0 -17
- package/dist/src/client/types/com/atproto/resolveName.d.ts +0 -17
- package/dist/src/client/types/com/atproto/server/getAccountsConfig.d.ts +0 -24
- package/dist/src/client/types/com/atproto/session/create.d.ts +0 -29
- package/dist/src/client/types/com/atproto/session/delete.d.ts +0 -13
- package/dist/src/client/types/com/atproto/session/get.d.ts +0 -18
- package/dist/src/client/types/com/atproto/session/refresh.d.ts +0 -24
- package/dist/src/client/types/com/atproto/sync/getRepo.d.ts +0 -15
- package/dist/src/client/types/com/atproto/sync/getRoot.d.ts +0 -18
- package/dist/src/client/types/com/atproto/sync/updateRepo.d.ts +0 -15
- package/dist/src/client/types/com/atproto/syncGetRepo.d.ts +0 -15
- package/dist/src/client/types/com/atproto/syncGetRoot.d.ts +0 -17
- package/dist/src/client/types/com/atproto/syncUpdateRepo.d.ts +0 -15
- package/dist/src/index.d.ts +0 -4
- package/dist/src/schemas.d.ts +0 -19
- package/dist/src/session.d.ts +0 -42
- package/dist/src/types/app/bsky/badge.d.ts +0 -22
- package/dist/src/types/app/bsky/badgeAccept.d.ts +0 -11
- package/dist/src/types/app/bsky/badgeOffer.d.ts +0 -11
- package/dist/src/types/app/bsky/declaration.d.ts +0 -6
- package/dist/src/types/app/bsky/follow.d.ts +0 -9
- package/dist/src/types/app/bsky/getAuthorFeed.d.ts +0 -56
- package/dist/src/types/app/bsky/getBadgeMembers.d.ts +0 -29
- package/dist/src/types/app/bsky/getHomeFeed.d.ts +0 -56
- package/dist/src/types/app/bsky/getLikedBy.d.ts +0 -29
- package/dist/src/types/app/bsky/getNotificationCount.d.ts +0 -16
- package/dist/src/types/app/bsky/getNotifications.d.ts +0 -33
- package/dist/src/types/app/bsky/getPostThread.d.ts +0 -55
- package/dist/src/types/app/bsky/getProfile.d.ts +0 -42
- package/dist/src/types/app/bsky/getRepostedBy.d.ts +0 -29
- package/dist/src/types/app/bsky/getUserFollowers.d.ts +0 -31
- package/dist/src/types/app/bsky/getUserFollows.d.ts +0 -31
- package/dist/src/types/app/bsky/getUsersSearch.d.ts +0 -26
- package/dist/src/types/app/bsky/getUsersTypeahead.d.ts +0 -22
- package/dist/src/types/app/bsky/invite.d.ts +0 -10
- package/dist/src/types/app/bsky/inviteAccept.d.ts +0 -14
- package/dist/src/types/app/bsky/like.d.ts +0 -10
- package/dist/src/types/app/bsky/mediaEmbed.d.ts +0 -15
- package/dist/src/types/app/bsky/post.d.ts +0 -23
- package/dist/src/types/app/bsky/postNotificationsSeen.d.ts +0 -19
- package/dist/src/types/app/bsky/profile.d.ts +0 -11
- package/dist/src/types/app/bsky/repost.d.ts +0 -10
- package/dist/src/types/app/bsky/updateProfile.d.ts +0 -27
- package/dist/src/types/com/atproto/createAccount.d.ts +0 -39
- package/dist/src/types/com/atproto/createInviteCode.d.ts +0 -19
- package/dist/src/types/com/atproto/createSession.d.ts +0 -23
- package/dist/src/types/com/atproto/deleteAccount.d.ts +0 -19
- package/dist/src/types/com/atproto/deleteSession.d.ts +0 -16
- package/dist/src/types/com/atproto/getAccount.d.ts +0 -19
- package/dist/src/types/com/atproto/getAccountsConfig.d.ts +0 -17
- package/dist/src/types/com/atproto/getSession.d.ts +0 -17
- package/dist/src/types/com/atproto/refreshSession.d.ts +0 -19
- package/dist/src/types/com/atproto/repoBatchWrite.d.ts +0 -35
- package/dist/src/types/com/atproto/repoCreateRecord.d.ts +0 -23
- package/dist/src/types/com/atproto/repoDeleteRecord.d.ts +0 -15
- package/dist/src/types/com/atproto/repoDescribe.d.ts +0 -21
- package/dist/src/types/com/atproto/repoGetRecord.d.ts +0 -22
- package/dist/src/types/com/atproto/repoListRecords.d.ts +0 -27
- package/dist/src/types/com/atproto/repoPutRecord.d.ts +0 -24
- package/dist/src/types/com/atproto/requestAccountPasswordReset.d.ts +0 -18
- package/dist/src/types/com/atproto/resetAccountPassword.d.ts +0 -25
- package/dist/src/types/com/atproto/resolveName.d.ts +0 -17
- package/dist/src/types/com/atproto/syncGetRepo.d.ts +0 -15
- package/dist/src/types/com/atproto/syncGetRoot.d.ts +0 -17
- package/dist/src/types/com/atproto/syncUpdateRepo.d.ts +0 -14
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/src/session.ts +0 -194
- package/tests/session.test.ts +0 -239
|
@@ -8,19 +8,17 @@ import * as ComAtprotoRepoRepoRef from '../repo/repoRef'
|
|
|
8
8
|
import * as ComAtprotoRepoStrongRef from '../repo/strongRef'
|
|
9
9
|
import * as ComAtprotoAdminRepo from './repo'
|
|
10
10
|
import * as ComAtprotoAdminRecord from './record'
|
|
11
|
+
import * as ComAtprotoAdminBlob from './blob'
|
|
11
12
|
import * as ComAtprotoAdminModerationReport from './moderationReport'
|
|
12
13
|
|
|
13
14
|
export interface View {
|
|
14
15
|
id: number
|
|
15
|
-
action:
|
|
16
|
-
| 'com.atproto.admin.moderationAction#takedown'
|
|
17
|
-
| 'com.atproto.admin.moderationAction#flag'
|
|
18
|
-
| 'com.atproto.admin.moderationAction#acknowledge'
|
|
19
|
-
| (string & {})
|
|
16
|
+
action: ActionType
|
|
20
17
|
subject:
|
|
21
18
|
| ComAtprotoRepoRepoRef.Main
|
|
22
19
|
| ComAtprotoRepoStrongRef.Main
|
|
23
20
|
| { $type: string; [k: string]: unknown }
|
|
21
|
+
subjectBlobCids: string[]
|
|
24
22
|
reason: string
|
|
25
23
|
createdBy: string
|
|
26
24
|
createdAt: string
|
|
@@ -43,15 +41,12 @@ export function validateView(v: unknown): ValidationResult {
|
|
|
43
41
|
|
|
44
42
|
export interface ViewDetail {
|
|
45
43
|
id: number
|
|
46
|
-
action:
|
|
47
|
-
| 'com.atproto.admin.moderationAction#takedown'
|
|
48
|
-
| 'com.atproto.admin.moderationAction#flag'
|
|
49
|
-
| 'com.atproto.admin.moderationAction#acknowledge'
|
|
50
|
-
| (string & {})
|
|
44
|
+
action: ActionType
|
|
51
45
|
subject:
|
|
52
46
|
| ComAtprotoAdminRepo.View
|
|
53
47
|
| ComAtprotoAdminRecord.View
|
|
54
48
|
| { $type: string; [k: string]: unknown }
|
|
49
|
+
subjectBlobs: ComAtprotoAdminBlob.View[]
|
|
55
50
|
reason: string
|
|
56
51
|
createdBy: string
|
|
57
52
|
createdAt: string
|
|
@@ -72,6 +67,24 @@ export function validateViewDetail(v: unknown): ValidationResult {
|
|
|
72
67
|
return lexicons.validate('com.atproto.admin.moderationAction#viewDetail', v)
|
|
73
68
|
}
|
|
74
69
|
|
|
70
|
+
export interface ViewCurrent {
|
|
71
|
+
id: number
|
|
72
|
+
action: ActionType
|
|
73
|
+
[k: string]: unknown
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function isViewCurrent(v: unknown): v is ViewCurrent {
|
|
77
|
+
return (
|
|
78
|
+
isObj(v) &&
|
|
79
|
+
hasProp(v, '$type') &&
|
|
80
|
+
v.$type === 'com.atproto.admin.moderationAction#viewCurrent'
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function validateViewCurrent(v: unknown): ValidationResult {
|
|
85
|
+
return lexicons.validate('com.atproto.admin.moderationAction#viewCurrent', v)
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
export interface Reversal {
|
|
76
89
|
reason: string
|
|
77
90
|
createdBy: string
|
|
@@ -91,6 +104,12 @@ export function validateReversal(v: unknown): ValidationResult {
|
|
|
91
104
|
return lexicons.validate('com.atproto.admin.moderationAction#reversal', v)
|
|
92
105
|
}
|
|
93
106
|
|
|
107
|
+
export type ActionType =
|
|
108
|
+
| 'com.atproto.admin.moderationAction#takedown'
|
|
109
|
+
| 'com.atproto.admin.moderationAction#flag'
|
|
110
|
+
| 'com.atproto.admin.moderationAction#acknowledge'
|
|
111
|
+
| (string & {})
|
|
112
|
+
|
|
94
113
|
/** Moderation action type: Takedown. Indicates that content should not be served by the PDS. */
|
|
95
114
|
export const TAKEDOWN = 'com.atproto.admin.moderationAction#takedown'
|
|
96
115
|
/** Moderation action type: Flag. Indicates that the content was reviewed and considered to violate PDS rules, but may still be served. */
|
|
@@ -5,6 +5,7 @@ import { ValidationResult } from '@atproto/lexicon'
|
|
|
5
5
|
import { isObj, hasProp } from '../../../../util'
|
|
6
6
|
import { lexicons } from '../../../../lexicons'
|
|
7
7
|
import * as ComAtprotoAdminRepo from './repo'
|
|
8
|
+
import * as ComAtprotoAdminBlob from './blob'
|
|
8
9
|
import * as ComAtprotoAdminModerationAction from './moderationAction'
|
|
9
10
|
import * as ComAtprotoAdminModerationReport from './moderationReport'
|
|
10
11
|
|
|
@@ -12,6 +13,7 @@ export interface View {
|
|
|
12
13
|
uri: string
|
|
13
14
|
cid: string
|
|
14
15
|
value: {}
|
|
16
|
+
blobCids: string[]
|
|
15
17
|
indexedAt: string
|
|
16
18
|
moderation: Moderation
|
|
17
19
|
repo: ComAtprotoAdminRepo.View
|
|
@@ -34,6 +36,7 @@ export interface ViewDetail {
|
|
|
34
36
|
uri: string
|
|
35
37
|
cid: string
|
|
36
38
|
value: {}
|
|
39
|
+
blobs: ComAtprotoAdminBlob.View[]
|
|
37
40
|
indexedAt: string
|
|
38
41
|
moderation: ModerationDetail
|
|
39
42
|
repo: ComAtprotoAdminRepo.View
|
|
@@ -53,7 +56,7 @@ export function validateViewDetail(v: unknown): ValidationResult {
|
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
export interface Moderation {
|
|
56
|
-
|
|
59
|
+
currentAction?: ComAtprotoAdminModerationAction.ViewCurrent
|
|
57
60
|
[k: string]: unknown
|
|
58
61
|
}
|
|
59
62
|
|
|
@@ -70,9 +73,9 @@ export function validateModeration(v: unknown): ValidationResult {
|
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
export interface ModerationDetail {
|
|
76
|
+
currentAction?: ComAtprotoAdminModerationAction.ViewCurrent
|
|
73
77
|
actions: ComAtprotoAdminModerationAction.View[]
|
|
74
78
|
reports: ComAtprotoAdminModerationReport.View[]
|
|
75
|
-
takedownId?: number
|
|
76
79
|
[k: string]: unknown
|
|
77
80
|
}
|
|
78
81
|
|
|
@@ -67,7 +67,7 @@ export function validateAccount(v: unknown): ValidationResult {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
export interface Moderation {
|
|
70
|
-
|
|
70
|
+
currentAction?: ComAtprotoAdminModerationAction.ViewCurrent
|
|
71
71
|
[k: string]: unknown
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -84,9 +84,9 @@ export function validateModeration(v: unknown): ValidationResult {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
export interface ModerationDetail {
|
|
87
|
+
currentAction?: ComAtprotoAdminModerationAction.ViewCurrent
|
|
87
88
|
actions: ComAtprotoAdminModerationAction.View[]
|
|
88
89
|
reports: ComAtprotoAdminModerationReport.View[]
|
|
89
|
-
takedownId?: number
|
|
90
90
|
[k: string]: unknown
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -21,6 +21,7 @@ export interface InputSchema {
|
|
|
21
21
|
| ComAtprotoRepoRepoRef.Main
|
|
22
22
|
| ComAtprotoRepoRecordRef.Main
|
|
23
23
|
| { $type: string; [k: string]: unknown }
|
|
24
|
+
subjectBlobCids?: string[]
|
|
24
25
|
reason: string
|
|
25
26
|
createdBy: string
|
|
26
27
|
[k: string]: unknown
|
|
@@ -40,8 +41,15 @@ export interface Response {
|
|
|
40
41
|
data: OutputSchema
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
export class SubjectHasActionError extends XRPCError {
|
|
45
|
+
constructor(src: XRPCError) {
|
|
46
|
+
super(src.status, src.error, src.message)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
export function toKnownErr(e: any) {
|
|
44
51
|
if (e instanceof XRPCError) {
|
|
52
|
+
if (e.error === 'SubjectHasAction') return new SubjectHasActionError(e)
|
|
45
53
|
}
|
|
46
54
|
return e
|
|
47
55
|
}
|
package/src/index.ts
CHANGED
package/src/types.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Used by the PersistSessionHandler to indicate what change occurred
|
|
3
|
+
*/
|
|
4
|
+
export type AtpSessionEvent = 'create' | 'create-failed' | 'update' | 'expired'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Used by AtpAgent to store active sessions
|
|
8
|
+
*/
|
|
9
|
+
export interface AtpSessionData {
|
|
10
|
+
refreshJwt: string
|
|
11
|
+
accessJwt: string
|
|
12
|
+
handle: string
|
|
13
|
+
did: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Handler signature passed to AtpAgent to store session data
|
|
18
|
+
*/
|
|
19
|
+
export type AtpPersistSessionHandler = (
|
|
20
|
+
evt: AtpSessionEvent,
|
|
21
|
+
session: AtpSessionData | undefined,
|
|
22
|
+
) => void | Promise<void>
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* AtpAgent constructor() opts
|
|
26
|
+
*/
|
|
27
|
+
export interface AtpAgentOpts {
|
|
28
|
+
service: string | URL
|
|
29
|
+
persistSession?: AtpPersistSessionHandler
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* AtpAgent createAccount() opts
|
|
34
|
+
*/
|
|
35
|
+
export interface AtpAgentCreateAccountOpts {
|
|
36
|
+
email: string
|
|
37
|
+
password: string
|
|
38
|
+
handle: string
|
|
39
|
+
inviteCode?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* AtpAgent login() opts
|
|
44
|
+
*/
|
|
45
|
+
export interface AtpAgentLoginOpts {
|
|
46
|
+
identifier: string
|
|
47
|
+
password: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* AtpAgent global fetch handler
|
|
52
|
+
*/
|
|
53
|
+
type AtpAgentFetchHeaders = Record<string, string>
|
|
54
|
+
export interface AtpAgentFetchHandlerResponse {
|
|
55
|
+
status: number
|
|
56
|
+
headers: Record<string, string>
|
|
57
|
+
body: any
|
|
58
|
+
}
|
|
59
|
+
export type AptAgentFetchHandler = (
|
|
60
|
+
httpUri: string,
|
|
61
|
+
httpMethod: string,
|
|
62
|
+
httpHeaders: AtpAgentFetchHeaders,
|
|
63
|
+
httpReqBody: any,
|
|
64
|
+
) => Promise<AtpAgentFetchHandlerResponse>
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* AtpAgent global config opts
|
|
68
|
+
*/
|
|
69
|
+
export interface AtpAgentGlobalOpts {
|
|
70
|
+
fetch: AptAgentFetchHandler
|
|
71
|
+
}
|
package/tests/_util.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AtpAgentFetchHandlerResponse } from '..'
|
|
2
|
+
|
|
3
|
+
export async function fetchHandler(
|
|
4
|
+
httpUri: string,
|
|
5
|
+
httpMethod: string,
|
|
6
|
+
httpHeaders: Record<string, string>,
|
|
7
|
+
httpReqBody: unknown,
|
|
8
|
+
): Promise<AtpAgentFetchHandlerResponse> {
|
|
9
|
+
// The duplex field is now required for streaming bodies, but not yet reflected
|
|
10
|
+
// anywhere in docs or types. See whatwg/fetch#1438, nodejs/node#46221.
|
|
11
|
+
const reqInit: RequestInit & { duplex: string } = {
|
|
12
|
+
method: httpMethod,
|
|
13
|
+
headers: httpHeaders,
|
|
14
|
+
body: httpReqBody
|
|
15
|
+
? new TextEncoder().encode(JSON.stringify(httpReqBody))
|
|
16
|
+
: undefined,
|
|
17
|
+
duplex: 'half',
|
|
18
|
+
}
|
|
19
|
+
const res = await fetch(httpUri, reqInit)
|
|
20
|
+
const resBody = await res.arrayBuffer()
|
|
21
|
+
return {
|
|
22
|
+
status: res.status,
|
|
23
|
+
headers: Object.fromEntries(res.headers.entries()),
|
|
24
|
+
body: resBody ? JSON.parse(new TextDecoder().decode(resBody)) : undefined,
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { defaultFetchHandler } from '@atproto/xrpc'
|
|
2
|
+
import {
|
|
3
|
+
CloseFn,
|
|
4
|
+
runTestServer,
|
|
5
|
+
TestServerInfo,
|
|
6
|
+
} from '@atproto/pds/tests/_util'
|
|
7
|
+
import {
|
|
8
|
+
AtpAgent,
|
|
9
|
+
AtpAgentFetchHandlerResponse,
|
|
10
|
+
AtpSessionEvent,
|
|
11
|
+
AtpSessionData,
|
|
12
|
+
} from '..'
|
|
13
|
+
|
|
14
|
+
describe('agent', () => {
|
|
15
|
+
let server: TestServerInfo
|
|
16
|
+
let close: CloseFn
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
server = await runTestServer({
|
|
20
|
+
dbPostgresSchema: 'session',
|
|
21
|
+
})
|
|
22
|
+
close = server.close
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
afterAll(async () => {
|
|
26
|
+
await close()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('creates a new session on account creation.', async () => {
|
|
30
|
+
const events: string[] = []
|
|
31
|
+
const sessions: (AtpSessionData | undefined)[] = []
|
|
32
|
+
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
33
|
+
events.push(evt)
|
|
34
|
+
sessions.push(sess)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const agent = new AtpAgent({ service: server.url, persistSession })
|
|
38
|
+
|
|
39
|
+
const res = await agent.createAccount({
|
|
40
|
+
handle: 'user1.test',
|
|
41
|
+
email: 'user1@test.com',
|
|
42
|
+
password: 'password',
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
expect(agent.hasSession).toEqual(true)
|
|
46
|
+
expect(agent.session?.accessJwt).toEqual(res.data.accessJwt)
|
|
47
|
+
expect(agent.session?.refreshJwt).toEqual(res.data.refreshJwt)
|
|
48
|
+
expect(agent.session?.handle).toEqual(res.data.handle)
|
|
49
|
+
expect(agent.session?.did).toEqual(res.data.did)
|
|
50
|
+
|
|
51
|
+
const { data: sessionInfo } = await agent.api.com.atproto.session.get({})
|
|
52
|
+
expect(sessionInfo).toEqual({
|
|
53
|
+
did: res.data.did,
|
|
54
|
+
handle: res.data.handle,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
expect(events.length).toEqual(1)
|
|
58
|
+
expect(events[0]).toEqual('create')
|
|
59
|
+
expect(sessions.length).toEqual(1)
|
|
60
|
+
expect(sessions[0]?.accessJwt).toEqual(agent.session?.accessJwt)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('creates a new session on login.', async () => {
|
|
64
|
+
const events: string[] = []
|
|
65
|
+
const sessions: (AtpSessionData | undefined)[] = []
|
|
66
|
+
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
67
|
+
events.push(evt)
|
|
68
|
+
sessions.push(sess)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const agent1 = new AtpAgent({ service: server.url, persistSession })
|
|
72
|
+
|
|
73
|
+
await agent1.createAccount({
|
|
74
|
+
handle: 'user2.test',
|
|
75
|
+
email: 'user2@test.com',
|
|
76
|
+
password: 'password',
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const agent2 = new AtpAgent({ service: server.url, persistSession })
|
|
80
|
+
const res1 = await agent2.login({
|
|
81
|
+
identifier: 'user2.test',
|
|
82
|
+
password: 'password',
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
expect(agent2.hasSession).toEqual(true)
|
|
86
|
+
expect(agent2.session?.accessJwt).toEqual(res1.data.accessJwt)
|
|
87
|
+
expect(agent2.session?.refreshJwt).toEqual(res1.data.refreshJwt)
|
|
88
|
+
expect(agent2.session?.handle).toEqual(res1.data.handle)
|
|
89
|
+
expect(agent2.session?.did).toEqual(res1.data.did)
|
|
90
|
+
|
|
91
|
+
const { data: sessionInfo } = await agent2.api.com.atproto.session.get({})
|
|
92
|
+
expect(sessionInfo).toEqual({
|
|
93
|
+
did: res1.data.did,
|
|
94
|
+
handle: res1.data.handle,
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
expect(events.length).toEqual(2)
|
|
98
|
+
expect(events[0]).toEqual('create')
|
|
99
|
+
expect(events[1]).toEqual('create')
|
|
100
|
+
expect(sessions.length).toEqual(2)
|
|
101
|
+
expect(sessions[0]?.accessJwt).toEqual(agent1.session?.accessJwt)
|
|
102
|
+
expect(sessions[1]?.accessJwt).toEqual(agent2.session?.accessJwt)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('resumes an existing session.', async () => {
|
|
106
|
+
const events: string[] = []
|
|
107
|
+
const sessions: (AtpSessionData | undefined)[] = []
|
|
108
|
+
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
109
|
+
events.push(evt)
|
|
110
|
+
sessions.push(sess)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const agent1 = new AtpAgent({ service: server.url, persistSession })
|
|
114
|
+
|
|
115
|
+
await agent1.createAccount({
|
|
116
|
+
handle: 'user3.test',
|
|
117
|
+
email: 'user3@test.com',
|
|
118
|
+
password: 'password',
|
|
119
|
+
})
|
|
120
|
+
if (!agent1.session) {
|
|
121
|
+
throw new Error('No session created')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const agent2 = new AtpAgent({ service: server.url, persistSession })
|
|
125
|
+
const res1 = await agent2.resumeSession(agent1.session)
|
|
126
|
+
|
|
127
|
+
expect(agent2.hasSession).toEqual(true)
|
|
128
|
+
expect(agent2.session?.handle).toEqual(res1.data.handle)
|
|
129
|
+
expect(agent2.session?.did).toEqual(res1.data.did)
|
|
130
|
+
|
|
131
|
+
const { data: sessionInfo } = await agent2.api.com.atproto.session.get({})
|
|
132
|
+
expect(sessionInfo).toEqual({
|
|
133
|
+
did: res1.data.did,
|
|
134
|
+
handle: res1.data.handle,
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
expect(events.length).toEqual(2)
|
|
138
|
+
expect(events[0]).toEqual('create')
|
|
139
|
+
expect(events[1]).toEqual('create')
|
|
140
|
+
expect(sessions.length).toEqual(2)
|
|
141
|
+
expect(sessions[0]?.accessJwt).toEqual(agent1.session?.accessJwt)
|
|
142
|
+
expect(sessions[1]?.accessJwt).toEqual(agent2.session?.accessJwt)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('refreshes existing session.', async () => {
|
|
146
|
+
const events: string[] = []
|
|
147
|
+
const sessions: (AtpSessionData | undefined)[] = []
|
|
148
|
+
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
149
|
+
events.push(evt)
|
|
150
|
+
sessions.push(sess)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const agent = new AtpAgent({ service: server.url, persistSession })
|
|
154
|
+
|
|
155
|
+
// create an account and a session with it
|
|
156
|
+
await agent.createAccount({
|
|
157
|
+
handle: 'user4.test',
|
|
158
|
+
email: 'user4@test.com',
|
|
159
|
+
password: 'password',
|
|
160
|
+
})
|
|
161
|
+
if (!agent.session) {
|
|
162
|
+
throw new Error('No session created')
|
|
163
|
+
}
|
|
164
|
+
const session1 = agent.session
|
|
165
|
+
const origAccessJwt = session1.accessJwt
|
|
166
|
+
|
|
167
|
+
// wait 1 second so that a token refresh will issue a new access token
|
|
168
|
+
// (if the timestamp, which has 1 second resolution, is the same -- then the access token won't change)
|
|
169
|
+
await new Promise((r) => setTimeout(r, 1000))
|
|
170
|
+
|
|
171
|
+
// patch the fetch handler to fake an expired token error on the next request
|
|
172
|
+
const tokenExpiredFetchHandler = async function (
|
|
173
|
+
httpUri: string,
|
|
174
|
+
httpMethod: string,
|
|
175
|
+
httpHeaders: Record<string, string>,
|
|
176
|
+
httpReqBody: unknown,
|
|
177
|
+
): Promise<AtpAgentFetchHandlerResponse> {
|
|
178
|
+
if (httpHeaders.authorization === `Bearer ${origAccessJwt}`) {
|
|
179
|
+
return {
|
|
180
|
+
status: 400,
|
|
181
|
+
headers: {},
|
|
182
|
+
body: { error: 'ExpiredToken' },
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return defaultFetchHandler(httpUri, httpMethod, httpHeaders, httpReqBody)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// put the agent through the auth flow
|
|
189
|
+
AtpAgent.configure({ fetch: tokenExpiredFetchHandler })
|
|
190
|
+
const res1 = await agent.api.app.bsky.feed.getTimeline()
|
|
191
|
+
AtpAgent.configure({ fetch: defaultFetchHandler })
|
|
192
|
+
|
|
193
|
+
expect(res1.success).toEqual(true)
|
|
194
|
+
expect(agent.hasSession).toEqual(true)
|
|
195
|
+
expect(agent.session?.accessJwt).not.toEqual(session1.accessJwt)
|
|
196
|
+
expect(agent.session?.refreshJwt).not.toEqual(session1.refreshJwt)
|
|
197
|
+
expect(agent.session?.handle).toEqual(session1.handle)
|
|
198
|
+
expect(agent.session?.did).toEqual(session1.did)
|
|
199
|
+
|
|
200
|
+
expect(events.length).toEqual(2)
|
|
201
|
+
expect(events[0]).toEqual('create')
|
|
202
|
+
expect(events[1]).toEqual('update')
|
|
203
|
+
expect(sessions.length).toEqual(2)
|
|
204
|
+
expect(sessions[0]?.accessJwt).toEqual(origAccessJwt)
|
|
205
|
+
expect(sessions[1]?.accessJwt).toEqual(agent.session?.accessJwt)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('dedupes session refreshes.', async () => {
|
|
209
|
+
const events: string[] = []
|
|
210
|
+
const sessions: (AtpSessionData | undefined)[] = []
|
|
211
|
+
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
212
|
+
events.push(evt)
|
|
213
|
+
sessions.push(sess)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const agent = new AtpAgent({ service: server.url, persistSession })
|
|
217
|
+
|
|
218
|
+
// create an account and a session with it
|
|
219
|
+
await agent.createAccount({
|
|
220
|
+
handle: 'user5.test',
|
|
221
|
+
email: 'user5@test.com',
|
|
222
|
+
password: 'password',
|
|
223
|
+
})
|
|
224
|
+
if (!agent.session) {
|
|
225
|
+
throw new Error('No session created')
|
|
226
|
+
}
|
|
227
|
+
const session1 = agent.session
|
|
228
|
+
const origAccessJwt = session1.accessJwt
|
|
229
|
+
|
|
230
|
+
// wait 1 second so that a token refresh will issue a new access token
|
|
231
|
+
// (if the timestamp, which has 1 second resolution, is the same -- then the access token won't change)
|
|
232
|
+
await new Promise((r) => setTimeout(r, 1000))
|
|
233
|
+
|
|
234
|
+
// patch the fetch handler to fake an expired token error on the next request
|
|
235
|
+
let expiredCalls = 0
|
|
236
|
+
let refreshCalls = 0
|
|
237
|
+
const tokenExpiredFetchHandler = async function (
|
|
238
|
+
httpUri: string,
|
|
239
|
+
httpMethod: string,
|
|
240
|
+
httpHeaders: Record<string, string>,
|
|
241
|
+
httpReqBody: unknown,
|
|
242
|
+
): Promise<AtpAgentFetchHandlerResponse> {
|
|
243
|
+
if (httpHeaders.authorization === `Bearer ${origAccessJwt}`) {
|
|
244
|
+
expiredCalls++
|
|
245
|
+
return {
|
|
246
|
+
status: 400,
|
|
247
|
+
headers: {},
|
|
248
|
+
body: { error: 'ExpiredToken' },
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (httpUri.includes('com.atproto.session.refresh')) {
|
|
252
|
+
refreshCalls++
|
|
253
|
+
}
|
|
254
|
+
return defaultFetchHandler(httpUri, httpMethod, httpHeaders, httpReqBody)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// put the agent through the auth flow
|
|
258
|
+
AtpAgent.configure({ fetch: tokenExpiredFetchHandler })
|
|
259
|
+
const [res1, res2, res3] = await Promise.all([
|
|
260
|
+
agent.api.app.bsky.feed.getTimeline(),
|
|
261
|
+
agent.api.app.bsky.feed.getTimeline(),
|
|
262
|
+
agent.api.app.bsky.feed.getTimeline(),
|
|
263
|
+
])
|
|
264
|
+
AtpAgent.configure({ fetch: defaultFetchHandler })
|
|
265
|
+
|
|
266
|
+
expect(expiredCalls).toEqual(3)
|
|
267
|
+
expect(refreshCalls).toEqual(1)
|
|
268
|
+
expect(res1.success).toEqual(true)
|
|
269
|
+
expect(res2.success).toEqual(true)
|
|
270
|
+
expect(res3.success).toEqual(true)
|
|
271
|
+
expect(agent.hasSession).toEqual(true)
|
|
272
|
+
expect(agent.session?.accessJwt).not.toEqual(session1.accessJwt)
|
|
273
|
+
expect(agent.session?.refreshJwt).not.toEqual(session1.refreshJwt)
|
|
274
|
+
expect(agent.session?.handle).toEqual(session1.handle)
|
|
275
|
+
expect(agent.session?.did).toEqual(session1.did)
|
|
276
|
+
|
|
277
|
+
expect(events.length).toEqual(2)
|
|
278
|
+
expect(events[0]).toEqual('create')
|
|
279
|
+
expect(events[1]).toEqual('update')
|
|
280
|
+
expect(sessions.length).toEqual(2)
|
|
281
|
+
expect(sessions[0]?.accessJwt).toEqual(origAccessJwt)
|
|
282
|
+
expect(sessions[1]?.accessJwt).toEqual(agent.session?.accessJwt)
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('persists an empty session on login and resumeSession failures', async () => {
|
|
286
|
+
const events: string[] = []
|
|
287
|
+
const sessions: (AtpSessionData | undefined)[] = []
|
|
288
|
+
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
289
|
+
events.push(evt)
|
|
290
|
+
sessions.push(sess)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const agent = new AtpAgent({ service: server.url, persistSession })
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
await agent.login({
|
|
297
|
+
identifier: 'baduser.test',
|
|
298
|
+
password: 'password',
|
|
299
|
+
})
|
|
300
|
+
} catch (_e: any) {
|
|
301
|
+
// ignore
|
|
302
|
+
}
|
|
303
|
+
expect(agent.hasSession).toEqual(false)
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
await agent.resumeSession({
|
|
307
|
+
accessJwt: 'bad',
|
|
308
|
+
refreshJwt: 'bad',
|
|
309
|
+
did: 'bad',
|
|
310
|
+
handle: 'bad',
|
|
311
|
+
})
|
|
312
|
+
} catch (_e: any) {
|
|
313
|
+
// ignore
|
|
314
|
+
}
|
|
315
|
+
expect(agent.hasSession).toEqual(false)
|
|
316
|
+
|
|
317
|
+
expect(events.length).toEqual(2)
|
|
318
|
+
expect(events[0]).toEqual('create-failed')
|
|
319
|
+
expect(events[1]).toEqual('create-failed')
|
|
320
|
+
expect(sessions.length).toEqual(2)
|
|
321
|
+
expect(typeof sessions[0]).toEqual('undefined')
|
|
322
|
+
expect(typeof sessions[1]).toEqual('undefined')
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
it('does not modify or persist the session on a failed refresh', async () => {
|
|
326
|
+
const events: string[] = []
|
|
327
|
+
const sessions: (AtpSessionData | undefined)[] = []
|
|
328
|
+
const persistSession = (evt: AtpSessionEvent, sess?: AtpSessionData) => {
|
|
329
|
+
events.push(evt)
|
|
330
|
+
sessions.push(sess)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const agent = new AtpAgent({ service: server.url, persistSession })
|
|
334
|
+
|
|
335
|
+
// create an account and a session with it
|
|
336
|
+
await agent.createAccount({
|
|
337
|
+
handle: 'user6.test',
|
|
338
|
+
email: 'user6@test.com',
|
|
339
|
+
password: 'password',
|
|
340
|
+
})
|
|
341
|
+
if (!agent.session) {
|
|
342
|
+
throw new Error('No session created')
|
|
343
|
+
}
|
|
344
|
+
const session1 = agent.session
|
|
345
|
+
const origAccessJwt = session1.accessJwt
|
|
346
|
+
|
|
347
|
+
// patch the fetch handler to fake an expired token error on the next request
|
|
348
|
+
const tokenExpiredFetchHandler = async function (
|
|
349
|
+
httpUri: string,
|
|
350
|
+
httpMethod: string,
|
|
351
|
+
httpHeaders: Record<string, string>,
|
|
352
|
+
httpReqBody: unknown,
|
|
353
|
+
): Promise<AtpAgentFetchHandlerResponse> {
|
|
354
|
+
if (httpHeaders.authorization === `Bearer ${origAccessJwt}`) {
|
|
355
|
+
return {
|
|
356
|
+
status: 400,
|
|
357
|
+
headers: {},
|
|
358
|
+
body: { error: 'ExpiredToken' },
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (httpUri.includes('com.atproto.session.refresh')) {
|
|
362
|
+
return {
|
|
363
|
+
status: 500,
|
|
364
|
+
headers: {},
|
|
365
|
+
body: undefined,
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return defaultFetchHandler(httpUri, httpMethod, httpHeaders, httpReqBody)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// put the agent through the auth flow
|
|
372
|
+
AtpAgent.configure({ fetch: tokenExpiredFetchHandler })
|
|
373
|
+
try {
|
|
374
|
+
await agent.api.app.bsky.feed.getTimeline()
|
|
375
|
+
throw new Error('Should have failed')
|
|
376
|
+
} catch (e: any) {
|
|
377
|
+
// the original error passes through
|
|
378
|
+
expect(e.status).toEqual(400)
|
|
379
|
+
expect(e.error).toEqual('ExpiredToken')
|
|
380
|
+
}
|
|
381
|
+
AtpAgent.configure({ fetch: defaultFetchHandler })
|
|
382
|
+
|
|
383
|
+
// still has session because it wasn't invalidated
|
|
384
|
+
expect(agent.hasSession).toEqual(true)
|
|
385
|
+
|
|
386
|
+
expect(events.length).toEqual(1)
|
|
387
|
+
expect(events[0]).toEqual('create')
|
|
388
|
+
expect(sessions.length).toEqual(1)
|
|
389
|
+
expect(sessions[0]?.accessJwt).toEqual(origAccessJwt)
|
|
390
|
+
})
|
|
391
|
+
})
|
package/tests/errors.test.ts
CHANGED
|
@@ -3,22 +3,18 @@ import {
|
|
|
3
3
|
runTestServer,
|
|
4
4
|
TestServerInfo,
|
|
5
5
|
} from '@atproto/pds/tests/_util'
|
|
6
|
-
import {
|
|
7
|
-
sessionClient,
|
|
8
|
-
SessionServiceClient,
|
|
9
|
-
ComAtprotoAccountCreate,
|
|
10
|
-
} from '..'
|
|
6
|
+
import { AtpAgent, ComAtprotoAccountCreate } from '..'
|
|
11
7
|
|
|
12
8
|
describe('errors', () => {
|
|
13
9
|
let server: TestServerInfo
|
|
14
|
-
let client:
|
|
10
|
+
let client: AtpAgent
|
|
15
11
|
let close: CloseFn
|
|
16
12
|
|
|
17
13
|
beforeAll(async () => {
|
|
18
14
|
server = await runTestServer({
|
|
19
15
|
dbPostgresSchema: 'known_errors',
|
|
20
16
|
})
|
|
21
|
-
client =
|
|
17
|
+
client = new AtpAgent({ service: server.url })
|
|
22
18
|
close = server.close
|
|
23
19
|
})
|
|
24
20
|
|
|
@@ -27,7 +23,7 @@ describe('errors', () => {
|
|
|
27
23
|
})
|
|
28
24
|
|
|
29
25
|
it('constructs the correct error instance', async () => {
|
|
30
|
-
const res = client.com.atproto.account.create({
|
|
26
|
+
const res = client.api.com.atproto.account.create({
|
|
31
27
|
handle: 'admin',
|
|
32
28
|
email: 'admin@test.com',
|
|
33
29
|
password: 'password',
|