@atproto/bsky 0.0.25 → 0.0.26

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 (71) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/buf.gen.yaml +12 -0
  3. package/dist/api/app/bsky/unspecced/getTaggedSuggestions.d.ts +3 -0
  4. package/dist/bsync.d.ts +8 -0
  5. package/dist/config.d.ts +20 -0
  6. package/dist/context.d.ts +6 -3
  7. package/dist/courier.d.ts +8 -0
  8. package/dist/db/database-schema.d.ts +2 -1
  9. package/dist/db/index.js +15 -1
  10. package/dist/db/index.js.map +3 -3
  11. package/dist/db/migrations/20240124T023719200Z-tagged-suggestions.d.ts +3 -0
  12. package/dist/db/migrations/index.d.ts +1 -0
  13. package/dist/db/tables/tagged-suggestion.d.ts +9 -0
  14. package/dist/index.js +47930 -16807
  15. package/dist/index.js.map +3 -3
  16. package/dist/indexer/config.d.ts +8 -0
  17. package/dist/indexer/context.d.ts +3 -0
  18. package/dist/ingester/config.d.ts +8 -0
  19. package/dist/ingester/context.d.ts +3 -0
  20. package/dist/ingester/mute-subscription.d.ts +22 -0
  21. package/dist/lexicon/index.d.ts +2 -0
  22. package/dist/lexicon/lexicons.d.ts +48 -0
  23. package/dist/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.d.ts +39 -0
  24. package/dist/notifications.d.ts +27 -16
  25. package/dist/proto/bsync_connect.d.ts +25 -0
  26. package/dist/proto/bsync_pb.d.ts +90 -0
  27. package/dist/proto/courier_connect.d.ts +25 -0
  28. package/dist/proto/courier_pb.d.ts +91 -0
  29. package/dist/services/actor/index.d.ts +2 -2
  30. package/dist/services/indexing/index.d.ts +2 -2
  31. package/dist/services/util/post.d.ts +6 -6
  32. package/dist/util/retry.d.ts +2 -0
  33. package/package.json +15 -7
  34. package/proto/courier.proto +56 -0
  35. package/src/api/app/bsky/graph/muteActor.ts +32 -5
  36. package/src/api/app/bsky/graph/muteActorList.ts +32 -5
  37. package/src/api/app/bsky/graph/unmuteActor.ts +32 -5
  38. package/src/api/app/bsky/graph/unmuteActorList.ts +32 -5
  39. package/src/api/app/bsky/notification/registerPush.ts +42 -8
  40. package/src/api/app/bsky/unspecced/getTaggedSuggestions.ts +21 -0
  41. package/src/api/index.ts +2 -0
  42. package/src/bsync.ts +41 -0
  43. package/src/config.ts +79 -0
  44. package/src/context.ts +12 -6
  45. package/src/courier.ts +41 -0
  46. package/src/db/database-schema.ts +2 -0
  47. package/src/db/migrations/20240124T023719200Z-tagged-suggestions.ts +15 -0
  48. package/src/db/migrations/index.ts +1 -0
  49. package/src/db/tables/tagged-suggestion.ts +11 -0
  50. package/src/index.ts +26 -3
  51. package/src/indexer/config.ts +36 -0
  52. package/src/indexer/context.ts +6 -0
  53. package/src/indexer/index.ts +27 -3
  54. package/src/ingester/config.ts +34 -0
  55. package/src/ingester/context.ts +6 -0
  56. package/src/ingester/index.ts +18 -0
  57. package/src/ingester/mute-subscription.ts +213 -0
  58. package/src/lexicon/index.ts +12 -0
  59. package/src/lexicon/lexicons.ts +50 -0
  60. package/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts +65 -0
  61. package/src/notifications.ts +165 -149
  62. package/src/proto/bsync_connect.ts +54 -0
  63. package/src/proto/bsync_pb.ts +459 -0
  64. package/src/proto/courier_connect.ts +50 -0
  65. package/src/proto/courier_pb.ts +473 -0
  66. package/src/services/actor/index.ts +17 -2
  67. package/src/services/indexing/processor.ts +1 -1
  68. package/src/util/retry.ts +12 -0
  69. package/tests/notification-server.test.ts +59 -19
  70. package/tests/subscription/mutes.test.ts +170 -0
  71. package/tests/views/suggestions.test.ts +22 -0
