@recursiv/sdk 0.7.51 → 0.7.53
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 +43 -5
- package/dist/client.d.ts +6 -2
- package/dist/client.js +10 -9
- package/dist/index.d.ts +15 -4
- package/dist/index.js +11 -1
- package/dist/resources/admin.d.ts +13 -0
- package/dist/resources/agents.d.ts +13 -2
- package/dist/resources/agents.js +7 -1
- package/dist/resources/app-subscriptions.d.ts +48 -2
- package/dist/resources/app-subscriptions.js +18 -2
- package/dist/resources/bookmarks.d.ts +172 -0
- package/dist/resources/bookmarks.js +44 -0
- package/dist/resources/boosts.d.ts +23 -1
- package/dist/resources/boosts.js +6 -1
- package/dist/resources/communities.d.ts +12 -2
- package/dist/resources/communities.js +15 -1
- package/dist/resources/curator.d.ts +24 -6
- package/dist/resources/curator.js +2 -1
- package/dist/resources/discovery.d.ts +104 -2
- package/dist/resources/discovery.js +111 -20
- package/dist/resources/invite-codes-admin.d.ts +13 -0
- package/dist/resources/invite-codes.d.ts +10 -0
- package/dist/resources/invite-codes.js +6 -6
- 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 +45 -6
- package/dist/resources/posts.js +29 -4
- package/dist/resources/profiles.d.ts +23 -14
- package/dist/resources/profiles.js +18 -5
- package/dist/resources/protocols.d.ts +54 -7
- package/dist/resources/protocols.js +10 -4
- package/dist/resources/search.d.ts +159 -0
- package/dist/resources/search.js +21 -0
- package/dist/types.d.ts +146 -5
- package/llm.md +41 -8
- package/package.json +2 -2
|
@@ -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;
|
|
@@ -962,6 +968,12 @@ export interface SocialPost {
|
|
|
962
968
|
replyCount: number | null;
|
|
963
969
|
likeCount: number | null;
|
|
964
970
|
repostCount: number | null;
|
|
971
|
+
/**
|
|
972
|
+
* When the counts were read from the source network. Present only on a post a category feed served
|
|
973
|
+
* from the open-network index, whose copy can be older than the page (the server refreshes counts
|
|
974
|
+
* older than 15 minutes when the provider answers in time). Absent: the counts are from this read.
|
|
975
|
+
*/
|
|
976
|
+
countsObservedAt?: string;
|
|
965
977
|
/** 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
978
|
media: Array<{
|
|
967
979
|
type: 'image' | 'video';
|
|
@@ -1165,17 +1177,46 @@ export interface SocialRadarPosts {
|
|
|
1165
1177
|
builtAt: string | null;
|
|
1166
1178
|
items: SocialPost[];
|
|
1167
1179
|
}
|
|
1180
|
+
/** 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). */
|
|
1181
|
+
export interface SocialRadarStoryArchive {
|
|
1182
|
+
/** The story as the radar last published it. Articles keep their headline and link; a post is a member by reference only (no `title`). */
|
|
1183
|
+
cluster: Pick<SocialRadarCluster, 'id' | 'label' | 'kind' | 'seed' | 'theme' | 'sources' | 'laneMix'> & {
|
|
1184
|
+
members: Array<Pick<SocialRadarMember, 'kind' | 'key' | 'url' | 'publishedAt' | 'outlet'> & {
|
|
1185
|
+
title?: string;
|
|
1186
|
+
}>;
|
|
1187
|
+
};
|
|
1188
|
+
/** Its open-network posts the app's source index still holds and its source controls and safety floor admit today, in member order. */
|
|
1189
|
+
items: SocialPost[];
|
|
1190
|
+
/** A World Today story's dek: an outlet's own sentence, or the editor model's. */
|
|
1191
|
+
summary: {
|
|
1192
|
+
text: string;
|
|
1193
|
+
origin: 'outlet' | 'editor';
|
|
1194
|
+
} | null;
|
|
1195
|
+
/** The first and the last radar build that published it. */
|
|
1196
|
+
firstSeenAt: string;
|
|
1197
|
+
lastSeenAt: string;
|
|
1198
|
+
/** The index holds none of its open-network posts any more (retention or deletion), so none are listed. */
|
|
1199
|
+
postsExpired: boolean;
|
|
1200
|
+
}
|
|
1168
1201
|
/**
|
|
1169
1202
|
* One radar story by id, with its social member posts.
|
|
1170
|
-
* `live`: on the current radar (`cluster` and `items` present). `aged_out`: on
|
|
1171
|
-
*
|
|
1203
|
+
* `live`: on the current radar (`cluster` and `items` present). `aged_out`: not on this radar, but
|
|
1204
|
+
* held by the story archive (`archive`, and the live story it continues as in `successor`) or seen on
|
|
1205
|
+
* one of the last six radars (`archive` null). `unknown`: in neither (never existed, or left longer ago
|
|
1206
|
+
* than the archive keeps stories). A server without the story archive leaves `archive` and `successor` out.
|
|
1172
1207
|
*/
|
|
1173
1208
|
export interface SocialRadarStory {
|
|
1174
1209
|
id: string;
|
|
1175
1210
|
builtAt: string | null;
|
|
1176
1211
|
status: 'live' | 'aged_out' | 'unknown';
|
|
1177
1212
|
cluster: SocialRadarCluster | null;
|
|
1213
|
+
/** The live story's posts; empty otherwise (an archived story's are in `archive.items`). */
|
|
1178
1214
|
items: SocialPost[];
|
|
1215
|
+
archive?: SocialRadarStoryArchive | null;
|
|
1216
|
+
successor?: {
|
|
1217
|
+
id: string;
|
|
1218
|
+
label: string;
|
|
1219
|
+
} | null;
|
|
1179
1220
|
}
|
|
1180
1221
|
/** Read options for callers that hold their own deadline, such as a discovery page. */
|
|
1181
1222
|
export interface SocialReadOptions {
|
|
@@ -1255,7 +1296,10 @@ export declare class ProtocolsResource {
|
|
|
1255
1296
|
limit?: number;
|
|
1256
1297
|
cursor?: string;
|
|
1257
1298
|
}): Promise<SingleResponse<SocialAuthorFeed>>;
|
|
1258
|
-
/**
|
|
1299
|
+
/**
|
|
1300
|
+
* Public, app-moderated discovery without account authority. Enrichment uses cached judgments only.
|
|
1301
|
+
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
|
|
1302
|
+
*/
|
|
1259
1303
|
getSharedTrends(projectId: string, params: {
|
|
1260
1304
|
protocol: string;
|
|
1261
1305
|
limit?: number;
|
|
@@ -1264,7 +1308,7 @@ export declare class ProtocolsResource {
|
|
|
1264
1308
|
category?: string;
|
|
1265
1309
|
discovery?: boolean;
|
|
1266
1310
|
}): Promise<SingleResponse<SocialTrends>>;
|
|
1267
|
-
/** Open a provider-listed topic through the same public-only app boundary. */
|
|
1311
|
+
/** Open a provider-listed topic through the same public-only app boundary, with the same safety floor. */
|
|
1268
1312
|
getSharedTrendFeed(projectId: string, params: {
|
|
1269
1313
|
protocol: string;
|
|
1270
1314
|
topic: string;
|
|
@@ -1314,7 +1358,10 @@ export declare class ProtocolsResource {
|
|
|
1314
1358
|
post: string;
|
|
1315
1359
|
readRef?: string;
|
|
1316
1360
|
}): Promise<SingleResponse<SocialConversation>>;
|
|
1317
|
-
/**
|
|
1361
|
+
/**
|
|
1362
|
+
* Current provider-ranked topics or posts, with explicit source and coverage. Does not ingest content.
|
|
1363
|
+
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
|
|
1364
|
+
*/
|
|
1318
1365
|
getPublicTrends(params: {
|
|
1319
1366
|
protocol: string;
|
|
1320
1367
|
limit?: number;
|
|
@@ -1323,7 +1370,7 @@ export declare class ProtocolsResource {
|
|
|
1323
1370
|
category?: string;
|
|
1324
1371
|
discovery?: boolean;
|
|
1325
1372
|
}, options?: SocialReadOptions): Promise<SingleResponse<SocialTrends>>;
|
|
1326
|
-
/** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. */
|
|
1373
|
+
/** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. Screened like `getPublicTrends`. */
|
|
1327
1374
|
getPublicTrendFeed(params: {
|
|
1328
1375
|
protocol: string;
|
|
1329
1376
|
topic: string;
|
|
@@ -89,12 +89,15 @@ export class ProtocolsResource {
|
|
|
89
89
|
return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/author-feed`, { protocol: params.protocol, actor: params.actor,
|
|
90
90
|
limit: params.limit, cursor: params.cursor });
|
|
91
91
|
}
|
|
92
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Public, app-moderated discovery without account authority. Enrichment uses cached judgments only.
|
|
94
|
+
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
|
|
95
|
+
*/
|
|
93
96
|
getSharedTrends(projectId, params) {
|
|
94
97
|
return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/trends`, { protocol: params.protocol, limit: params.limit, cursor: params.cursor,
|
|
95
98
|
includePreviews: params.includePreviews, category: params.category, discovery: params.discovery });
|
|
96
99
|
}
|
|
97
|
-
/** Open a provider-listed topic through the same public-only app boundary. */
|
|
100
|
+
/** Open a provider-listed topic through the same public-only app boundary, with the same safety floor. */
|
|
98
101
|
getSharedTrendFeed(projectId, params) {
|
|
99
102
|
return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/trend-feed`, { protocol: params.protocol, topic: params.topic,
|
|
100
103
|
limit: params.limit, cursor: params.cursor, category: params.category, discovery: params.discovery });
|
|
@@ -148,14 +151,17 @@ export class ProtocolsResource {
|
|
|
148
151
|
getPublicConversation(params) {
|
|
149
152
|
return this.client.get('/protocols/social/conversation', { protocol: params.protocol, post: params.post, readRef: params.readRef });
|
|
150
153
|
}
|
|
151
|
-
/**
|
|
154
|
+
/**
|
|
155
|
+
* Current provider-ranked topics or posts, with explicit source and coverage. Does not ingest content.
|
|
156
|
+
* Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
|
|
157
|
+
*/
|
|
152
158
|
getPublicTrends(params, options) {
|
|
153
159
|
return this.client.get('/protocols/social/trends', { protocol: params.protocol, limit: params.limit, cursor: params.cursor,
|
|
154
160
|
...(params.includePreviews !== undefined ? { includePreviews: params.includePreviews } : {}),
|
|
155
161
|
...(params.category !== undefined ? { category: params.category } : {}),
|
|
156
162
|
...(params.discovery !== undefined ? { discovery: params.discovery } : {}) }, ...readOptions(options));
|
|
157
163
|
}
|
|
158
|
-
/** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. */
|
|
164
|
+
/** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. Screened like `getPublicTrends`. */
|
|
159
165
|
getPublicTrendFeed(params, options) {
|
|
160
166
|
return this.client.get('/protocols/social/trend-feed', { protocol: params.protocol, topic: params.topic, limit: params.limit, cursor: params.cursor,
|
|
161
167
|
...(params.category !== undefined ? { category: params.category } : {}),
|
|
@@ -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
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -123,9 +123,31 @@ export interface MediaItem {
|
|
|
123
123
|
id: string;
|
|
124
124
|
url: string;
|
|
125
125
|
type: string;
|
|
126
|
+
/**
|
|
127
|
+
* Set on a legacy Minds video whose file is known to be gone (the server probes it): leave it out
|
|
128
|
+
* of Watch and clip queues and show the poster instead. Absent when the file plays or is unjudged.
|
|
129
|
+
*/
|
|
130
|
+
unplayable?: boolean;
|
|
131
|
+
}
|
|
132
|
+
/** The group a post is in, as every post payload carries it. */
|
|
133
|
+
export interface PostCommunity {
|
|
134
|
+
id: string;
|
|
135
|
+
name: string;
|
|
136
|
+
slug: string;
|
|
137
|
+
/**
|
|
138
|
+
* A post in a group that is not public is for the group's members, and is
|
|
139
|
+
* never boosted: offer Boost only when this is 'public'.
|
|
140
|
+
*/
|
|
141
|
+
privacy: 'public' | 'private' | 'hidden';
|
|
126
142
|
}
|
|
127
143
|
export interface Post {
|
|
144
|
+
/** Stored replies a reader can open. */
|
|
128
145
|
reply_count?: number;
|
|
146
|
+
/**
|
|
147
|
+
* The comment count a legacy import brought with the post (minds.com). None of those comments are
|
|
148
|
+
* stored as replies, so it is not in `reply_count`.
|
|
149
|
+
*/
|
|
150
|
+
reply_count_external?: number;
|
|
129
151
|
id: string;
|
|
130
152
|
/** Original Minds numeric GUID when this row came from the legacy import. */
|
|
131
153
|
legacy_guid?: string | null;
|
|
@@ -133,6 +155,8 @@ export interface Post {
|
|
|
133
155
|
content_format: 'plain' | 'markdown';
|
|
134
156
|
author: UserSummary;
|
|
135
157
|
community_id: string | null;
|
|
158
|
+
/** The group the post is in (null when it is in none). */
|
|
159
|
+
community?: PostCommunity | null;
|
|
136
160
|
organization_id: string | null;
|
|
137
161
|
reply_to_id?: string | null;
|
|
138
162
|
/** Present when this post is an X-style repost — the original post is nested. */
|
|
@@ -154,10 +178,25 @@ export interface RepostedPost {
|
|
|
154
178
|
content_format: 'plain' | 'markdown';
|
|
155
179
|
author: UserSummary | null;
|
|
156
180
|
community_id: string | null;
|
|
181
|
+
/** The group the original is in (null when it is in none): it decides what the repost card offers. */
|
|
182
|
+
community?: PostCommunity | null;
|
|
157
183
|
organization_id: string | null;
|
|
158
184
|
tags: Tag[];
|
|
159
185
|
reactions_count: number;
|
|
160
186
|
score: number;
|
|
187
|
+
/** The original's engagement, as the REST payload sends it for the original itself. */
|
|
188
|
+
upvote_count?: number;
|
|
189
|
+
downvote_count?: number;
|
|
190
|
+
/** The viewer's own reaction on the original ('upvote', 'downvote', ...), if any. */
|
|
191
|
+
user_reaction?: string | null;
|
|
192
|
+
/** Stored replies a reader can open. */
|
|
193
|
+
reply_count?: number;
|
|
194
|
+
/** A legacy import's comment count; none of those comments are stored as replies, so it is not in `reply_count`. */
|
|
195
|
+
reply_count_external?: number;
|
|
196
|
+
unique_commenters?: number;
|
|
197
|
+
reposts_count?: number;
|
|
198
|
+
/** Id of the viewer's own repost of the original; delete it to undo the repost. */
|
|
199
|
+
reposted_by_viewer_id?: string | null;
|
|
161
200
|
media: MediaItem[];
|
|
162
201
|
created_at: string;
|
|
163
202
|
}
|
|
@@ -178,6 +217,8 @@ export interface PostReply {
|
|
|
178
217
|
user_reaction?: string | null;
|
|
179
218
|
/** This reply's own reply count, so its sub-thread can be opened. */
|
|
180
219
|
reply_count?: number;
|
|
220
|
+
/** A legacy import's comment count for this reply; not stored as replies, so not in `reply_count`. */
|
|
221
|
+
reply_count_external?: number;
|
|
181
222
|
created_at: string;
|
|
182
223
|
}
|
|
183
224
|
export interface ListPostRepliesParams {
|
|
@@ -226,7 +267,11 @@ export interface UpdatePostInput {
|
|
|
226
267
|
}
|
|
227
268
|
export type ReactionType = 'like' | 'heart' | 'fire' | 'laugh' | 'sad' | 'angry' | 'upvote' | 'downvote';
|
|
228
269
|
export interface ListPostsParams extends PaginationParams {
|
|
229
|
-
/**
|
|
270
|
+
/**
|
|
271
|
+
* Public-only feed with a bounded Jev presentation hint. Limit <=25.
|
|
272
|
+
* The safety floor does not depend on it: a category page and every app-wide
|
|
273
|
+
* `sort: 'hot'` or `'score'` read are screened with or without a layout.
|
|
274
|
+
*/
|
|
230
275
|
discovery?: boolean;
|
|
231
276
|
/** Public-only broad category. Requires an enabled app experiment; limit <=25. */
|
|
232
277
|
category?: string;
|
|
@@ -239,6 +284,11 @@ export interface ListPostsParams extends PaginationParams {
|
|
|
239
284
|
audience_user_id?: 'me' | string;
|
|
240
285
|
/** Return only posts the authenticated caller has upvoted. */
|
|
241
286
|
liked?: boolean;
|
|
287
|
+
/**
|
|
288
|
+
* Return only the caller's saved posts, newest saved first, each with `bookmarked_at`. Takes only `limit`
|
|
289
|
+
* (at most 50) and `cursor`; see `posts.bookmarked` / `bookmarks.posts`.
|
|
290
|
+
*/
|
|
291
|
+
bookmarked?: boolean;
|
|
242
292
|
}
|
|
243
293
|
export interface SearchPostsParams extends PaginationParams {
|
|
244
294
|
q: string;
|
|
@@ -247,6 +297,42 @@ export interface SearchPostsParams extends PaginationParams {
|
|
|
247
297
|
sort?: 'relevance' | 'new';
|
|
248
298
|
since?: string;
|
|
249
299
|
until?: string;
|
|
300
|
+
/**
|
|
301
|
+
* The `meta.next_cursor` of the previous page. Later relevance pages are slices of the ranking the
|
|
302
|
+
* first page kept (ten minutes), so nothing is skipped or repeated while it lives; after that the page
|
|
303
|
+
* continues past the last post's score boundary. Newest-first pages continue by (created_at, id). A
|
|
304
|
+
* cursor is bound to its search and sort: another's is refused (400 `invalid_cursor`). Without one,
|
|
305
|
+
* `offset` counts.
|
|
306
|
+
*/
|
|
307
|
+
cursor?: string;
|
|
308
|
+
}
|
|
309
|
+
/** A way on from the end of a search: newest first, the open networks, or a topic the matches share. */
|
|
310
|
+
export type SearchFallOverLane = {
|
|
311
|
+
kind: 'new';
|
|
312
|
+
} | {
|
|
313
|
+
kind: 'open_network';
|
|
314
|
+
} | {
|
|
315
|
+
kind: 'tag';
|
|
316
|
+
id: string;
|
|
317
|
+
name: string;
|
|
318
|
+
slug: string;
|
|
319
|
+
};
|
|
320
|
+
/** One page of `posts.search`. */
|
|
321
|
+
export interface SearchPostsResponse extends PaginatedResponse<Post> {
|
|
322
|
+
/**
|
|
323
|
+
* On the first relevance page: up to three of the newest matches that are not on the page, newest
|
|
324
|
+
* first, one per author, for a "Latest" strip above the best matches. Absent when there are none, and on later pages.
|
|
325
|
+
*/
|
|
326
|
+
latest?: Post[];
|
|
327
|
+
/**
|
|
328
|
+
* On the last page (`meta.has_more` false): what exists past the end of this search, so a client
|
|
329
|
+
* can offer a way on instead of a wall. `new` when newest-first goes deeper than the ranking did;
|
|
330
|
+
* `open_network` when the caller's app can search the open networks; `tag` for the topic the
|
|
331
|
+
* page's posts share most. May be empty.
|
|
332
|
+
*/
|
|
333
|
+
fall_over?: {
|
|
334
|
+
lanes: SearchFallOverLane[];
|
|
335
|
+
};
|
|
250
336
|
}
|
|
251
337
|
export type QueryParams = Record<string, unknown>;
|
|
252
338
|
export interface CommunityCreator {
|
|
@@ -269,6 +355,8 @@ export interface Community {
|
|
|
269
355
|
post_count?: number;
|
|
270
356
|
/** Whether the caller is an accepted member. Returned by `list` and `search`. */
|
|
271
357
|
is_member?: boolean;
|
|
358
|
+
/** The caller's role in the group, or null outside it. Returned by `list` and the detail read; a group's creator reads as its owner. */
|
|
359
|
+
viewer_role?: 'owner' | 'admin' | 'moderator' | 'member' | null;
|
|
272
360
|
created_at: string;
|
|
273
361
|
}
|
|
274
362
|
/** Filters accepted by the communities directory. */
|
|
@@ -277,14 +365,36 @@ export interface ListCommunitiesParams extends PaginationParams {
|
|
|
277
365
|
member?: boolean;
|
|
278
366
|
/** Restrict personal keys to one organization. Project-bound keys are already scoped. */
|
|
279
367
|
organization_id?: string;
|
|
280
|
-
/**
|
|
281
|
-
|
|
368
|
+
/**
|
|
369
|
+
* Server-side ordering. With `q`, name matches come first and this breaks ties. `joined` (with
|
|
370
|
+
* `member: true`) lists the caller's groups by when they joined, newest first.
|
|
371
|
+
*/
|
|
372
|
+
sort?: 'members' | 'active' | 'newest' | 'joined';
|
|
282
373
|
/**
|
|
283
374
|
* Case-insensitive text search over name and description, up to 200
|
|
284
375
|
* characters. With `member: true` it searches only the caller's own groups.
|
|
285
376
|
*/
|
|
286
377
|
q?: string;
|
|
287
378
|
}
|
|
379
|
+
/** Which join requests to answer at once: named people (at most 100), or the oldest 100 waiting. */
|
|
380
|
+
export type AnswerJoinRequestsInput = {
|
|
381
|
+
user_ids: string[];
|
|
382
|
+
} | {
|
|
383
|
+
all: true;
|
|
384
|
+
};
|
|
385
|
+
/** What a many-at-once answer did: how many it answered, who it skipped, and how many still wait. */
|
|
386
|
+
export interface ApprovedJoinRequests {
|
|
387
|
+
approved: number;
|
|
388
|
+
/** Named people with no pending request here: already in, already answered, banned, or never asked. */
|
|
389
|
+
skipped: string[];
|
|
390
|
+
/** Pending requests left in the group (bans not counted). */
|
|
391
|
+
remaining: number;
|
|
392
|
+
}
|
|
393
|
+
export interface DeclinedJoinRequests {
|
|
394
|
+
declined: number;
|
|
395
|
+
skipped: string[];
|
|
396
|
+
remaining: number;
|
|
397
|
+
}
|
|
288
398
|
/** The caller's own membership row in a group, if any. */
|
|
289
399
|
export type CommunityMembershipStatus = 'accepted' | 'pending' | 'invited' | 'banned';
|
|
290
400
|
export interface CommunityDetail extends Community {
|
|
@@ -366,7 +476,8 @@ export interface CommunityBannedMember {
|
|
|
366
476
|
}
|
|
367
477
|
export interface CreateCommunityInput {
|
|
368
478
|
name: string;
|
|
369
|
-
slug
|
|
479
|
+
/** Optional vanity slug. Omit it for a server-generated collision-resistant default. */
|
|
480
|
+
slug?: string;
|
|
370
481
|
description?: string;
|
|
371
482
|
privacy?: 'public' | 'private' | 'hidden';
|
|
372
483
|
}
|
|
@@ -719,7 +830,8 @@ export interface AgentDetail extends Agent {
|
|
|
719
830
|
}
|
|
720
831
|
export interface CreateAgentInput {
|
|
721
832
|
name: string;
|
|
722
|
-
|
|
833
|
+
/** Optional public handle. Omit it for a server-generated collision-resistant default. */
|
|
834
|
+
username?: string;
|
|
723
835
|
bio?: string;
|
|
724
836
|
model?: string;
|
|
725
837
|
system_prompt?: string;
|
|
@@ -856,6 +968,14 @@ export interface Profile {
|
|
|
856
968
|
/** A row of a followers/following list: the profile plus the caller's relationship with it. */
|
|
857
969
|
export interface FollowListProfile extends Profile, ViewerRelationship {
|
|
858
970
|
}
|
|
971
|
+
/** Paging and search for a follower or following list (profiles.followers / following). */
|
|
972
|
+
export interface FollowListParams {
|
|
973
|
+
limit?: number;
|
|
974
|
+
offset?: number;
|
|
975
|
+
/** Name or handle, matched anywhere in either (at most 200 characters); a blank term lists everyone. */
|
|
976
|
+
search?: string;
|
|
977
|
+
[key: string]: unknown;
|
|
978
|
+
}
|
|
859
979
|
/** An account the caller blocked. */
|
|
860
980
|
export interface BlockedProfile extends Profile {
|
|
861
981
|
blocked_at: string;
|
|
@@ -895,10 +1015,24 @@ export interface UpdateProfileInput {
|
|
|
895
1015
|
username?: string;
|
|
896
1016
|
bio?: string | null;
|
|
897
1017
|
image?: string | null;
|
|
1018
|
+
/** The profile banner as an absolute image URL, or null to remove it (PUT /profiles/me has accepted it since #1867). */
|
|
1019
|
+
banner?: string | null;
|
|
898
1020
|
}
|
|
899
1021
|
export interface SearchProfilesParams extends PaginationParams {
|
|
900
1022
|
q: string;
|
|
901
1023
|
}
|
|
1024
|
+
/** A row of `profiles.search`: the profile plus what the ranking read (match x ln(1 + followers + min(posts, 100))). */
|
|
1025
|
+
export interface ProfileSearchResult extends Profile {
|
|
1026
|
+
followers_count: number;
|
|
1027
|
+
posts_count: number;
|
|
1028
|
+
/** Nobody follows it and it never posted: listed after every account with a public life, for a client to fold under "more handles". */
|
|
1029
|
+
empty: boolean;
|
|
1030
|
+
/** An account the operator vouches for: draw the verified badge. */
|
|
1031
|
+
verified: boolean;
|
|
1032
|
+
plus: boolean;
|
|
1033
|
+
pro: boolean;
|
|
1034
|
+
founder: boolean;
|
|
1035
|
+
}
|
|
902
1036
|
export interface SearchMentionsParams {
|
|
903
1037
|
/** What the member typed after `@` (a leading `@` is ignored). */
|
|
904
1038
|
q: string;
|
|
@@ -1082,6 +1216,13 @@ export interface LoginHistoryEntry {
|
|
|
1082
1216
|
}
|
|
1083
1217
|
export interface RecursivConfig {
|
|
1084
1218
|
apiKey?: string;
|
|
1219
|
+
/**
|
|
1220
|
+
* Whether to fall back to `RECURSIV_API_KEY` when `apiKey` is omitted.
|
|
1221
|
+
* Defaults to true for backwards compatibility and branded pre-auth flows.
|
|
1222
|
+
* Set to false for public clients that must remain keyless even in a
|
|
1223
|
+
* credentialed server process.
|
|
1224
|
+
*/
|
|
1225
|
+
inheritEnvironmentApiKey?: boolean;
|
|
1085
1226
|
baseUrl?: string;
|
|
1086
1227
|
timeout?: number;
|
|
1087
1228
|
maxRetries?: number;
|