@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.
Files changed (210) hide show
  1. package/README.md +44 -6
  2. package/dist/agent.d.ts +22 -0
  3. package/dist/client/index.d.ts +63 -61
  4. package/dist/client/lexicons.d.ts +168 -22
  5. package/dist/client/types/app/bsky/actor/getSuggestions.d.ts +0 -7
  6. package/dist/client/types/com/atproto/account/delete.d.ts +14 -2
  7. package/dist/{src/client/types/com/atproto/account/delete.d.ts → client/types/com/atproto/account/requestDelete.d.ts} +0 -0
  8. package/dist/client/types/com/atproto/admin/blob.d.ts +37 -0
  9. package/dist/client/types/com/atproto/admin/moderationAction.d.ts +13 -2
  10. package/dist/client/types/com/atproto/admin/record.d.ts +5 -2
  11. package/dist/client/types/com/atproto/admin/repo.d.ts +2 -2
  12. package/dist/client/types/com/atproto/admin/takeModerationAction.d.ts +5 -1
  13. package/dist/index.d.ts +3 -3
  14. package/dist/index.js +667 -368
  15. package/dist/index.js.map +4 -4
  16. package/dist/types.d.ts +33 -0
  17. package/package.json +1 -1
  18. package/src/agent.ts +305 -0
  19. package/src/client/index.ts +75 -63
  20. package/src/client/lexicons.ts +219 -49
  21. package/src/client/types/app/bsky/actor/getSuggestions.ts +0 -18
  22. package/src/client/types/com/atproto/account/delete.ts +21 -1
  23. package/src/client/types/com/atproto/account/requestDelete.ts +27 -0
  24. package/src/client/types/com/atproto/admin/blob.ts +84 -0
  25. package/src/client/types/com/atproto/admin/moderationAction.ts +29 -10
  26. package/src/client/types/com/atproto/admin/record.ts +5 -2
  27. package/src/client/types/com/atproto/admin/repo.ts +2 -2
  28. package/src/client/types/com/atproto/admin/takeModerationAction.ts +8 -0
  29. package/src/index.ts +3 -3
  30. package/src/types.ts +71 -0
  31. package/tests/_util.ts +26 -0
  32. package/tests/agent.test.ts +391 -0
  33. package/tests/errors.test.ts +4 -8
  34. package/tsconfig.build.tsbuildinfo +1 -1
  35. package/dist/src/client/index.d.ts +0 -474
  36. package/dist/src/client/lexicons.d.ts +0 -3036
  37. package/dist/src/client/schemas.d.ts +0 -17
  38. package/dist/src/client/types/app/bsky/actor/createScene.d.ts +0 -32
  39. package/dist/src/client/types/app/bsky/actor/getProfile.d.ts +0 -36
  40. package/dist/src/client/types/app/bsky/actor/getSuggestions.d.ts +0 -36
  41. package/dist/src/client/types/app/bsky/actor/profile.d.ts +0 -15
  42. package/dist/src/client/types/app/bsky/actor/ref.d.ts +0 -14
  43. package/dist/src/client/types/app/bsky/actor/search.d.ts +0 -32
  44. package/dist/src/client/types/app/bsky/actor/searchTypeahead.d.ts +0 -28
  45. package/dist/src/client/types/app/bsky/actor/updateProfile.d.ts +0 -48
  46. package/dist/src/client/types/app/bsky/administration/moderationAction.d.ts +0 -16
  47. package/dist/src/client/types/app/bsky/administration/reverseModerationAction.d.ts +0 -22
  48. package/dist/src/client/types/app/bsky/administration/takeModerationAction.d.ts +0 -27
  49. package/dist/src/client/types/app/bsky/badge.d.ts +0 -22
  50. package/dist/src/client/types/app/bsky/badgeAccept.d.ts +0 -11
  51. package/dist/src/client/types/app/bsky/badgeOffer.d.ts +0 -11
  52. package/dist/src/client/types/app/bsky/declaration.d.ts +0 -6
  53. package/dist/src/client/types/app/bsky/embed/external.d.ts +0 -26
  54. package/dist/src/client/types/app/bsky/embed/images.d.ts +0 -23
  55. package/dist/src/client/types/app/bsky/feed/embed.d.ts +0 -36
  56. package/dist/src/client/types/app/bsky/feed/feedViewPost.d.ts +0 -26
  57. package/dist/src/client/types/app/bsky/feed/getAuthorFeed.d.ts +0 -22
  58. package/dist/src/client/types/app/bsky/feed/getPostThread.d.ts +0 -43
  59. package/dist/src/client/types/app/bsky/feed/getRepostedBy.d.ts +0 -35
  60. package/dist/src/client/types/app/bsky/feed/getTimeline.d.ts +0 -22
  61. package/dist/src/client/types/app/bsky/feed/getVotes.d.ts +0 -33
  62. package/dist/src/client/types/app/bsky/feed/mediaEmbed.d.ts +0 -15
  63. package/dist/src/client/types/app/bsky/feed/post.d.ts +0 -54
  64. package/dist/src/client/types/app/bsky/feed/repost.d.ts +0 -6
  65. package/dist/src/client/types/app/bsky/feed/setVote.d.ts +0 -25
  66. package/dist/src/client/types/app/bsky/feed/trend.d.ts +0 -6
  67. package/dist/src/client/types/app/bsky/feed/vote.d.ts +0 -7
  68. package/dist/src/client/types/app/bsky/follow.d.ts +0 -9
  69. package/dist/src/client/types/app/bsky/getAuthorFeed.d.ts +0 -56
  70. package/dist/src/client/types/app/bsky/getBadgeMembers.d.ts +0 -29
  71. package/dist/src/client/types/app/bsky/getHomeFeed.d.ts +0 -56
  72. package/dist/src/client/types/app/bsky/getLikedBy.d.ts +0 -29
  73. package/dist/src/client/types/app/bsky/getNotificationCount.d.ts +0 -16
  74. package/dist/src/client/types/app/bsky/getNotifications.d.ts +0 -33
  75. package/dist/src/client/types/app/bsky/getPostThread.d.ts +0 -55
  76. package/dist/src/client/types/app/bsky/getProfile.d.ts +0 -26
  77. package/dist/src/client/types/app/bsky/getRepostedBy.d.ts +0 -29
  78. package/dist/src/client/types/app/bsky/getTimeline.d.ts +0 -56
  79. package/dist/src/client/types/app/bsky/getUserFollowers.d.ts +0 -31
  80. package/dist/src/client/types/app/bsky/getUserFollows.d.ts +0 -31
  81. package/dist/src/client/types/app/bsky/getUsersSearch.d.ts +0 -26
  82. package/dist/src/client/types/app/bsky/getUsersTypeahead.d.ts +0 -22
  83. package/dist/src/client/types/app/bsky/graph/assertCreator.d.ts +0 -1
  84. package/dist/src/client/types/app/bsky/graph/assertMember.d.ts +0 -1
  85. package/dist/src/client/types/app/bsky/graph/assertion.d.ts +0 -7
  86. package/dist/src/client/types/app/bsky/graph/confirmation.d.ts +0 -8
  87. package/dist/src/client/types/app/bsky/graph/follow.d.ts +0 -6
  88. package/dist/src/client/types/app/bsky/graph/getAssertions.d.ts +0 -43
  89. package/dist/src/client/types/app/bsky/graph/getFollowers.d.ts +0 -34
  90. package/dist/src/client/types/app/bsky/graph/getFollows.d.ts +0 -33
  91. package/dist/src/client/types/app/bsky/graph/getMembers.d.ts +0 -33
  92. package/dist/src/client/types/app/bsky/graph/getMemberships.d.ts +0 -33
  93. package/dist/src/client/types/app/bsky/invite.d.ts +0 -10
  94. package/dist/src/client/types/app/bsky/inviteAccept.d.ts +0 -14
  95. package/dist/src/client/types/app/bsky/like.d.ts +0 -10
  96. package/dist/src/client/types/app/bsky/mediaEmbed.d.ts +0 -15
  97. package/dist/src/client/types/app/bsky/notification/getCount.d.ts +0 -17
  98. package/dist/src/client/types/app/bsky/notification/list.d.ts +0 -32
  99. package/dist/src/client/types/app/bsky/notification/updateSeen.d.ts +0 -17
  100. package/dist/src/client/types/app/bsky/post.d.ts +0 -23
  101. package/dist/src/client/types/app/bsky/postNotificationsSeen.d.ts +0 -20
  102. package/dist/src/client/types/app/bsky/profile.d.ts +0 -5
  103. package/dist/src/client/types/app/bsky/repost.d.ts +0 -10
  104. package/dist/src/client/types/app/bsky/system/actorScene.d.ts +0 -1
  105. package/dist/src/client/types/app/bsky/system/actorUser.d.ts +0 -1
  106. package/dist/src/client/types/app/bsky/system/declRef.d.ts +0 -5
  107. package/dist/src/client/types/app/bsky/system/declaration.d.ts +0 -4
  108. package/dist/src/client/types/app/bsky/updateProfile.d.ts +0 -23
  109. package/dist/src/client/types/com/atproto/account/create.d.ts +0 -41
  110. package/dist/src/client/types/com/atproto/account/createInviteCode.d.ts +0 -22
  111. package/dist/src/client/types/com/atproto/account/get.d.ts +0 -12
  112. package/dist/src/client/types/com/atproto/account/requestPasswordReset.d.ts +0 -17
  113. package/dist/src/client/types/com/atproto/account/resetPassword.d.ts +0 -24
  114. package/dist/src/client/types/com/atproto/blob/upload.d.ts +0 -19
  115. package/dist/src/client/types/com/atproto/createAccount.d.ts +0 -40
  116. package/dist/src/client/types/com/atproto/createInviteCode.d.ts +0 -20
  117. package/dist/src/client/types/com/atproto/createSession.d.ts +0 -24
  118. package/dist/src/client/types/com/atproto/data/uploadFile.d.ts +0 -19
  119. package/dist/src/client/types/com/atproto/deleteAccount.d.ts +0 -20
  120. package/dist/src/client/types/com/atproto/deleteSession.d.ts +0 -17
  121. package/dist/src/client/types/com/atproto/getAccount.d.ts +0 -16
  122. package/dist/src/client/types/com/atproto/getAccountsConfig.d.ts +0 -17
  123. package/dist/src/client/types/com/atproto/getSession.d.ts +0 -17
  124. package/dist/src/client/types/com/atproto/handle/resolve.d.ts +0 -18
  125. package/dist/src/client/types/com/atproto/refreshSession.d.ts +0 -20
  126. package/dist/src/client/types/com/atproto/repo/batchWrite.d.ts +0 -39
  127. package/dist/src/client/types/com/atproto/repo/createRecord.d.ts +0 -26
  128. package/dist/src/client/types/com/atproto/repo/deleteRecord.d.ts +0 -19
  129. package/dist/src/client/types/com/atproto/repo/describe.d.ts +0 -22
  130. package/dist/src/client/types/com/atproto/repo/getRecord.d.ts +0 -23
  131. package/dist/src/client/types/com/atproto/repo/listRecords.d.ts +0 -30
  132. package/dist/src/client/types/com/atproto/repo/putRecord.d.ts +0 -27
  133. package/dist/src/client/types/com/atproto/repo/strongRef.d.ts +0 -5
  134. package/dist/src/client/types/com/atproto/repoBatchWrite.d.ts +0 -36
  135. package/dist/src/client/types/com/atproto/repoCreateRecord.d.ts +0 -24
  136. package/dist/src/client/types/com/atproto/repoDeleteRecord.d.ts +0 -18
  137. package/dist/src/client/types/com/atproto/repoDescribe.d.ts +0 -21
  138. package/dist/src/client/types/com/atproto/repoGetRecord.d.ts +0 -22
  139. package/dist/src/client/types/com/atproto/repoListRecords.d.ts +0 -27
  140. package/dist/src/client/types/com/atproto/repoPutRecord.d.ts +0 -25
  141. package/dist/src/client/types/com/atproto/requestAccountPasswordReset.d.ts +0 -19
  142. package/dist/src/client/types/com/atproto/resetAccountPassword.d.ts +0 -26
  143. package/dist/src/client/types/com/atproto/resolveHandle.d.ts +0 -17
  144. package/dist/src/client/types/com/atproto/resolveName.d.ts +0 -17
  145. package/dist/src/client/types/com/atproto/server/getAccountsConfig.d.ts +0 -24
  146. package/dist/src/client/types/com/atproto/session/create.d.ts +0 -29
  147. package/dist/src/client/types/com/atproto/session/delete.d.ts +0 -13
  148. package/dist/src/client/types/com/atproto/session/get.d.ts +0 -18
  149. package/dist/src/client/types/com/atproto/session/refresh.d.ts +0 -24
  150. package/dist/src/client/types/com/atproto/sync/getRepo.d.ts +0 -15
  151. package/dist/src/client/types/com/atproto/sync/getRoot.d.ts +0 -18
  152. package/dist/src/client/types/com/atproto/sync/updateRepo.d.ts +0 -15
  153. package/dist/src/client/types/com/atproto/syncGetRepo.d.ts +0 -15
  154. package/dist/src/client/types/com/atproto/syncGetRoot.d.ts +0 -17
  155. package/dist/src/client/types/com/atproto/syncUpdateRepo.d.ts +0 -15
  156. package/dist/src/index.d.ts +0 -4
  157. package/dist/src/schemas.d.ts +0 -19
  158. package/dist/src/session.d.ts +0 -42
  159. package/dist/src/types/app/bsky/badge.d.ts +0 -22
  160. package/dist/src/types/app/bsky/badgeAccept.d.ts +0 -11
  161. package/dist/src/types/app/bsky/badgeOffer.d.ts +0 -11
  162. package/dist/src/types/app/bsky/declaration.d.ts +0 -6
  163. package/dist/src/types/app/bsky/follow.d.ts +0 -9
  164. package/dist/src/types/app/bsky/getAuthorFeed.d.ts +0 -56
  165. package/dist/src/types/app/bsky/getBadgeMembers.d.ts +0 -29
  166. package/dist/src/types/app/bsky/getHomeFeed.d.ts +0 -56
  167. package/dist/src/types/app/bsky/getLikedBy.d.ts +0 -29
  168. package/dist/src/types/app/bsky/getNotificationCount.d.ts +0 -16
  169. package/dist/src/types/app/bsky/getNotifications.d.ts +0 -33
  170. package/dist/src/types/app/bsky/getPostThread.d.ts +0 -55
  171. package/dist/src/types/app/bsky/getProfile.d.ts +0 -42
  172. package/dist/src/types/app/bsky/getRepostedBy.d.ts +0 -29
  173. package/dist/src/types/app/bsky/getUserFollowers.d.ts +0 -31
  174. package/dist/src/types/app/bsky/getUserFollows.d.ts +0 -31
  175. package/dist/src/types/app/bsky/getUsersSearch.d.ts +0 -26
  176. package/dist/src/types/app/bsky/getUsersTypeahead.d.ts +0 -22
  177. package/dist/src/types/app/bsky/invite.d.ts +0 -10
  178. package/dist/src/types/app/bsky/inviteAccept.d.ts +0 -14
  179. package/dist/src/types/app/bsky/like.d.ts +0 -10
  180. package/dist/src/types/app/bsky/mediaEmbed.d.ts +0 -15
  181. package/dist/src/types/app/bsky/post.d.ts +0 -23
  182. package/dist/src/types/app/bsky/postNotificationsSeen.d.ts +0 -19
  183. package/dist/src/types/app/bsky/profile.d.ts +0 -11
  184. package/dist/src/types/app/bsky/repost.d.ts +0 -10
  185. package/dist/src/types/app/bsky/updateProfile.d.ts +0 -27
  186. package/dist/src/types/com/atproto/createAccount.d.ts +0 -39
  187. package/dist/src/types/com/atproto/createInviteCode.d.ts +0 -19
  188. package/dist/src/types/com/atproto/createSession.d.ts +0 -23
  189. package/dist/src/types/com/atproto/deleteAccount.d.ts +0 -19
  190. package/dist/src/types/com/atproto/deleteSession.d.ts +0 -16
  191. package/dist/src/types/com/atproto/getAccount.d.ts +0 -19
  192. package/dist/src/types/com/atproto/getAccountsConfig.d.ts +0 -17
  193. package/dist/src/types/com/atproto/getSession.d.ts +0 -17
  194. package/dist/src/types/com/atproto/refreshSession.d.ts +0 -19
  195. package/dist/src/types/com/atproto/repoBatchWrite.d.ts +0 -35
  196. package/dist/src/types/com/atproto/repoCreateRecord.d.ts +0 -23
  197. package/dist/src/types/com/atproto/repoDeleteRecord.d.ts +0 -15
  198. package/dist/src/types/com/atproto/repoDescribe.d.ts +0 -21
  199. package/dist/src/types/com/atproto/repoGetRecord.d.ts +0 -22
  200. package/dist/src/types/com/atproto/repoListRecords.d.ts +0 -27
  201. package/dist/src/types/com/atproto/repoPutRecord.d.ts +0 -24
  202. package/dist/src/types/com/atproto/requestAccountPasswordReset.d.ts +0 -18
  203. package/dist/src/types/com/atproto/resetAccountPassword.d.ts +0 -25
  204. package/dist/src/types/com/atproto/resolveName.d.ts +0 -17
  205. package/dist/src/types/com/atproto/syncGetRepo.d.ts +0 -15
  206. package/dist/src/types/com/atproto/syncGetRoot.d.ts +0 -17
  207. package/dist/src/types/com/atproto/syncUpdateRepo.d.ts +0 -14
  208. package/dist/tsconfig.build.tsbuildinfo +0 -1
  209. package/src/session.ts +0 -194
  210. 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
- takedownId?: number
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
- takedownId?: number
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
@@ -1,4 +1,4 @@
1
+ export * from './types'
1
2
  export * from './client'
2
- export { default } from './client'
3
- export * from './session'
4
- export { default as sessionClient } from './session'
3
+ export * from './agent'
4
+ export { AtpAgent as default } from './agent'
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
+ })
@@ -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: SessionServiceClient
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 = sessionClient.service(server.url)
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',