@@ -2,28 +2,28 @@ import DatabaseSchema from '../../db/database-schema';
2
2
  export declare const getDescendentsQb: (db: DatabaseSchema, opts: {
3
3
  uri: string;
4
4
  depth: number;
5
- }) => import("kysely/dist/cjs/parser/with-parser").QueryCreatorWithCommonTableExpression<import("../../db/database-schema").DatabaseSchemaType, "descendent(uri, depth)", (cte: import("kysely").QueryCreator<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"descendent", {
5
+ }) => import("kysely/dist/cjs/parser/with-parser").QueryCreatorWithCommonTableExpression<import("../../db/database-schema").DatabaseSchemaType, "descendent(uri, depth)", (cte: import("kysely").QueryCreator<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/tagged-suggestion").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"descendent", {
6
6
  uri: any;
7
7
  depth: any;
8
- }>>) => import("kysely").SelectQueryBuilder<import("kysely/dist/cjs/parser/table-parser").From<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"descendent", {
8
+ }>>) => import("kysely").SelectQueryBuilder<import("kysely/dist/cjs/parser/table-parser").From<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/tagged-suggestion").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"descendent", {
9
9
  uri: any;
10
10
  depth: any;
11
- }>, "post">, "post", import("kysely").Selection<import("kysely/dist/cjs/parser/table-parser").From<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"descendent", {
11
+ }>, "post">, "post", import("kysely").Selection<import("kysely/dist/cjs/parser/table-parser").From<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/tagged-suggestion").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"descendent", {
12
12
  uri: any;
13
13
  depth: any;
14
14
  }>, "post">, "post", "post.uri as uri" | import("kysely").AliasedRawBuilder<number, "depth">>>>;
15
15
  export declare const getAncestorsAndSelfQb: (db: DatabaseSchema, opts: {
16
16
  uri: string;
17
17
  parentHeight: number;
18
- }) => import("kysely/dist/cjs/parser/with-parser").QueryCreatorWithCommonTableExpression<import("../../db/database-schema").DatabaseSchemaType, "ancestor(uri, ancestorUri, height)", (cte: import("kysely").QueryCreator<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"ancestor", {
18
+ }) => import("kysely/dist/cjs/parser/with-parser").QueryCreatorWithCommonTableExpression<import("../../db/database-schema").DatabaseSchemaType, "ancestor(uri, ancestorUri, height)", (cte: import("kysely").QueryCreator<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/tagged-suggestion").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"ancestor", {
19
19
  uri: any;
20
20
  height: any;
21
21
  ancestorUri: any;
22
- }>>) => import("kysely").SelectQueryBuilder<import("kysely/dist/cjs/parser/table-parser").From<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"ancestor", {
22
+ }>>) => import("kysely").SelectQueryBuilder<import("kysely/dist/cjs/parser/table-parser").From<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/tagged-suggestion").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"ancestor", {
23
23
  uri: any;
24
24
  height: any;
25
25
  ancestorUri: any;
