@atproto/bsky 0.0.151 → 0.0.153

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 (104) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.d.ts +4 -0
  3. package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.d.ts.map +1 -0
  4. package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.js +77 -0
  5. package/dist/api/app/bsky/unspecced/getPostThreadHiddenV2.js.map +1 -0
  6. package/dist/api/app/bsky/unspecced/getPostThreadV2.d.ts +4 -0
  7. package/dist/api/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -0
  8. package/dist/api/app/bsky/unspecced/getPostThreadV2.js +86 -0
  9. package/dist/api/app/bsky/unspecced/getPostThreadV2.js.map +1 -0
  10. package/dist/api/com/atproto/repo/getRecord.d.ts.map +1 -1
  11. package/dist/api/com/atproto/repo/getRecord.js +1 -1
  12. package/dist/api/com/atproto/repo/getRecord.js.map +1 -1
  13. package/dist/api/index.d.ts.map +1 -1
  14. package/dist/api/index.js +4 -0
  15. package/dist/api/index.js.map +1 -1
  16. package/dist/config.d.ts +6 -0
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +17 -0
  19. package/dist/config.js.map +1 -1
  20. package/dist/data-plane/server/db/migrations/20250528T221913281Z-add-record-tags.d.ts +4 -0
  21. package/dist/data-plane/server/db/migrations/20250528T221913281Z-add-record-tags.d.ts.map +1 -0
  22. package/dist/data-plane/server/db/migrations/20250528T221913281Z-add-record-tags.js +11 -0
  23. package/dist/data-plane/server/db/migrations/20250528T221913281Z-add-record-tags.js.map +1 -0
  24. package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
  25. package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
  26. package/dist/data-plane/server/db/migrations/index.js +2 -1
  27. package/dist/data-plane/server/db/migrations/index.js.map +1 -1
  28. package/dist/data-plane/server/db/tables/record.d.ts +1 -0
  29. package/dist/data-plane/server/db/tables/record.d.ts.map +1 -1
  30. package/dist/data-plane/server/db/tables/record.js.map +1 -1
  31. package/dist/data-plane/server/routes/records.d.ts.map +1 -1
  32. package/dist/data-plane/server/routes/records.js +1 -0
  33. package/dist/data-plane/server/routes/records.js.map +1 -1
  34. package/dist/hydration/feed.d.ts +1 -0
  35. package/dist/hydration/feed.d.ts.map +1 -1
  36. package/dist/hydration/feed.js +2 -0
  37. package/dist/hydration/feed.js.map +1 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +2 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/lexicon/index.d.ts +6 -2
  42. package/dist/lexicon/index.d.ts.map +1 -1
  43. package/dist/lexicon/index.js +12 -4
  44. package/dist/lexicon/index.js.map +1 -1
  45. package/dist/lexicon/lexicons.d.ts +508 -82
  46. package/dist/lexicon/lexicons.d.ts.map +1 -1
  47. package/dist/lexicon/lexicons.js +264 -42
  48. package/dist/lexicon/lexicons.js.map +1 -1
  49. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.d.ts +63 -0
  50. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.d.ts.map +1 -0
  51. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.js +25 -0
  52. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.js.map +1 -0
  53. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.d.ts +92 -0
  54. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -0
  55. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.js +52 -0
  56. package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.js.map +1 -0
  57. package/dist/proto/bsky_pb.d.ts +4 -0
  58. package/dist/proto/bsky_pb.d.ts.map +1 -1
  59. package/dist/proto/bsky_pb.js +16 -0
  60. package/dist/proto/bsky_pb.js.map +1 -1
  61. package/dist/proto/bsync_connect.d.ts +19 -1
  62. package/dist/proto/bsync_connect.d.ts.map +1 -1
  63. package/dist/proto/bsync_connect.js +18 -0
  64. package/dist/proto/bsync_connect.js.map +1 -1
  65. package/dist/proto/bsync_pb.d.ts +150 -0
  66. package/dist/proto/bsync_pb.d.ts.map +1 -1
  67. package/dist/proto/bsync_pb.js +401 -1
  68. package/dist/proto/bsync_pb.js.map +1 -1
  69. package/dist/views/index.d.ts +40 -0
  70. package/dist/views/index.d.ts.map +1 -1
  71. package/dist/views/index.js +499 -0
  72. package/dist/views/index.js.map +1 -1
  73. package/dist/views/threads-v2.d.ts +65 -0
  74. package/dist/views/threads-v2.d.ts.map +1 -0
  75. package/dist/views/threads-v2.js +205 -0
  76. package/dist/views/threads-v2.js.map +1 -0
  77. package/package.json +5 -5
  78. package/proto/bsky.proto +1 -0
  79. package/src/api/app/bsky/unspecced/getPostThreadHiddenV2.ts +117 -0
  80. package/src/api/app/bsky/unspecced/getPostThreadV2.ts +130 -0
  81. package/src/api/com/atproto/repo/getRecord.ts +4 -1
  82. package/src/api/index.ts +4 -0
  83. package/src/config.ts +24 -0
  84. package/src/data-plane/server/db/migrations/20250528T221913281Z-add-record-tags.ts +9 -0
  85. package/src/data-plane/server/db/migrations/index.ts +1 -0
  86. package/src/data-plane/server/db/tables/record.ts +1 -0
  87. package/src/data-plane/server/routes/records.ts +1 -0
  88. package/src/hydration/feed.ts +4 -0
  89. package/src/index.ts +2 -0
  90. package/src/lexicon/index.ts +33 -9
  91. package/src/lexicon/lexicons.ts +284 -43
  92. package/src/lexicon/types/app/bsky/unspecced/getPostThreadHiddenV2.ts +95 -0
  93. package/src/lexicon/types/app/bsky/unspecced/getPostThreadV2.ts +160 -0
  94. package/src/proto/bsky_pb.ts +12 -0
  95. package/src/proto/bsync_connect.ts +22 -0
  96. package/src/proto/bsync_pb.ts +355 -0
  97. package/src/views/index.ts +780 -0
  98. package/src/views/threads-v2.ts +381 -0
  99. package/tests/seed/thread-v2.ts +874 -0
  100. package/tests/seed/util.ts +52 -0
  101. package/tests/views/__snapshots__/thread-v2.test.ts.snap +1091 -0
  102. package/tests/views/thread-v2.test.ts +2121 -0
  103. package/tsconfig.build.tsbuildinfo +1 -1
  104. package/tsconfig.tests.tsbuildinfo +1 -1
