@atproto/pds 0.5.4 → 0.5.6

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 (330) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/account-manager/account-manager.d.ts +52 -14
  3. package/dist/account-manager/account-manager.d.ts.map +1 -1
  4. package/dist/account-manager/account-manager.js +86 -26
  5. package/dist/account-manager/account-manager.js.map +1 -1
  6. package/dist/account-manager/db/index.d.ts.map +1 -1
  7. package/dist/account-manager/db/migrations/005-oauth-account-management.d.ts.map +1 -1
  8. package/dist/account-manager/db/migrations/005-oauth-account-management.js +3 -4
  9. package/dist/account-manager/db/migrations/005-oauth-account-management.js.map +1 -1
  10. package/dist/account-manager/db/migrations/index.d.ts.map +1 -1
  11. package/dist/account-manager/db/schema/authorization-request.d.ts +2 -2
  12. package/dist/account-manager/db/schema/authorization-request.d.ts.map +1 -1
  13. package/dist/account-manager/db/schema/authorization-request.js.map +1 -1
  14. package/dist/account-manager/db/schema/authorized-client.d.ts +2 -2
  15. package/dist/account-manager/db/schema/authorized-client.d.ts.map +1 -1
  16. package/dist/account-manager/db/schema/authorized-client.js.map +1 -1
  17. package/dist/account-manager/db/schema/token.d.ts +2 -2
  18. package/dist/account-manager/db/schema/token.d.ts.map +1 -1
  19. package/dist/account-manager/db/schema/token.js.map +1 -1
  20. package/dist/account-manager/helpers/account-device.d.ts +23 -196
  21. package/dist/account-manager/helpers/account-device.d.ts.map +1 -1
  22. package/dist/account-manager/helpers/account-device.js +3 -3
  23. package/dist/account-manager/helpers/account-device.js.map +1 -1
  24. package/dist/account-manager/helpers/account.d.ts +20 -10
  25. package/dist/account-manager/helpers/account.d.ts.map +1 -1
  26. package/dist/account-manager/helpers/account.js +17 -11
  27. package/dist/account-manager/helpers/account.js.map +1 -1
  28. package/dist/account-manager/helpers/auth.d.ts.map +1 -1
  29. package/dist/account-manager/helpers/auth.js +1 -1
  30. package/dist/account-manager/helpers/auth.js.map +1 -1
  31. package/dist/account-manager/helpers/authorization-request.d.ts +83 -5
  32. package/dist/account-manager/helpers/authorization-request.d.ts.map +1 -1
  33. package/dist/account-manager/helpers/authorization-request.js +8 -8
  34. package/dist/account-manager/helpers/authorization-request.js.map +1 -1
  35. package/dist/account-manager/helpers/authorized-client.d.ts +5 -4
  36. package/dist/account-manager/helpers/authorized-client.d.ts.map +1 -1
  37. package/dist/account-manager/helpers/authorized-client.js +3 -0
  38. package/dist/account-manager/helpers/authorized-client.js.map +1 -1
  39. package/dist/account-manager/helpers/device.d.ts +10 -4
  40. package/dist/account-manager/helpers/device.d.ts.map +1 -1
  41. package/dist/account-manager/helpers/device.js +4 -4
  42. package/dist/account-manager/helpers/device.js.map +1 -1
  43. package/dist/account-manager/helpers/email-token.d.ts.map +1 -1
  44. package/dist/account-manager/helpers/invite.d.ts +35 -2
  45. package/dist/account-manager/helpers/invite.d.ts.map +1 -1
  46. package/dist/account-manager/helpers/lexicon.d.ts.map +1 -1
  47. package/dist/account-manager/helpers/lexicon.js +6 -0
  48. package/dist/account-manager/helpers/lexicon.js.map +1 -1
  49. package/dist/account-manager/helpers/password.d.ts +1 -0
  50. package/dist/account-manager/helpers/password.d.ts.map +1 -1
  51. package/dist/account-manager/helpers/password.js +3 -0
  52. package/dist/account-manager/helpers/password.js.map +1 -1
  53. package/dist/account-manager/helpers/repo.d.ts.map +1 -1
  54. package/dist/account-manager/helpers/scrypt.d.ts.map +1 -1
  55. package/dist/account-manager/helpers/token.d.ts +72 -1031
  56. package/dist/account-manager/helpers/token.d.ts.map +1 -1
  57. package/dist/account-manager/helpers/token.js +13 -10
  58. package/dist/account-manager/helpers/token.js.map +1 -1
  59. package/dist/account-manager/helpers/used-refresh-token.d.ts +6 -2
  60. package/dist/account-manager/helpers/used-refresh-token.d.ts.map +1 -1
  61. package/dist/account-manager/oauth-store.d.ts +17 -13
  62. package/dist/account-manager/oauth-store.d.ts.map +1 -1
  63. package/dist/account-manager/oauth-store.js +89 -39
  64. package/dist/account-manager/oauth-store.js.map +1 -1
  65. package/dist/account-manager/scope-reference-getter.d.ts.map +1 -1
  66. package/dist/account-manager/scope-reference-getter.js.map +1 -1
  67. package/dist/actor-store/actor-store-reader.d.ts.map +1 -1
  68. package/dist/actor-store/actor-store-reader.js.map +1 -1
  69. package/dist/actor-store/actor-store-transactor.d.ts.map +1 -1
  70. package/dist/actor-store/actor-store-transactor.js.map +1 -1
  71. package/dist/actor-store/actor-store-writer.d.ts.map +1 -1
  72. package/dist/actor-store/actor-store.d.ts.map +1 -1
  73. package/dist/actor-store/actor-store.js.map +1 -1
  74. package/dist/actor-store/blob/reader.d.ts.map +1 -1
  75. package/dist/actor-store/blob/reader.js +2 -3
  76. package/dist/actor-store/blob/reader.js.map +1 -1
  77. package/dist/actor-store/blob/transactor.d.ts.map +1 -1
  78. package/dist/actor-store/blob/transactor.js.map +1 -1
  79. package/dist/actor-store/db/index.d.ts.map +1 -1
  80. package/dist/actor-store/db/migrations/index.d.ts.map +1 -1
  81. package/dist/actor-store/migrate.d.ts.map +1 -1
  82. package/dist/actor-store/preference/reader.d.ts.map +1 -1
  83. package/dist/actor-store/preference/reader.js.map +1 -1
  84. package/dist/actor-store/preference/transactor.d.ts.map +1 -1
  85. package/dist/actor-store/record/reader.d.ts +3 -3
  86. package/dist/actor-store/record/reader.d.ts.map +1 -1
  87. package/dist/actor-store/record/reader.js +3 -3
  88. package/dist/actor-store/record/reader.js.map +1 -1
  89. package/dist/actor-store/record/transactor.d.ts.map +1 -1
  90. package/dist/actor-store/record/transactor.js.map +1 -1
  91. package/dist/actor-store/repo/reader.d.ts.map +1 -1
  92. package/dist/actor-store/repo/reader.js.map +1 -1
  93. package/dist/actor-store/repo/sql-repo-reader.d.ts +5 -1
  94. package/dist/actor-store/repo/sql-repo-reader.d.ts.map +1 -1
  95. package/dist/actor-store/repo/sql-repo-reader.js.map +1 -1
  96. package/dist/actor-store/repo/sql-repo-transactor.d.ts.map +1 -1
  97. package/dist/actor-store/repo/transactor.d.ts.map +1 -1
  98. package/dist/actor-store/repo/transactor.js.map +1 -1
  99. package/dist/api/app/bsky/util/resolver.d.ts +4 -4
  100. package/dist/api/app/bsky/util/resolver.d.ts.map +1 -1
  101. package/dist/api/com/atproto/admin/getInviteCodes.d.ts.map +1 -1
  102. package/dist/api/com/atproto/admin/updateSubjectStatus.d.ts.map +1 -1
  103. package/dist/api/com/atproto/admin/updateSubjectStatus.js +12 -4
  104. package/dist/api/com/atproto/admin/updateSubjectStatus.js.map +1 -1
  105. package/dist/api/com/atproto/server/activateAccount.d.ts.map +1 -1
  106. package/dist/api/com/atproto/server/activateAccount.js +25 -31
  107. package/dist/api/com/atproto/server/activateAccount.js.map +1 -1
  108. package/dist/api/com/atproto/server/deactivateAccount.d.ts.map +1 -1
  109. package/dist/api/com/atproto/server/deactivateAccount.js +3 -4
  110. package/dist/api/com/atproto/server/deactivateAccount.js.map +1 -1
  111. package/dist/api/com/atproto/server/deleteAccount.d.ts.map +1 -1
  112. package/dist/api/com/atproto/server/deleteAccount.js +51 -37
  113. package/dist/api/com/atproto/server/deleteAccount.js.map +1 -1
  114. package/dist/api/com/atproto/server/util.d.ts +25 -6
  115. package/dist/api/com/atproto/server/util.d.ts.map +1 -1
  116. package/dist/api/com/atproto/server/util.js.map +1 -1
  117. package/dist/api/com/atproto/sync/getRepo.d.ts.map +1 -1
  118. package/dist/api/com/atproto/sync/listRepos.d.ts.map +1 -1
  119. package/dist/api/com/atproto/sync/util.d.ts.map +1 -1
  120. package/dist/api/proxy.d.ts.map +1 -1
  121. package/dist/app-view.d.ts.map +1 -1
  122. package/dist/auth-routes.d.ts.map +1 -1
  123. package/dist/auth-verifier.d.ts.map +1 -1
  124. package/dist/background.d.ts.map +1 -1
  125. package/dist/basic-routes.d.ts.map +1 -1
  126. package/dist/bsky-app-view.d.ts.map +1 -1
  127. package/dist/config/config.d.ts +2 -1
  128. package/dist/config/config.d.ts.map +1 -1
  129. package/dist/config/config.js +4 -1
  130. package/dist/config/config.js.map +1 -1
  131. package/dist/config/env.d.ts.map +1 -1
  132. package/dist/config/secrets.d.ts.map +1 -1
  133. package/dist/context.d.ts.map +1 -1
  134. package/dist/context.js +1 -1
  135. package/dist/context.js.map +1 -1
  136. package/dist/crawlers.d.ts.map +1 -1
  137. package/dist/db/db.d.ts.map +1 -1
  138. package/dist/db/migrator.d.ts +4 -3
  139. package/dist/db/migrator.d.ts.map +1 -1
  140. package/dist/db/migrator.js +1 -1
  141. package/dist/db/migrator.js.map +1 -1
  142. package/dist/db/pagination.d.ts +3 -2
  143. package/dist/db/pagination.d.ts.map +1 -1
  144. package/dist/db/pagination.js +2 -2
  145. package/dist/db/pagination.js.map +1 -1
  146. package/dist/db/util.d.ts +3 -3
  147. package/dist/db/util.d.ts.map +1 -1
  148. package/dist/db/util.js.map +1 -1
  149. package/dist/did-cache/db/index.d.ts.map +1 -1
  150. package/dist/did-cache/db/migrations.d.ts.map +1 -1
  151. package/dist/did-cache/index.d.ts.map +1 -1
  152. package/dist/did-cache/index.js.map +1 -1
  153. package/dist/disk-blobstore.d.ts.map +1 -1
  154. package/dist/disk-blobstore.js.map +1 -1
  155. package/dist/handle/explicit-slurs.d.ts.map +1 -1
  156. package/dist/handle/index.d.ts.map +1 -1
  157. package/dist/image/image-url-builder.d.ts.map +1 -1
  158. package/dist/image/image-url-builder.js.map +1 -1
  159. package/dist/index.d.ts.map +1 -1
  160. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts +75 -155
  161. package/dist/lexicons/app/bsky/actor/defs.defs.d.ts.map +1 -1
  162. package/dist/lexicons/app/bsky/actor/profile.defs.d.ts +100 -300
  163. package/dist/lexicons/app/bsky/actor/profile.defs.d.ts.map +1 -1
  164. package/dist/lexicons/app/bsky/actor/status.defs.d.ts +40 -40
  165. package/dist/lexicons/app/bsky/actor/status.defs.d.ts.map +1 -1
  166. package/dist/lexicons/app/bsky/authCreatePosts.defs.d.ts +1 -1
  167. package/dist/lexicons/app/bsky/authDeleteContent.defs.d.ts +1 -1
  168. package/dist/lexicons/app/bsky/authFullApp.defs.d.ts +1 -1
  169. package/dist/lexicons/app/bsky/authManageFeedDeclarations.defs.d.ts +1 -1
  170. package/dist/lexicons/app/bsky/authManageLabelerService.defs.d.ts +1 -1
  171. package/dist/lexicons/app/bsky/authManageModeration.defs.d.ts +1 -1
  172. package/dist/lexicons/app/bsky/authManageNotifications.defs.d.ts +1 -1
  173. package/dist/lexicons/app/bsky/authManageProfile.defs.d.ts +1 -1
  174. package/dist/lexicons/app/bsky/authViewAll.defs.d.ts +1 -1
  175. package/dist/lexicons/app/bsky/draft/defs.defs.d.ts +4 -4
  176. package/dist/lexicons/app/bsky/draft/defs.defs.d.ts.map +1 -1
  177. package/dist/lexicons/app/bsky/embed/external.defs.d.ts +10 -10
  178. package/dist/lexicons/app/bsky/embed/gallery.defs.d.ts +10 -42
  179. package/dist/lexicons/app/bsky/embed/gallery.defs.d.ts.map +1 -1
  180. package/dist/lexicons/app/bsky/embed/images.defs.d.ts +10 -10
  181. package/dist/lexicons/app/bsky/embed/record.defs.d.ts +10 -10
  182. package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.d.ts +10 -10
  183. package/dist/lexicons/app/bsky/embed/video.defs.d.ts +50 -162
  184. package/dist/lexicons/app/bsky/embed/video.defs.d.ts.map +1 -1
  185. package/dist/lexicons/app/bsky/feed/generator.defs.d.ts +80 -160
  186. package/dist/lexicons/app/bsky/feed/generator.defs.d.ts.map +1 -1
  187. package/dist/lexicons/app/bsky/feed/like.defs.d.ts +30 -30
  188. package/dist/lexicons/app/bsky/feed/like.defs.d.ts.map +1 -1
  189. package/dist/lexicons/app/bsky/feed/post.defs.d.ts +80 -200
  190. package/dist/lexicons/app/bsky/feed/post.defs.d.ts.map +1 -1
  191. package/dist/lexicons/app/bsky/feed/postgate.defs.d.ts +40 -80
  192. package/dist/lexicons/app/bsky/feed/postgate.defs.d.ts.map +1 -1
  193. package/dist/lexicons/app/bsky/feed/repost.defs.d.ts +30 -30
  194. package/dist/lexicons/app/bsky/feed/repost.defs.d.ts.map +1 -1
  195. package/dist/lexicons/app/bsky/feed/threadgate.defs.d.ts +30 -30
  196. package/dist/lexicons/app/bsky/feed/threadgate.defs.d.ts.map +1 -1
  197. package/dist/lexicons/app/bsky/graph/block.defs.d.ts +20 -20
  198. package/dist/lexicons/app/bsky/graph/block.defs.d.ts.map +1 -1
  199. package/dist/lexicons/app/bsky/graph/follow.defs.d.ts +30 -30
  200. package/dist/lexicons/app/bsky/graph/follow.defs.d.ts.map +1 -1
  201. package/dist/lexicons/app/bsky/graph/list.defs.d.ts +60 -60
  202. package/dist/lexicons/app/bsky/graph/list.defs.d.ts.map +1 -1
  203. package/dist/lexicons/app/bsky/graph/listblock.defs.d.ts +20 -20
  204. package/dist/lexicons/app/bsky/graph/listblock.defs.d.ts.map +1 -1
  205. package/dist/lexicons/app/bsky/graph/listitem.defs.d.ts +20 -20
  206. package/dist/lexicons/app/bsky/graph/listitem.defs.d.ts.map +1 -1
  207. package/dist/lexicons/app/bsky/graph/starterpack.defs.d.ts +50 -90
  208. package/dist/lexicons/app/bsky/graph/starterpack.defs.d.ts.map +1 -1
  209. package/dist/lexicons/app/bsky/graph/verification.defs.d.ts +30 -30
  210. package/dist/lexicons/app/bsky/graph/verification.defs.d.ts.map +1 -1
  211. package/dist/lexicons/app/bsky/labeler/service.defs.d.ts +50 -130
  212. package/dist/lexicons/app/bsky/labeler/service.defs.d.ts.map +1 -1
  213. package/dist/lexicons/app/bsky/notification/declaration.defs.d.ts +10 -10
  214. package/dist/lexicons/app/bsky/notification/declaration.defs.d.ts.map +1 -1
  215. package/dist/lexicons/app/bsky/richtext/facet.defs.d.ts +10 -10
  216. package/dist/lexicons/chat/bsky/actor/declaration.defs.d.ts +20 -20
  217. package/dist/lexicons/chat/bsky/actor/declaration.defs.d.ts.map +1 -1
  218. package/dist/lexicons/chat/bsky/authFullChatClient.defs.d.ts +1 -1
  219. package/dist/lexicons/chat/bsky/convo/defs.defs.d.ts +17 -0
  220. package/dist/lexicons/chat/bsky/convo/defs.defs.d.ts.map +1 -1
  221. package/dist/lexicons/chat/bsky/convo/defs.defs.js +11 -0
  222. package/dist/lexicons/chat/bsky/convo/defs.defs.js.map +1 -1
  223. package/dist/lexicons/chat/bsky/convo/sendMessage.defs.d.ts +1 -1
  224. package/dist/lexicons/chat/bsky/convo/sendMessage.defs.d.ts.map +1 -1
  225. package/dist/lexicons/chat/bsky/convo/sendMessage.defs.js +1 -0
  226. package/dist/lexicons/chat/bsky/convo/sendMessage.defs.js.map +1 -1
  227. package/dist/lexicons/chat/bsky/convo/sendMessageBatch.defs.d.ts +1 -1
  228. package/dist/lexicons/chat/bsky/convo/sendMessageBatch.defs.d.ts.map +1 -1
  229. package/dist/lexicons/chat/bsky/convo/sendMessageBatch.defs.js +1 -0
  230. package/dist/lexicons/chat/bsky/convo/sendMessageBatch.defs.js.map +1 -1
  231. package/dist/lexicons/chat/bsky/embed/joinLink.defs.d.ts +10 -42
  232. package/dist/lexicons/chat/bsky/embed/joinLink.defs.d.ts.map +1 -1
  233. package/dist/lexicons/com/atproto/lexicon/schema.defs.d.ts +10 -10
  234. package/dist/lexicons/com/atproto/lexicon/schema.defs.d.ts.map +1 -1
  235. package/dist/lexicons/com/atproto/repo/strongRef.defs.d.ts +10 -10
  236. package/dist/lexicons/com/germnetwork/declaration.defs.d.ts +40 -120
  237. package/dist/lexicons/com/germnetwork/declaration.defs.d.ts.map +1 -1
  238. package/dist/lexicons/site/standard/document.defs.d.ts +130 -450
  239. package/dist/lexicons/site/standard/document.defs.d.ts.map +1 -1
  240. package/dist/lexicons/site/standard/graph/recommend.defs.d.ts +10 -10
  241. package/dist/lexicons/site/standard/graph/recommend.defs.d.ts.map +1 -1
  242. package/dist/lexicons/site/standard/graph/subscription.defs.d.ts +20 -60
  243. package/dist/lexicons/site/standard/graph/subscription.defs.d.ts.map +1 -1
  244. package/dist/lexicons/site/standard/publication.defs.d.ts +60 -220
  245. package/dist/lexicons/site/standard/publication.defs.d.ts.map +1 -1
  246. package/dist/lexicons/site/standard/theme/basic.defs.d.ts +10 -10
  247. package/dist/lexicons/site/standard/theme/basic.defs.d.ts.map +1 -1
  248. package/dist/lexicons/tools/ozone/moderation/queryStatuses.defs.d.ts +2 -2
  249. package/dist/lexicons/tools/ozone/set/querySets.defs.d.ts +2 -2
  250. package/dist/logger.d.ts +14 -14
  251. package/dist/logger.d.ts.map +1 -1
  252. package/dist/mailer/index.d.ts +2 -0
  253. package/dist/mailer/index.d.ts.map +1 -1
  254. package/dist/mailer/index.js +2 -0
  255. package/dist/mailer/index.js.map +1 -1
  256. package/dist/mailer/moderation.d.ts.map +1 -1
  257. package/dist/pipethrough.d.ts +1 -1
  258. package/dist/pipethrough.d.ts.map +1 -1
  259. package/dist/pipethrough.js.map +1 -1
  260. package/dist/rate-limits.d.ts.map +1 -1
  261. package/dist/read-after-write/util.d.ts.map +1 -1
  262. package/dist/read-after-write/viewer.d.ts +8 -8
  263. package/dist/read-after-write/viewer.d.ts.map +1 -1
  264. package/dist/read-after-write/viewer.js.map +1 -1
  265. package/dist/redis.d.ts.map +1 -1
  266. package/dist/repo/prepare.d.ts.map +1 -1
  267. package/dist/repo/types.d.ts.map +1 -1
  268. package/dist/repo/types.js.map +1 -1
  269. package/dist/scripts/index.d.ts.map +1 -1
  270. package/dist/scripts/publish-identity.d.ts.map +1 -1
  271. package/dist/scripts/rebuild-repo.d.ts.map +1 -1
  272. package/dist/scripts/rotate-keys.d.ts.map +1 -1
  273. package/dist/scripts/sequencer-recovery/index.d.ts.map +1 -1
  274. package/dist/scripts/sequencer-recovery/recoverer.d.ts.map +1 -1
  275. package/dist/scripts/sequencer-recovery/recoverer.js.map +1 -1
  276. package/dist/scripts/sequencer-recovery/recovery-db.d.ts.map +1 -1
  277. package/dist/scripts/sequencer-recovery/recovery-db.js.map +1 -1
  278. package/dist/scripts/sequencer-recovery/repair-repos.d.ts.map +1 -1
  279. package/dist/scripts/sequencer-recovery/user-queues.d.ts.map +1 -1
  280. package/dist/scripts/util.d.ts.map +1 -1
  281. package/dist/sequencer/db/index.d.ts.map +1 -1
  282. package/dist/sequencer/db/migrations/index.d.ts.map +1 -1
  283. package/dist/sequencer/events.d.ts +19 -19
  284. package/dist/sequencer/events.d.ts.map +1 -1
  285. package/dist/sequencer/events.js +6 -13
  286. package/dist/sequencer/events.js.map +1 -1
  287. package/dist/sequencer/outbox.d.ts.map +1 -1
  288. package/dist/sequencer/outbox.js.map +1 -1
  289. package/dist/sequencer/sequencer.d.ts +1 -1
  290. package/dist/sequencer/sequencer.d.ts.map +1 -1
  291. package/dist/sequencer/sequencer.js.map +1 -1
  292. package/dist/util/debug.d.ts.map +1 -1
  293. package/dist/well-known.d.ts.map +1 -1
  294. package/package.json +26 -27
  295. package/src/account-manager/account-manager.ts +135 -36
  296. package/src/account-manager/db/migrations/005-oauth-account-management.ts +6 -5
  297. package/src/account-manager/db/schema/authorization-request.ts +2 -1
  298. package/src/account-manager/db/schema/authorized-client.ts +6 -2
  299. package/src/account-manager/db/schema/token.ts +2 -2
  300. package/src/account-manager/helpers/account-device.ts +5 -11
  301. package/src/account-manager/helpers/account.ts +39 -16
  302. package/src/account-manager/helpers/auth.ts +2 -2
  303. package/src/account-manager/helpers/authorization-request.ts +12 -8
  304. package/src/account-manager/helpers/authorized-client.ts +12 -6
  305. package/src/account-manager/helpers/device.ts +4 -4
  306. package/src/account-manager/helpers/lexicon.ts +8 -3
  307. package/src/account-manager/helpers/password.ts +6 -0
  308. package/src/account-manager/helpers/token.ts +17 -11
  309. package/src/account-manager/oauth-store.ts +129 -61
  310. package/src/actor-store/blob/reader.ts +8 -5
  311. package/src/actor-store/record/reader.ts +3 -3
  312. package/src/actor-store/repo/sql-repo-reader.ts +1 -1
  313. package/src/api/com/atproto/admin/updateSubjectStatus.ts +16 -4
  314. package/src/api/com/atproto/server/activateAccount.ts +27 -49
  315. package/src/api/com/atproto/server/deactivateAccount.ts +3 -7
  316. package/src/api/com/atproto/server/deleteAccount.ts +60 -43
  317. package/src/api/com/atproto/server/util.ts +24 -7
  318. package/src/config/config.ts +7 -2
  319. package/src/context.ts +2 -3
  320. package/src/db/migrator.ts +2 -1
  321. package/src/db/pagination.ts +7 -7
  322. package/src/db/util.ts +5 -2
  323. package/src/mailer/index.ts +4 -2
  324. package/src/scripts/sequencer-recovery/recovery-db.ts +1 -1
  325. package/src/sequencer/events.ts +8 -13
  326. package/src/sequencer/sequencer.ts +1 -3
  327. package/tests/account-manager.test.ts +79 -0
  328. package/tests/account-status.test.ts +206 -0
  329. package/tests/oauth.test.ts +91 -0
  330. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,59 +1,37 @@
