@atproto/bsky 0.0.25 → 0.0.27

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 (81) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/buf.gen.yaml +12 -0
  3. package/dist/api/app/bsky/graph/getRelationships.d.ts +3 -0
  4. package/dist/api/app/bsky/unspecced/getTaggedSuggestions.d.ts +3 -0
  5. package/dist/bsync.d.ts +8 -0
  6. package/dist/config.d.ts +20 -0
  7. package/dist/context.d.ts +6 -3
  8. package/dist/courier.d.ts +8 -0
  9. package/dist/db/database-schema.d.ts +2 -1
  10. package/dist/db/index.js +15 -1
  11. package/dist/db/index.js.map +3 -3
  12. package/dist/db/migrations/20240124T023719200Z-tagged-suggestions.d.ts +3 -0
  13. package/dist/db/migrations/index.d.ts +1 -0
  14. package/dist/db/tables/tagged-suggestion.d.ts +9 -0
  15. package/dist/index.js +48246 -16828
  16. package/dist/index.js.map +3 -3
  17. package/dist/indexer/config.d.ts +8 -0
  18. package/dist/indexer/context.d.ts +3 -0
  19. package/dist/ingester/config.d.ts +8 -0
  20. package/dist/ingester/context.d.ts +3 -0
  21. package/dist/ingester/mute-subscription.d.ts +22 -0
  22. package/dist/lexicon/index.d.ts +4 -0
  23. package/dist/lexicon/lexicons.d.ts +153 -0
  24. package/dist/lexicon/types/app/bsky/actor/defs.d.ts +7 -1
  25. package/dist/lexicon/types/app/bsky/graph/defs.d.ts +15 -0
  26. package/dist/lexicon/types/app/bsky/graph/getRelationships.d.ts +38 -0
  27. package/dist/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.d.ts +39 -0
  28. package/dist/notifications.d.ts +27 -16
  29. package/dist/proto/bsync_connect.d.ts +25 -0
  30. package/dist/proto/bsync_pb.d.ts +90 -0
  31. package/dist/proto/courier_connect.d.ts +25 -0
  32. package/dist/proto/courier_pb.d.ts +91 -0
  33. package/dist/services/actor/index.d.ts +2 -2
  34. package/dist/services/indexing/index.d.ts +2 -2
  35. package/dist/services/util/post.d.ts +6 -6
  36. package/dist/util/retry.d.ts +2 -0
  37. package/package.json +15 -7
  38. package/proto/courier.proto +56 -0
  39. package/src/api/app/bsky/graph/getRelationships.ts +71 -0
  40. package/src/api/app/bsky/graph/muteActor.ts +32 -5
  41. package/src/api/app/bsky/graph/muteActorList.ts +32 -5
  42. package/src/api/app/bsky/graph/unmuteActor.ts +32 -5
  43. package/src/api/app/bsky/graph/unmuteActorList.ts +32 -5
  44. package/src/api/app/bsky/notification/registerPush.ts +42 -8
  45. package/src/api/app/bsky/unspecced/getTaggedSuggestions.ts +21 -0
  46. package/src/api/index.ts +4 -0
  47. package/src/bsync.ts +41 -0
  48. package/src/config.ts +79 -0
  49. package/src/context.ts +12 -6
  50. package/src/courier.ts +41 -0
  51. package/src/db/database-schema.ts +2 -0
  52. package/src/db/migrations/20240124T023719200Z-tagged-suggestions.ts +15 -0
  53. package/src/db/migrations/index.ts +1 -0
  54. package/src/db/tables/tagged-suggestion.ts +11 -0
  55. package/src/index.ts +26 -3
  56. package/src/indexer/config.ts +36 -0
  57. package/src/indexer/context.ts +6 -0
  58. package/src/indexer/index.ts +27 -3
  59. package/src/ingester/config.ts +34 -0
  60. package/src/ingester/context.ts +6 -0
  61. package/src/ingester/index.ts +18 -0
  62. package/src/ingester/mute-subscription.ts +213 -0
  63. package/src/lexicon/index.ts +24 -0
  64. package/src/lexicon/lexicons.ts +167 -0
  65. package/src/lexicon/types/app/bsky/actor/defs.ts +19 -0
  66. package/src/lexicon/types/app/bsky/graph/defs.ts +41 -0
  67. package/src/lexicon/types/app/bsky/graph/getRelationships.ts +53 -0
  68. package/src/lexicon/types/app/bsky/unspecced/getTaggedSuggestions.ts +65 -0
  69. package/src/notifications.ts +165 -149
  70. package/src/proto/bsync_connect.ts +54 -0
  71. package/src/proto/bsync_pb.ts +459 -0
  72. package/src/proto/courier_connect.ts +50 -0
  73. package/src/proto/courier_pb.ts +473 -0
  74. package/src/services/actor/index.ts +17 -2
  75. package/src/services/indexing/processor.ts +1 -1
  76. package/src/util/retry.ts +12 -0
  77. package/tests/notification-server.test.ts +59 -19
  78. package/tests/subscription/mutes.test.ts +170 -0
  79. package/tests/views/__snapshots__/follows.test.ts.snap +28 -0
  80. package/tests/views/follows.test.ts +10 -0
  81. package/tests/views/suggestions.test.ts +22 -0