@@ -11,6 +11,7 @@ const threadgate_1 = require("../lexicon/types/app/bsky/feed/threadgate");
11
11
  const service_1 = require("../lexicon/types/app/bsky/labeler/service");
12
12
  const defs_2 = require("../lexicon/types/com/atproto/label/defs");
13
13
  const uris_1 = require("../util/uris");
14
+ const threads_v2_1 = require("./threads-v2");
14
15
  const types_1 = require("./types");
15
16
  const util_1 = require("./util");
16
17
  const notificationDeletedRecord = {
@@ -44,6 +45,18 @@ class Views {
44
45
  writable: true,
45
46
  value: this.opts.indexedAtEpoch
46
47
  });
48
+ Object.defineProperty(this, "threadTagsBumpDown", {
49
+ enumerable: true,
50
+ configurable: true,
51
+ writable: true,
52
+ value: this.opts.threadTagsBumpDown
53
+ });
54
+ Object.defineProperty(this, "threadTagsHide", {
55
+ enumerable: true,
56
+ configurable: true,
57
+ writable: true,
58
+ value: this.opts.threadTagsHide
59
+ });
47
60
  }
48
61
  // Actor
49
62
  // ------------
@@ -67,6 +80,10 @@ class Views {
67
80
  return true;
68
81
  return false;
69
82
  }
83
+ noUnauthenticatedPost(state, post) {
84
+ const isNoUnauthenticated = post.author.labels?.some((l) => l.val === '!no-unauthenticated');
85
+ return !state.ctx?.viewer && !!isNoUnauthenticated;
86
+ }
70
87
  viewerBlockExists(did, state) {
71
88
  const viewer = state.profileViewers?.get(did);
72
89
  if (!viewer)
@@ -851,6 +868,488 @@ class Views {
851
868
  };
852
869
  });
