@recursiv/sdk 0.7.52 → 0.7.54
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/README.md +49 -3
- package/dist/client.d.ts +6 -2
- package/dist/client.js +7 -8
- package/dist/index.d.ts +12 -2
- package/dist/index.js +11 -1
- package/dist/resources/auth.d.ts +17 -3
- package/dist/resources/auth.js +7 -2
- package/dist/resources/bookmarks.d.ts +172 -0
- package/dist/resources/bookmarks.js +44 -0
- package/dist/resources/communities.d.ts +11 -1
- package/dist/resources/communities.js +14 -0
- package/dist/resources/curator.d.ts +1 -1
- package/dist/resources/discovery.d.ts +168 -3
- package/dist/resources/discovery.js +133 -21
- package/dist/resources/human-input.js +2 -1
- package/dist/resources/link-preview.d.ts +11 -4
- package/dist/resources/link-preview.js +4 -3
- package/dist/resources/notifications.d.ts +26 -0
- package/dist/resources/posts.d.ts +21 -3
- package/dist/resources/posts.js +21 -1
- package/dist/resources/profiles.d.ts +29 -16
- package/dist/resources/profiles.js +23 -6
- package/dist/resources/protocols.d.ts +60 -3
- package/dist/resources/search.d.ts +159 -0
- package/dist/resources/search.js +21 -0
- package/dist/types.d.ts +178 -2
- package/llm.md +36 -5
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HttpClient } from '../client.js';
|
|
2
|
-
import type { PaginatedResponse, SingleResponse, SuccessResponse, Profile, ProfileDetail, ProfileMe, LeaderboardEntry, UpdateProfileInput, SearchProfilesParams, SearchMentionsParams, MentionCandidate, FollowListProfile, BlockedProfile, MutedProfile, ModerationListParams,
|
|
2
|
+
import type { PaginatedResponse, SingleResponse, SuccessResponse, Profile, ProfileDetail, ProfileMe, LeaderboardEntry, UpdateProfileInput, SearchProfilesParams, ProfileSearchResult, SearchMentionsParams, MentionCandidate, FollowListParams, FollowListProfile, BlockedProfile, MutedProfile, ModerationListParams, ProfileRelationship } from '../types.js';
|
|
3
3
|
export declare class ProfilesResource {
|
|
4
4
|
private client;
|
|
5
5
|
constructor(client: HttpClient);
|
|
@@ -15,14 +15,25 @@ export declare class ProfilesResource {
|
|
|
15
15
|
/** Get a user by username */
|
|
16
16
|
getByUsername(username: string): Promise<SingleResponse<ProfileDetail>>;
|
|
17
17
|
/** Search users by name or username */
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Search accounts by name or handle, ranked by match x ln(1 + followers + min(posts, 100)) (doubled for an
|
|
20
|
+
* official account): empties last and marked `empty`, the official ones `verified`. Every page is
|
|
21
|
+
* a slice of one ranked pool of 200; past it `has_more` is false.
|
|
22
|
+
*/
|
|
23
|
+
search(params: SearchProfilesParams): Promise<PaginatedResponse<ProfileSearchResult>>;
|
|
19
24
|
/**
|
|
20
25
|
* Accounts to @mention: people and agents on the caller's network, the
|
|
21
26
|
* caller's follows first. Unlike `search`, this includes agents the app may
|
|
22
27
|
* discover (an agent answers when mentioned); another member's personal AI
|
|
23
28
|
* is never offered.
|
|
29
|
+
*
|
|
30
|
+
* A search that runs over its 2 s budget answers 503 with Retry-After. A
|
|
31
|
+
* type-ahead has its own deadline, so pass `{ retry: false }` to get that
|
|
32
|
+
* answer at once instead of waiting through retries.
|
|
24
33
|
*/
|
|
25
|
-
mentions(params: SearchMentionsParams
|
|
34
|
+
mentions(params: SearchMentionsParams, options?: {
|
|
35
|
+
retry?: boolean;
|
|
36
|
+
}): Promise<PaginatedResponse<MentionCandidate>>;
|
|
26
37
|
/** Update own profile */
|
|
27
38
|
update(input: UpdateProfileInput): Promise<SingleResponse<Profile>>;
|
|
28
39
|
/** Follow a user */
|
|
@@ -30,18 +41,16 @@ export declare class ProfilesResource {
|
|
|
30
41
|
/** Unfollow a user */
|
|
31
42
|
unfollow(userId: string): Promise<SuccessResponse>;
|
|
32
43
|
/**
|
|
33
|
-
* List a user's followers
|
|
34
|
-
*
|
|
44
|
+
* List a user's followers, newest follow first once the server's database
|
|
45
|
+
* has the follow-list order indexes (until then, in the order it reads
|
|
46
|
+
* them). Each row carries the caller's relationship with it (`is_following`,
|
|
47
|
+
* `follows_you`), so a Follow button needs no second read. `search` narrows
|
|
48
|
+
* the whole list to accounts whose name or handle contains it (at most 200
|
|
49
|
+
* characters); page the matches with `limit` and `offset`.
|
|
35
50
|
*/
|
|
36
|
-
followers(userId: string, params?:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}): Promise<PaginatedResponse<FollowListProfile>>;
|
|
40
|
-
/** List who a user follows, each row with the caller's relationship with it. */
|
|
41
|
-
following(userId: string, params?: {
|
|
42
|
-
limit?: number;
|
|
43
|
-
offset?: number;
|
|
44
|
-
}): Promise<PaginatedResponse<FollowListProfile>>;
|
|
51
|
+
followers(userId: string, params?: FollowListParams): Promise<PaginatedResponse<FollowListProfile>>;
|
|
52
|
+
/** List who a user follows, in the order `followers` uses, each row with the caller's relationship with it; `search` as on `followers`. */
|
|
53
|
+
following(userId: string, params?: FollowListParams): Promise<PaginatedResponse<FollowListProfile>>;
|
|
45
54
|
/**
|
|
46
55
|
* The caller's whole follow set as ids, up to 5000 per page (default 1000).
|
|
47
56
|
* Answers what `isFollowing` answers, for every account at once. Pass
|
|
@@ -51,8 +60,12 @@ export declare class ProfilesResource {
|
|
|
51
60
|
cursor?: string;
|
|
52
61
|
limit?: number;
|
|
53
62
|
}): Promise<PaginatedResponse<string>>;
|
|
54
|
-
/**
|
|
55
|
-
|
|
63
|
+
/**
|
|
64
|
+
* The caller's relationship with another user: follows in both directions
|
|
65
|
+
* (mutual when both are true), and whether the caller blocks (`is_blocking`)
|
|
66
|
+
* or mutes (`is_muting`) them, left out when the server cannot say.
|
|
67
|
+
*/
|
|
68
|
+
isFollowing(userId: string): Promise<SingleResponse<ProfileRelationship>>;
|
|
56
69
|
/**
|
|
57
70
|
* Block a user. A block is a two-way wall: it severs follows in both
|
|
58
71
|
* directions and hides each side's posts from the other.
|
|
@@ -20,6 +20,11 @@ export class ProfilesResource {
|
|
|
20
20
|
return this.client.get(`/profiles/by-username/${username}`);
|
|
21
21
|
}
|
|
22
22
|
/** Search users by name or username */
|
|
23
|
+
/**
|
|
24
|
+
* Search accounts by name or handle, ranked by match x ln(1 + followers + min(posts, 100)) (doubled for an
|
|
25
|
+
* official account): empties last and marked `empty`, the official ones `verified`. Every page is
|
|
26
|
+
* a slice of one ranked pool of 200; past it `has_more` is false.
|
|
27
|
+
*/
|
|
23
28
|
search(params) {
|
|
24
29
|
return this.client.get('/profiles/search', params);
|
|
25
30
|
}
|
|
@@ -28,9 +33,13 @@ export class ProfilesResource {
|
|
|
28
33
|
* caller's follows first. Unlike `search`, this includes agents the app may
|
|
29
34
|
* discover (an agent answers when mentioned); another member's personal AI
|
|
30
35
|
* is never offered.
|
|
36
|
+
*
|
|
37
|
+
* A search that runs over its 2 s budget answers 503 with Retry-After. A
|
|
38
|
+
* type-ahead has its own deadline, so pass `{ retry: false }` to get that
|
|
39
|
+
* answer at once instead of waiting through retries.
|
|
31
40
|
*/
|
|
32
|
-
mentions(params) {
|
|
33
|
-
return this.client.get('/profiles/mentions', params);
|
|
41
|
+
mentions(params, options) {
|
|
42
|
+
return this.client.get('/profiles/mentions', params, options);
|
|
34
43
|
}
|
|
35
44
|
/** Update own profile */
|
|
36
45
|
update(input) {
|
|
@@ -45,13 +54,17 @@ export class ProfilesResource {
|
|
|
45
54
|
return this.client.delete(`/profiles/${userId}/follow`);
|
|
46
55
|
}
|
|
47
56
|
/**
|
|
48
|
-
* List a user's followers
|
|
49
|
-
*
|
|
57
|
+
* List a user's followers, newest follow first once the server's database
|
|
58
|
+
* has the follow-list order indexes (until then, in the order it reads
|
|
59
|
+
* them). Each row carries the caller's relationship with it (`is_following`,
|
|
60
|
+
* `follows_you`), so a Follow button needs no second read. `search` narrows
|
|
61
|
+
* the whole list to accounts whose name or handle contains it (at most 200
|
|
62
|
+
* characters); page the matches with `limit` and `offset`.
|
|
50
63
|
*/
|
|
51
64
|
followers(userId, params) {
|
|
52
65
|
return this.client.get(`/profiles/${userId}/followers`, params);
|
|
53
66
|
}
|
|
54
|
-
/** List who a user follows, each row with the caller's relationship with it
|
|
67
|
+
/** List who a user follows, in the order `followers` uses, each row with the caller's relationship with it; `search` as on `followers`. */
|
|
55
68
|
following(userId, params) {
|
|
56
69
|
return this.client.get(`/profiles/${userId}/following`, params);
|
|
57
70
|
}
|
|
@@ -63,7 +76,11 @@ export class ProfilesResource {
|
|
|
63
76
|
followingIds(params) {
|
|
64
77
|
return this.client.get('/profiles/me/following/ids', params);
|
|
65
78
|
}
|
|
66
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* The caller's relationship with another user: follows in both directions
|
|
81
|
+
* (mutual when both are true), and whether the caller blocks (`is_blocking`)
|
|
82
|
+
* or mutes (`is_muting`) them, left out when the server cannot say.
|
|
83
|
+
*/
|
|
67
84
|
isFollowing(userId) {
|
|
68
85
|
return this.client.get(`/profiles/${userId}/is-following`);
|
|
69
86
|
}
|
|
@@ -305,6 +305,8 @@ export interface SocialSourceControlDisplay {
|
|
|
305
305
|
avatarUrl: string | null;
|
|
306
306
|
/** A hidden post's text, at most 140 characters; null for accounts, deleted posts, and moderated posts or authors. */
|
|
307
307
|
excerpt: string | null;
|
|
308
|
+
/** A hidden ActivityPub post's public reader id, for the conversation read; absent for accounts, other networks and posts observed without one. */
|
|
309
|
+
readRef?: string;
|
|
308
310
|
observedAt: string;
|
|
309
311
|
}
|
|
310
312
|
export interface SocialSourceControl {
|
|
@@ -641,6 +643,8 @@ export interface NativeReplyBatch {
|
|
|
641
643
|
authorHandle: string;
|
|
642
644
|
text: string;
|
|
643
645
|
textTruncated?: boolean;
|
|
646
|
+
/** An ActivityPub parent's public reader id, when the reply was prepared with one: pass it to the conversation read. */
|
|
647
|
+
readRef?: string;
|
|
644
648
|
};
|
|
645
649
|
text: string;
|
|
646
650
|
wireText: string;
|
|
@@ -742,7 +746,7 @@ export interface NativeDelegatedReplyHistoryItem {
|
|
|
742
746
|
createdAt: string;
|
|
743
747
|
text: string;
|
|
744
748
|
textTruncated: boolean;
|
|
745
|
-
source: Pick<NativeReplyBatch['deliveries'][number]['parent'], 'protocol' | 'nativeId' | 'sourceUrl'>;
|
|
749
|
+
source: Pick<NativeReplyBatch['deliveries'][number]['parent'], 'protocol' | 'nativeId' | 'sourceUrl' | 'readRef'>;
|
|
746
750
|
deliveries: Array<Pick<NativeReplyBatch['deliveries'][number], 'id' | 'protocol' | 'kind' | 'state' | 'attemptedAt' | 'account' | 'recoverable' | 'replacedBy'> & {
|
|
747
751
|
removalReceipt: NativeReplyRemovalReceipt | null;
|
|
748
752
|
}>;
|
|
@@ -751,10 +755,12 @@ export interface NativeNostrReplyResult {
|
|
|
751
755
|
id: string;
|
|
752
756
|
createdAt: string;
|
|
753
757
|
text: string;
|
|
758
|
+
/** `readRef`: a linked ActivityPub source's public reader id, for the conversation read (replies saved before it existed have none). */
|
|
754
759
|
source: {
|
|
755
760
|
protocol: SocialSourceRef['protocol'];
|
|
756
761
|
nativeId: string;
|
|
757
762
|
sourceUrl: string;
|
|
763
|
+
readRef?: string;
|
|
758
764
|
};
|
|
759
765
|
identityId: string;
|
|
760
766
|
pubkey: string;
|
|
@@ -922,6 +928,8 @@ export interface SocialProfile {
|
|
|
922
928
|
followersCount: number | null;
|
|
923
929
|
followingCount: number | null;
|
|
924
930
|
postsCount: number | null;
|
|
931
|
+
/** Nostr only: the NIP-05 name its domain has confirmed for this key ("lyn@primal.net"); `handle` stays the key. */
|
|
932
|
+
nip05?: string;
|
|
925
933
|
subscription: SocialSubscription | null;
|
|
926
934
|
}
|
|
927
935
|
export interface SocialSubscription {
|
|
@@ -962,6 +970,12 @@ export interface SocialPost {
|
|
|
962
970
|
replyCount: number | null;
|
|
963
971
|
likeCount: number | null;
|
|
964
972
|
repostCount: number | null;
|
|
973
|
+
/**
|
|
974
|
+
* When the counts were read from the source network. Present only on a post a category feed served
|
|
975
|
+
* from the open-network index, whose copy can be older than the page (the server refreshes counts
|
|
976
|
+
* older than 15 minutes when the provider answers in time). Absent: the counts are from this read.
|
|
977
|
+
*/
|
|
978
|
+
countsObservedAt?: string;
|
|
965
979
|
/** Pixel dimensions when the source declares them (Bluesky aspect ratio, Mastodon attachment meta), so a card can take the clip's shape before it loads. */
|
|
966
980
|
media: Array<{
|
|
967
981
|
type: 'image' | 'video';
|
|
@@ -1061,6 +1075,20 @@ export interface SocialTrendFeed extends SocialTrendProvenance {
|
|
|
1061
1075
|
trend: SocialTrend;
|
|
1062
1076
|
items: SocialPost[];
|
|
1063
1077
|
cursor: string | null;
|
|
1078
|
+
/**
|
|
1079
|
+
* Present when the page came from the app's open-network source index instead of the provider:
|
|
1080
|
+
* with a category, a topic the provider no longer lists (`gone`) or whose feed held nothing in the
|
|
1081
|
+
* category (`empty`) is served the network's indexed posts in the category. While the provider has
|
|
1082
|
+
* more of the topic, `cursor` is the provider's own continuation (the topic's deeper pages may hold
|
|
1083
|
+
* the category); once it has none, or the topic is gone, it is an `idx:tf:` cursor that continues in
|
|
1084
|
+
* the index (`cursor`), the same stream for every topic of the network. The `trend` of a gone topic
|
|
1085
|
+
* carries its id as its title. Pass the cursor back unchanged like any other. When the index cannot
|
|
1086
|
+
* answer, the page is what the provider answered (the empty page with its cursor, or the gone topic's
|
|
1087
|
+
* 404); only an `idx:tf:` page is 503 `unavailable`, which ends that continuation, not the network.
|
|
1088
|
+
*/
|
|
1089
|
+
sourceIndex?: SocialTrendSourceIndex & {
|
|
1090
|
+
reason: 'gone' | 'empty' | 'cursor';
|
|
1091
|
+
};
|
|
1064
1092
|
}
|
|
1065
1093
|
/** A member of a radar cluster: a headline or excerpt and a link only, never a body. */
|
|
1066
1094
|
export type SocialRadarSourceKind = 'outlet' | 'hackernews' | 'bluesky' | 'activitypub' | 'nostr' | 'minds';
|
|
@@ -1165,17 +1193,46 @@ export interface SocialRadarPosts {
|
|
|
1165
1193
|
builtAt: string | null;
|
|
1166
1194
|
items: SocialPost[];
|
|
1167
1195
|
}
|
|
1196
|
+
/** A story that has left the radar, as the radar's story archive keeps it (up to 30 days after it was last on the radar). */
|
|
1197
|
+
export interface SocialRadarStoryArchive {
|
|
1198
|
+
/** The story as the radar last published it. Articles keep their headline and link; a post is a member by reference only (no `title`). */
|
|
1199
|
+
cluster: Pick<SocialRadarCluster, 'id' | 'label' | 'kind' | 'seed' | 'theme' | 'sources' | 'laneMix'> & {
|
|
1200
|
+
members: Array<Pick<SocialRadarMember, 'kind' | 'key' | 'url' | 'publishedAt' | 'outlet'> & {
|
|
1201
|
+
title?: string;
|
|
1202
|
+
}>;
|
|
1203
|
+
};
|
|
1204
|
+
/** Its open-network posts the app's source index still holds and its source controls and safety floor admit today, in member order. */
|
|
1205
|
+
items: SocialPost[];
|
|
1206
|
+
/** A World Today story's dek: an outlet's own sentence, or the editor model's. */
|
|
1207
|
+
summary: {
|
|
1208
|
+
text: string;
|
|
1209
|
+
origin: 'outlet' | 'editor';
|
|
1210
|
+
} | null;
|
|
1211
|
+
/** The first and the last radar build that published it. */
|
|
1212
|
+
firstSeenAt: string;
|
|
1213
|
+
lastSeenAt: string;
|
|
1214
|
+
/** The index holds none of its open-network posts any more (retention or deletion), so none are listed. */
|
|
1215
|
+
postsExpired: boolean;
|
|
1216
|
+
}
|
|
1168
1217
|
/**
|
|
1169
1218
|
* One radar story by id, with its social member posts.
|
|
1170
|
-
* `live`: on the current radar (`cluster` and `items` present). `aged_out`: on
|
|
1171
|
-
*
|
|
1219
|
+
* `live`: on the current radar (`cluster` and `items` present). `aged_out`: not on this radar, but
|
|
1220
|
+
* held by the story archive (`archive`, and the live story it continues as in `successor`) or seen on
|
|
1221
|
+
* one of the last six radars (`archive` null). `unknown`: in neither (never existed, or left longer ago
|
|
1222
|
+
* than the archive keeps stories). A server without the story archive leaves `archive` and `successor` out.
|
|
1172
1223
|
*/
|
|
1173
1224
|
export interface SocialRadarStory {
|
|
1174
1225
|
id: string;
|
|
1175
1226
|
builtAt: string | null;
|
|
1176
1227
|
status: 'live' | 'aged_out' | 'unknown';
|
|
1177
1228
|
cluster: SocialRadarCluster | null;
|
|
1229
|
+
/** The live story's posts; empty otherwise (an archived story's are in `archive.items`). */
|
|
1178
1230
|
items: SocialPost[];
|
|
1231
|
+
archive?: SocialRadarStoryArchive | null;
|
|
1232
|
+
successor?: {
|
|
1233
|
+
id: string;
|
|
1234
|
+
label: string;
|
|
1235
|
+
} | null;
|
|
1179
1236
|
}
|
|
1180
1237
|
/** Read options for callers that hold their own deadline, such as a discovery page. */
|
|
1181
1238
|
export interface SocialReadOptions {
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { HttpClient } from '../client.js';
|
|
2
|
+
import type { Post } from '../types.js';
|
|
3
|
+
import type { SocialPost } from './protocols.js';
|
|
4
|
+
/** The sections a blended search can answer, in no particular order (see `SearchAnswer.order`). */
|
|
5
|
+
export declare const SEARCH_SECTIONS: readonly ["top_posts", "latest_posts", "open_network", "people", "groups", "topics", "stories", "related", "trending"];
|
|
6
|
+
export type SearchSection = (typeof SEARCH_SECTIONS)[number];
|
|
7
|
+
/** Why a section is absent: out of time, the server was busy, not available for this app, not allowed for this key, or it failed. */
|
|
8
|
+
export type SearchOmitReason = 'timeout' | 'saturated' | 'unavailable' | 'scope' | 'error';
|
|
9
|
+
export interface SearchParams {
|
|
10
|
+
q: string;
|
|
11
|
+
/** Items per section, 1-20 (default 6). */
|
|
12
|
+
limit?: number;
|
|
13
|
+
/** Only these sections (default: all). */
|
|
14
|
+
sections?: readonly SearchSection[];
|
|
15
|
+
/** For organization-bound keys, as on `posts.search`. */
|
|
16
|
+
organization_id?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SearchPerson {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string | null;
|
|
21
|
+
username: string | null;
|
|
22
|
+
image: string | null;
|
|
23
|
+
is_ai: boolean;
|
|
24
|
+
/** The query is this account's handle. */
|
|
25
|
+
exact: boolean;
|
|
26
|
+
/** The account's public life, what the ranking read: match x ln(1 + followers + min(posts, 100)). */
|
|
27
|
+
followers_count: number;
|
|
28
|
+
posts_count: number;
|
|
29
|
+
/** Nobody follows it and it never posted: listed after every account with a public life, so a client can fold these under "more handles". */
|
|
30
|
+
empty: boolean;
|
|
31
|
+
/** An account the operator vouches for: draw the verified badge. */
|
|
32
|
+
verified: boolean;
|
|
33
|
+
/** Membership badges, as every embedded author carries them. */
|
|
34
|
+
plus: boolean;
|
|
35
|
+
pro: boolean;
|
|
36
|
+
founder: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface SearchGroup {
|
|
39
|
+
id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
slug: string;
|
|
42
|
+
image: string | null;
|
|
43
|
+
member_count: number;
|
|
44
|
+
post_count: number;
|
|
45
|
+
is_member: boolean;
|
|
46
|
+
}
|
|
47
|
+
export type SearchTopic = {
|
|
48
|
+
kind: 'tag';
|
|
49
|
+
id: string;
|
|
50
|
+
name: string;
|
|
51
|
+
slug: string;
|
|
52
|
+
} | {
|
|
53
|
+
kind: 'category';
|
|
54
|
+
id: string;
|
|
55
|
+
name: string;
|
|
56
|
+
};
|
|
57
|
+
export interface SearchStory {
|
|
58
|
+
/** Radar cluster id: open it with `protocols` radar story reads. */
|
|
59
|
+
id: string;
|
|
60
|
+
label: string;
|
|
61
|
+
kind: 'breaking' | 'viral' | 'story';
|
|
62
|
+
theme: string | null;
|
|
63
|
+
newest_at: string;
|
|
64
|
+
sources: Partial<Record<'outlet' | 'hackernews' | 'bluesky' | 'activitypub' | 'nostr' | 'minds', number>>;
|
|
65
|
+
lead: {
|
|
66
|
+
type: 'image' | 'video';
|
|
67
|
+
url: string;
|
|
68
|
+
poster?: string;
|
|
69
|
+
width?: number;
|
|
70
|
+
height?: number;
|
|
71
|
+
} | null;
|
|
72
|
+
/** Where the words were found: the story's headline, or only one of its members'. */
|
|
73
|
+
matched: 'label' | 'member';
|
|
74
|
+
}
|
|
75
|
+
export interface SearchRelated {
|
|
76
|
+
categories: Array<{
|
|
77
|
+
id: string;
|
|
78
|
+
name: string;
|
|
79
|
+
count: number;
|
|
80
|
+
}>;
|
|
81
|
+
tags: Array<{
|
|
82
|
+
id: string;
|
|
83
|
+
name: string;
|
|
84
|
+
slug: string;
|
|
85
|
+
count: number;
|
|
86
|
+
}>;
|
|
87
|
+
}
|
|
88
|
+
/** One blended search answer. Only answered sections are present; an answered section may be empty. */
|
|
89
|
+
export interface SearchAnswer {
|
|
90
|
+
query: string;
|
|
91
|
+
sections: {
|
|
92
|
+
/** Relevance x recency x engagement, through the promotion safety floor. `has_more`: page further with `posts.search`. */
|
|
93
|
+
top_posts?: {
|
|
94
|
+
items: Post[];
|
|
95
|
+
has_more: boolean;
|
|
96
|
+
};
|
|
97
|
+
/** Newest first, never repeating a top post. `partial`: these are the newest of what was read in time. */
|
|
98
|
+
latest_posts?: {
|
|
99
|
+
items: Post[];
|
|
100
|
+
partial: boolean;
|
|
101
|
+
};
|
|
102
|
+
open_network?: {
|
|
103
|
+
items: SocialPost[];
|
|
104
|
+
withheld: number;
|
|
105
|
+
};
|
|
106
|
+
people?: {
|
|
107
|
+
items: SearchPerson[];
|
|
108
|
+
};
|
|
109
|
+
groups?: {
|
|
110
|
+
items: SearchGroup[];
|
|
111
|
+
};
|
|
112
|
+
topics?: {
|
|
113
|
+
items: SearchTopic[];
|
|
114
|
+
};
|
|
115
|
+
stories?: {
|
|
116
|
+
items: SearchStory[];
|
|
117
|
+
};
|
|
118
|
+
/** Categories and topics around the matches. */
|
|
119
|
+
related?: SearchRelated;
|
|
120
|
+
/** Present when the answer is thin or old: what is moving in the nearest category. */
|
|
121
|
+
trending?: {
|
|
122
|
+
category: {
|
|
123
|
+
id: string;
|
|
124
|
+
name: string;
|
|
125
|
+
} | null;
|
|
126
|
+
items: Post[];
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
/** Answered, non-empty sections in the order to render them. */
|
|
130
|
+
order: SearchSection[];
|
|
131
|
+
/** Sections the server left out, and why; the rest of the answer stands. */
|
|
132
|
+
omitted: Array<{
|
|
133
|
+
section: SearchSection;
|
|
134
|
+
reason: SearchOmitReason;
|
|
135
|
+
}>;
|
|
136
|
+
/** Fewer than five matches across Minds and the open networks. */
|
|
137
|
+
few_results: boolean;
|
|
138
|
+
}
|
|
139
|
+
export interface SearchResponse {
|
|
140
|
+
data: SearchAnswer;
|
|
141
|
+
meta: {
|
|
142
|
+
limit: number;
|
|
143
|
+
elapsed_ms: number | null;
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
export declare class SearchResource {
|
|
147
|
+
private client;
|
|
148
|
+
constructor(client: HttpClient);
|
|
149
|
+
/**
|
|
150
|
+
* Blended search (GET /search): the best and newest posts, open-network posts, people, groups, topics,
|
|
151
|
+
* news stories, and, so a search never dead-ends, related categories and trending posts. Each section
|
|
152
|
+
* has its own time budget; one that runs out is listed in `omitted` instead of failing the answer.
|
|
153
|
+
* Needs `posts:read`. People need `users:read`, groups `communities:read`; open network and stories need
|
|
154
|
+
* `users:read` and the caller's own app key bound to its network, trending that app key. A section the
|
|
155
|
+
* key cannot have is listed in `omitted` with reason `scope` or `unavailable`.
|
|
156
|
+
* Servers before this endpoint answer 404: fall back to `posts.search`.
|
|
157
|
+
*/
|
|
158
|
+
query(params: SearchParams): Promise<SearchResponse>;
|
|
159
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** The sections a blended search can answer, in no particular order (see `SearchAnswer.order`). */
|
|
2
|
+
export const SEARCH_SECTIONS = ['top_posts', 'latest_posts', 'open_network', 'people', 'groups', 'topics', 'stories', 'related', 'trending'];
|
|
3
|
+
export class SearchResource {
|
|
4
|
+
client;
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Blended search (GET /search): the best and newest posts, open-network posts, people, groups, topics,
|
|
10
|
+
* news stories, and, so a search never dead-ends, related categories and trending posts. Each section
|
|
11
|
+
* has its own time budget; one that runs out is listed in `omitted` instead of failing the answer.
|
|
12
|
+
* Needs `posts:read`. People need `users:read`, groups `communities:read`; open network and stories need
|
|
13
|
+
* `users:read` and the caller's own app key bound to its network, trending that app key. A section the
|
|
14
|
+
* key cannot have is listed in `omitted` with reason `scope` or `unavailable`.
|
|
15
|
+
* Servers before this endpoint answer 404: fall back to `posts.search`.
|
|
16
|
+
*/
|
|
17
|
+
query(params) {
|
|
18
|
+
const { sections, ...rest } = params;
|
|
19
|
+
return this.client.get('/search', { ...rest, ...(sections && sections.length > 0 ? { sections: sections.join(',') } : {}) });
|
|
20
|
+
}
|
|
21
|
+
}
|