1
- import { INVALID_HANDLE } from '@atproto/syntax'
2
- import {
3
- ForbiddenError,
4
- InvalidRequestError,
5
- Server,
6
- } from '@atproto/xrpc-server'
1
+ import { ForbiddenError, Server } from '@atproto/xrpc-server'
7
2
  import { ACCESS_FULL } from '../../../../auth-scope.js'
8
3
  import { AppContext } from '../../../../context.js'
9
4
  import { com } from '../../../../lexicons/index.js'
10
- import { assertValidDidDocumentForService } from './util.js'
11
5
 
12
6
  export default function (server: Server, ctx: AppContext) {
13
- server.add(com.atproto.server.activateAccount, {
14
- auth: ctx.authVerifier.authorization({
15
- scopes: ACCESS_FULL,
16
- authorize: () => {
17
- throw new ForbiddenError(
18
- 'OAuth credentials are not supported for this endpoint',
19
- )
20
- },
21
- }),
22
- handler: async ({ req, auth }) => {
23
- // in the case of entryway, the full flow is activateAccount (PDS) -> activateAccount (Entryway) -> updateSubjectStatus(PDS)
24
- if (ctx.entrywayClient) {
25
- const { headers } = ctx.entrywayPassthruHeaders(req)
26
- await ctx.entrywayClient.xrpc(com.atproto.server.activateAccount, {
27
- headers,
28
- })
29
- return
30
- }
31
-
32
- const requester = auth.credentials.did
33
-
34
- await assertValidDidDocumentForService(ctx, requester)
35
-
36
- const account = await ctx.accountManager.getAccount(requester, {
37
- includeDeactivated: true,
38
- })
39
- if (!account) {
40
- throw new InvalidRequestError('user not found', 'AccountNotFound')
41
- }
42
-
43
- await ctx.accountManager.activateAccount(requester)
7
+ const { entrywayClient } = ctx
44
8
 
45
- const syncData = await ctx.actorStore.read(requester, (store) =>
46
- store.repo.getSyncEventData(),
47
- )
48
-
49
- // @NOTE: we're over-emitting for now for backwards compatibility, can reduce this in the future
50
- const status = await ctx.accountManager.getAccountStatus(requester)
51
- await ctx.sequencer.sequenceAccountActivation(
52
- requester,
53
- account.handle ?? INVALID_HANDLE,
54
- status,
55
- syncData,
9
+ const auth = ctx.authVerifier.authorization({
10
+ scopes: ACCESS_FULL,
11
+ authorize: () => {
12
+ throw new ForbiddenError(
13
+ 'OAuth credentials are not supported for this endpoint',
56
14
  )
57
15
  },
58
16
  })
17
+
18
+ if (entrywayClient) {
19
+ // in the case of entryway, the full flow is activateAccount (PDS) -> activateAccount (Entryway) -> updateSubjectStatus(PDS)
20
+ server.add(com.atproto.server.activateAccount, {
21
+ auth,
22
+ handler: async ({ req }) => {
23
+ const { headers } = ctx.entrywayPassthruHeaders(req)
24
+ await entrywayClient.xrpc(com.atproto.server.activateAccount, {
25
+ headers,
26
+ })
27
+ },
28
+ })
29
+ } else {
30
+ server.add(com.atproto.server.activateAccount, {
31
+ auth,
32
+ handler: async ({ auth }) => {
33
+ await ctx.accountManager.activateAccount(auth.credentials.did)
34
+ },
35
+ })
36
+ }
59
37
  }
@@ -32,13 +32,9 @@ export default function (server: Server, ctx: AppContext) {
32
32
  server.add(com.atproto.server.deactivateAccount, {
33
33
  auth,
34
34
  handler: async ({ input: { body }, auth }) => {
35
- const requester = auth.credentials.did
36
- await ctx.accountManager.deactivateAccount(
37
- requester,
38
- body.deleteAfter ?? null,
39
- )
40
- const status = await ctx.accountManager.getAccountStatus(requester)
41
- await ctx.sequencer.sequenceAccount(requester, status)
35
+ await ctx.accountManager.deactivateAccount(auth.credentials.did, {
36
+ deleteAfter: body.deleteAfter ?? null,
37
+ })
42
38
  },
43
39
  })
44
40
  }
@@ -2,6 +2,7 @@ import { MINUTE } from '@atproto/common'
2
2
  import {
3
3
  AuthRequiredError,
4
4
  InvalidRequestError,
5
+ MethodRateLimit,
5
6
  Server,
6
7
  } from '@atproto/xrpc-server'
7
8
  import { OLD_PASSWORD_MAX_LENGTH } from '../../../../account-manager/helpers/scrypt.js'
@@ -11,58 +12,74 @@ import { com } from '../../../../lexicons/index.js'
11
12
  export default function (server: Server, ctx: AppContext) {
12
13
  const { entrywayClient } = ctx
13
14
 
14
- server.add(com.atproto.server.deleteAccount, {
15
- rateLimit: {
16
- durationMs: 5 * MINUTE,
17
- points: 50,
18
- },
19
- handler: async ({ input: { body }, req }) => {
20
- const { did, password, token } = body
15
+ const rateLimit: MethodRateLimit = {
16
+ durationMs: 5 * MINUTE,
17
+ points: 50,
18
+ }
21
19
 
22
- const account = await ctx.accountManager.getAccount(did, {
23
- includeDeactivated: true,
24
- includeTakenDown: true,
25
- })
26
- if (!account) {
27
- throw new InvalidRequestError('account not found')
28
- }
20
+ if (entrywayClient) {
21
+ server.add(com.atproto.server.deleteAccount, {
22
+ rateLimit,
23
+ handler: async ({ input: { body }, req }) => {
24
+ const account = await ctx.accountManager.getAccount(body.did, {
25
+ includeDeactivated: true,
26
+ includeTakenDown: true,
27
+ })
28
+ if (!account) {
29
+ throw new InvalidRequestError('account not found')
30
+ }
29
31
 
30
- if (entrywayClient) {
31
32
  const { headers } = ctx.entrywayPassthruHeaders(req)
32
33
  await entrywayClient.xrpc(com.atproto.server.deleteAccount, {
33
34
  body,
34
35
  headers,
35
36
  })
36
- return
37
- }
37
+ },
38
+ })
39
+ } else {
40
+ server.add(com.atproto.server.deleteAccount, {
41
+ rateLimit,
42
+ handler: async ({ input: { body } }) => {
43
+ const { did, password, token } = body
44
+
45
+ if (password.length > OLD_PASSWORD_MAX_LENGTH) {
46
+ throw new AuthRequiredError(
47
+ 'Password too long. Consider resetting your password.',
48
+ )
49
+ }
38
50
 
39
- if (password.length > OLD_PASSWORD_MAX_LENGTH) {
40
- throw new InvalidRequestError('Invalid password length.')
41
- }
51
+ const account = await ctx.accountManager.getAccount(did, {
52
+ includeDeactivated: true,
53
+ includeTakenDown: true,
54
+ })
55
+ if (!account) {
56
+ throw new InvalidRequestError('account not found')
57
+ }
42
58
 
43
- const validPass = await ctx.accountManager.verifyAccountPassword(
44
- did,
45
- password,
46
- )
47
- if (!validPass) {
48
- throw new AuthRequiredError('Invalid did or password')
49
- }
59
+ const validPass = await ctx.accountManager.verifyAccountPassword(
60
+ did,
61
+ password,
62
+ )
63
+ if (!validPass) {
64
+ throw new AuthRequiredError('Invalid did or password')
65
+ }
50
66
 
51
- await ctx.accountManager.assertValidEmailToken(
52
- did,
53
- 'delete_account',
54
- token,
55
- )
67
+ await ctx.accountManager.assertValidEmailToken(
68
+ did,
69
+ 'delete_account',
70
+ token,
71
+ )
56
72
 
57
- // @NOTE Order matters here: first "unlink" the account by removing it
58
- // from the account manager database ("source of truth"), then notify the
59
- // sequencer, and finally cleanup files from the file system.
60
- await ctx.accountManager.deleteAccount(did)
61
- try {
62
- await ctx.sequencer.sequenceAccountDeletion(did)
63
- } finally {
64
- await ctx.actorStore.destroy(did)
65
- }
66
- },
67
- })
73
+ // @NOTE Order matters here: first "unlink" the account by removing it
74
+ // from the account manager database ("source of truth"), then notify the
75
+ // sequencer, and finally cleanup files from the file system.
76
+ await ctx.accountManager.deleteAccount(did)
77
+ try {
78
+ await ctx.sequencer.sequenceAccountDeletion(did)
79
+ } finally {
80
+ await ctx.actorStore.destroy(did)
81
+ }
82
+ },
83
+ })
84
+ }
68
85
  }
@@ -1,9 +1,10 @@
1
+ import * as plc from '@did-plc/lib'
1
2
  import { getPdsEndpoint, getSigningDidKey } from '@atproto/common'
2
3
  import * as crypto from '@atproto/crypto'
3
- import { DidDocument } from '@atproto/identity'
4
+ import { DidDocument, IdResolver } from '@atproto/identity'
4
5
  import { InvalidRequestError } from '@atproto/xrpc-server'
6
+ import { ActorStore } from '../../../../actor-store/actor-store.js'
5
7
  import { ServerConfig } from '../../../../config/index.js'
6
- import { AppContext } from '../../../../context.js'
7
8
  import { httpLogger } from '../../../../logger.js'
8
9
 
9
10
  // generate an invite code preceded by the hostname
@@ -29,7 +30,7 @@ export const getRandomToken = () => {
29
30
  }
30
31
 
31
32
  export const safeResolveDidDoc = async (
32
- ctx: AppContext,
33
+ ctx: { idResolver: IdResolver },
33
34
  did: string,
34
35
  forceRefresh?: boolean,
35
36
  ): Promise<DidDocument | undefined> => {
@@ -42,7 +43,7 @@ export const safeResolveDidDoc = async (
42
43
  }
43
44
 
44
45
  export const didDocForSession = async (
45
- ctx: AppContext,
46
+ ctx: { idResolver: IdResolver; cfg: ServerConfig },
46
47
  did: string,
47
48
  forceRefresh?: boolean,
48
49
  ): Promise<DidDocument | undefined> => {
@@ -51,7 +52,13 @@ export const didDocForSession = async (
51
52
  }
52
53
 
53
54
  export const isValidDidDocForService = async (
54
- ctx: AppContext,
55
+ ctx: {
56
+ plcClient: plc.Client
57
+ idResolver: IdResolver
58
+ cfg: ServerConfig
59
+ plcRotationKey: crypto.Keypair
60
+ actorStore: ActorStore
61
+ },
55
62
  did: string,
56
63
  ): Promise<boolean> => {
57
64
  try {
@@ -63,7 +70,13 @@ export const isValidDidDocForService = async (
63
70
  }
64
71
 
65
72
  export const assertValidDidDocumentForService = async (
66
- ctx: AppContext,
73
+ ctx: {
74
+ plcClient: plc.Client
75
+ idResolver: IdResolver
76
+ cfg: ServerConfig
77
+ plcRotationKey: crypto.Keypair
78
+ actorStore: ActorStore
79
+ },
67
80
  did: string,
68
81
  ) => {
69
82
  if (did.startsWith('did:plc')) {
@@ -86,7 +99,11 @@ export const assertValidDidDocumentForService = async (
86
99
  }
87
100
 
88
101
  const assertValidDocContents = async (
89
- ctx: AppContext,
102
+ ctx: {
103
+ cfg: ServerConfig
104
+ plcRotationKey: crypto.Keypair
105
+ actorStore: ActorStore
106
+ },
90
107
  did: string,
91
108
  contents: {
92
109
  signingKey?: string
@@ -5,7 +5,7 @@ import {
5
5
  BrandingInput as BrandingConfig,
6
6
  HcaptchaConfig,
7
7
  } from '@atproto/oauth-provider'
8
- import { ensureValidDid } from '@atproto/syntax'
8
+ import { DidString, ensureValidDid, isValidDid } from '@atproto/syntax'
9
9
  import { ServerEnvironment } from './env.js'
10
10
 
11
11
  export type { BrandingConfig }
@@ -21,6 +21,11 @@ export const envToCfg = (env: ServerEnvironment): ServerConfig => {
21
21
  ? `http://localhost:${port}`
22
22
  : `https://${hostname}`
23
23
  const did = env.serviceDid ?? `did:web:${hostname}`
24
+
25
+ if (!isValidDid(did)) {
26
+ throw new Error(`Invalid service DID: ${did}`)
27
+ }
28
+
24
29
  const serviceCfg: ServerConfig['service'] = {
25
30
  port,
26
31
  hostname,
@@ -400,7 +405,7 @@ export type ServiceConfig = {
400
405
  port: number
401
406
  hostname: string
402
407
  publicUrl: string
403
- did: string
408
+ did: DidString
404
409
  version?: string
405
410
  privacyPolicyUrl?: string
406
411
  termsOfServiceUrl?: string
package/src/context.ts CHANGED
@@ -277,15 +277,14 @@ export class AppContext {
277
277
  )
278
278
 
279
279
  const accountManager = new AccountManager(
280
+ cfg,
281
+ actorStore,
280
282
  idResolver,
281
283
  jwtSecretKey,
282
284
  mailer,
283
285
  sequencer,
284
286
  plcClient,
285
287
  plcRotationKey,
286
- cfg.service.did,
287
- cfg.identity.serviceHandleDomains,
288
- cfg.db,
289
288
  )
290
289
  await accountManager.migrateOrThrow()
291
290
 
@@ -1,4 +1,5 @@
1
- import { Kysely, Migration, Migrator as KyselyMigrator } from 'kysely'
1
+ import { Kysely } from 'kysely'
2
+ import { Migration, Migrator as KyselyMigrator } from 'kysely/migration'
2
3
 
3
4
  export class Migrator<T> extends KyselyMigrator {
4
5
  constructor(
@@ -1,4 +1,4 @@
1
- import { sql } from 'kysely'
1
+ import { SqlBool, sql } from 'kysely'
2
2
  import { InvalidRequestError } from '@atproto/xrpc-server'
3
3
  import { AnyQb, DbRef } from './util.js'
4
4
 
@@ -66,16 +66,16 @@ export abstract class GenericKeyset<R, LR extends LabeledResult> {
66
66
  if (tryIndex) {
67
67
  // The tryIndex param will likely disappear and become the default implementation: here for now for gradual rollout query-by-query.
68
68
  if (direction === 'asc') {
69
- return sql`((${this.primary}, ${this.secondary}) > (${labeled.primary}, ${labeled.secondary}))`
69
+ return sql<SqlBool>`((${this.primary}, ${this.secondary}) > (${labeled.primary}, ${labeled.secondary}))`
70
70
  } else {
71
- return sql`((${this.primary}, ${this.secondary}) < (${labeled.primary}, ${labeled.secondary}))`
71
+ return sql<SqlBool>`((${this.primary}, ${this.secondary}) < (${labeled.primary}, ${labeled.secondary}))`
72
72
  }
73
73
  } else {
74
74
  // @NOTE this implementation can struggle to use an index on (primary, secondary) for pagination due to the "or" usage.
75
75
  if (direction === 'asc') {
76
- return sql`((${this.primary} > ${labeled.primary}) or (${this.primary} = ${labeled.primary} and ${this.secondary} > ${labeled.secondary}))`
76
+ return sql<SqlBool>`((${this.primary} > ${labeled.primary}) or (${this.primary} = ${labeled.primary} and ${this.secondary} > ${labeled.secondary}))`
77
77
  } else {
78
- return sql`((${this.primary} < ${labeled.primary}) or (${this.primary} = ${labeled.primary} and ${this.secondary} < ${labeled.secondary}))`
78
+ return sql<SqlBool>`((${this.primary} < ${labeled.primary}) or (${this.primary} = ${labeled.primary} and ${this.secondary} < ${labeled.secondary}))`
79
79
  }
80
80
  }
81
81
  }
@@ -125,8 +125,8 @@ export const paginate = <
125
125
  const { limit, cursor, keyset, direction = 'desc', tryIndex } = opts
126
126
  const keysetSql = keyset.getSql(keyset.unpack(cursor), direction, tryIndex)
127
127
  return qb
128
- .if(!!limit, (q) => q.limit(limit as number))
128
+ .$if(!!limit, (q) => q.limit(limit as number))
129
129
  .orderBy(keyset.primary, direction)
130
130
  .orderBy(keyset.secondary, direction)
131
- .if(!!keysetSql, (qb) => (keysetSql ? qb.where(keysetSql) : qb)) as QB
131
+ .$if(!!keysetSql, (qb) => (keysetSql ? qb.where(keysetSql) : qb)) as QB
132
132
  }
package/src/db/util.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  RawBuilder,
6
6
  ReferenceExpression,
7
7
  SelectQueryBuilder,
8
+ SqlBool,
8
9
  SqliteAdapter,
9
10
  SqliteIntrospector,
10
11
  SqliteQueryCompiler,
@@ -14,7 +15,7 @@ import { retry } from '@atproto/common'
14
15
 
15
16
  // Applies to repo_root or record table
16
17
  export const notSoftDeletedClause = (alias: DbRef) => {
17
- return sql`${alias}."takedownRef" is null`
18
+ return sql<SqlBool>`${alias}."takedownRef" is null`
18
19
  }
19
20
 
20
21
  export const softDeleted = (repoOrRecord: { takedownRef: string | null }) => {
@@ -92,7 +93,9 @@ const RETRY_ERRORS = new Set([
92
93
 
93
94
  export type Ref = ReferenceExpression<any, any>
94
95
 
95
- export type DbRef = RawBuilder | ReturnType<DynamicModule['ref']>
96
+ export type DbRef =
97
+ | RawBuilder<unknown>
98
+ | ReturnType<DynamicModule<unknown>['ref']>
96
99
 
97
100
  export type AnyQb = SelectQueryBuilder<any, any, any>
98
101
 
@@ -36,9 +36,10 @@ export class ServerMailer {
36
36
  }
37
37
 
38
38
  async sendResetPassword(
39
- params: { handle: string; token: string },
39
+ params: { handle: string; token: string; locale?: string },
40
40
  mailOpts: SendMailOptions,
41
41
  ) {
42
+ // @TODO (later) handle locale in the template
42
43
  await this.sendTemplate('resetPassword', params, {
43
44
  subject: 'Password Reset Requested',
44
45
  ...mailOpts,
@@ -46,9 +47,10 @@ export class ServerMailer {
46
47
  }
47
48
 
48
49
  async sendAccountDelete(
49
- params: { token: string },
50
+ params: { token: string; locale?: string },
50
51
  mailOpts: SendMailOptions,
51
52
  ) {
53
+ // @TODO (later) handle locale in the template
52
54
  await this.sendTemplate('deleteAccount', params, {
53
55
  subject: 'Account Deletion Requested',
54
56
  ...mailOpts,
@@ -35,7 +35,7 @@ export const getAndMigrateRecoveryDb = async (
35
35
  const pragmas: Record<string, string> = disableWalAutoCheckpoint
36
36
  ? { wal_autocheckpoint: '0' }
37
37
  : {}
38
- const db = Database.sqlite(location, pragmas)
38
+ const db = Database.sqlite<RecoveryDbSchema>(location, pragmas)
39
39
  const migrator = new Migrator(db.db, migrations)
40
40
  await migrator.migrateToLatestOrThrow()
41
41
  return db
@@ -83,12 +83,10 @@ export const formatSeqIdentityEvt = async (
83
83
  did: DidString,
84
84
  handle?: HandleString,
85
85
  ): Promise<RepoSeqInsert> => {
86
- const evt: IdentityEvt = {
87
- did,
88
- }
89
- if (handle) {
90
- evt.handle = handle
91
- }
86
+ const evt: IdentityEvt = handle //
87
+ ? { did, handle }
88
+ : { did }
89
+
92
90
  return {
93
91
  did,
94
92
  eventType: 'identity',
@@ -101,13 +99,10 @@ export const formatSeqAccountEvt = async (
101
99
  did: DidString,
102
100
  status: AccountStatus,
103
101
  ): Promise<RepoSeqInsert> => {
104
- const evt: AccountEvt = {
105
- did,
106
- active: status === 'active',
107
- }
108
- if (status !== AccountStatus.Active) {
109
- evt.status = status
110
- }
102
+ const evt: AccountEvt =
103
+ status === AccountStatus.Active
104
+ ? { did, active: true }
105
+ : { did, active: false, status }
111
106
 
112
107
  return {
113
108
  did,
@@ -162,9 +162,7 @@ export class Sequencer extends (EventEmitter as new () => SequencerEmitter) {
162
162
  await wait(waitTime)
163
163
  }
164
164
 
165
- protected async sequenceEvts(
166
- events: readonly RepoSeqInsert[],
167
- ): Promise<number[]> {
165
+ async sequenceEvts(events: readonly RepoSeqInsert[]): Promise<number[]> {
168
166
  if (!events.length) return []
169
167
  const rows = await this.db.executeWithRetry(
170
168
  this.db.db.insertInto('repo_seq').values(events).returning('seq'),
@@ -134,6 +134,7 @@ describe('account manager', () => {
134
134
  const [params] = sendResetPasswordMock.mock.lastCall!
135
135
  expect(params).toEqual({
136
136
  handle: 'bob.test',
137
+ locale: 'fr',
137
138
  token: expect.any(String),
138
139
  })
139
140
 
@@ -380,4 +381,82 @@ describe('account manager', () => {
380
381
  await page.clickOnText('Retour')
381
382
  await page.ensureTextVisibility('bob-renamed.test', 'span')
382
383
  })
384
+
385
+ it('allows deactivating & reactivating the account', async () => {
386
+ await using page = await PageHelper.from(browser, { languages })
387
+
388
+ await page.goto(new URL('/account', network.pds.url))
389
+
390
+ await page.assertTitle('Mon compte Atmosphère')
391
+
392
+ await page.clickOnText('Compte utilisateur', 'a')
393
+
394
+ await page.clickOnText('Désactiver le compte')
395
+
396
+ await page.ensureTextVisibility(
397
+ 'limite de temps pour la désactivation du compte',
398
+ )
399
+
400
+ await page.clickOnText('Oui, désactiver')
401
+
402
+ await page.waitForNetworkIdle()
403
+
404
+ // The row should now offer re-activation
405
+ await page.ensureTextVisibility('Réactiver le compte', 'span')
406
+
407
+ await page.clickOnText('Réactiver le compte')
408
+
409
+ // @NOTE The dialog's submit label ("Réactiver") is a substring of the
410
+ // trigger row ("Réactiver le compte"), which comes first in DOM order, so
411
+ // we target the dialog's submit button instead of using its text.
412
+ await page.clickOn('[role="dialog"] button[type="submit"]')
413
+
414
+ await page.waitForNetworkIdle()
415
+
416
+ await page.ensureTextVisibility('Désactiver le compte', 'span')
417
+ })
418
+
419
+ it('allows deleting the account', async () => {
420
+ await using page = await PageHelper.from(browser, { languages })
421
+
422
+ await page.goto(new URL('/account', network.pds.url))
423
+
424
+ await page.assertTitle('Mon compte Atmosphère')
425
+
426
+ await page.clickOnText('Compte utilisateur', 'a')
427
+
428
+ await page.clickOnText('Supprimer le compte')
429
+
430
+ using sendAccountDeleteMock = jest
431
+ .spyOn(network.pds.ctx.mailer, 'sendAccountDelete')
432
+ .mockImplementation(async () => {
433
+ // noop
434
+ })
435
+
436
+ await page.clickOnText("Envoyer l'email")
437
+
438
+ await page.waitForNetworkIdle()
439
+
440
+ expect(sendAccountDeleteMock).toHaveBeenCalledTimes(1)
441
+
442
+ const [params] = sendAccountDeleteMock.mock.lastCall!
443
+ expect(params).toEqual({
444
+ locale: 'fr',
445
+ token: expect.any(String),
446
+ })
447
+
448
+ await page.typeInInput('code', params.token)
449
+ await page.typeInInput('password', 'bob-new-pass')
450
+
451
+ await page.clickOnText('Supprimer mon compte')
452
+
453
+ // A final confirmation step is displayed
454
+ await page.ensureTextVisibility('Êtes-vous vraiment, vraiment sûr ?', 'h2')
455
+
456
+ await page.clickOnText('Oui, supprimer mon compte')
457
+
458
+ // Once the account is deleted, the session is gone and the user is
459
+ // brought back to the sign-in page.
460
+ await page.assertTitle('Se connecter')
461
+ })
383
462
  })