853
870
  }
871
+ // Threads V2
872
+ // ------------
873
+ threadV2(skeleton, state, { above, below, branchingFactor, prioritizeFollowedUsers, sort, }) {
874
+ const { anchor: anchorUri, uris } = skeleton;
875
+ // Not found.
876
+ const postView = this.post(anchorUri, state);
877
+ const post = state.posts?.get(anchorUri);
878
+ if (!post || !postView) {
879
+ return {
880
+ hasHiddenReplies: false,
881
+ thread: [
882
+ this.threadV2ItemNotFound({
883
+ uri: anchorUri,
884
+ depth: 0,
885
+ }),
886
+ ],
887
+ };
888
+ }
889
+ // Blocked (only 1p for anchor).
890
+ if (this.viewerBlockExists(postView.author.did, state)) {
891
+ return {
892
+ hasHiddenReplies: false,
893
+ thread: [
894
+ this.threadV2ItemBlocked({
895
+ uri: anchorUri,
896
+ depth: 0,
897
+ authorDid: postView.author.did,
898
+ state,
899
+ }),
900
+ ],
901
+ };
902
+ }
903
+ const childrenByParentUri = this.groupThreadChildrenByParent(anchorUri, uris, state);
904
+ const rootUri = getRootUri(anchorUri, post);
905
+ const opDid = (0, uris_1.uriToDid)(rootUri);
906
+ const authorDid = postView.author.did;
907
+ const isOPPost = authorDid === opDid;
908
+ const anchorViolatesThreadGate = post.violatesThreadGate;
909
+ // Builds the parent tree, and whether it is a contiguous OP thread.
910
+ const parentTree = !anchorViolatesThreadGate
911
+ ? this.threadV2Parent({
912
+ childUri: anchorUri,
913
+ opDid,
914
+ rootUri,
915
+ above,
916
+ depth: -1,
917
+ }, state)
918
+ : undefined;
919
+ const { tree: parent, isOPThread: isOPThreadFromRootToParent } = parentTree ?? { tree: undefined, isOPThread: false };
920
+ const isOPThread = parent
921
+ ? isOPThreadFromRootToParent && isOPPost
922
+ : isOPPost;
923
+ const anchorDepth = 0; // The depth of the anchor post is always 0.
924
+ let anchorTree;
925
+ let hasHiddenReplies = false;
926
+ if (this.noUnauthenticatedPost(state, postView)) {
927
+ anchorTree = {
928
+ type: 'noUnauthenticated',
929
+ item: this.threadV2ItemNoUnauthenticated({
930
+ uri: anchorUri,
931
+ depth: anchorDepth,
932
+ }),
933
+ parent,
934
+ };
935
+ }
936
+ else {
937
+ const { replies, hasHiddenReplies: hasHiddenRepliesShadow } = !anchorViolatesThreadGate
938
+ ? this.threadV2Replies({
939
+ parentUri: anchorUri,
940
+ isOPThread,
941
+ opDid,
942
+ rootUri,
943
+ childrenByParentUri,
944
+ below,
945
+ depth: 1,
946
+ branchingFactor,
947
+ prioritizeFollowedUsers,
948
+ }, state)
949
+ : { replies: undefined, hasHiddenReplies: false };
950
+ hasHiddenReplies = hasHiddenRepliesShadow;
951
+ anchorTree = {
952
+ type: 'post',
953
+ item: this.threadV2ItemPost({
954
+ depth: anchorDepth,
955
+ isOPThread,
956
+ postView,
957
+ repliesAllowance: Infinity, // While we don't have pagination.
958
+ uri: anchorUri,
959
+ }),
960
+ tags: post.tags,
961
+ hasOPLike: !!state.threadContexts?.get(postView.uri)?.like,
962
+ parent,
963
+ replies,
964
+ };
965
+ }
966
+ const thread = (0, threads_v2_1.sortTrimFlattenThreadTree)(anchorTree, {
967
+ opDid,
968
+ branchingFactor,
969
+ sort,
970
+ prioritizeFollowedUsers,
971
+ viewer: state.ctx?.viewer ?? null,
972
+ threadTagsBumpDown: this.threadTagsBumpDown,
973
+ threadTagsHide: this.threadTagsHide,
974
+ });
975
+ return {
976
+ hasHiddenReplies,
977
+ thread,
978
+ };
979
+ }
980
+ threadV2Parent({ childUri, opDid, rootUri, above, depth, }, state) {
981
+ // Reached the `above` limit.
982
+ if (Math.abs(depth) > above) {
983
+ return undefined;
984
+ }
985
+ // Not found.
986
+ const uri = state.posts?.get(childUri)?.record.reply?.parent.uri;
987
+ if (!uri) {
988
+ return undefined;
989
+ }
990
+ const postView = this.post(uri, state);
991
+ const post = state.posts?.get(uri);
992
+ if (!post || !postView) {
993
+ return {
994
+ tree: {
995
+ type: 'notFound',
996
+ item: this.threadV2ItemNotFound({ uri, depth }),
997
+ },
998
+ isOPThread: false,
999
+ };
1000
+ }
1001
+ if (rootUri !== getRootUri(uri, post)) {
1002
+ // Outside thread boundary.
1003
+ return undefined;
1004
+ }
1005
+ // Blocked (1p and 3p for parent).
1006
+ const authorDid = postView.author.did;
1007
+ const has1pBlock = this.viewerBlockExists(authorDid, state);
1008
+ const has3pBlock = !state.ctx?.include3pBlocks && state.postBlocks?.get(childUri)?.parent;
1009
+ if (has1pBlock || has3pBlock) {
1010
+ return {
1011
+ tree: {
1012
+ type: 'blocked',
1013
+ item: this.threadV2ItemBlocked({
1014
+ uri,
1015
+ depth,
1016
+ authorDid,
1017
+ state,
1018
+ }),
1019
+ },
1020
+ isOPThread: false,
1021
+ };
1022
+ }
1023
+ // Recurse up.
1024
+ const parentTree = this.threadV2Parent({
1025
+ childUri: uri,
1026
+ opDid,
1027
+ rootUri,
1028
+ above,
1029
+ depth: depth - 1,
1030
+ }, state);
1031
+ const { tree: parent, isOPThread: isOPThreadFromRootToParent } = parentTree ?? { tree: undefined, isOPThread: false };
1032
+ const isOPPost = authorDid === opDid;
1033
+ const isOPThread = parent
1034
+ ? isOPThreadFromRootToParent && isOPPost
1035
+ : isOPPost;
1036
+ if (this.noUnauthenticatedPost(state, postView)) {
1037
+ return {
1038
+ tree: {
1039
+ type: 'noUnauthenticated',
1040
+ item: this.threadV2ItemNoUnauthenticated({
1041
+ uri,
1042
+ depth,
1043
+ }),
1044
+ parent,
1045
+ },
1046
+ isOPThread,
1047
+ };
1048
+ }
1049
+ const parentUri = post.record.reply?.parent.uri;
1050
+ const hasMoreParents = !!parentUri && !parent;
1051
+ return {
1052
+ tree: {
1053
+ type: 'post',
1054
+ item: this.threadV2ItemPost({
1055
+ depth,
1056
+ isOPThread,
1057
+ moreParents: hasMoreParents,
1058
+ postView,
1059
+ uri,
1060
+ }),
1061
+ tags: post.tags,
1062
+ hasOPLike: !!state.threadContexts?.get(postView.uri)?.like,
1063
+ parent,
1064
+ replies: undefined,
1065
+ },
1066
+ isOPThread,
1067
+ };
1068
+ }
1069
+ threadV2Replies({ parentUri, isOPThread: isOPThreadFromRootToParent, opDid, rootUri, childrenByParentUri, below, depth, branchingFactor, prioritizeFollowedUsers, }, state) {
1070
+ // Reached the `below` limit.
1071
+ if (depth > below) {
1072
+ return { replies: undefined, hasHiddenReplies: false };
1073
+ }
1074
+ const childrenUris = childrenByParentUri[parentUri] ?? [];
1075
+ let hasHiddenReplies = false;
1076
+ const replies = (0, common_1.mapDefined)(childrenUris, (uri) => {
1077
+ const replyInclusion = this.checkThreadV2ReplyInclusion({
1078
+ uri,
1079
+ rootUri,
1080
+ state,
1081
+ });
1082
+ if (!replyInclusion) {
1083
+ return undefined;
1084
+ }
1085
+ const { authorDid, post, postView } = replyInclusion;
1086
+ // Hidden.
1087
+ const { isHidden } = this.isHiddenThreadPost({ post, postView, prioritizeFollowedUsers, rootUri, uri }, state);
1088
+ if (isHidden) {
1089
+ // Only care about anchor replies
1090
+ if (depth === 1) {
1091
+ hasHiddenReplies = true;
1092
+ }
1093
+ return undefined;
1094
+ }
1095
+ // Recurse down.
1096
+ const isOPThread = isOPThreadFromRootToParent && authorDid === opDid;
1097
+ const { replies: nestedReplies } = this.threadV2Replies({
1098
+ parentUri: uri,
1099
+ isOPThread,
1100
+ opDid,
1101
+ rootUri,
1102
+ childrenByParentUri,
1103
+ below,
1104
+ depth: depth + 1,
1105
+ branchingFactor,
1106
+ prioritizeFollowedUsers,
1107
+ }, state);
1108
+ const reachedDepth = depth === below;
1109
+ const repliesAllowance = reachedDepth ? 0 : branchingFactor;
1110
+ const tree = {
1111
+ type: 'post',
1112
+ item: this.threadV2ItemPost({
1113
+ depth,
1114
+ isOPThread,
1115
+ postView,
1116
+ repliesAllowance,
1117
+ uri,
1118
+ }),
1119
+ tags: post.tags,
1120
+ hasOPLike: !!state.threadContexts?.get(postView.uri)?.like,
1121
+ parent: undefined,
1122
+ replies: nestedReplies,
1123
+ };
1124
+ return tree;
1125
+ });
1126
+ return {
1127
+ replies,
1128
+ hasHiddenReplies,
1129
+ };
1130
+ }
1131
+ threadV2ItemPost({ depth, isOPThread, moreParents, postView, repliesAllowance, uri, }) {
1132
+ const moreReplies = repliesAllowance === undefined
1133
+ ? 0
1134
+ : Math.max((postView.replyCount ?? 0) - repliesAllowance, 0);
1135
+ return {
1136
+ uri,
1137
+ depth,
1138
+ value: {
1139
+ $type: 'app.bsky.unspecced.getPostThreadV2#threadItemPost',
1140
+ post: postView,
1141
+ moreParents: moreParents ?? false,
1142
+ moreReplies,
1143
+ opThread: isOPThread,
1144
+ },
1145
+ };
1146
+ }
1147
+ threadV2ItemNoUnauthenticated({ uri, depth, }) {
1148
+ return {
1149
+ uri,
1150
+ depth,
1151
+ value: {
1152
+ $type: 'app.bsky.unspecced.getPostThreadV2#threadItemNoUnauthenticated',
1153
+ },
1154
+ };
1155
+ }
1156
+ threadV2ItemNotFound({ uri, depth, }) {
1157
+ return {
1158
+ uri,
1159
+ depth,
1160
+ value: {
1161
+ $type: 'app.bsky.unspecced.getPostThreadV2#threadItemNotFound',
1162
+ },
1163
+ };
1164
+ }
1165
+ threadV2ItemBlocked({ uri, depth, authorDid, state, }) {
1166
+ return {
1167
+ uri,
1168
+ depth,
1169
+ value: {
1170
+ $type: 'app.bsky.unspecced.getPostThreadV2#threadItemBlocked',
1171
+ author: {
1172
+ did: authorDid,
1173
+ viewer: this.blockedProfileViewer(authorDid, state),
1174
+ },
1175
+ },
1176
+ };
1177
+ }
1178
+ threadHiddenV2(skeleton, state, { below, branchingFactor, prioritizeFollowedUsers, }) {
1179
+ const { anchor: anchorUri, uris } = skeleton;
1180
+ // Not found.
1181
+ const postView = this.post(anchorUri, state);
1182
+ const post = state.posts?.get(anchorUri);
1183
+ if (!post || !postView) {
1184
+ return [];
1185
+ }
1186
+ // Blocked (only 1p for anchor).
1187
+ if (this.viewerBlockExists(postView.author.did, state)) {
1188
+ return [];
1189
+ }
1190
+ const childrenByParentUri = this.groupThreadChildrenByParent(anchorUri, uris, state);
1191
+ const rootUri = getRootUri(anchorUri, post);
1192
+ const opDid = (0, uris_1.uriToDid)(rootUri);
1193
+ const anchorTree = {
1194
+ type: 'hiddenAnchor',
1195
+ item: this.threadHiddenV2ItemPostAnchor({ depth: 0, uri: anchorUri }),
1196
+ replies: this.threadHiddenV2Replies({
1197
+ parentUri: anchorUri,
1198
+ rootUri,
1199
+ childrenByParentUri,
1200
+ below,
1201
+ depth: 1,
1202
+ prioritizeFollowedUsers,
1203
+ }, state),
1204
+ };
1205
+ return (0, threads_v2_1.sortTrimFlattenThreadTree)(anchorTree, {
1206
+ opDid,
1207
+ branchingFactor,
1208
+ prioritizeFollowedUsers: false,
1209
+ viewer: state.ctx?.viewer ?? null,
1210
+ threadTagsBumpDown: this.threadTagsBumpDown,
1211
+ threadTagsHide: this.threadTagsHide,
1212
+ });
1213
+ }
1214
+ threadHiddenV2Replies({ parentUri, rootUri, childrenByParentUri, below, depth, prioritizeFollowedUsers, }, state) {
1215
+ // Reached the `below` limit.
1216
+ if (depth > below) {
1217
+ return undefined;
1218
+ }
1219
+ const childrenUris = childrenByParentUri[parentUri] ?? [];
1220
+ return (0, common_1.mapDefined)(childrenUris, (uri) => {
1221
+ const replyInclusion = this.checkThreadV2ReplyInclusion({
1222
+ uri,
1223
+ rootUri,
1224
+ state,
1225
+ });
1226
+ if (!replyInclusion) {
1227
+ return undefined;
1228
+ }
1229
+ const { post, postView } = replyInclusion;
1230
+ // Hidden.
1231
+ const { isHidden, hiddenByThreadgate, mutedByViewer } = this.isHiddenThreadPost({ post, postView, rootUri, prioritizeFollowedUsers, uri }, state);
1232
+ if (isHidden) {
1233
+ // Only show hidden anchor replies, not all hidden.
1234
+ if (depth > 1) {
1235
+ return undefined;
1236
+ }
1237
+ }
1238
+ else if (depth === 1) {
1239
+ // Don't include non-hidden anchor replies.
1240
+ return undefined;
1241
+ }
1242
+ // Recurse down.
1243
+ const replies = this.threadHiddenV2Replies({
1244
+ parentUri: uri,
1245
+ rootUri,
1246
+ childrenByParentUri,
1247
+ below,
1248
+ depth: depth + 1,
1249
+ prioritizeFollowedUsers,
1250
+ }, state);
1251
+ const item = this.threadHiddenV2ItemPost({
1252
+ depth,
1253
+ hiddenByThreadgate,
1254
+ mutedByViewer,
1255
+ postView,
1256
+ uri,
1257
+ });
1258
+ const tree = {
1259
+ type: 'hiddenPost',
1260
+ item: item,
1261
+ tags: post.tags,
1262
+ replies,
1263
+ };
1264
+ return tree;
1265
+ });
1266
+ }
1267
+ threadHiddenV2ItemPostAnchor({ depth, uri, }) {
1268
+ return {
1269
+ uri,
1270
+ depth,
1271
+ // In hidden replies, the anchor value is undefined, so it doesn't include the anchor in the result.
1272
+ // This is helpful so we can use the same internal structure for hidden and non-hidden, while omitting anchor for hidden.
1273
+ value: undefined,
1274
+ };
1275
+ }
1276
+ threadHiddenV2ItemPost({ depth, hiddenByThreadgate, mutedByViewer, postView, uri, }) {
1277
+ const base = this.threadHiddenV2ItemPostAnchor({ depth, uri });
1278
+ return {
1279
+ ...base,
1280
+ value: {
1281
+ $type: 'app.bsky.unspecced.getPostThreadHiddenV2#threadHiddenItemPost',
1282
+ post: postView,
1283
+ hiddenByThreadgate,
1284
+ mutedByViewer,
1285
+ },
1286
+ };
1287
+ }
1288
+ checkThreadV2ReplyInclusion({ uri, rootUri, state, }) {
1289
+ // Not found.
1290
+ const post = state.posts?.get(uri);
1291
+ if (post?.violatesThreadGate) {
1292
+ return null;
1293
+ }
1294
+ const postView = this.post(uri, state);
1295
+ if (!post || !postView) {
1296
+ return null;
1297
+ }
1298
+ const authorDid = postView.author.did;
1299
+ if (rootUri !== getRootUri(uri, post)) {
1300
+ // outside thread boundary
1301
+ return null;
1302
+ }
1303
+ // Blocked (1p and 3p for replies).
1304
+ const has1pBlock = this.viewerBlockExists(authorDid, state);
1305
+ const has3pBlock = !state.ctx?.include3pBlocks && state.postBlocks?.get(uri)?.parent;
1306
+ if (has1pBlock || has3pBlock) {
1307
+ return null;
1308
+ }
1309
+ if (!this.viewerSeesNeedsReview({ uri, did: authorDid }, state)) {
1310
+ return null;
1311
+ }
1312
+ // No unauthenticated.
1313
+ if (this.noUnauthenticatedPost(state, postView)) {
1314
+ return null;
1315
+ }
1316
+ return { authorDid, post, postView };
1317
+ }
1318
+ isHiddenThreadPost({ post, postView, prioritizeFollowedUsers, rootUri, uri, }, state) {
1319
+ const opDid = (0, uris_1.uriToDid)(rootUri);
1320
+ const authorDid = (0, uris_1.uriToDid)(uri);
1321
+ const showBecauseFollowing = prioritizeFollowedUsers && !!postView.author.viewer?.following;
1322
+ const hiddenByTag = authorDid !== opDid &&
1323
+ authorDid !== state.ctx?.viewer &&
1324
+ !showBecauseFollowing &&
1325
+ this.threadTagsHide.some((t) => post.tags.has(t));
1326
+ const hiddenByThreadgate = state.ctx?.viewer !== authorDid &&
1327
+ this.replyIsHiddenByThreadgate(uri, rootUri, state);
1328
+ const mutedByViewer = this.viewerMuteExists(authorDid, state);
1329
+ return {
1330
+ isHidden: hiddenByTag || hiddenByThreadgate || mutedByViewer,
1331
+ hiddenByTag,
1332
+ hiddenByThreadgate,
1333
+ mutedByViewer,
1334
+ };
1335
+ }
1336
+ groupThreadChildrenByParent(anchorUri, uris, state) {
1337
+ // Groups children of each parent.
1338
+ const includedPosts = new Set([anchorUri]);
1339
+ const childrenByParentUri = {};
1340
+ uris.forEach((uri) => {
1341
+ const post = state.posts?.get(uri);
1342
+ const parentUri = post?.record.reply?.parent.uri;
1343
+ if (!parentUri)
1344
+ return;
1345
+ if (includedPosts.has(uri))
1346
+ return;
1347
+ includedPosts.add(uri);
1348
+ childrenByParentUri[parentUri] ?? (childrenByParentUri[parentUri] = []);
1349
+ childrenByParentUri[parentUri].push(uri);
1350
+ });
1351
+ return childrenByParentUri;
1352
+ }
854
1353
  // Embeds
855
1354
  // ------------
856
1355
  embed(postUri, embed, state, depth) {