@atproto/bsky 0.0.150 → 0.0.152

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