@@ -0,0 +1,91 @@
1
+ import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from '@bufbuild/protobuf';
2
+ import { Message, proto3, Struct, Timestamp } from '@bufbuild/protobuf';
3
+ export declare enum AppPlatform {
4
+ UNSPECIFIED = 0,
5
+ IOS = 1,
6
+ ANDROID = 2,
7
+ WEB = 3
8
+ }
9
+ export declare class PingRequest extends Message<PingRequest> {
10
+ constructor(data?: PartialMessage<PingRequest>);
11
+ static readonly runtime: typeof proto3;
12
+ static readonly typeName = "courier.PingRequest";
13
+ static readonly fields: FieldList;
14
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PingRequest;
15
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PingRequest;
16
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PingRequest;
17
+ static equals(a: PingRequest | PlainMessage<PingRequest> | undefined, b: PingRequest | PlainMessage<PingRequest> | undefined): boolean;
18
+ }
19
+ export declare class PingResponse extends Message<PingResponse> {
20
+ constructor(data?: PartialMessage<PingResponse>);
21
+ static readonly runtime: typeof proto3;
22
+ static readonly typeName = "courier.PingResponse";
23
+ static readonly fields: FieldList;
24
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PingResponse;
25
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PingResponse;
26
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PingResponse;
27
+ static equals(a: PingResponse | PlainMessage<PingResponse> | undefined, b: PingResponse | PlainMessage<PingResponse> | undefined): boolean;
28
+ }
29
+ export declare class Notification extends Message<Notification> {
30
+ id: string;
31
+ recipientDid: string;
32
+ title: string;
33
+ message: string;
34
+ collapseKey: string;
35
+ alwaysDeliver: boolean;
36
+ timestamp?: Timestamp;
37
+ additional?: Struct;
38
+ constructor(data?: PartialMessage<Notification>);
39
+ static readonly runtime: typeof proto3;
40
+ static readonly typeName = "courier.Notification";
41
+ static readonly fields: FieldList;
42
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Notification;
43
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Notification;
44
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Notification;
45
+ static equals(a: Notification | PlainMessage<Notification> | undefined, b: Notification | PlainMessage<Notification> | undefined): boolean;
46
+ }
47
+ export declare class PushNotificationsRequest extends Message<PushNotificationsRequest> {
48
+ notifications: Notification[];
49
+ constructor(data?: PartialMessage<PushNotificationsRequest>);
50
+ static readonly runtime: typeof proto3;
51
+ static readonly typeName = "courier.PushNotificationsRequest";
52
+ static readonly fields: FieldList;
53
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PushNotificationsRequest;
54
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PushNotificationsRequest;
55
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PushNotificationsRequest;
56
+ static equals(a: PushNotificationsRequest | PlainMessage<PushNotificationsRequest> | undefined, b: PushNotificationsRequest | PlainMessage<PushNotificationsRequest> | undefined): boolean;
57
+ }
58
+ export declare class PushNotificationsResponse extends Message<PushNotificationsResponse> {
59
+ constructor(data?: PartialMessage<PushNotificationsResponse>);
60
+ static readonly runtime: typeof proto3;
61
+ static readonly typeName = "courier.PushNotificationsResponse";
62
+ static readonly fields: FieldList;
63
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PushNotificationsResponse;
64
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PushNotificationsResponse;
65
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PushNotificationsResponse;
66
+ static equals(a: PushNotificationsResponse | PlainMessage<PushNotificationsResponse> | undefined, b: PushNotificationsResponse | PlainMessage<PushNotificationsResponse> | undefined): boolean;
67
+ }
68
+ export declare class RegisterDeviceTokenRequest extends Message<RegisterDeviceTokenRequest> {
69
+ did: string;
70
+ token: string;
71
+ appId: string;
72
+ platform: AppPlatform;
73
+ constructor(data?: PartialMessage<RegisterDeviceTokenRequest>);
74
+ static readonly runtime: typeof proto3;
75
+ static readonly typeName = "courier.RegisterDeviceTokenRequest";
76
+ static readonly fields: FieldList;
77
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): RegisterDeviceTokenRequest;
78
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): RegisterDeviceTokenRequest;
79
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): RegisterDeviceTokenRequest;
80
+ static equals(a: RegisterDeviceTokenRequest | PlainMessage<RegisterDeviceTokenRequest> | undefined, b: RegisterDeviceTokenRequest | PlainMessage<RegisterDeviceTokenRequest> | undefined): boolean;
81
+ }
82
+ export declare class RegisterDeviceTokenResponse extends Message<RegisterDeviceTokenResponse> {
83
+ constructor(data?: PartialMessage<RegisterDeviceTokenResponse>);
84
+ static readonly runtime: typeof proto3;
85
+ static readonly typeName = "courier.RegisterDeviceTokenResponse";
86
+ static readonly fields: FieldList;
87
+ static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): RegisterDeviceTokenResponse;
88
+ static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): RegisterDeviceTokenResponse;
89
+ static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): RegisterDeviceTokenResponse;
90
+ static equals(a: RegisterDeviceTokenResponse | PlainMessage<RegisterDeviceTokenResponse> | undefined, b: RegisterDeviceTokenResponse | PlainMessage<RegisterDeviceTokenResponse> | undefined): boolean;
91
+ }
@@ -6,12 +6,11 @@ import { TimeCidKeyset } from '../../db/pagination';
6
6
  import { FromDb } from '../types';
