@recursiv/sdk 0.7.63 → 0.7.66
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 +53 -0
- package/dist/feed-definition.d.ts +5 -1
- package/dist/feed-definition.js +5 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/resources/curator.d.ts +163 -1
- package/dist/resources/curator.js +9 -0
- package/dist/resources/discovery.d.ts +159 -5
- package/dist/resources/discovery.js +360 -30
- package/dist/resources/feeds.d.ts +228 -0
- package/dist/resources/feeds.js +61 -0
- package/dist/resources/profiles.d.ts +4 -1
- package/dist/resources/profiles.js +4 -1
- package/dist/resources/protocols.d.ts +4 -5
- package/dist/resources/search.d.ts +3 -2
- package/dist/types.d.ts +3 -2
- package/llm.md +25 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,6 +95,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
|
|
|
95
95
|
|----------|------------|
|
|
96
96
|
| `r.posts` | `create`, `update`, `list`, `liked`, `bookmarked`, `bookmark`, `unbookmark`, `search`, `replies`, `react` |
|
|
97
97
|
| `r.bookmarks` | `save`, `unsave`, `posts`, `list`, `keys`, `merge` — the member's saved posts, open-network posts and articles, synced across devices |
|
|
98
|
+
| `r.feeds` | `top` (what the member is most likely to engage with, rotated by what they saw, with open-network lanes and the lead story), `stories` (the story tail), `publicTop` (a visitor's top, keyless and edge-cacheable) |
|
|
98
99
|
| `r.communities` | `create`, `join`, `search`, `members` |
|
|
99
100
|
| `r.chat` | `send`, `dm`, `createGroup` (`encrypted: true` for an end-to-end encrypted group, 0.7.59), `messages` (cursor paging), `acceptRequest`, `declineRequest`, `leave`, `deleteConversation`; a private group's admin: `addMembers`, `removeMember`, `updateGroup` (0.7.59) |
|
|
100
101
|
| `r.tags` | `create`, `list` |
|
|
@@ -669,6 +670,58 @@ and says whether the member is personalized yet (`meta.personalized`). With a
|
|
|
669
670
|
`category` (the member's first), the session also reads open-network posts from it;
|
|
670
671
|
without, it is Minds only. It never carries the radar.
|
|
671
672
|
|
|
673
|
+
### For You: the world by the member's interests
|
|
674
|
+
|
|
675
|
+
`sdk.curator.forYouWorld({ part?, session?, snapshot?, compass? })` reads the For You overview
|
|
676
|
+
for the signed-in member, with their own key of the app. `stories` are world stories (Today,
|
|
677
|
+
the radar and the viral read) matched to the member's interests with breadth: at least four
|
|
678
|
+
themes and two breaking or general stories in the first eight, at most two per theme, and a
|
|
679
|
+
story shown three times without an open rests for a week. `items` are the member's For You
|
|
680
|
+
picks interleaved with the most engaging posts of their top interests (half or more on their
|
|
681
|
+
interests, no interest over 40%, videos weighted up), and `blocks` lay the page out like All:
|
|
682
|
+
`stories`, `lead_video`, `clips`, `posts`, `people`, `groups`, `topics`, `viral` and
|
|
683
|
+
`categories`, never the same type twice in a row, each naming the refs its views should send.
|
|
684
|
+
Every story and item carries `reasons` (`interest:<category>`, `followed:@<handle>`, `breaking`,
|
|
685
|
+
`general`, `for_you`, `engaging`, `breadth`). `part: 'stories'` and `part: 'items'` read the
|
|
686
|
+
two halves on their own, so an app can draw each as it lands; a part=items answer keeps the
|
|
687
|
+
stories rail as a `deferred` block. `meta.interests` are category ids for chips (never put them
|
|
688
|
+
in a URL), `meta.partial` names a source that missed its deadline, and `meta.foryou_cursor`
|
|
689
|
+
continues the same ranking with `curator.forYou({ cursor })`. 404 while the app's operator has
|
|
690
|
+
it off; a 503 `for_you_world_unavailable` carries Retry-After.
|
|
691
|
+
|
|
692
|
+
### The top of a feed
|
|
693
|
+
|
|
694
|
+
`sdk.feeds.top({ ref, limit?, session?, compass? })` answers the top of a feed for the
|
|
695
|
+
signed-in member, read with their own key of the app (`ref`: `builtin:all`,
|
|
696
|
+
`category:<id>`, `following`, `watch` or `story:<cluster id>`): `items` (the posts most
|
|
697
|
+
likely to be engaged with, often video, rotated by what the member has seen), `networks`
|
|
698
|
+
(Bluesky, Fediverse and Nostr lanes of up to six posts, each with a `card` to draw and no
|
|
699
|
+
URL on its network), `lead` (the story the feed should open with) and `meta` (`short`,
|
|
700
|
+
`safety`, whether the order used impressions). Every item's `target` is in the app: a
|
|
701
|
+
post id, `/social/post/<protocol>/<id>`, or a story id. `sdk.feeds.stories({ after?, limit? })`
|
|
702
|
+
is the story tail from the same ranker. `sdk.feeds.publicTop({ projectId, ref?, limit?, compass? })`
|
|
703
|
+
is a visitor's top (safe items only, the same for every visitor in one 15-minute bucket),
|
|
704
|
+
read with no credential so the edge can hold it. Each answers 404 while the app's operator
|
|
705
|
+
has it off. Send `block: 'top'` and the item's position with a visible view of a top item.
|
|
706
|
+
|
|
707
|
+
A feed session opens with it when asked: `createFeed({ networks, top: { ref, session? }, seen,
|
|
708
|
+
seenClusters?, bucket? })` (or `new DiscoveryFeedSession(..., { top: { ref, read, post? }, seen })`
|
|
709
|
+
with your own readers). The top's read starts with the first page's reads, and the top holds
|
|
710
|
+
the page at most 1.5 s from its start, the reads of its items' posts included; a top that is
|
|
711
|
+
off, fails or is late leaves the session exactly as it is without one. The radar's story reads
|
|
712
|
+
its posts as soon as the radar answers, whatever the top does. When it answers, page 0 opens with the story it leads with, then its items (each item
|
|
713
|
+
carries `top: { ref, position, block }`, a lane entry also `lane`), and its lanes stand in
|
|
714
|
+
for the networks' first reads. Minds items the first hot page does not carry are read by id
|
|
715
|
+
once, within the same 1.5 s (`top.post`, or `GET /posts/:id` without retries). `seen` (Minds post ids and
|
|
716
|
+
`<protocol>/<nativeId>` refs the member already saw) leaves those posts out of the hot pages,
|
|
717
|
+
and no hot page repeats a top item; a hot page left under five posts walks on, up to three
|
|
718
|
+
more pages, and the feed's `fallover` still reports when the ranking ends. Without impressions
|
|
719
|
+
in the top's order, the member's seen top items follow the unseen ones. With no server lead,
|
|
720
|
+
`seenClusters` (`Map<id, { n, clicked }>`) skips a story shown three times without an open,
|
|
721
|
+
and a visitor's `bucket` rotates page 0's story among the breaking ones
|
|
722
|
+
(`pickRadarCluster` is the same rule on its own). With none of these options a session is
|
|
723
|
+
unchanged.
|
|
724
|
+
|
|
672
725
|
Both take a Politics choice, `compass` (`all`, `mix`, `hide`, or cells in the order
|
|
673
726
|
`lo,co,ro,lm,cm,rm,ll,cl,rl`, optionally `np`), and send it with every Minds read
|
|
674
727
|
(`posts.list` and `curator.forYou` take it too). Each such page says what it did in
|
|
@@ -17,7 +17,11 @@ export declare const FEED_SOURCES_MAX = 50;
|
|
|
17
17
|
export declare const FEED_DEFINITION_MAX_BYTES: number;
|
|
18
18
|
export declare const FEED_SOURCE_KINDS: readonly ["category", "tag", "account", "community", "collection", "list", "story", "bsky_feed", "bsky_list", "bsky_starterpack", "nostr_set"];
|
|
19
19
|
export type FeedSourceKind = typeof FEED_SOURCE_KINDS[number];
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* 'engagement' ranks by predicted engagement for the reader: the top of a feed (`feeds.top`). Like For you
|
|
22
|
+
* and viral it reaches past the sources on the reader's behalf.
|
|
23
|
+
*/
|
|
24
|
+
export declare const FEED_RANKING_MODES: readonly ["hot", "new", "top", "for_you", "viral", "engagement"];
|
|
21
25
|
export type FeedRankingMode = typeof FEED_RANKING_MODES[number];
|
|
22
26
|
export declare const FEED_RANKING_RANGES: readonly ["24h", "7d", "30d", "1y"];
|
|
23
27
|
export type FeedRankingRange = typeof FEED_RANKING_RANGES[number];
|
package/dist/feed-definition.js
CHANGED
|
@@ -19,7 +19,11 @@ export const FEED_SOURCE_KINDS = [
|
|
|
19
19
|
'category', 'tag', 'account', 'community', 'collection', 'list', 'story',
|
|
20
20
|
'bsky_feed', 'bsky_list', 'bsky_starterpack', 'nostr_set',
|
|
21
21
|
];
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* 'engagement' ranks by predicted engagement for the reader: the top of a feed (`feeds.top`). Like For you
|
|
24
|
+
* and viral it reaches past the sources on the reader's behalf.
|
|
25
|
+
*/
|
|
26
|
+
export const FEED_RANKING_MODES = ['hot', 'new', 'top', 'for_you', 'viral', 'engagement'];
|
|
23
27
|
export const FEED_RANKING_RANGES = ['24h', '7d', '30d', '1y'];
|
|
24
28
|
export const FEED_MEDIA = ['any', 'images', 'video', 'longread'];
|
|
25
29
|
export const FEED_NETWORKS = ['minds', 'bluesky', 'activitypub', 'nostr'];
|
package/dist/index.d.ts
CHANGED
|
@@ -62,6 +62,7 @@ import { ModerationResource } from './resources/moderation.js';
|
|
|
62
62
|
import { LinkPreviewResource } from './resources/link-preview.js';
|
|
63
63
|
import { SearchResource } from './resources/search.js';
|
|
64
64
|
import { BookmarksResource } from './resources/bookmarks.js';
|
|
65
|
+
import { FeedsResource } from './resources/feeds.js';
|
|
65
66
|
import { AccountsResource } from './resources/accounts.js';
|
|
66
67
|
import { RealtimeClient } from './realtime.js';
|
|
67
68
|
import type { RecursivConfig } from './types.js';
|
|
@@ -121,6 +122,8 @@ export declare class Recursiv {
|
|
|
121
122
|
readonly jobs: JobsResource;
|
|
122
123
|
readonly curator: CuratorResource;
|
|
123
124
|
readonly discovery: DiscoveryResource;
|
|
125
|
+
/** The top of a feed: what the member (or a visitor) is most likely to engage with, rotated by what they have seen. */
|
|
126
|
+
readonly feeds: FeedsResource;
|
|
124
127
|
readonly video: VideoResource;
|
|
125
128
|
readonly appSubscriptions: AppSubscriptionsResource;
|
|
126
129
|
readonly boosts: BoostsResource;
|
|
@@ -192,7 +195,7 @@ export { VideoResource } from './resources/video.js';
|
|
|
192
195
|
export type { VideoUploadCreds, CreateVideoUploadResult, VideoStatusInfo, VideoPlaybackToken, VideoEntitlement, } from './resources/video.js';
|
|
193
196
|
export { AppSubscriptionsResource } from './resources/app-subscriptions.js';
|
|
194
197
|
export type { AppSubscriptionBoostCredit, AppSubscriptionCheckoutResult, AppSubscriptionPortalResult, AppSubscriptionStatusResult, AppSubscriptionTierChangePreview, AppSubscriptionTierChangeResult, } from './resources/app-subscriptions.js';
|
|
195
|
-
export type { CuratorSource, CuratorPrompt, CuratorRunInput, CuratorRunResult, CuratorTodayEdition, CuratorTodayExcerpt, CuratorTodayLeadImage, CuratorTodayRadar, CuratorTodayReaction, CuratorTodayResponse, CuratorTodaySource, CuratorTodayStory, ForYouMeta, ForYouParams, ForYouSurface, } from './resources/curator.js';
|
|
198
|
+
export type { CuratorSource, CuratorPrompt, CuratorRunInput, CuratorRunResult, CuratorTodayEdition, CuratorTodayExcerpt, CuratorTodayLeadImage, CuratorTodayRadar, CuratorTodayReaction, CuratorTodayResponse, CuratorTodaySource, CuratorTodayStory, ForYouMeta, ForYouParams, ForYouSurface, ForYouWorldBlock, ForYouWorldBlockType, ForYouWorldCategoryShortcut, ForYouWorldItem, ForYouWorldMeta, ForYouWorldParams, ForYouWorldPart, ForYouWorldResponse, ForYouWorldStory, ForYouWorldStoryTarget, ForYouWorldViral, } from './resources/curator.js';
|
|
196
199
|
export { ReportsResource } from './resources/reports.js';
|
|
197
200
|
export type { ReportTargetType, ReportStatus, CreateReportInput, ReportRecord, ResolveReportInput, } from './resources/reports.js';
|
|
198
201
|
export { ModerationResource } from './resources/moderation.js';
|
|
@@ -201,6 +204,8 @@ export { LinkPreviewResource } from './resources/link-preview.js';
|
|
|
201
204
|
export { SEARCH_SECTIONS, SearchResource } from './resources/search.js';
|
|
202
205
|
export type { SearchAnswer, SearchGroup, SearchOmitReason, SearchParams, SearchPerson, SearchRelated, SearchResponse, SearchSection, SearchStory, SearchTopic } from './resources/search.js';
|
|
203
206
|
export { BOOKMARK_MERGE_MAX_ITEMS, BookmarksResource } from './resources/bookmarks.js';
|
|
207
|
+
export { FEED_TOP_NETWORKS, FeedsResource } from './resources/feeds.js';
|
|
208
|
+
export type { FeedStoriesParams, FeedStoriesResponse, FeedStory, FeedTopExplain, FeedTopItem, FeedTopMeta, FeedTopNetwork, FeedTopNetworkCard, FeedTopNetworkEntry, FeedTopParams, FeedTopResponse, FeedTopTarget, PublicFeedTopMeta, PublicFeedTopParams, PublicFeedTopResponse, } from './resources/feeds.js';
|
|
204
209
|
export type { BookmarkedPost, BookmarkInput, BookmarkItem, BookmarkKey, BookmarkKeyRow, BookmarkKind, BookmarkMergeItem, BookmarkMergeResult, BookmarkProtocol, BookmarkRequestOptions, BookmarksPage, SavedBookmark, UnsavedBookmark, } from './resources/bookmarks.js';
|
|
205
210
|
export { AccountsResource } from './resources/accounts.js';
|
|
206
211
|
export type { SiblingAccount, SiblingAccountsResponse, SwitchAccountResult, } from './resources/accounts.js';
|
package/dist/index.js
CHANGED
|
@@ -63,6 +63,7 @@ import { ModerationResource } from './resources/moderation.js';
|
|
|
63
63
|
import { LinkPreviewResource } from './resources/link-preview.js';
|
|
64
64
|
import { SearchResource } from './resources/search.js';
|
|
65
65
|
import { BookmarksResource } from './resources/bookmarks.js';
|
|
66
|
+
import { FeedsResource } from './resources/feeds.js';
|
|
66
67
|
import { AccountsResource } from './resources/accounts.js';
|
|
67
68
|
import { RealtimeClient } from './realtime.js';
|
|
68
69
|
export class Recursiv {
|
|
@@ -122,6 +123,8 @@ export class Recursiv {
|
|
|
122
123
|
jobs;
|
|
123
124
|
curator;
|
|
124
125
|
discovery;
|
|
126
|
+
/** The top of a feed: what the member (or a visitor) is most likely to engage with, rotated by what they have seen. */
|
|
127
|
+
feeds;
|
|
125
128
|
video;
|
|
126
129
|
appSubscriptions;
|
|
127
130
|
boosts;
|
|
@@ -188,7 +191,8 @@ export class Recursiv {
|
|
|
188
191
|
this.webhooks = new WebhooksResource(client);
|
|
189
192
|
this.jobs = new JobsResource(client);
|
|
190
193
|
this.curator = new CuratorResource(client);
|
|
191
|
-
this.
|
|
194
|
+
this.feeds = new FeedsResource(client);
|
|
195
|
+
this.discovery = new DiscoveryResource(this.posts, this.protocols, this.curator, client, this.feeds);
|
|
192
196
|
this.video = new VideoResource(client);
|
|
193
197
|
this.appSubscriptions = new AppSubscriptionsResource(client);
|
|
194
198
|
this.boosts = new BoostsResource(client);
|
|
@@ -254,6 +258,7 @@ export { ModerationResource } from './resources/moderation.js';
|
|
|
254
258
|
export { LinkPreviewResource } from './resources/link-preview.js';
|
|
255
259
|
export { SEARCH_SECTIONS, SearchResource } from './resources/search.js';
|
|
256
260
|
export { BOOKMARK_MERGE_MAX_ITEMS, BookmarksResource } from './resources/bookmarks.js';
|
|
261
|
+
export { FEED_TOP_NETWORKS, FeedsResource } from './resources/feeds.js';
|
|
257
262
|
export { AccountsResource } from './resources/accounts.js';
|
|
258
263
|
export { OrganizationSettingsResource } from './resources/organization-settings.js';
|
|
259
264
|
export { ProjectSettingsResource } from './resources/project-settings.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { HttpClient } from '../client.js';
|
|
2
|
-
import type { DiscoveryLayout, PaginatedResponse, PaginationMeta, Post, SingleResponse } from '../types.js';
|
|
2
|
+
import type { CompassFilterMeta, DiscoveryLayout, PaginatedResponse, PaginationMeta, Post, SingleResponse } from '../types.js';
|
|
3
|
+
import type { FeedTopNetworkCard, FeedTopTarget } from './feeds.js';
|
|
3
4
|
/** Which surface is asking: Home's For You (default) or Discover's For you. */
|
|
4
5
|
export type ForYouSurface = 'home' | 'discover';
|
|
5
6
|
export type ForYouParams = {
|
|
@@ -22,6 +23,158 @@ export interface ForYouMeta extends PaginationMeta {
|
|
|
22
23
|
/** Whether the ranker can personalize for this member yet (3 follows or 3 upvoted authors). */
|
|
23
24
|
personalized?: boolean;
|
|
24
25
|
}
|
|
26
|
+
/** Which half to read: the stories rail, the items, or both (the default). The app can read the two at once and draw each as it lands. */
|
|
27
|
+
export type ForYouWorldPart = 'stories' | 'items' | 'all';
|
|
28
|
+
export interface ForYouWorldParams {
|
|
29
|
+
part?: ForYouWorldPart;
|
|
30
|
+
/** Opaque per app session ([A-Za-z0-9_-], up to 64): the same token keeps one session's order stable across reads. */
|
|
31
|
+
session?: string;
|
|
32
|
+
/** A For You snapshot nonce the app holds (`meta.snapshot` of a previous answer): reused while the server holds it. */
|
|
33
|
+
snapshot?: string;
|
|
34
|
+
/** A Politics choice, as `posts.list` takes it. */
|
|
35
|
+
compass?: string;
|
|
36
|
+
}
|
|
37
|
+
/** Where a story opens in the app: a radar story by its cluster id, or a World Today story the radar has none for by its Today id. */
|
|
38
|
+
export type ForYouWorldStoryTarget = {
|
|
39
|
+
kind: 'story';
|
|
40
|
+
id: string;
|
|
41
|
+
} | {
|
|
42
|
+
kind: 'today_story';
|
|
43
|
+
id: string;
|
|
44
|
+
};
|
|
45
|
+
/** One world story: a radar story (with Today's headline when Today seeded it), a Today story, or a viral post. */
|
|
46
|
+
export interface ForYouWorldStory {
|
|
47
|
+
/** What an impression of it names: 'cluster:<id>', 'today:<id>' or the post's ref. Send it with `block: 'top'` on a visible view. */
|
|
48
|
+
ref: string;
|
|
49
|
+
id: string;
|
|
50
|
+
source: 'today' | 'radar' | 'viral';
|
|
51
|
+
target: FeedTopTarget | ForYouWorldStoryTarget;
|
|
52
|
+
label: string;
|
|
53
|
+
kind: 'breaking' | 'viral' | 'story';
|
|
54
|
+
breaking: boolean;
|
|
55
|
+
/** A front-page or general-news story: one of those shown whatever the member's interests. */
|
|
56
|
+
general: boolean;
|
|
57
|
+
theme: string | null;
|
|
58
|
+
categories: string[];
|
|
59
|
+
/** How widely it is told: posts per source kind, and the outlets that ran it (Today), when known. */
|
|
60
|
+
counts: {
|
|
61
|
+
sources: Record<string, number>;
|
|
62
|
+
outlets: number | null;
|
|
63
|
+
};
|
|
64
|
+
media: {
|
|
65
|
+
images: number;
|
|
66
|
+
videos: number;
|
|
67
|
+
};
|
|
68
|
+
freshness: {
|
|
69
|
+
newestAt: string | null;
|
|
70
|
+
};
|
|
71
|
+
position: number;
|
|
72
|
+
/** Why it is here: `interest:<category>`, `breaking`, `general`, `viral`, `breadth` or `trending`. */
|
|
73
|
+
reasons: string[];
|
|
74
|
+
explain: {
|
|
75
|
+
score: number;
|
|
76
|
+
interest: number;
|
|
77
|
+
rank: number;
|
|
78
|
+
impression: 'fresh' | 'demoted';
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** One item: a Minds post (`ref` is its id) or an open-network post (`ref` is '<protocol>/<nativeId>'). */
|
|
82
|
+
export interface ForYouWorldItem {
|
|
83
|
+
ref: string;
|
|
84
|
+
kind: 'post' | 'network_post';
|
|
85
|
+
authorId: string;
|
|
86
|
+
target: FeedTopTarget;
|
|
87
|
+
/** Reading order through the blocks. */
|
|
88
|
+
position: number;
|
|
89
|
+
/** 'top' for the first 10: send `block: 'top'` and the position with a visible view of one. */
|
|
90
|
+
block: 'top' | 'below';
|
|
91
|
+
media: 'video' | 'image' | 'none';
|
|
92
|
+
topic: string | null;
|
|
93
|
+
/** Why it is here: `interest:<category>`, `followed:@<handle>`, `for_you`, `engaging` or `breadth`. */
|
|
94
|
+
reasons: string[];
|
|
95
|
+
explain: {
|
|
96
|
+
score: number;
|
|
97
|
+
source: 'foryou' | 'category' | 'all';
|
|
98
|
+
label: 'safe' | 'open';
|
|
99
|
+
impression: 'fresh' | 'demoted';
|
|
100
|
+
engagement: {
|
|
101
|
+
source: string;
|
|
102
|
+
rate: number;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/** A viral post in the viral block. `card` is an open-network post's card (no URL on its network); null for a Minds post, read by id. */
|
|
107
|
+
export interface ForYouWorldViral {
|
|
108
|
+
ref: string;
|
|
109
|
+
kind: 'post' | 'network_post';
|
|
110
|
+
protocol: string;
|
|
111
|
+
target: FeedTopTarget;
|
|
112
|
+
label: string;
|
|
113
|
+
category: string | null;
|
|
114
|
+
media: 'video' | 'image' | null;
|
|
115
|
+
rank: number;
|
|
116
|
+
velocity: {
|
|
117
|
+
perHour1h: number;
|
|
118
|
+
z: number;
|
|
119
|
+
};
|
|
120
|
+
card: FeedTopNetworkCard | null;
|
|
121
|
+
}
|
|
122
|
+
export interface ForYouWorldCategoryShortcut {
|
|
123
|
+
id: string;
|
|
124
|
+
name: string;
|
|
125
|
+
reason: 'interest' | 'breadth';
|
|
126
|
+
target: {
|
|
127
|
+
kind: 'category';
|
|
128
|
+
id: string;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** The page's components, laid out like All: no type follows itself. */
|
|
132
|
+
export type ForYouWorldBlockType = 'stories' | 'lead_video' | 'clips' | 'posts' | 'people' | 'groups' | 'topics' | 'categories' | 'viral';
|
|
133
|
+
export interface ForYouWorldBlock {
|
|
134
|
+
id: string;
|
|
135
|
+
type: ForYouWorldBlockType;
|
|
136
|
+
/** What it shows, in order: item refs (lead_video, clips, posts), story refs, entity keys, category ids or viral refs. */
|
|
137
|
+
refs: string[];
|
|
138
|
+
/** The stories rail of a part=items answer: fill it from your own part=stories read. */
|
|
139
|
+
deferred?: true;
|
|
140
|
+
/** people, groups and topics: the recommender's suggestions (three showings without a tap rest one). */
|
|
141
|
+
entities?: Array<Record<string, unknown> & {
|
|
142
|
+
kind: string;
|
|
143
|
+
key: string;
|
|
144
|
+
}>;
|
|
145
|
+
categories?: ForYouWorldCategoryShortcut[];
|
|
146
|
+
viral?: ForYouWorldViral[];
|
|
147
|
+
}
|
|
148
|
+
export interface ForYouWorldMeta {
|
|
149
|
+
part: ForYouWorldPart;
|
|
150
|
+
flag: {
|
|
151
|
+
world: 'on';
|
|
152
|
+
impressions: 'on' | 'off' | 'unavailable';
|
|
153
|
+
};
|
|
154
|
+
/** Fewer than 3 follows and no signals yet: a breadth-only overview. */
|
|
155
|
+
cold_start: boolean;
|
|
156
|
+
personalized: boolean | null;
|
|
157
|
+
/** The member's top interests as browse category ids, for chips. Never put them in a URL. */
|
|
158
|
+
interests: string[];
|
|
159
|
+
/**
|
|
160
|
+
* Sources left out because they missed their deadline or failed. `consent` is the Bluesky consent read: when it is
|
|
161
|
+
* named, Bluesky posts from the category pools and the viral read were left out of this answer.
|
|
162
|
+
*/
|
|
163
|
+
partial: string[];
|
|
164
|
+
safety: 'safe' | 'open' | null;
|
|
165
|
+
/** The For You snapshot the items came from; `foryou_cursor` continues it on `curator.forYou({ cursor })`. */
|
|
166
|
+
snapshot: string | null;
|
|
167
|
+
foryou_cursor: string | null;
|
|
168
|
+
short: boolean;
|
|
169
|
+
compass_filter?: CompassFilterMeta;
|
|
170
|
+
}
|
|
171
|
+
/** GET /curator/foryou/world: `stories` unless part=items, `items` and `blocks` unless part=stories. */
|
|
172
|
+
export interface ForYouWorldResponse {
|
|
173
|
+
stories?: ForYouWorldStory[];
|
|
174
|
+
items?: ForYouWorldItem[];
|
|
175
|
+
blocks?: ForYouWorldBlock[];
|
|
176
|
+
meta: ForYouWorldMeta;
|
|
177
|
+
}
|
|
25
178
|
/** RSS / Atom source. Default — `type` may be omitted for back-compat. */
|
|
26
179
|
export interface CuratorRssSource {
|
|
27
180
|
type?: 'rss';
|
|
@@ -278,4 +431,13 @@ export declare class CuratorResource {
|
|
|
278
431
|
forYou(params?: ForYouParams): Promise<PaginatedResponse<Post> & {
|
|
279
432
|
meta: ForYouMeta;
|
|
280
433
|
}>;
|
|
434
|
+
/**
|
|
435
|
+
* For You as an overview of the world by the member's interests, read with their own key of the app: world stories
|
|
436
|
+
* (interest-matched, with breadth), then a page of typed blocks laid out like All (a lead video, clip strips, posts,
|
|
437
|
+
* people and groups to follow, trending topics, viral posts, category shortcuts). `part` reads one half on its own.
|
|
438
|
+
* 404 until the app's operator turns it on; a 503 `for_you_world_unavailable` carries Retry-After. Always private.
|
|
439
|
+
*/
|
|
440
|
+
forYouWorld(params?: ForYouWorldParams, options?: {
|
|
441
|
+
retry?: boolean;
|
|
442
|
+
}): Promise<ForYouWorldResponse>;
|
|
281
443
|
}
|
|
@@ -57,4 +57,13 @@ export class CuratorResource {
|
|
|
57
57
|
const { retry, ...query } = params;
|
|
58
58
|
return retry === false ? this.client.get('/curator/foryou', query, { retry: false }) : this.client.get('/curator/foryou', query);
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* For You as an overview of the world by the member's interests, read with their own key of the app: world stories
|
|
62
|
+
* (interest-matched, with breadth), then a page of typed blocks laid out like All (a lead video, clip strips, posts,
|
|
63
|
+
* people and groups to follow, trending topics, viral posts, category shortcuts). `part` reads one half on its own.
|
|
64
|
+
* 404 until the app's operator turns it on; a 503 `for_you_world_unavailable` carries Retry-After. Always private.
|
|
65
|
+
*/
|
|
66
|
+
forYouWorld(params = {}, options) {
|
|
67
|
+
return this.client.get('/curator/foryou/world', { part: params.part, session: params.session, snapshot: params.snapshot, compass: params.compass }, options);
|
|
68
|
+
}
|
|
60
69
|
}
|
|
@@ -2,6 +2,7 @@ import type { HttpClient } from '../client.js';
|
|
|
2
2
|
import type { CompassFilterMeta, DiscoveryLayout, FalloverStep, MediaItem, Post } from '../types.js';
|
|
3
3
|
import type { CuratorResource } from './curator.js';
|
|
4
4
|
import type { DiscoveryLearning } from './discovery-learning.js';
|
|
5
|
+
import type { FeedStory, FeedTopItem, FeedTopNetwork, FeedTopNetworkEntry, FeedsResource } from './feeds.js';
|
|
5
6
|
import type { PostsResource } from './posts.js';
|
|
6
7
|
import type { ProtocolsResource, SocialNetwork, SocialPost, SocialRadar, SocialRadarPosts } from './protocols.js';
|
|
7
8
|
export type DiscoveryRecommendationKind = 'groups' | 'people' | 'tags';
|
|
@@ -284,15 +285,97 @@ export interface DiscoveryRadarMemberSources {
|
|
|
284
285
|
}>;
|
|
285
286
|
}
|
|
286
287
|
export type DiscoveryRadarPresentation = 'breaking' | 'story';
|
|
288
|
+
/** Where an item sits in the top of the feed: set only on the items a session's top placed on its first page. */
|
|
289
|
+
export interface DiscoveryTopPlacement {
|
|
290
|
+
/** The item's ref in the top (a Minds post id, or '<protocol>/<nativeId>'): what a visible view of it names. */
|
|
291
|
+
ref: string;
|
|
292
|
+
/** Its position in the top, or in its network's lane. */
|
|
293
|
+
position: number;
|
|
294
|
+
/** 'top' for the first 10: send `block: 'top'` and the position with a visible view of one (an impression). */
|
|
295
|
+
block: 'top' | 'below';
|
|
296
|
+
/** Set on an open-network lane's entry: its network. */
|
|
297
|
+
lane?: FeedTopNetwork;
|
|
298
|
+
}
|
|
287
299
|
export type DiscoveryPostItem = {
|
|
288
300
|
key: string;
|
|
289
301
|
kind: 'minds';
|
|
290
302
|
post: Post;
|
|
303
|
+
top?: DiscoveryTopPlacement;
|
|
291
304
|
} | {
|
|
292
305
|
key: string;
|
|
293
306
|
kind: 'social';
|
|
294
307
|
post: SocialPost;
|
|
308
|
+
top?: DiscoveryTopPlacement;
|
|
295
309
|
};
|
|
310
|
+
/** What a session reads of a top answer: the member route's (`feeds.top`) and the visitor read's (`feeds.publicTop`) common shape. */
|
|
311
|
+
export interface DiscoveryTopAnswer {
|
|
312
|
+
items: FeedTopItem[];
|
|
313
|
+
networks: Partial<Record<FeedTopNetwork, FeedTopNetworkEntry[]>> | null;
|
|
314
|
+
lead: FeedStory | null;
|
|
315
|
+
meta?: {
|
|
316
|
+
flag?: {
|
|
317
|
+
impressions?: string;
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
/** Reads the top of the session's feed. */
|
|
322
|
+
export type DiscoveryTopSource = (params: {
|
|
323
|
+
ref: string;
|
|
324
|
+
limit: number;
|
|
325
|
+
session?: string;
|
|
326
|
+
}) => Promise<DiscoveryTopAnswer>;
|
|
327
|
+
/** The top of the feed a session opens with (`discovery.createFeed({ top })`, or DiscoveryFeedSession's options). */
|
|
328
|
+
export interface DiscoveryTopOptions {
|
|
329
|
+
/**
|
|
330
|
+
* The feed's ref as the reader takes it: `builtin:all`, `category:<id>`, `following`, `watch` or `story:<id>` for
|
|
331
|
+
* `feeds.top`; `all`, `watch` or `category:<id>` for `feeds.publicTop`.
|
|
332
|
+
*/
|
|
333
|
+
ref: string;
|
|
334
|
+
/** Items asked for, 1..25 (default DISCOVERY_TOP_LIMIT). */
|
|
335
|
+
limit?: number;
|
|
336
|
+
/** The app's session token (`feeds.top` keeps one session's order stable with it). */
|
|
337
|
+
session?: string;
|
|
338
|
+
/** The reader. createFeed reads `feeds.top` (with its `compass`); a session built directly has no top without one. */
|
|
339
|
+
read?: DiscoveryTopSource;
|
|
340
|
+
/**
|
|
341
|
+
* Reads one Minds post by id, for a top item the first hot page does not carry, within the top's one deadline
|
|
342
|
+
* (DISCOVERY_TOP_TIMEOUT_MS from the page's start). Defaults to the session's radar Minds reader; createFeed passes
|
|
343
|
+
* `GET /posts/:id` without retries. Without either, a Minds top item shows only when the first hot page carries it.
|
|
344
|
+
*/
|
|
345
|
+
post?: (id: string) => Promise<{
|
|
346
|
+
data: Post;
|
|
347
|
+
}>;
|
|
348
|
+
}
|
|
349
|
+
/** What a member saw of a radar story: how often its lead was on screen, and whether they ever opened it. */
|
|
350
|
+
export interface DiscoverySeenCluster {
|
|
351
|
+
n: number;
|
|
352
|
+
clicked: boolean;
|
|
353
|
+
}
|
|
354
|
+
/** The options a session takes besides its readers. With none set, a session answers exactly what 0.7.63's did. */
|
|
355
|
+
export interface DiscoveryFeedSessionOptions {
|
|
356
|
+
/**
|
|
357
|
+
* The top of the feed. Its read starts with the first page's reads, and it holds the page at most
|
|
358
|
+
* DISCOVERY_TOP_TIMEOUT_MS from the page's start, the reads of its items' posts included: a top that fails (404 while
|
|
359
|
+
* it is off, a network error, a timeout) leaves the session exactly as it is without one. The radar's own story reads
|
|
360
|
+
* its member posts as soon as the radar answers, whatever the top does; a different story the top leads with reads its
|
|
361
|
+
* own from the moment the top answers.
|
|
362
|
+
* When it answers, the first page opens with its items (each carrying `top`), its open-network lanes stand in for the
|
|
363
|
+
* networks' first reads (a topics network's previews), its lead is the page's story, and no later hot page repeats an
|
|
364
|
+
* item it placed.
|
|
365
|
+
*/
|
|
366
|
+
top?: DiscoveryTopOptions;
|
|
367
|
+
/**
|
|
368
|
+
* What the member already saw (Minds post ids and '<protocol>/<nativeId>' refs, as the app records visible views): left
|
|
369
|
+
* out of the Minds hot pages. A page the filter leaves under DISCOVERY_SEEN_MIN_ITEMS walks on, up to
|
|
370
|
+
* DISCOVERY_SEEN_WALK more hot pages, and the stream's fall-through (`fallover`) still reports when it ends. When the
|
|
371
|
+
* top's order did not use impressions (`meta.flag.impressions` not 'on'), the top's seen items follow its unseen ones.
|
|
372
|
+
*/
|
|
373
|
+
seen?: Iterable<string>;
|
|
374
|
+
/** The client's own story rotation, for a session with no server lead: a story shown RADAR_REST_AFTER times with no open is skipped. */
|
|
375
|
+
seenClusters?: ReadonlyMap<string, DiscoverySeenCluster>;
|
|
376
|
+
/** A visitor's rotation bucket (e.g. floor(now / 15 min)): page 0's story rotates among the breaking stories by it. */
|
|
377
|
+
bucket?: number;
|
|
378
|
+
}
|
|
296
379
|
export type DiscoveryItem = DiscoveryPostItem | {
|
|
297
380
|
key: string;
|
|
298
381
|
kind: 'group';
|
|
@@ -325,6 +408,22 @@ export declare const DISCOVERY_SOURCE_TIMEOUT_MS = 15000;
|
|
|
325
408
|
* "Minds posts didn't load" with Try again and which its Politics fallback (minds#1212) waits for.
|
|
326
409
|
*/
|
|
327
410
|
export declare const DISCOVERY_SOURCE_RETRIES = 2;
|
|
411
|
+
/**
|
|
412
|
+
* The longest the top of the feed holds the first page, counted from the page's start. The top's read and the reads of
|
|
413
|
+
* the posts its items need (Minds posts by id, a story's network posts) share this one deadline: a top that has not
|
|
414
|
+
* answered by then is left out, and an item whose post was not read by then shows only if the first hot page carries it.
|
|
415
|
+
*/
|
|
416
|
+
export declare const DISCOVERY_TOP_TIMEOUT_MS = 1500;
|
|
417
|
+
/** Top items a session asks for unless its options say otherwise: the top block. */
|
|
418
|
+
export declare const DISCOVERY_TOP_LIMIT = 10;
|
|
419
|
+
/** A hot page the seen set (or the top) leaves under this many posts walks on to the next hot page... */
|
|
420
|
+
export declare const DISCOVERY_SEEN_MIN_ITEMS = 5;
|
|
421
|
+
/** ...up to this many more pages in one read; then the page is what it is and the next read goes on from there. */
|
|
422
|
+
export declare const DISCOVERY_SEEN_WALK = 3;
|
|
423
|
+
/** A story whose lead was on screen this many times with no open is skipped (DiscoveryFeedSessionOptions.seenClusters). */
|
|
424
|
+
export declare const RADAR_REST_AFTER = 3;
|
|
425
|
+
/** A visitor's page-0 story rotates by bucket among this many best breaking stories (or best stories when none is breaking). */
|
|
426
|
+
export declare const RADAR_BUCKET_ROTATION = 5;
|
|
328
427
|
/**
|
|
329
428
|
* Whether a source's read failed for a reason that passes: it ran past its deadline, its call never got an answer (a
|
|
330
429
|
* failed connection or an abort, as the session's read reports it), or the server answered 408, 425, 429 or 5xx (or no
|
|
@@ -332,6 +431,17 @@ export declare const DISCOVERY_SOURCE_RETRIES = 2;
|
|
|
332
431
|
* checking it) and any other 4xx are the source's own failure and are not asked again.
|
|
333
432
|
*/
|
|
334
433
|
export declare function transientSourceFailure(error: unknown): boolean;
|
|
434
|
+
/** Page 0's story, else a later page's: the client's own pick, for a session with no lead from the top of the feed. */
|
|
435
|
+
export interface RadarPickOptions {
|
|
436
|
+
/** Stories this session already showed. */
|
|
437
|
+
used?: ReadonlySet<string>;
|
|
438
|
+
/** What the member saw of each story: one shown RADAR_REST_AFTER times with no open is skipped. */
|
|
439
|
+
seenClusters?: ReadonlyMap<string, DiscoverySeenCluster>;
|
|
440
|
+
/** A visitor's bucket: page 0's story rotates by it among the RADAR_BUCKET_ROTATION best breaking stories (best stories when none is breaking). */
|
|
441
|
+
bucket?: number;
|
|
442
|
+
}
|
|
443
|
+
/** Page 0: the top breaking story, else the top story. Later pages: the next unused story that is not breaking. */
|
|
444
|
+
export declare function pickRadarCluster(clusters: readonly DiscoveryCluster[], page: number, options?: RadarPickOptions): DiscoveryCluster | undefined;
|
|
335
445
|
/** json-render Spec: values are references to authenticated SDK results, never
|
|
336
446
|
* arbitrary executable components, links, generated text or provider markup. */
|
|
337
447
|
export interface DiscoveryModule {
|
|
@@ -407,7 +517,21 @@ export declare class DiscoveryFeedSession {
|
|
|
407
517
|
private radarLoading;
|
|
408
518
|
/** Member reads per story, started as soon as the story is known and shared by whichever page shows it. */
|
|
409
519
|
private memberReads;
|
|
410
|
-
|
|
520
|
+
/** The top of the feed (options.top with a reader), its read started with page 0's reads, and the keys it placed. */
|
|
521
|
+
private top?;
|
|
522
|
+
private topLoading;
|
|
523
|
+
/** The story the top leads with, once its answer is in (null: it names none); undefined until then. */
|
|
524
|
+
private topLead;
|
|
525
|
+
private topKeys;
|
|
526
|
+
/** What the member already saw (options.seen): left out of the Minds hot pages. */
|
|
527
|
+
private appSeen;
|
|
528
|
+
private seenClusters?;
|
|
529
|
+
private bucket?;
|
|
530
|
+
constructor(posts: Pick<PostsResource, 'list'>, protocols: Pick<ProtocolsResource, 'getPublicTrends' | 'getPublicTrendFeed'>, networks: SocialNetwork[], category?: string | undefined, source?: string, learning?: DiscoveryLearning | undefined, recommend?: DiscoveryRecommendationSources | undefined, radar?: DiscoveryRadarSource, members?: DiscoveryRadarMemberSources | undefined, options?: DiscoveryFeedSessionOptions);
|
|
531
|
+
/** The Minds hot pages leave out what the member saw or the top placed, and walk on past a page that leaves too little. */
|
|
532
|
+
private get filtering();
|
|
533
|
+
/** A Minds hot item the page leaves out: shown on an earlier page, placed by the top, or in the member's seen set. */
|
|
534
|
+
private hiddenFromHot;
|
|
411
535
|
retryFailed(): void;
|
|
412
536
|
next(): Promise<DiscoveryFeedPage>;
|
|
413
537
|
private read;
|
|
@@ -421,10 +545,35 @@ export declare class DiscoveryFeedSession {
|
|
|
421
545
|
*/
|
|
422
546
|
private walkTopics;
|
|
423
547
|
private readPage;
|
|
424
|
-
/**
|
|
548
|
+
/**
|
|
549
|
+
* Page 0: the top's lead when it names one, else the top breaking cluster, else the top cluster. Every second later
|
|
550
|
+
* page: the next unused story or viral cluster.
|
|
551
|
+
*/
|
|
425
552
|
private radarCluster;
|
|
426
|
-
/** Page 0: the top breaking cluster, else the top cluster. Later pages: the next unused story or viral cluster. */
|
|
553
|
+
/** Page 0: the top breaking cluster, else the top cluster. Later pages: the next unused story or viral cluster (pickRadarCluster). */
|
|
427
554
|
private pickCluster;
|
|
555
|
+
/** Page 0's story: the top's lead (from the radar snapshot when it holds it, so its links and members come along), else the client's pick. */
|
|
556
|
+
private leadOf;
|
|
557
|
+
/** One top item as its own module, in the top's order: a video plays as a video module (unless the member's layouts say posts). */
|
|
558
|
+
private topModule;
|
|
559
|
+
/**
|
|
560
|
+
* The top's read, then the posts its items need, all under one deadline of DISCOVERY_TOP_TIMEOUT_MS from the page's
|
|
561
|
+
* start: the reads of its posts get what is left of it, never a deadline of their own. Null when it failed, timed out
|
|
562
|
+
* or answered nothing usable: the page is then as without a top.
|
|
563
|
+
*/
|
|
564
|
+
private loadTop;
|
|
565
|
+
/**
|
|
566
|
+
* Checks a top answer and reads what its items need before `deadline`: Minds posts by id, a story's network posts
|
|
567
|
+
* (lanes come with their cards). A lead it names starts reading its member posts at once.
|
|
568
|
+
*/
|
|
569
|
+
private readyTop;
|
|
570
|
+
/**
|
|
571
|
+
* Places the top on page 0: each lane stands in for its network's first read (a topics network's catalog previews; a
|
|
572
|
+
* posts network's read follows its lane), and the top's items, read by id or carried by this page, become the page's
|
|
573
|
+
* first items, marked seen so no hot page repeats them. Without impressions in the top's order, the member's seen
|
|
574
|
+
* items follow the unseen ones.
|
|
575
|
+
*/
|
|
576
|
+
private placeTop;
|
|
428
577
|
/** The story's members as feed items, read once per story and bounded to three seconds; a failed read is simply none. */
|
|
429
578
|
private memberPosts;
|
|
430
579
|
private readMembers;
|
|
@@ -437,7 +586,8 @@ export declare class DiscoveryResource {
|
|
|
437
586
|
private protocols;
|
|
438
587
|
private curator?;
|
|
439
588
|
private http?;
|
|
440
|
-
|
|
589
|
+
private feeds?;
|
|
590
|
+
constructor(posts: PostsResource, protocols: ProtocolsResource, curator?: Pick<CuratorResource, "forYou"> | undefined, http?: Pick<HttpClient, "get" | "post"> | undefined, feeds?: Pick<FeedsResource, "top"> | undefined);
|
|
441
591
|
/**
|
|
442
592
|
* Suggestions for the signed-in viewer from the app's one recommender: active or warm members, groups
|
|
443
593
|
* with several posters, tags used by several people, open-network accounts or stories, overall or in one
|
|
@@ -497,6 +647,10 @@ export declare class DiscoveryResource {
|
|
|
497
647
|
* Discover's feed session. `compass` (a Politics choice, as `posts.list` takes it) goes on every Minds read, whose
|
|
498
648
|
* pages then carry `meta.compass_filter`; the open networks' reads and the radar are not filtered by it, so a view
|
|
499
649
|
* that leaves posts out (Hide politics, Choose sides) is best read with `source: 'minds'`.
|
|
650
|
+
*
|
|
651
|
+
* `top` opens the first page with the top of the feed (`feeds.top`, with the same `compass`, unless `top.read` is
|
|
652
|
+
* given); `seen` leaves what the member saw out of the hot pages; `seenClusters` and `bucket` rotate the story when
|
|
653
|
+
* the top names none (DiscoveryFeedSessionOptions). With none of them the session is what it was before they existed.
|
|
500
654
|
*/
|
|
501
655
|
createFeed(options: {
|
|
502
656
|
networks: SocialNetwork[];
|
|
@@ -505,5 +659,5 @@ export declare class DiscoveryResource {
|
|
|
505
659
|
learning?: DiscoveryLearning;
|
|
506
660
|
recommend?: DiscoveryRecommendationSources;
|
|
507
661
|
compass?: string;
|
|
508
|
-
}): DiscoveryFeedSession;
|
|
662
|
+
} & DiscoveryFeedSessionOptions): DiscoveryFeedSession;
|
|
509
663
|
}
|