@atproto/bsky 0.0.194 → 0.0.196
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.
- package/CHANGELOG.md +27 -0
- package/dist/api/app/bsky/feed/searchPosts.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/searchPosts.js +60 -14
- package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getPostThreadOtherV2.js +1 -2
- package/dist/api/app/bsky/unspecced/getPostThreadOtherV2.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getPostThreadV2.js +1 -1
- package/dist/api/app/bsky/unspecced/getPostThreadV2.js.map +1 -1
- package/dist/auth-verifier.d.ts +1 -0
- package/dist/auth-verifier.d.ts.map +1 -1
- package/dist/auth-verifier.js +10 -1
- package/dist/auth-verifier.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -0
- package/dist/config.js.map +1 -1
- package/dist/data-plane/server/routes/search.d.ts.map +1 -1
- package/dist/data-plane/server/routes/search.js +15 -1
- package/dist/data-plane/server/routes/search.js.map +1 -1
- package/dist/data-plane/server/util.d.ts +7 -0
- package/dist/data-plane/server/util.d.ts.map +1 -1
- package/dist/data-plane/server/util.js +38 -1
- package/dist/data-plane/server/util.js.map +1 -1
- package/dist/feature-gates.d.ts +17 -6
- package/dist/feature-gates.d.ts.map +1 -1
- package/dist/feature-gates.js +24 -13
- package/dist/feature-gates.js.map +1 -1
- package/dist/hydration/feed.d.ts +4 -1
- package/dist/hydration/feed.d.ts.map +1 -1
- package/dist/hydration/feed.js +10 -2
- package/dist/hydration/feed.js.map +1 -1
- package/dist/hydration/hydrator.d.ts +5 -2
- package/dist/hydration/hydrator.d.ts.map +1 -1
- package/dist/hydration/hydrator.js +17 -3
- package/dist/hydration/hydrator.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lexicon/index.d.ts +2 -0
- package/dist/lexicon/index.d.ts.map +1 -1
- package/dist/lexicon/index.js +4 -0
- package/dist/lexicon/index.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +98 -28
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +52 -14
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +0 -2
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadOtherV2.d.ts +0 -2
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadOtherV2.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadOtherV2.js.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.d.ts +0 -2
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getPostThreadV2.js.map +1 -1
- package/dist/lexicon/types/com/atproto/lexicon/resolveLexicon.d.ts +28 -0
- package/dist/lexicon/types/com/atproto/lexicon/resolveLexicon.d.ts.map +1 -0
- package/dist/lexicon/types/com/atproto/lexicon/resolveLexicon.js +7 -0
- package/dist/lexicon/types/com/atproto/lexicon/resolveLexicon.js.map +1 -0
- package/dist/proto/bsky_pb.d.ts +8 -0
- package/dist/proto/bsky_pb.d.ts.map +1 -1
- package/dist/proto/bsky_pb.js +20 -0
- package/dist/proto/bsky_pb.js.map +1 -1
- package/dist/views/index.d.ts +6 -4
- package/dist/views/index.d.ts.map +1 -1
- package/dist/views/index.js +38 -21
- package/dist/views/index.js.map +1 -1
- package/dist/views/threads-v2.d.ts +3 -2
- package/dist/views/threads-v2.d.ts.map +1 -1
- package/dist/views/threads-v2.js +124 -16
- package/dist/views/threads-v2.js.map +1 -1
- package/package.json +7 -7
- package/proto/bsky.proto +2 -0
- package/src/api/app/bsky/feed/searchPosts.ts +78 -11
- package/src/api/app/bsky/unspecced/getPostThreadOtherV2.ts +1 -2
- package/src/api/app/bsky/unspecced/getPostThreadV2.ts +4 -1
- package/src/auth-verifier.ts +14 -1
- package/src/config.ts +23 -0
- package/src/data-plane/server/routes/search.ts +18 -1
- package/src/data-plane/server/util.ts +51 -0
- package/src/feature-gates.ts +28 -9
- package/src/hydration/feed.ts +17 -1
- package/src/hydration/hydrator.ts +17 -1
- package/src/index.ts +2 -0
- package/src/lexicon/index.ts +13 -0
- package/src/lexicon/lexicons.ts +52 -16
- package/src/lexicon/types/app/bsky/actor/defs.ts +0 -2
- package/src/lexicon/types/app/bsky/unspecced/getPostThreadOtherV2.ts +0 -2
- package/src/lexicon/types/app/bsky/unspecced/getPostThreadV2.ts +0 -2
- package/src/lexicon/types/com/atproto/lexicon/resolveLexicon.ts +46 -0
- package/src/proto/bsky_pb.ts +12 -0
- package/src/views/index.ts +54 -44
- package/src/views/threads-v2.ts +158 -25
- package/tests/utils.test.ts +45 -0
- package/tests/views/post-search.test.ts +221 -0
- package/tests/views/thread-v2.test.ts +2 -109
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.tests.tsbuildinfo +1 -1
|
@@ -52,15 +52,16 @@ export type ThreadTreeVisible = ThreadBlockedNode | ThreadNoUnauthenticatedNode
|
|
|
52
52
|
export type ThreadTreeOther = ThreadOtherAnchorPostNode | ThreadOtherPostNode;
|
|
53
53
|
export type ThreadTree = ThreadTreeVisible | ThreadTreeOther;
|
|
54
54
|
/** This function mutates the tree parameter. */
|
|
55
|
-
export declare function sortTrimFlattenThreadTree(anchorTree: ThreadTree, options: SortTrimFlattenOptions): any;
|
|
55
|
+
export declare function sortTrimFlattenThreadTree(anchorTree: ThreadTree, options: SortTrimFlattenOptions, useExploration?: boolean): any;
|
|
56
56
|
type SortTrimFlattenOptions = {
|
|
57
57
|
branchingFactor: GetPostThreadV2QueryParams['branchingFactor'];
|
|
58
58
|
opDid: string;
|
|
59
|
-
prioritizeFollowedUsers: boolean;
|
|
60
59
|
sort?: GetPostThreadV2QueryParams['sort'];
|
|
61
60
|
viewer: HydrateCtx['viewer'];
|
|
62
61
|
threadTagsBumpDown: readonly string[];
|
|
63
62
|
threadTagsHide: readonly string[];
|
|
63
|
+
visibilityTagRankPrefix: string;
|
|
64
64
|
};
|
|
65
|
+
export declare function sortTrimThreadTreeExploration(n: ThreadTree, opts: SortTrimFlattenOptions): ThreadTree;
|
|
65
66
|
export {};
|
|
66
67
|
//# sourceMappingURL=threads-v2.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"threads-v2.d.ts","sourceRoot":"","sources":["../../src/views/threads-v2.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"threads-v2.d.ts","sourceRoot":"","sources":["../../src/views/threads-v2.ts"],"names":[],"mappings":"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"}
|
package/dist/views/threads-v2.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.sortTrimFlattenThreadTree = sortTrimFlattenThreadTree;
|
|
4
|
-
|
|
5
|
-
const post_1 = require("../lexicon/types/app/bsky/feed/post");
|
|
4
|
+
exports.sortTrimThreadTreeExploration = sortTrimThreadTreeExploration;
|
|
6
5
|
const isNodeWithReplies = (node) => 'replies' in node && node.replies !== undefined;
|
|
7
6
|
const isPostNode = (node) => node.type === 'post' || node.type === 'hiddenPost';
|
|
8
7
|
/** This function mutates the tree parameter. */
|
|
9
|
-
function sortTrimFlattenThreadTree(anchorTree, options) {
|
|
10
|
-
const sortedAnchorTree =
|
|
8
|
+
function sortTrimFlattenThreadTree(anchorTree, options, useExploration) {
|
|
9
|
+
const sortedAnchorTree = useExploration
|
|
10
|
+
? sortTrimThreadTreeExploration(anchorTree, options)
|
|
11
|
+
: sortTrimThreadTree(anchorTree, options);
|
|
11
12
|
return flattenTree(sortedAnchorTree);
|
|
12
13
|
}
|
|
13
|
-
const isPostRecord = (0, api_1.asPredicate)(post_1.validateRecord);
|
|
14
14
|
/** This function mutates the tree parameter. */
|
|
15
15
|
function sortTrimThreadTree(n, opts) {
|
|
16
16
|
if (!isNodeWithReplies(n)) {
|
|
@@ -50,7 +50,7 @@ function applyBumping(aNode, bNode, opts) {
|
|
|
50
50
|
if (!isPostNode(bNode)) {
|
|
51
51
|
return null;
|
|
52
52
|
}
|
|
53
|
-
const { opDid,
|
|
53
|
+
const { opDid, viewer, threadTagsBumpDown, threadTagsHide } = opts;
|
|
54
54
|
const maybeBump = (bump, predicateFn) => {
|
|
55
55
|
const aPredicate = predicateFn(aNode);
|
|
56
56
|
const bPredicate = predicateFn(bNode);
|
|
@@ -82,22 +82,13 @@ function applyBumping(aNode, bNode, opts) {
|
|
|
82
82
|
// Followers posts.
|
|
83
83
|
[
|
|
84
84
|
'up',
|
|
85
|
-
(i) => i.type === 'post' &&
|
|
86
|
-
prioritizeFollowedUsers &&
|
|
87
|
-
!!i.item.value.post.author.viewer?.following,
|
|
85
|
+
(i) => i.type === 'post' && !!i.item.value.post.author.viewer?.following,
|
|
88
86
|
],
|
|
89
87
|
// Bump-down tags.
|
|
90
88
|
[
|
|
91
89
|
'down',
|
|
92
90
|
(i) => i.type === 'post' && threadTagsBumpDown.some((t) => i.tags.has(t)),
|
|
93
91
|
],
|
|
94
|
-
// Pushpin-only.
|
|
95
|
-
[
|
|
96
|
-
'down',
|
|
97
|
-
(i) => i.type === 'post' &&
|
|
98
|
-
isPostRecord(i.item.value.post.record) &&
|
|
99
|
-
i.item.value.post.record.text.trim() === '📌',
|
|
100
|
-
],
|
|
101
92
|
/*
|
|
102
93
|
Bumps within hidden replies.
|
|
103
94
|
This determines the order of hidden replies:
|
|
@@ -202,4 +193,121 @@ function* flattenInDirection({ tree, direction, }) {
|
|
|
202
193
|
}
|
|
203
194
|
}
|
|
204
195
|
}
|
|
196
|
+
function sortTrimThreadTreeExploration(n, opts) {
|
|
197
|
+
if (!isNodeWithReplies(n)) {
|
|
198
|
+
return n;
|
|
199
|
+
}
|
|
200
|
+
const node = n;
|
|
201
|
+
if (node.replies) {
|
|
202
|
+
node.replies.sort((an, bn) => {
|
|
203
|
+
if (!isPostNode(an)) {
|
|
204
|
+
return 1;
|
|
205
|
+
}
|
|
206
|
+
if (!isPostNode(bn)) {
|
|
207
|
+
return -1;
|
|
208
|
+
}
|
|
209
|
+
const aNode = an;
|
|
210
|
+
const bNode = bn;
|
|
211
|
+
// First applies bumping.
|
|
212
|
+
const bump = applyBumpingExploration(aNode, bNode, opts);
|
|
213
|
+
if (bump !== null) {
|
|
214
|
+
return bump;
|
|
215
|
+
}
|
|
216
|
+
// Then applies sorting.
|
|
217
|
+
return applySortingExploration(aNode, bNode, opts);
|
|
218
|
+
});
|
|
219
|
+
// Trimming: after sorting, apply branching factor to all levels of replies except the anchor direct replies.
|
|
220
|
+
if (node.item.depth !== 0) {
|
|
221
|
+
node.replies = node.replies.slice(0, opts.branchingFactor);
|
|
222
|
+
}
|
|
223
|
+
node.replies.forEach((reply) => sortTrimThreadTreeExploration(reply, opts));
|
|
224
|
+
}
|
|
225
|
+
return node;
|
|
226
|
+
}
|
|
227
|
+
function applyBumpingExploration(aNode, bNode, opts) {
|
|
228
|
+
if (!isPostNode(aNode)) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
if (!isPostNode(bNode)) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
const { opDid, viewer } = opts;
|
|
235
|
+
const maybeBump = (bump, predicateFn) => {
|
|
236
|
+
const aPredicate = predicateFn(aNode);
|
|
237
|
+
const bPredicate = predicateFn(bNode);
|
|
238
|
+
if (aPredicate && bPredicate) {
|
|
239
|
+
return applySortingExploration(aNode, bNode, opts);
|
|
240
|
+
}
|
|
241
|
+
else if (aPredicate) {
|
|
242
|
+
return bump === 'up' ? -1 : 1;
|
|
243
|
+
}
|
|
244
|
+
else if (bPredicate) {
|
|
245
|
+
return bump === 'up' ? 1 : -1;
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
};
|
|
249
|
+
// The order of the bumps determines the priority with which they are applied.
|
|
250
|
+
// Bumps-up applied first make the item appear higher in the list than later bumps-up.
|
|
251
|
+
// Bumps-down applied first make the item appear lower in the list than later bumps-down.
|
|
252
|
+
const bumps = [
|
|
253
|
+
/*
|
|
254
|
+
General bumps.
|
|
255
|
+
*/
|
|
256
|
+
// OP replies.
|
|
257
|
+
['up', (i) => i.item.value.post.author.did === opDid],
|
|
258
|
+
// Viewer replies.
|
|
259
|
+
['up', (i) => i.item.value.post.author.did === viewer],
|
|
260
|
+
];
|
|
261
|
+
for (const [bump, predicateFn] of bumps) {
|
|
262
|
+
const bumpResult = maybeBump(bump, predicateFn);
|
|
263
|
+
if (bumpResult !== null) {
|
|
264
|
+
return bumpResult;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
function applySortingExploration(aNode, bNode, opts) {
|
|
270
|
+
const { visibilityTagRankPrefix: rp } = opts;
|
|
271
|
+
const a = aNode.item.value;
|
|
272
|
+
const ar = !rp ? 0 : parseRankFromTag(rp, findRankTag(aNode.tags, rp));
|
|
273
|
+
const b = bNode.item.value;
|
|
274
|
+
const br = !rp ? 0 : parseRankFromTag(rp, findRankTag(bNode.tags, rp));
|
|
275
|
+
// Only customize sort for visible posts.
|
|
276
|
+
if (aNode.type === 'post' && bNode.type === 'post') {
|
|
277
|
+
const { sort } = opts;
|
|
278
|
+
if (sort === 'oldest') {
|
|
279
|
+
return a.post.indexedAt.localeCompare(b.post.indexedAt);
|
|
280
|
+
}
|
|
281
|
+
if (sort === 'top') {
|
|
282
|
+
const aLikes = a.post.likeCount ?? 0;
|
|
283
|
+
const bLikes = b.post.likeCount ?? 0;
|
|
284
|
+
const aTop = topSortValue(aLikes, aNode.hasOPLike);
|
|
285
|
+
const bTop = topSortValue(bLikes, bNode.hasOPLike);
|
|
286
|
+
const aRank = aTop + ar;
|
|
287
|
+
const bRank = bTop + br;
|
|
288
|
+
if (aRank !== bRank) {
|
|
289
|
+
return bRank - aRank;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Fallback to newest.
|
|
294
|
+
return b.post.indexedAt.localeCompare(a.post.indexedAt);
|
|
295
|
+
}
|
|
296
|
+
function findRankTag(tags, prefix) {
|
|
297
|
+
return Array.from(tags.values()).find((tag) => tag.startsWith(prefix));
|
|
298
|
+
}
|
|
299
|
+
function parseRankFromTag(prefix, tag) {
|
|
300
|
+
if (!tag)
|
|
301
|
+
return 0;
|
|
302
|
+
try {
|
|
303
|
+
const rank = parseInt(tag.slice(prefix.length), 10);
|
|
304
|
+
if (typeof rank !== 'number' || isNaN(rank)) {
|
|
305
|
+
return 0;
|
|
306
|
+
}
|
|
307
|
+
return rank;
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
205
313
|
//# sourceMappingURL=threads-v2.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"threads-v2.js","sourceRoot":"","sources":["../../src/views/threads-v2.ts"],"names":[],"mappings":"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","sourcesContent":["import { asPredicate } from '@atproto/api'\nimport { HydrateCtx } from '../hydration/hydrator'\nimport { validateRecord as validatePostRecord } from '../lexicon/types/app/bsky/feed/post'\nimport {\n ThreadItemBlocked,\n ThreadItemNoUnauthenticated,\n ThreadItemNotFound,\n ThreadItemPost,\n} from '../lexicon/types/app/bsky/unspecced/defs'\nimport { ThreadItem as ThreadOtherItem } from '../lexicon/types/app/bsky/unspecced/getPostThreadOtherV2'\nimport {\n QueryParams as GetPostThreadV2QueryParams,\n ThreadItem,\n} from '../lexicon/types/app/bsky/unspecced/getPostThreadV2'\nimport { $Typed } from '../lexicon/util'\n\ntype ThreadMaybeOtherPostNode = ThreadPostNode | ThreadOtherPostNode\ntype ThreadNodeWithReplies =\n | ThreadPostNode\n | ThreadOtherPostNode\n | ThreadOtherAnchorPostNode\n\ntype ThreadItemValue<T extends ThreadItem['value']> = Omit<\n ThreadItem,\n 'value'\n> & {\n value: T\n}\n\nexport type ThreadItemValueBlocked = ThreadItemValue<$Typed<ThreadItemBlocked>>\n\nexport type ThreadItemValueNoUnauthenticated = ThreadItemValue<\n $Typed<ThreadItemNoUnauthenticated>\n>\n\nexport type ThreadItemValueNotFound = ThreadItemValue<\n $Typed<ThreadItemNotFound>\n>\n\nexport type ThreadItemValuePost = ThreadItemValue<$Typed<ThreadItemPost>>\n\ntype ThreadBlockedNode = {\n type: 'blocked'\n item: ThreadItemValueBlocked\n}\ntype ThreadNoUnauthenticatedNode = {\n type: 'noUnauthenticated'\n parent: ThreadTree | undefined\n item: ThreadItemValueNoUnauthenticated\n}\n\ntype ThreadNotFoundNode = {\n type: 'notFound'\n item: ThreadItemValueNotFound\n}\n\ntype ThreadPostNode = {\n type: 'post'\n item: ThreadItemValuePost\n tags: Set<string>\n hasOPLike: boolean\n parent: ThreadTree | undefined\n replies: ThreadTree[] | undefined\n}\n\ntype ThreadOtherItemValue<T extends ThreadOtherItem['value']> = Omit<\n ThreadOtherItem,\n 'value'\n> & {\n value: T\n}\n\nexport type ThreadOtherItemValuePost = ThreadOtherItemValue<\n $Typed<ThreadItemPost>\n>\n\n// This is an intermediary type that doesn't map to the views.\n// It is useful to differentiate between the anchor post and the replies for the hidden case,\n// while also differentiating between hidden and visible cases.\nexport type ThreadOtherAnchorPostNode = {\n type: 'hiddenAnchor'\n item: Omit<ThreadOtherItem, 'value'> & { value: undefined }\n replies: ThreadOtherPostNode[] | undefined\n}\n\nexport type ThreadOtherPostNode = {\n type: 'hiddenPost'\n item: ThreadOtherItemValuePost\n tags: Set<string>\n replies: ThreadOtherPostNode[] | undefined\n}\n\nconst isNodeWithReplies = (node: ThreadTree): node is ThreadNodeWithReplies =>\n 'replies' in node && node.replies !== undefined\n\nconst isPostNode = (node: ThreadTree): node is ThreadMaybeOtherPostNode =>\n node.type === 'post' || node.type === 'hiddenPost'\n\nexport type ThreadTreeVisible =\n | ThreadBlockedNode\n | ThreadNoUnauthenticatedNode\n | ThreadNotFoundNode\n | ThreadPostNode\n\nexport type ThreadTreeOther = ThreadOtherAnchorPostNode | ThreadOtherPostNode\n\nexport type ThreadTree = ThreadTreeVisible | ThreadTreeOther\n\n/** This function mutates the tree parameter. */\nexport function sortTrimFlattenThreadTree(\n anchorTree: ThreadTree,\n options: SortTrimFlattenOptions,\n) {\n const sortedAnchorTree = sortTrimThreadTree(anchorTree, options)\n\n return flattenTree(sortedAnchorTree)\n}\n\ntype SortTrimFlattenOptions = {\n branchingFactor: GetPostThreadV2QueryParams['branchingFactor']\n opDid: string\n prioritizeFollowedUsers: boolean\n sort?: GetPostThreadV2QueryParams['sort']\n viewer: HydrateCtx['viewer']\n threadTagsBumpDown: readonly string[]\n threadTagsHide: readonly string[]\n}\n\nconst isPostRecord = asPredicate(validatePostRecord)\n\n/** This function mutates the tree parameter. */\nfunction sortTrimThreadTree(\n n: ThreadTree,\n opts: SortTrimFlattenOptions,\n): ThreadTree {\n if (!isNodeWithReplies(n)) {\n return n\n }\n const node: ThreadNodeWithReplies = n\n\n if (node.replies) {\n node.replies.sort((an: ThreadTree, bn: ThreadTree) => {\n if (!isPostNode(an)) {\n return 1\n }\n if (!isPostNode(bn)) {\n return -1\n }\n const aNode: ThreadMaybeOtherPostNode = an\n const bNode: ThreadMaybeOtherPostNode = bn\n\n // First applies bumping.\n const bump = applyBumping(aNode, bNode, opts)\n if (bump !== null) {\n return bump\n }\n\n // Then applies sorting.\n return applySorting(aNode, bNode, opts)\n })\n\n // Trimming: after sorting, apply branching factor to all levels of replies except the anchor direct replies.\n if (node.item.depth !== 0) {\n node.replies = node.replies.slice(0, opts.branchingFactor)\n }\n\n node.replies.forEach((reply) => sortTrimThreadTree(reply, opts))\n }\n\n return node\n}\n\nfunction applyBumping(\n aNode: ThreadMaybeOtherPostNode,\n bNode: ThreadMaybeOtherPostNode,\n opts: SortTrimFlattenOptions,\n): number | null {\n if (!isPostNode(aNode)) {\n return null\n }\n if (!isPostNode(bNode)) {\n return null\n }\n\n const {\n opDid,\n prioritizeFollowedUsers,\n viewer,\n threadTagsBumpDown,\n threadTagsHide,\n } = opts\n\n type BumpDirection = 'up' | 'down'\n type BumpPredicateFn = (i: ThreadMaybeOtherPostNode) => boolean\n\n const maybeBump = (\n bump: BumpDirection,\n predicateFn: BumpPredicateFn,\n ): number | null => {\n const aPredicate = predicateFn(aNode)\n const bPredicate = predicateFn(bNode)\n if (aPredicate && bPredicate) {\n return applySorting(aNode, bNode, opts)\n } else if (aPredicate) {\n return bump === 'up' ? -1 : 1\n } else if (bPredicate) {\n return bump === 'up' ? 1 : -1\n }\n return null\n }\n\n // The order of the bumps determines the priority with which they are applied.\n // Bumps-up applied first make the item appear higher in the list than later bumps-up.\n // Bumps-down applied first make the item appear lower in the list than later bumps-down.\n const bumps: [BumpDirection, BumpPredicateFn][] = [\n /*\n General bumps.\n */\n // OP replies.\n ['up', (i) => i.item.value.post.author.did === opDid],\n // Viewer replies.\n ['up', (i) => i.item.value.post.author.did === viewer],\n\n /*\n Bumps within visible replies.\n */\n // Followers posts.\n [\n 'up',\n (i) =>\n i.type === 'post' &&\n prioritizeFollowedUsers &&\n !!i.item.value.post.author.viewer?.following,\n ],\n // Bump-down tags.\n [\n 'down',\n (i) => i.type === 'post' && threadTagsBumpDown.some((t) => i.tags.has(t)),\n ],\n // Pushpin-only.\n [\n 'down',\n (i) =>\n i.type === 'post' &&\n isPostRecord(i.item.value.post.record) &&\n i.item.value.post.record.text.trim() === '📌',\n ],\n\n /*\n Bumps within hidden replies.\n This determines the order of hidden replies:\n 1. hidden by threadgate.\n 2. hidden by tags.\n 3. muted by viewer.\n */\n // Muted account by the viewer.\n ['down', (i) => i.type === 'hiddenPost' && i.item.value.mutedByViewer],\n // Hidden by tags.\n [\n 'down',\n (i) =>\n i.type === 'hiddenPost' && threadTagsHide.some((t) => i.tags.has(t)),\n ],\n // Hidden by threadgate.\n ['down', (i) => i.type === 'hiddenPost' && i.item.value.hiddenByThreadgate],\n ]\n\n for (const [bump, predicateFn] of bumps) {\n const bumpResult = maybeBump(bump, predicateFn)\n if (bumpResult !== null) {\n return bumpResult\n }\n }\n\n return null\n}\n\nfunction applySorting(\n aNode: ThreadMaybeOtherPostNode,\n bNode: ThreadMaybeOtherPostNode,\n opts: SortTrimFlattenOptions,\n): number {\n const a = aNode.item.value\n const b = bNode.item.value\n\n // Only customize sort for visible posts.\n if (aNode.type === 'post' && bNode.type === 'post') {\n const { sort } = opts\n\n if (sort === 'oldest') {\n return a.post.indexedAt.localeCompare(b.post.indexedAt)\n }\n if (sort === 'top') {\n const aLikes = a.post.likeCount ?? 0\n const bLikes = b.post.likeCount ?? 0\n const aTop = topSortValue(aLikes, aNode.hasOPLike)\n const bTop = topSortValue(bLikes, bNode.hasOPLike)\n if (aTop !== bTop) {\n return bTop - aTop\n }\n }\n }\n\n // Fallback to newest.\n return b.post.indexedAt.localeCompare(a.post.indexedAt)\n}\n\nfunction topSortValue(likeCount: number, hasOPLike: boolean): number {\n return Math.log(3 + likeCount) * (hasOPLike ? 1.45 : 1.0)\n}\n\nfunction flattenTree(tree: ThreadTree) {\n return [\n // All parents above.\n ...Array.from(\n flattenInDirection({\n tree,\n direction: 'up',\n }),\n ),\n\n // The anchor.\n // In the case of hidden replies, the anchor item itself is undefined.\n ...(tree.item.value ? [tree.item] : []),\n\n // All replies below.\n ...Array.from(\n flattenInDirection({\n tree,\n direction: 'down',\n }),\n ),\n ]\n}\n\nfunction* flattenInDirection({\n tree,\n direction,\n}: {\n tree: ThreadTree\n direction: 'up' | 'down'\n}) {\n if (tree.type === 'noUnauthenticated') {\n if (direction === 'up') {\n if (tree.parent) {\n // Unfold all parents above.\n yield* flattenTree(tree.parent)\n }\n }\n }\n\n if (tree.type === 'post') {\n if (direction === 'up') {\n if (tree.parent) {\n // Unfold all parents above.\n yield* flattenTree(tree.parent)\n }\n } else {\n // Unfold all replies below.\n if (tree.replies?.length) {\n for (const reply of tree.replies) {\n yield* flattenTree(reply)\n }\n }\n }\n }\n\n // For the first level of hidden replies, the items are undefined.\n if (tree.type === 'hiddenAnchor' || tree.type === 'hiddenPost') {\n if (direction === 'down') {\n // Unfold all replies below.\n if (tree.replies?.length) {\n for (const reply of tree.replies) {\n yield* flattenTree(reply)\n }\n }\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"threads-v2.js","sourceRoot":"","sources":["../../src/views/threads-v2.ts"],"names":[],"mappings":"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","sourcesContent":["import { HydrateCtx } from '../hydration/hydrator'\nimport {\n ThreadItemBlocked,\n ThreadItemNoUnauthenticated,\n ThreadItemNotFound,\n ThreadItemPost,\n} from '../lexicon/types/app/bsky/unspecced/defs'\nimport { ThreadItem as ThreadOtherItem } from '../lexicon/types/app/bsky/unspecced/getPostThreadOtherV2'\nimport {\n QueryParams as GetPostThreadV2QueryParams,\n ThreadItem,\n} from '../lexicon/types/app/bsky/unspecced/getPostThreadV2'\nimport { $Typed } from '../lexicon/util'\n\ntype ThreadMaybeOtherPostNode = ThreadPostNode | ThreadOtherPostNode\ntype ThreadNodeWithReplies =\n | ThreadPostNode\n | ThreadOtherPostNode\n | ThreadOtherAnchorPostNode\n\ntype ThreadItemValue<T extends ThreadItem['value']> = Omit<\n ThreadItem,\n 'value'\n> & {\n value: T\n}\n\nexport type ThreadItemValueBlocked = ThreadItemValue<$Typed<ThreadItemBlocked>>\n\nexport type ThreadItemValueNoUnauthenticated = ThreadItemValue<\n $Typed<ThreadItemNoUnauthenticated>\n>\n\nexport type ThreadItemValueNotFound = ThreadItemValue<\n $Typed<ThreadItemNotFound>\n>\n\nexport type ThreadItemValuePost = ThreadItemValue<$Typed<ThreadItemPost>>\n\ntype ThreadBlockedNode = {\n type: 'blocked'\n item: ThreadItemValueBlocked\n}\ntype ThreadNoUnauthenticatedNode = {\n type: 'noUnauthenticated'\n parent: ThreadTree | undefined\n item: ThreadItemValueNoUnauthenticated\n}\n\ntype ThreadNotFoundNode = {\n type: 'notFound'\n item: ThreadItemValueNotFound\n}\n\ntype ThreadPostNode = {\n type: 'post'\n item: ThreadItemValuePost\n tags: Set<string>\n hasOPLike: boolean\n parent: ThreadTree | undefined\n replies: ThreadTree[] | undefined\n}\n\ntype ThreadOtherItemValue<T extends ThreadOtherItem['value']> = Omit<\n ThreadOtherItem,\n 'value'\n> & {\n value: T\n}\n\nexport type ThreadOtherItemValuePost = ThreadOtherItemValue<\n $Typed<ThreadItemPost>\n>\n\n// This is an intermediary type that doesn't map to the views.\n// It is useful to differentiate between the anchor post and the replies for the hidden case,\n// while also differentiating between hidden and visible cases.\nexport type ThreadOtherAnchorPostNode = {\n type: 'hiddenAnchor'\n item: Omit<ThreadOtherItem, 'value'> & { value: undefined }\n replies: ThreadOtherPostNode[] | undefined\n}\n\nexport type ThreadOtherPostNode = {\n type: 'hiddenPost'\n item: ThreadOtherItemValuePost\n tags: Set<string>\n replies: ThreadOtherPostNode[] | undefined\n}\n\nconst isNodeWithReplies = (node: ThreadTree): node is ThreadNodeWithReplies =>\n 'replies' in node && node.replies !== undefined\n\nconst isPostNode = (node: ThreadTree): node is ThreadMaybeOtherPostNode =>\n node.type === 'post' || node.type === 'hiddenPost'\n\nexport type ThreadTreeVisible =\n | ThreadBlockedNode\n | ThreadNoUnauthenticatedNode\n | ThreadNotFoundNode\n | ThreadPostNode\n\nexport type ThreadTreeOther = ThreadOtherAnchorPostNode | ThreadOtherPostNode\n\nexport type ThreadTree = ThreadTreeVisible | ThreadTreeOther\n\n/** This function mutates the tree parameter. */\nexport function sortTrimFlattenThreadTree(\n anchorTree: ThreadTree,\n options: SortTrimFlattenOptions,\n useExploration?: boolean,\n) {\n const sortedAnchorTree = useExploration\n ? sortTrimThreadTreeExploration(anchorTree, options)\n : sortTrimThreadTree(anchorTree, options)\n\n return flattenTree(sortedAnchorTree)\n}\n\ntype SortTrimFlattenOptions = {\n branchingFactor: GetPostThreadV2QueryParams['branchingFactor']\n opDid: string\n sort?: GetPostThreadV2QueryParams['sort']\n viewer: HydrateCtx['viewer']\n threadTagsBumpDown: readonly string[]\n threadTagsHide: readonly string[]\n visibilityTagRankPrefix: string\n}\n\n/** This function mutates the tree parameter. */\nfunction sortTrimThreadTree(\n n: ThreadTree,\n opts: SortTrimFlattenOptions,\n): ThreadTree {\n if (!isNodeWithReplies(n)) {\n return n\n }\n const node: ThreadNodeWithReplies = n\n\n if (node.replies) {\n node.replies.sort((an: ThreadTree, bn: ThreadTree) => {\n if (!isPostNode(an)) {\n return 1\n }\n if (!isPostNode(bn)) {\n return -1\n }\n const aNode: ThreadMaybeOtherPostNode = an\n const bNode: ThreadMaybeOtherPostNode = bn\n\n // First applies bumping.\n const bump = applyBumping(aNode, bNode, opts)\n if (bump !== null) {\n return bump\n }\n\n // Then applies sorting.\n return applySorting(aNode, bNode, opts)\n })\n\n // Trimming: after sorting, apply branching factor to all levels of replies except the anchor direct replies.\n if (node.item.depth !== 0) {\n node.replies = node.replies.slice(0, opts.branchingFactor)\n }\n\n node.replies.forEach((reply) => sortTrimThreadTree(reply, opts))\n }\n\n return node\n}\n\nfunction applyBumping(\n aNode: ThreadMaybeOtherPostNode,\n bNode: ThreadMaybeOtherPostNode,\n opts: SortTrimFlattenOptions,\n): number | null {\n if (!isPostNode(aNode)) {\n return null\n }\n if (!isPostNode(bNode)) {\n return null\n }\n\n const { opDid, viewer, threadTagsBumpDown, threadTagsHide } = opts\n\n type BumpDirection = 'up' | 'down'\n type BumpPredicateFn = (i: ThreadMaybeOtherPostNode) => boolean\n\n const maybeBump = (\n bump: BumpDirection,\n predicateFn: BumpPredicateFn,\n ): number | null => {\n const aPredicate = predicateFn(aNode)\n const bPredicate = predicateFn(bNode)\n if (aPredicate && bPredicate) {\n return applySorting(aNode, bNode, opts)\n } else if (aPredicate) {\n return bump === 'up' ? -1 : 1\n } else if (bPredicate) {\n return bump === 'up' ? 1 : -1\n }\n return null\n }\n\n // The order of the bumps determines the priority with which they are applied.\n // Bumps-up applied first make the item appear higher in the list than later bumps-up.\n // Bumps-down applied first make the item appear lower in the list than later bumps-down.\n const bumps: [BumpDirection, BumpPredicateFn][] = [\n /*\n General bumps.\n */\n // OP replies.\n ['up', (i) => i.item.value.post.author.did === opDid],\n // Viewer replies.\n ['up', (i) => i.item.value.post.author.did === viewer],\n\n /*\n Bumps within visible replies.\n */\n // Followers posts.\n [\n 'up',\n (i) => i.type === 'post' && !!i.item.value.post.author.viewer?.following,\n ],\n // Bump-down tags.\n [\n 'down',\n (i) => i.type === 'post' && threadTagsBumpDown.some((t) => i.tags.has(t)),\n ],\n\n /*\n Bumps within hidden replies.\n This determines the order of hidden replies:\n 1. hidden by threadgate.\n 2. hidden by tags.\n 3. muted by viewer.\n */\n // Muted account by the viewer.\n ['down', (i) => i.type === 'hiddenPost' && i.item.value.mutedByViewer],\n // Hidden by tags.\n [\n 'down',\n (i) =>\n i.type === 'hiddenPost' && threadTagsHide.some((t) => i.tags.has(t)),\n ],\n // Hidden by threadgate.\n ['down', (i) => i.type === 'hiddenPost' && i.item.value.hiddenByThreadgate],\n ]\n\n for (const [bump, predicateFn] of bumps) {\n const bumpResult = maybeBump(bump, predicateFn)\n if (bumpResult !== null) {\n return bumpResult\n }\n }\n\n return null\n}\n\nfunction applySorting(\n aNode: ThreadMaybeOtherPostNode,\n bNode: ThreadMaybeOtherPostNode,\n opts: SortTrimFlattenOptions,\n): number {\n const a = aNode.item.value\n const b = bNode.item.value\n\n // Only customize sort for visible posts.\n if (aNode.type === 'post' && bNode.type === 'post') {\n const { sort } = opts\n\n if (sort === 'oldest') {\n return a.post.indexedAt.localeCompare(b.post.indexedAt)\n }\n if (sort === 'top') {\n const aLikes = a.post.likeCount ?? 0\n const bLikes = b.post.likeCount ?? 0\n const aTop = topSortValue(aLikes, aNode.hasOPLike)\n const bTop = topSortValue(bLikes, bNode.hasOPLike)\n if (aTop !== bTop) {\n return bTop - aTop\n }\n }\n }\n\n // Fallback to newest.\n return b.post.indexedAt.localeCompare(a.post.indexedAt)\n}\n\nfunction topSortValue(likeCount: number, hasOPLike: boolean): number {\n return Math.log(3 + likeCount) * (hasOPLike ? 1.45 : 1.0)\n}\n\nfunction flattenTree(tree: ThreadTree) {\n return [\n // All parents above.\n ...Array.from(\n flattenInDirection({\n tree,\n direction: 'up',\n }),\n ),\n\n // The anchor.\n // In the case of hidden replies, the anchor item itself is undefined.\n ...(tree.item.value ? [tree.item] : []),\n\n // All replies below.\n ...Array.from(\n flattenInDirection({\n tree,\n direction: 'down',\n }),\n ),\n ]\n}\n\nfunction* flattenInDirection({\n tree,\n direction,\n}: {\n tree: ThreadTree\n direction: 'up' | 'down'\n}) {\n if (tree.type === 'noUnauthenticated') {\n if (direction === 'up') {\n if (tree.parent) {\n // Unfold all parents above.\n yield* flattenTree(tree.parent)\n }\n }\n }\n\n if (tree.type === 'post') {\n if (direction === 'up') {\n if (tree.parent) {\n // Unfold all parents above.\n yield* flattenTree(tree.parent)\n }\n } else {\n // Unfold all replies below.\n if (tree.replies?.length) {\n for (const reply of tree.replies) {\n yield* flattenTree(reply)\n }\n }\n }\n }\n\n // For the first level of hidden replies, the items are undefined.\n if (tree.type === 'hiddenAnchor' || tree.type === 'hiddenPost') {\n if (direction === 'down') {\n // Unfold all replies below.\n if (tree.replies?.length) {\n for (const reply of tree.replies) {\n yield* flattenTree(reply)\n }\n }\n }\n }\n}\n\nexport function sortTrimThreadTreeExploration(\n n: ThreadTree,\n opts: SortTrimFlattenOptions,\n): ThreadTree {\n if (!isNodeWithReplies(n)) {\n return n\n }\n const node: ThreadNodeWithReplies = n\n\n if (node.replies) {\n node.replies.sort((an: ThreadTree, bn: ThreadTree) => {\n if (!isPostNode(an)) {\n return 1\n }\n if (!isPostNode(bn)) {\n return -1\n }\n const aNode: ThreadMaybeOtherPostNode = an\n const bNode: ThreadMaybeOtherPostNode = bn\n\n // First applies bumping.\n const bump = applyBumpingExploration(aNode, bNode, opts)\n if (bump !== null) {\n return bump\n }\n\n // Then applies sorting.\n return applySortingExploration(aNode, bNode, opts)\n })\n\n // Trimming: after sorting, apply branching factor to all levels of replies except the anchor direct replies.\n if (node.item.depth !== 0) {\n node.replies = node.replies.slice(0, opts.branchingFactor)\n }\n\n node.replies.forEach((reply) => sortTrimThreadTreeExploration(reply, opts))\n }\n\n return node\n}\n\nfunction applyBumpingExploration(\n aNode: ThreadMaybeOtherPostNode,\n bNode: ThreadMaybeOtherPostNode,\n opts: SortTrimFlattenOptions,\n): number | null {\n if (!isPostNode(aNode)) {\n return null\n }\n if (!isPostNode(bNode)) {\n return null\n }\n\n const { opDid, viewer } = opts\n\n type BumpDirection = 'up' | 'down'\n type BumpPredicateFn = (i: ThreadMaybeOtherPostNode) => boolean\n\n const maybeBump = (\n bump: BumpDirection,\n predicateFn: BumpPredicateFn,\n ): number | null => {\n const aPredicate = predicateFn(aNode)\n const bPredicate = predicateFn(bNode)\n if (aPredicate && bPredicate) {\n return applySortingExploration(aNode, bNode, opts)\n } else if (aPredicate) {\n return bump === 'up' ? -1 : 1\n } else if (bPredicate) {\n return bump === 'up' ? 1 : -1\n }\n return null\n }\n\n // The order of the bumps determines the priority with which they are applied.\n // Bumps-up applied first make the item appear higher in the list than later bumps-up.\n // Bumps-down applied first make the item appear lower in the list than later bumps-down.\n const bumps: [BumpDirection, BumpPredicateFn][] = [\n /*\n General bumps.\n */\n // OP replies.\n ['up', (i) => i.item.value.post.author.did === opDid],\n // Viewer replies.\n ['up', (i) => i.item.value.post.author.did === viewer],\n ]\n\n for (const [bump, predicateFn] of bumps) {\n const bumpResult = maybeBump(bump, predicateFn)\n if (bumpResult !== null) {\n return bumpResult\n }\n }\n\n return null\n}\n\nfunction applySortingExploration(\n aNode: ThreadMaybeOtherPostNode,\n bNode: ThreadMaybeOtherPostNode,\n opts: SortTrimFlattenOptions,\n): number {\n const { visibilityTagRankPrefix: rp } = opts\n\n const a = aNode.item.value\n const ar = !rp ? 0 : parseRankFromTag(rp, findRankTag(aNode.tags, rp))\n const b = bNode.item.value\n const br = !rp ? 0 : parseRankFromTag(rp, findRankTag(bNode.tags, rp))\n\n // Only customize sort for visible posts.\n if (aNode.type === 'post' && bNode.type === 'post') {\n const { sort } = opts\n\n if (sort === 'oldest') {\n return a.post.indexedAt.localeCompare(b.post.indexedAt)\n }\n if (sort === 'top') {\n const aLikes = a.post.likeCount ?? 0\n const bLikes = b.post.likeCount ?? 0\n const aTop = topSortValue(aLikes, aNode.hasOPLike)\n const bTop = topSortValue(bLikes, bNode.hasOPLike)\n const aRank = aTop + ar\n const bRank = bTop + br\n if (aRank !== bRank) {\n return bRank - aRank\n }\n }\n }\n\n // Fallback to newest.\n return b.post.indexedAt.localeCompare(a.post.indexedAt)\n}\n\nfunction findRankTag(tags: Set<string>, prefix: string) {\n return Array.from(tags.values()).find((tag) => tag.startsWith(prefix))\n}\n\nfunction parseRankFromTag(prefix: string, tag?: string) {\n if (!tag) return 0\n\n try {\n const rank = parseInt(tag.slice(prefix.length), 10)\n if (typeof rank !== 'number' || isNaN(rank)) {\n return 0\n }\n return rank\n } catch (e) {\n return 0\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/bsky",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.196",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Reference implementation of app.bsky App View (Bluesky API)",
|
|
6
6
|
"keywords": [
|
|
@@ -53,14 +53,14 @@
|
|
|
53
53
|
"zod": "3.23.8",
|
|
54
54
|
"@atproto-labs/fetch-node": "0.2.0",
|
|
55
55
|
"@atproto-labs/xrpc-utils": "0.0.22",
|
|
56
|
-
"@atproto/api": "^0.
|
|
57
|
-
"@atproto/common": "^0.4.12",
|
|
58
|
-
"@atproto/crypto": "^0.4.4",
|
|
56
|
+
"@atproto/api": "^0.18.0",
|
|
59
57
|
"@atproto/did": "^0.2.1",
|
|
58
|
+
"@atproto/crypto": "^0.4.4",
|
|
59
|
+
"@atproto/common": "^0.4.12",
|
|
60
60
|
"@atproto/identity": "^0.4.9",
|
|
61
61
|
"@atproto/lexicon": "^0.5.1",
|
|
62
62
|
"@atproto/repo": "^0.8.10",
|
|
63
|
-
"@atproto/sync": "^0.1.
|
|
63
|
+
"@atproto/sync": "^0.1.36",
|
|
64
64
|
"@atproto/syntax": "^0.4.1",
|
|
65
65
|
"@atproto/xrpc-server": "^0.9.5"
|
|
66
66
|
},
|
|
@@ -77,9 +77,9 @@
|
|
|
77
77
|
"jest": "^28.1.2",
|
|
78
78
|
"ts-node": "^10.8.2",
|
|
79
79
|
"typescript": "^5.6.3",
|
|
80
|
-
"@atproto/api": "^0.
|
|
80
|
+
"@atproto/api": "^0.18.0",
|
|
81
81
|
"@atproto/lex-cli": "^0.9.6",
|
|
82
|
-
"@atproto/pds": "^0.4.
|
|
82
|
+
"@atproto/pds": "^0.4.193",
|
|
83
83
|
"@atproto/xrpc": "^0.7.5"
|
|
84
84
|
},
|
|
85
85
|
"scripts": {
|
package/proto/bsky.proto
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { AtpAgent } from '@atproto/api'
|
|
2
2
|
import { mapDefined } from '@atproto/common'
|
|
3
|
+
import { ServerConfig } from '../../../../config'
|
|
3
4
|
import { AppContext } from '../../../../context'
|
|
4
5
|
import { DataPlaneClient } from '../../../../data-plane'
|
|
6
|
+
import {
|
|
7
|
+
PostSearchQuery,
|
|
8
|
+
parsePostSearchQuery,
|
|
9
|
+
} from '../../../../data-plane/server/util'
|
|
10
|
+
import { FeatureGateID } from '../../../../feature-gates'
|
|
5
11
|
import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator'
|
|
6
12
|
import { parseString } from '../../../../hydration/util'
|
|
7
13
|
import { Server } from '../../../../lexicon'
|
|
@@ -21,16 +27,27 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
21
27
|
const searchPosts = createPipeline(
|
|
22
28
|
skeleton,
|
|
23
29
|
hydration,
|
|
24
|
-
|
|
30
|
+
noBlocksOrTagged,
|
|
25
31
|
presentation,
|
|
26
32
|
)
|
|
27
33
|
server.app.bsky.feed.searchPosts({
|
|
28
34
|
auth: ctx.authVerifier.standardOptional,
|
|
29
35
|
handler: async ({ auth, params, req }) => {
|
|
30
|
-
const viewer =
|
|
36
|
+
const { viewer, isModService } = ctx.authVerifier.parseCreds(auth)
|
|
37
|
+
|
|
31
38
|
const labelers = ctx.reqLabelers(req)
|
|
32
|
-
const hydrateCtx = await ctx.hydrator.createContext({
|
|
33
|
-
|
|
39
|
+
const hydrateCtx = await ctx.hydrator.createContext({
|
|
40
|
+
labelers,
|
|
41
|
+
viewer,
|
|
42
|
+
featureGates: ctx.featureGates.checkGates(
|
|
43
|
+
[ctx.featureGates.ids.SearchFilteringExploration],
|
|
44
|
+
ctx.featureGates.user({ did: viewer ?? '' }),
|
|
45
|
+
),
|
|
46
|
+
})
|
|
47
|
+
const results = await searchPosts(
|
|
48
|
+
{ ...params, hydrateCtx, isModService },
|
|
49
|
+
ctx,
|
|
50
|
+
)
|
|
34
51
|
return {
|
|
35
52
|
encoding: 'application/json',
|
|
36
53
|
body: results,
|
|
@@ -42,6 +59,9 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
42
59
|
|
|
43
60
|
const skeleton = async (inputs: SkeletonFnInput<Context, Params>) => {
|
|
44
61
|
const { ctx, params } = inputs
|
|
62
|
+
const parsedQuery = parsePostSearchQuery(params.q, {
|
|
63
|
+
author: params.author,
|
|
64
|
+
})
|
|
45
65
|
|
|
46
66
|
if (ctx.searchAgent) {
|
|
47
67
|
// @NOTE cursors won't change on appview swap
|
|
@@ -64,6 +84,7 @@ const skeleton = async (inputs: SkeletonFnInput<Context, Params>) => {
|
|
|
64
84
|
return {
|
|
65
85
|
posts: res.posts.map(({ uri }) => uri),
|
|
66
86
|
cursor: parseString(res.cursor),
|
|
87
|
+
parsedQuery,
|
|
67
88
|
}
|
|
68
89
|
}
|
|
69
90
|
|
|
@@ -75,6 +96,7 @@ const skeleton = async (inputs: SkeletonFnInput<Context, Params>) => {
|
|
|
75
96
|
return {
|
|
76
97
|
posts: res.uris,
|
|
77
98
|
cursor: parseString(res.cursor),
|
|
99
|
+
parsedQuery,
|
|
78
100
|
}
|
|
79
101
|
}
|
|
80
102
|
|
|
@@ -85,14 +107,51 @@ const hydration = async (
|
|
|
85
107
|
return ctx.hydrator.hydratePosts(
|
|
86
108
|
skeleton.posts.map((uri) => ({ uri })),
|
|
87
109
|
params.hydrateCtx,
|
|
110
|
+
undefined,
|
|
111
|
+
{
|
|
112
|
+
processDynamicTagsForView: params.hydrateCtx.featureGates.get(
|
|
113
|
+
FeatureGateID.SearchFilteringExploration,
|
|
114
|
+
)
|
|
115
|
+
? 'search'
|
|
116
|
+
: undefined,
|
|
117
|
+
},
|
|
88
118
|
)
|
|
89
119
|
}
|
|
90
120
|
|
|
91
|
-
const
|
|
92
|
-
const { ctx, skeleton, hydration } = inputs
|
|
121
|
+
const noBlocksOrTagged = (inputs: RulesFnInput<Context, Params, Skeleton>) => {
|
|
122
|
+
const { ctx, params, skeleton, hydration } = inputs
|
|
123
|
+
const { parsedQuery } = skeleton
|
|
124
|
+
|
|
93
125
|
skeleton.posts = skeleton.posts.filter((uri) => {
|
|
126
|
+
const post = hydration.posts?.get(uri)
|
|
127
|
+
if (!post) return
|
|
128
|
+
|
|
94
129
|
const creator = creatorFromUri(uri)
|
|
95
|
-
|
|
130
|
+
const isCuratedSearch = params.sort === 'top'
|
|
131
|
+
const isPostByViewer = creator === params.hydrateCtx.viewer
|
|
132
|
+
|
|
133
|
+
// Cases to always show.
|
|
134
|
+
if (isPostByViewer) return true
|
|
135
|
+
if (params.isModService) return true
|
|
136
|
+
|
|
137
|
+
// Cases to never show.
|
|
138
|
+
if (ctx.views.viewerBlockExists(creator, hydration)) return false
|
|
139
|
+
|
|
140
|
+
let tagged = false
|
|
141
|
+
if (
|
|
142
|
+
params.hydrateCtx.featureGates.get(
|
|
143
|
+
FeatureGateID.SearchFilteringExploration,
|
|
144
|
+
)
|
|
145
|
+
) {
|
|
146
|
+
tagged = post.tags.has(ctx.cfg.visibilityTagHide)
|
|
147
|
+
} else {
|
|
148
|
+
tagged = [...ctx.cfg.searchTagsHide].some((t) => post.tags.has(t))
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Cases to conditionally show based on tagging.
|
|
152
|
+
if (isCuratedSearch && tagged) return false
|
|
153
|
+
if (!parsedQuery.author && tagged) return false
|
|
154
|
+
return true
|
|
96
155
|
})
|
|
97
156
|
return skeleton
|
|
98
157
|
}
|
|
@@ -101,9 +160,12 @@ const presentation = (
|
|
|
101
160
|
inputs: PresentationFnInput<Context, Params, Skeleton>,
|
|
102
161
|
) => {
|
|
103
162
|
const { ctx, skeleton, hydration } = inputs
|
|
104
|
-
const posts = mapDefined(skeleton.posts, (uri) =>
|
|
105
|
-
|
|
106
|
-
|
|
163
|
+
const posts = mapDefined(skeleton.posts, (uri) => {
|
|
164
|
+
const post = hydration.posts?.get(uri)
|
|
165
|
+
if (!post) return
|
|
166
|
+
|
|
167
|
+
return ctx.views.post(uri, hydration)
|
|
168
|
+
})
|
|
107
169
|
return {
|
|
108
170
|
posts,
|
|
109
171
|
cursor: skeleton.cursor,
|
|
@@ -112,16 +174,21 @@ const presentation = (
|
|
|
112
174
|
}
|
|
113
175
|
|
|
114
176
|
type Context = {
|
|
177
|
+
cfg: ServerConfig
|
|
115
178
|
dataplane: DataPlaneClient
|
|
116
179
|
hydrator: Hydrator
|
|
117
180
|
views: Views
|
|
118
181
|
searchAgent?: AtpAgent
|
|
119
182
|
}
|
|
120
183
|
|
|
121
|
-
type Params = QueryParams & {
|
|
184
|
+
type Params = QueryParams & {
|
|
185
|
+
hydrateCtx: HydrateCtx
|
|
186
|
+
isModService: boolean
|
|
187
|
+
}
|
|
122
188
|
|
|
123
189
|
type Skeleton = {
|
|
124
190
|
posts: string[]
|
|
125
191
|
hitsTotal?: number
|
|
126
192
|
cursor?: string
|
|
193
|
+
parsedQuery: PostSearchQuery
|
|
127
194
|
}
|
|
@@ -93,11 +93,10 @@ const hydration = async (
|
|
|
93
93
|
const presentation = (
|
|
94
94
|
inputs: PresentationFnInput<Context, Params, Skeleton>,
|
|
95
95
|
) => {
|
|
96
|
-
const { ctx,
|
|
96
|
+
const { ctx, skeleton, hydration } = inputs
|
|
97
97
|
const thread = ctx.views.threadOtherV2(skeleton, hydration, {
|
|
98
98
|
below: BELOW,
|
|
99
99
|
branchingFactor: BRANCHING_FACTOR,
|
|
100
|
-
prioritizeFollowedUsers: params.prioritizeFollowedUsers,
|
|
101
100
|
})
|
|
102
101
|
return { thread }
|
|
103
102
|
}
|
|
@@ -33,6 +33,10 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
33
33
|
viewer,
|
|
34
34
|
includeTakedowns,
|
|
35
35
|
include3pBlocks,
|
|
36
|
+
featureGates: ctx.featureGates.checkGates(
|
|
37
|
+
[ctx.featureGates.ids.ThreadsV2ReplyRankingExploration],
|
|
38
|
+
ctx.featureGates.user({ did: viewer ?? '' }),
|
|
39
|
+
),
|
|
36
40
|
})
|
|
37
41
|
|
|
38
42
|
return {
|
|
@@ -89,7 +93,6 @@ const presentation = (
|
|
|
89
93
|
above: calculateAbove(ctx, params),
|
|
90
94
|
below: calculateBelow(ctx, skeleton.anchor, params),
|
|
91
95
|
branchingFactor: params.branchingFactor,
|
|
92
|
-
prioritizeFollowedUsers: params.prioritizeFollowedUsers,
|
|
93
96
|
sort: params.sort,
|
|
94
97
|
})
|
|
95
98
|
|
package/src/auth-verifier.ts
CHANGED
|
@@ -234,7 +234,15 @@ export class AuthVerifier {
|
|
|
234
234
|
)
|
|
235
235
|
})
|
|
236
236
|
|
|
237
|
-
const { sub, aud, scope } = res.payload
|
|
237
|
+
const { sub, aud, scope, cnf } = res.payload
|
|
238
|
+
if (typeof cnf !== 'undefined') {
|
|
239
|
+
// Proof-of-Possession (PoP) tokens are not allowed here
|
|
240
|
+
// https://www.rfc-editor.org/rfc/rfc7800.html
|
|
241
|
+
throw new AuthRequiredError(
|
|
242
|
+
'Malformed token: DPoP not supported',
|
|
243
|
+
'InvalidToken',
|
|
244
|
+
)
|
|
245
|
+
}
|
|
238
246
|
if (typeof sub !== 'string' || !sub.startsWith('did:')) {
|
|
239
247
|
throw new AuthRequiredError('Malformed token', 'InvalidToken')
|
|
240
248
|
} else if (
|
|
@@ -388,12 +396,17 @@ export class AuthVerifier {
|
|
|
388
396
|
const canPerformTakedown =
|
|
389
397
|
(creds.credentials.type === 'role' && creds.credentials.admin) ||
|
|
390
398
|
creds.credentials.type === 'mod_service'
|
|
399
|
+
const isModService =
|
|
400
|
+
creds.credentials.type === 'mod_service' ||
|
|
401
|
+
(creds.credentials.type === 'standard' &&
|
|
402
|
+
this.isModService(creds.credentials.iss))
|
|
391
403
|
|
|
392
404
|
return {
|
|
393
405
|
viewer,
|
|
394
406
|
includeTakedowns: includeTakedownsAnd3pBlocks,
|
|
395
407
|
include3pBlocks: includeTakedownsAnd3pBlocks,
|
|
396
408
|
canPerformTakedown,
|
|
409
|
+
isModService,
|
|
397
410
|
}
|
|
398
411
|
}
|
|
399
412
|
}
|