26
- }>, "post">, "post", import("kysely").Selection<import("kysely/dist/cjs/parser/table-parser").From<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"ancestor", {
26
+ }>, "post">, "post", import("kysely").Selection<import("kysely/dist/cjs/parser/table-parser").From<import("../../db/tables/duplicate-record").PartialDB & import("../../db/tables/profile").PartialDB & import("../../db/tables/profile-agg").PartialDB & import("../../db/tables/post").PartialDB & import("../../db/tables/post-embed").PartialDB & import("../../db/tables/post-agg").PartialDB & import("../../db/tables/repost").PartialDB & import("../../db/tables/thread-gate").PartialDB & import("../../db/tables/feed-item").PartialDB & import("../../db/tables/follow").PartialDB & import("../../db/tables/like").PartialDB & import("../../db/tables/list").PartialDB & import("../../db/tables/list-item").PartialDB & import("../../db/tables/list-mute").PartialDB & import("../../db/tables/list-block").PartialDB & import("../../db/tables/mute").PartialDB & import("../../db/tables/actor-block").PartialDB & import("../../db/tables/feed-generator").PartialDB & import("../../db/tables/subscription").PartialDB & import("../../db/tables/actor").PartialDB & import("../../db/tables/actor-state").PartialDB & import("../../db/tables/actor-sync").PartialDB & import("../../db/tables/record").PartialDB & import("../../db/tables/notification").PartialDB & import("../../db/tables/notification-push-token").PartialDB & import("../../db/tables/moderation").PartialDB & import("../../db/tables/label").PartialDB & import("../../db/tables/algo").PartialDB & import("../../db/tables/view-param").PartialDB & import("../../db/tables/suggested-follow").PartialDB & import("../../db/tables/suggested-feed").PartialDB & import("../../db/tables/tagged-suggestion").PartialDB & import("../../db/tables/blob-takedown").PartialDB & Record<"ancestor", {
27
27
  uri: any;
28
28
  height: any;
29
29
  ancestorUri: any;
@@ -1,3 +1,5 @@
1
1
  import { RetryOptions } from '@atproto/common';
2
2
  export declare function retryHttp<T>(fn: () => Promise<T>, opts?: RetryOptions): Promise<T>;
3
3
  export declare function retryableHttp(err: unknown): boolean;
4
+ export declare function retryConnect<T>(fn: () => Promise<T>, opts?: RetryOptions): Promise<T>;
5
+ export declare function retryableConnect(err: unknown): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/bsky",
3
- "version": "0.0.25",
3
+ "version": "0.0.26",
4
4
  "license": "MIT",
5
5
  "description": "Reference implementation of app.bsky App View (Bluesky API)",
6
6
  "keywords": [
@@ -16,6 +16,9 @@
16
16
  "main": "dist/index.js",
17
17
  "bin": "dist/bin.js",
18
18
  "dependencies": {
19
+ "@bufbuild/protobuf": "^1.5.0",
20
+ "@connectrpc/connect": "^1.1.4",
21
+ "@connectrpc/connect-node": "^1.1.4",
19
22
  "@did-plc/lib": "^0.0.1",
20
23
  "@isaacs/ttlcache": "^1.4.1",
21
24
  "compression": "^1.7.4",
@@ -28,6 +31,7 @@
28
31
  "ioredis": "^5.3.2",
29
32
  "kysely": "^0.22.0",
30
33
  "multiformats": "^9.9.0",
34
+ "murmurhash": "^2.0.1",
31
35
  "p-queue": "^6.6.2",
32
36
  "pg": "^8.10.0",
33
37
  "pino": "^8.15.0",
@@ -35,16 +39,19 @@
35
39
  "sharp": "^0.32.6",
36
40
  "typed-emitter": "^2.1.0",
37
41
  "uint8arrays": "3.0.0",
38
- "@atproto/api": "^0.9.2",
42
+ "@atproto/api": "^0.9.3",
39
43
  "@atproto/common": "^0.3.3",
40
44
  "@atproto/crypto": "^0.3.0",
41
- "@atproto/syntax": "^0.1.5",
42
45
  "@atproto/identity": "^0.3.2",
43
46
  "@atproto/lexicon": "^0.3.1",
44
47
  "@atproto/repo": "^0.3.6",
48
+ "@atproto/syntax": "^0.1.5",
45
49
  "@atproto/xrpc-server": "^0.4.2"
46
50
  },
47
51
  "devDependencies": {
52
+ "@bufbuild/buf": "^1.28.1",
53
+ "@bufbuild/protoc-gen-es": "^1.5.0",
54
+ "@connectrpc/protoc-gen-connect-es": "^1.1.4",
48
55
  "@did-plc/server": "^0.0.1",
49
56
  "@types/cors": "^2.8.12",
50
57
  "@types/express": "^4.17.13",
@@ -52,10 +59,10 @@
52
59
  "@types/pg": "^8.6.6",
53
60
  "@types/qs": "^6.9.7",
54
61
  "axios": "^0.27.2",
55
- "@atproto/api": "^0.9.2",
56
- "@atproto/dev-env": "^0.2.25",
62
+ "@atproto/api": "^0.9.3",
63
+ "@atproto/dev-env": "^0.2.26",
57
64
  "@atproto/lex-cli": "^0.3.0",
58
- "@atproto/pds": "^0.3.13",
65
+ "@atproto/pds": "^0.3.14",
59
66
  "@atproto/xrpc": "^0.4.1"
60
67
  },
61
68
  "scripts": {
@@ -67,7 +74,8 @@
67
74
  "test": "../dev-infra/with-test-redis-and-db.sh jest",
68
75
  "test:log": "tail -50 test.log | pino-pretty",
69
76
  "test:updateSnapshot": "jest --updateSnapshot",
70
- "migration:create": "ts-node ./bin/migration-create.ts"
77
+ "migration:create": "ts-node ./bin/migration-create.ts",
78
+ "buf:gen": "buf generate ../bsync/proto && buf generate ./proto"
71
79
  },
72
80
  "types": "dist/index.d.ts"
73
81
  }
@@ -0,0 +1,56 @@
1
+ syntax = "proto3";
2
+
3
+ package courier;
4
+ option go_package = "./;courier";
5
+
6
+ import "google/protobuf/struct.proto";
7
+ import "google/protobuf/timestamp.proto";
8
+
9
+ //
10
+ // Messages
11
+ //
12
+
13
+ // Ping
14
+ message PingRequest {}
15
+ message PingResponse {}
16
+
17
+ // Notifications
18
+
19
+ enum AppPlatform {
20
+ APP_PLATFORM_UNSPECIFIED = 0;
21
+ APP_PLATFORM_IOS = 1;
22
+ APP_PLATFORM_ANDROID = 2;
23
+ APP_PLATFORM_WEB = 3;
24
+ }
25
+
26
+ message Notification {
27
+ string id = 1;
28
+ string recipient_did = 2;
29
+ string title = 3;
30
+ string message = 4;
31
+ string collapse_key = 5;
32
+ bool always_deliver = 6;
33
+ google.protobuf.Timestamp timestamp = 7;
34
+ google.protobuf.Struct additional = 8;
35
+ }
36
+
37
+ message PushNotificationsRequest {
38
+ repeated Notification notifications = 1;
39
+ }
40
+
41
+ message PushNotificationsResponse {}
42
+
43
+ message RegisterDeviceTokenRequest {
44
+ string did = 1;
45
+ string token = 2;
46
+ string app_id = 3;
47
+ AppPlatform platform = 4;
48
+ }
49
+
50
+ message RegisterDeviceTokenResponse {}
51
+
52
+ service Service {
53
+ rpc Ping(PingRequest) returns (PingResponse);
54
+ rpc PushNotifications(PushNotificationsRequest) returns (PushNotificationsResponse);
55
+ rpc RegisterDeviceToken(RegisterDeviceTokenRequest) returns (RegisterDeviceTokenResponse);
56
+ }
@@ -1,11 +1,14 @@
1
+ import assert from 'node:assert'
1
2
  import { InvalidRequestError } from '@atproto/xrpc-server'
2
3
  import { Server } from '../../../../lexicon'
3
4
  import AppContext from '../../../../context'
5
+ import { MuteOperation_Type } from '../../../../proto/bsync_pb'
6
+ import { BsyncClient } from '../../../../bsync'
4
7
 
5
8
  export default function (server: Server, ctx: AppContext) {
6
9
  server.app.bsky.graph.muteActor({
7
10
  auth: ctx.authVerifier.standard,
8
- handler: async ({ auth, input }) => {
11
+ handler: async ({ req, auth, input }) => {
9
12
  const { actor } = input.body
10
13
  const requester = auth.credentials.iss
11
14
  const db = ctx.db.getPrimary()
@@ -18,10 +21,34 @@ export default function (server: Server, ctx: AppContext) {
18
21
  throw new InvalidRequestError('Cannot mute oneself')
19
22
  }
20
23
 
21
- await ctx.services.graph(db).muteActor({
22
- subjectDid,
23
- mutedByDid: requester,
24
- })
24
+ const muteActor = async () => {
25
+ await ctx.services.graph(db).muteActor({
26
+ subjectDid,
27
+ mutedByDid: requester,
28
+ })
29
+ }
30
+
31
+ const addBsyncMuteOp = async (bsyncClient: BsyncClient) => {
32
+ await bsyncClient.addMuteOperation({
33
+ type: MuteOperation_Type.ADD,
34
+ actorDid: requester,
35
+ subject: subjectDid,
36
+ })
37
+ }
38
+
39
+ if (ctx.cfg.bsyncOnlyMutes) {
40
+ assert(ctx.bsyncClient)
41
+ await addBsyncMuteOp(ctx.bsyncClient)
42
+ } else {
43
+ await muteActor()
44
+ if (ctx.bsyncClient) {
45
+ try {
46
+ await addBsyncMuteOp(ctx.bsyncClient)
47
+ } catch (err) {
48
+ req.log.warn(err, 'failed to sync mute op to bsync')
49
+ }
50
+ }
51
+ }
25
52
  },
26
53
  })
27
54
  }
@@ -1,13 +1,16 @@
1
+ import assert from 'node:assert'
1
2
  import { InvalidRequestError } from '@atproto/xrpc-server'
2
3
  import { Server } from '../../../../lexicon'
3
4
  import * as lex from '../../../../lexicon/lexicons'
4
5
  import AppContext from '../../../../context'
5
6
  import { AtUri } from '@atproto/syntax'
7
+ import { MuteOperation_Type } from '../../../../proto/bsync_pb'
8
+ import { BsyncClient } from '../../../../bsync'
6
9
 
7
10
  export default function (server: Server, ctx: AppContext) {
8
11
  server.app.bsky.graph.muteActorList({
9
12
  auth: ctx.authVerifier.standard,
10
- handler: async ({ auth, input }) => {
13
+ handler: async ({ req, auth, input }) => {
11
14
  const { list } = input.body
12
15
  const requester = auth.credentials.iss
13
16
 
@@ -19,10 +22,34 @@ export default function (server: Server, ctx: AppContext) {
19
22
  throw new InvalidRequestError(`Invalid collection: expected: ${collId}`)
20
23
  }
21
24
 
22
- await ctx.services.graph(db).muteActorList({
23
- list,
24
- mutedByDid: requester,
25
- })
25
+ const muteActorList = async () => {
26
+ await ctx.services.graph(db).muteActorList({
27
+ list,
28
+ mutedByDid: requester,
29
+ })
30
+ }
31
+
32
+ const addBsyncMuteOp = async (bsyncClient: BsyncClient) => {
33
+ await bsyncClient.addMuteOperation({
34
+ type: MuteOperation_Type.ADD,
35
+ actorDid: requester,
36
+ subject: list,
37
+ })
38
+ }
39
+
40
+ if (ctx.cfg.bsyncOnlyMutes) {
41
+ assert(ctx.bsyncClient)
42
+ await addBsyncMuteOp(ctx.bsyncClient)
43
+ } else {
44
+ await muteActorList()
45
+ if (ctx.bsyncClient) {
46
+ try {
47
+ await addBsyncMuteOp(ctx.bsyncClient)
48
+ } catch (err) {
49
+ req.log.warn(err, 'failed to sync mute op to bsync')
50
+ }
51
+ }
52
+ }
26
53
  },
27
54
  })
28
55
  }
@@ -1,11 +1,14 @@
1
+ import assert from 'node:assert'
1
2
  import { InvalidRequestError } from '@atproto/xrpc-server'
2
3
  import { Server } from '../../../../lexicon'
3
4
  import AppContext from '../../../../context'
5
+ import { MuteOperation_Type } from '../../../../proto/bsync_pb'
6
+ import { BsyncClient } from '../../../../bsync'
4
7
 
5
8
  export default function (server: Server, ctx: AppContext) {
6
9
  server.app.bsky.graph.unmuteActor({
7
10
  auth: ctx.authVerifier.standard,
8
- handler: async ({ auth, input }) => {
11
+ handler: async ({ req, auth, input }) => {
9
12
  const { actor } = input.body
10
13
  const requester = auth.credentials.iss
11
14
  const db = ctx.db.getPrimary()
@@ -18,10 +21,34 @@ export default function (server: Server, ctx: AppContext) {
18
21
  throw new InvalidRequestError('Cannot mute oneself')
19
22
  }
20
23
 
21
- await ctx.services.graph(db).unmuteActor({
22
- subjectDid,
23
- mutedByDid: requester,
24
- })
24
+ const unmuteActor = async () => {
25
+ await ctx.services.graph(db).unmuteActor({
26
+ subjectDid,
27
+ mutedByDid: requester,
28
+ })
29
+ }
30
+
31
+ const addBsyncMuteOp = async (bsyncClient: BsyncClient) => {
32
+ await bsyncClient.addMuteOperation({
33
+ type: MuteOperation_Type.REMOVE,
34
+ actorDid: requester,
35
+ subject: subjectDid,
36
+ })
37
+ }
38
+
39
+ if (ctx.cfg.bsyncOnlyMutes) {
40
+ assert(ctx.bsyncClient)
41
+ await addBsyncMuteOp(ctx.bsyncClient)
42
+ } else {
43
+ await unmuteActor()
44
+ if (ctx.bsyncClient) {
45
+ try {
46
+ await addBsyncMuteOp(ctx.bsyncClient)
47
+ } catch (err) {
48
+ req.log.warn(err, 'failed to sync mute op to bsync')
49
+ }
50
+ }
51
+ }
25
52
  },
26
53
  })
27
54
  }
@@ -1,18 +1,45 @@
1
+ import assert from 'node:assert'
1
2
  import { Server } from '../../../../lexicon'
2
3
  import AppContext from '../../../../context'
4
+ import { MuteOperation_Type } from '../../../../proto/bsync_pb'
5
+ import { BsyncClient } from '../../../../bsync'
3
6
 
4
7
  export default function (server: Server, ctx: AppContext) {
5
8
  server.app.bsky.graph.unmuteActorList({
6
9
  auth: ctx.authVerifier.standard,
7
- handler: async ({ auth, input }) => {
10
+ handler: async ({ req, auth, input }) => {
8
11
  const { list } = input.body
9
12
  const requester = auth.credentials.iss
10
13
  const db = ctx.db.getPrimary()
11
14
 
12
- await ctx.services.graph(db).unmuteActorList({
13
- list,
14
- mutedByDid: requester,
15
- })
15
+ const unmuteActorList = async () => {
16
+ await ctx.services.graph(db).unmuteActorList({
17
+ list,
18
+ mutedByDid: requester,
19
+ })
20
+ }
21
+
22
+ const addBsyncMuteOp = async (bsyncClient: BsyncClient) => {
23
+ await bsyncClient.addMuteOperation({
24
+ type: MuteOperation_Type.REMOVE,
25
+ actorDid: requester,
26
+ subject: list,
27
+ })
28
+ }
29
+
30
+ if (ctx.cfg.bsyncOnlyMutes) {
31
+ assert(ctx.bsyncClient)
32
+ await addBsyncMuteOp(ctx.bsyncClient)
33
+ } else {
34
+ await unmuteActorList()
35
+ if (ctx.bsyncClient) {
36
+ try {
37
+ await addBsyncMuteOp(ctx.bsyncClient)
38
+ } catch (err) {
39
+ req.log.warn(err, 'failed to sync mute op to bsync')
40
+ }
41
+ }
42
+ }
16
43
  },
17
44
  })
18
45
  }
@@ -1,29 +1,63 @@
1
+ import assert from 'node:assert'
1
2
  import { InvalidRequestError } from '@atproto/xrpc-server'
2
3
  import { Server } from '../../../../lexicon'
3
4
  import AppContext from '../../../../context'
4
5
  import { Platform } from '../../../../notifications'
6
+ import { CourierClient } from '../../../../courier'
7
+ import { AppPlatform } from '../../../../proto/courier_pb'
5
8
 
6
9
  export default function (server: Server, ctx: AppContext) {
7
10
  server.app.bsky.notification.registerPush({
8
11
  auth: ctx.authVerifier.standard,
9
- handler: async ({ auth, input }) => {
12
+ handler: async ({ req, auth, input }) => {
10
13
  const { token, platform, serviceDid, appId } = input.body
11
14
  const did = auth.credentials.iss
12
15
  if (serviceDid !== auth.credentials.aud) {
13
16
  throw new InvalidRequestError('Invalid serviceDid.')
14
17
  }
15
- const { notifServer } = ctx
16
18
  if (platform !== 'ios' && platform !== 'android' && platform !== 'web') {
17
19
  throw new InvalidRequestError(
18
20
  'Unsupported platform: must be "ios", "android", or "web".',
19
21
  )
20
22
  }
21
- await notifServer.registerDeviceForPushNotifications(
22
- did,
23
- token,
24
- platform as Platform,
25
- appId,
26
- )
23
+
24
+ const db = ctx.db.getPrimary()
25
+
26
+ const registerDeviceWithAppview = async () => {
27
+ await ctx.services
28
+ .actor(db)
29
+ .registerPushDeviceToken(did, token, platform as Platform, appId)
30
+ }
31
+
32
+ const registerDeviceWithCourier = async (
33
+ courierClient: CourierClient,
34
+ ) => {
35
+ await courierClient.registerDeviceToken({
36
+ did,
37
+ token,
38
+ platform:
39
+ platform === 'ios'
40
+ ? AppPlatform.IOS
41
+ : platform === 'android'
42
+ ? AppPlatform.ANDROID
43
+ : AppPlatform.WEB,
44
+ appId,
45
+ })
46
+ }
47
+
48
+ if (ctx.cfg.courierOnlyRegistration) {
49
+ assert(ctx.courierClient)
50
+ await registerDeviceWithCourier(ctx.courierClient)
51
+ } else {
52
+ await registerDeviceWithAppview()
53
+ if (ctx.courierClient) {
54
+ try {
55
+ await registerDeviceWithCourier(ctx.courierClient)
56
+ } catch (err) {
57
+ req.log.warn(err, 'failed to register device token with courier')
58
+ }
59
+ }
60
+ }
27
61
  },
28
62
  })
29
63
  }
@@ -0,0 +1,21 @@
1
+ import { Server } from '../../../../lexicon'
2
+ import AppContext from '../../../../context'
3
+
4
+ // THIS IS A TEMPORARY UNSPECCED ROUTE
5
+ export default function (server: Server, ctx: AppContext) {
6
+ server.app.bsky.unspecced.getTaggedSuggestions({
7
+ handler: async () => {
8
+ const suggestions = await ctx.db
9
+ .getReplica()
10
+ .db.selectFrom('tagged_suggestion')
11
+ .selectAll()
12
+ .execute()
13
+ return {
14
+ encoding: 'application/json',
15
+ body: {
16
+ suggestions,
17
+ },
18
+ }
19
+ },
20
+ })
21
+ }
package/src/api/index.ts CHANGED
@@ -40,6 +40,7 @@ import updateSeen from './app/bsky/notification/updateSeen'
40
40
  import registerPush from './app/bsky/notification/registerPush'
41
41
  import getPopularFeedGenerators from './app/bsky/unspecced/getPopularFeedGenerators'
42
42
  import getTimelineSkeleton from './app/bsky/unspecced/getTimelineSkeleton'
43
+ import getTaggedSuggestions from './app/bsky/unspecced/getTaggedSuggestions'
43
44
  import getSubjectStatus from './com/atproto/admin/getSubjectStatus'
44
45
  import updateSubjectStatus from './com/atproto/admin/updateSubjectStatus'
45
46
  import getAccountInfos from './com/atproto/admin/getAccountInfos'
@@ -95,6 +96,7 @@ export default function (server: Server, ctx: AppContext) {
95
96
  registerPush(server, ctx)
96
97
  getPopularFeedGenerators(server, ctx)
97
98
  getTimelineSkeleton(server, ctx)
99
+ getTaggedSuggestions(server, ctx)
98
100
  // com.atproto
99
101
  getSubjectStatus(server, ctx)
100
102
  updateSubjectStatus(server, ctx)
package/src/bsync.ts ADDED
@@ -0,0 +1,41 @@
1
+ import { Service } from './proto/bsync_connect'
2
+ import {
3
+ Code,
4
+ ConnectError,
5
+ PromiseClient,
6
+ createPromiseClient,
7
+ Interceptor,
8
+ } from '@connectrpc/connect'
9
+ import {
10
+ createConnectTransport,
11
+ ConnectTransportOptions,
12
+ } from '@connectrpc/connect-node'
13
+
14
+ export type BsyncClient = PromiseClient<typeof Service>
15
+
16
+ export const createBsyncClient = (
17
+ opts: ConnectTransportOptions,
18
+ ): BsyncClient => {
19
+ const transport = createConnectTransport(opts)
20
+ return createPromiseClient(Service, transport)
21
+ }
22
+
23
+ export { Code }
24
+
25
+ export const isBsyncError = (
26
+ err: unknown,
27
+ code?: Code,
28
+ ): err is ConnectError => {
29
+ if (err instanceof ConnectError) {
30
+ return !code || err.code === code
31
+ }
32
+ return false
33
+ }
34
+
35
+ export const authWithApiKey =
36
+ (apiKey: string): Interceptor =>
37
+ (next) =>
38
+ (req) => {
39
+ req.header.set('authorization', `Bearer ${apiKey}`)
40
+ return next(req)
41
+ }