7
7
  import { GraphService } from '../graph';
8
8
  import { LabelService } from '../label';
9
+ import { Platform } from '../../notifications';
9
10
  export * from './types';
10
11
  export declare class ActorService {
11
12
  db: Database;
12
13
  imgUriBuilder: ImageUriBuilder;
13
- private graph;
14
- private label;
15
14
  views: ActorViews;
16
15
  constructor(db: Database, imgUriBuilder: ImageUriBuilder, graph: FromDb<GraphService>, label: FromDb<LabelService>);
17
16
  static creator(imgUriBuilder: ImageUriBuilder, graph: FromDb<GraphService>, label: FromDb<LabelService>): (db: Database) => ActorService;
@@ -40,6 +39,7 @@ export declare class ActorService {
40
39
  indexedAt: string;
41
40
  takedownRef: string | null;
42
41
  }, void, unknown>;
42
+ registerPushDeviceToken(did: string, token: string, platform: Platform, appId: string): Promise<void>;
43
43
  }
44
44
  type ActorResult = Actor;
45
45
  export declare class ListKeyset extends TimeCidKeyset<{
@@ -23,7 +23,7 @@ export declare class IndexingService {
23
23
  idResolver: IdResolver;
24
24
  autoMod: AutoModerator;
25
25
  backgroundQueue: BackgroundQueue;
26
- notifServer?: NotificationServer | undefined;
26
+ notifServer?: NotificationServer<unknown> | undefined;
27
27
  records: {
28
28
  post: Post.PluginType;
29
29
  threadGate: Threadgate.PluginType;
@@ -37,7 +37,7 @@ export declare class IndexingService {
37
37
  block: Block.PluginType;
38
38
  feedGenerator: FeedGenerator.PluginType;
39
39
  };
40
- constructor(db: PrimaryDatabase, idResolver: IdResolver, autoMod: AutoModerator, backgroundQueue: BackgroundQueue, notifServer?: NotificationServer | undefined);
40
+ constructor(db: PrimaryDatabase, idResolver: IdResolver, autoMod: AutoModerator, backgroundQueue: BackgroundQueue, notifServer?: NotificationServer<unknown> | undefined);
41
41
  transact(txn: PrimaryDatabase): IndexingService;
42
42
  static creator(idResolver: IdResolver, autoMod: AutoModerator, backgroundQueue: BackgroundQueue, notifServer?: NotificationServer): (db: PrimaryDatabase) => IndexingService;
43
43
  indexRecord(uri: AtUri, cid: CID, obj: unknown, action: WriteOpAction.Create | WriteOpAction.Update, timestamp: string, opts?: {
@@ -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.27",
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.4",
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.4",
63
+ "@atproto/dev-env": "^0.2.27",
57
64
  "@atproto/lex-cli": "^0.3.0",
58
- "@atproto/pds": "^0.3.13",
65
+ "@atproto/pds": "^0.3.15",
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
+ }
@@ -0,0 +1,71 @@
1
+ import { Server } from '../../../../lexicon'
2
+ import AppContext from '../../../../context'
3
+ import { Relationship } from '../../../../lexicon/types/app/bsky/graph/defs'
4
+
5
+ export default function (server: Server, ctx: AppContext) {
6
+ server.app.bsky.graph.getRelationships({
7
+ handler: async ({ params }) => {
8
+ const { actor, others = [] } = params
9
+ if (others.length < 1) {
10
+ return {
11
+ encoding: 'application/json',
12
+ body: {
13
+ actor,
14
+ relationships: [],
15
+ },
16
+ }
17
+ }
18
+ const db = ctx.db.getPrimary()
19
+ const { ref } = db.db.dynamic
20
+ const res = await db.db
21
+ .selectFrom('actor')
22
+ .select([
23
+ 'actor.did',
24
+ db.db
25
+ .selectFrom('follow')
26
+ .where('creator', '=', actor)
27
+ .whereRef('subjectDid', '=', ref('actor.did'))
28
+ .select('uri')
29
+ .as('following'),
30
+ db.db
31
+ .selectFrom('follow')
32
+ .whereRef('creator', '=', ref('actor.did'))
33
+ .where('subjectDid', '=', actor)
34
+ .select('uri')
35
+ .as('followedBy'),
36
+ ])
37
+ .where('actor.did', 'in', others)
38
+ .execute()
39
+
40
+ const relationshipsMap = res.reduce((acc, cur) => {
41
+ return acc.set(cur.did, {
42
+ did: cur.did,
43
+ following: cur.following ?? undefined,
44
+ followedBy: cur.followedBy ?? undefined,
45
+ })
46
+ }, new Map<string, Relationship>())
47
+
48
+ const relationships = others.map((did) => {
49
+ const relationship = relationshipsMap.get(did)
50
+ return relationship
51
+ ? {
52
+ $type: 'app.bsky.graph.defs#relationship',
53
+ ...relationship,
54
+ }
55
+ : {
56
+ $type: 'app.bsky.graph.defs#notFoundActor',
57
+ actor: did,
58
+ notFound: true,
59
+ }
60
+ })
61
+
62
+ return {
63
+ encoding: 'application/json',
64
+ body: {
65
+ actor,
66
+ relationships,
67
+ },
68
+ }
69
+ },
70
+ })
71
+ }
@@ -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
  }