@recursiv/sdk 0.7.57 → 0.7.59

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.
@@ -1,5 +1,5 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { DiscoveryLayout, PaginatedResponse, PaginationParams, SingleResponse } from '../types.js';
2
+ import type { CompassFilterMeta, DiscoveryLayout, PaginatedResponse, PaginationParams, SingleResponse } from '../types.js';
3
3
  /** Canonical protocol operation names returned by the adapter registry. */
4
4
  export declare const PROTOCOL_CAPABILITY_OPERATIONS: readonly ["discover", "stream", "backfill", "search", "resolveIdentity", "fetchProfile", "fetchThread", "publish", "reply", "react", "repost", "follow", "delete", "health", "checkpoint"];
5
5
  export declare const PROTOCOL_CAPABILITY_STATUSES: readonly ["supported", "planned", "unsupported"];
@@ -906,7 +906,8 @@ export interface SocialNetwork {
906
906
  url: string;
907
907
  };
908
908
  coverage: string;
909
- capabilities: Array<'profile_search' | 'profile_read' | 'original_author_posts' | 'minds_subscription' | 'following_feed' | 'conversation_read' | 'public_trends' | 'trend_feed' | 'popular_accounts' | 'post_search'>;
909
+ /** `compass_filter`: the Politics filter is served to this viewer (an app offers it only then); listed on every network. */
910
+ capabilities: Array<'profile_search' | 'profile_read' | 'original_author_posts' | 'minds_subscription' | 'following_feed' | 'conversation_read' | 'public_trends' | 'trend_feed' | 'popular_accounts' | 'post_search' | 'compass_filter'>;
910
911
  profileSearchPagination: boolean;
911
912
  postSearchAccountRequired?: boolean;
912
913
  /** Categories available from the current app configuration. */
@@ -930,8 +931,22 @@ export interface SocialProfile {
930
931
  postsCount: number | null;
931
932
  /** Nostr only: the NIP-05 name its domain has confirmed for this key ("lyn@primal.net"); `handle` stays the key. */
932
933
  nip05?: string;
934
+ /** A bridge (Bridgy Fed, Mostr) made this account from one on another network (SocialBridge). Present only on a bridged account. */
935
+ bridge?: SocialBridge;
933
936
  subscription: SocialSubscription | null;
934
937
  }
938
+ /**
939
+ * A post or account a bridge copied from another network: Bridgy Fed (`*.brid.gy`), Mostr (`mostr.pub`), or a Nostr
940
+ * event's NIP-48 `proxy` tag naming no bridge the server knows (`proxy`). `originId` names the original, normalised (a
941
+ * Bluesky at-uri or DID, an ActivityPub object URL or `user@host`, a Nostr event id or key, a web URL), when the bridge
942
+ * says. Where the server reads both, feeds, search and the radar already fold a copy into its original; an app that
943
+ * merges several networks' pages itself can fold the same way: a copy whose original is shown is the same post.
944
+ */
945
+ export interface SocialBridge {
946
+ via: 'bridgy_fed' | 'mostr' | 'proxy';
947
+ protocol: 'bluesky' | 'activitypub' | 'nostr' | 'web';
948
+ originId?: string;
949
+ }
935
950
  export interface SocialSubscription {
936
951
  id: string;
937
952
  protocol: string;
@@ -990,7 +1005,10 @@ export interface SocialPost {
990
1005
  * older than 15 minutes when the provider answers in time). Absent: the counts are from this read.
991
1006
  */
992
1007
  countsObservedAt?: string;
993
- /** 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. */
1008
+ /**
1009
+ * Pixel dimensions when the source declares them (Bluesky aspect ratio, Mastodon attachment meta, Nostr NIP-92 `dim`), so a
1010
+ * card can take the clip's shape before it loads. Each entry of a sensitive post carries `sensitive: true`.
1011
+ */
994
1012
  media: Array<{
995
1013
  type: 'image' | 'video';
996
1014
  url: string;
@@ -998,8 +1016,15 @@ export interface SocialPost {
998
1016
  poster?: string;
999
1017
  width?: number;
1000
1018
  height?: number;
1019
+ sensitive?: true;
1001
1020
  }>;
1002
1021
  hasAdditionalContent: boolean;
1022
+ /**
1023
+ * The author marked the post sensitive with one of Minds' sensitive tags (#nsfw, #nudity and the rest), in its text or
1024
+ * among the network's own tags: cover its media as a Minds post flagged NSFW is covered. A post the network itself marks
1025
+ * (a Mastodon sensitive flag or content warning, a Bluesky label, a Nostr content-warning) is never returned at all.
1026
+ */
1027
+ sensitive?: boolean;
1003
1028
  /** At most one independently filtered quoted post. */
1004
1029
  quote?: {
1005
1030
  state: 'available';
@@ -1018,6 +1043,8 @@ export interface SocialPost {
1018
1043
  * to signed-out visitors.
1019
1044
  */
1020
1045
  authorOptOut?: true;
1046
+ /** A bridge (Bridgy Fed, Mostr, a NIP-48 proxy tag) made this post from one on another network (SocialBridge). Present only on a copy read live. */
1047
+ bridge?: SocialBridge;
1021
1048
  }
1022
1049
  export interface SocialPostSearch {
1023
1050
  items: SocialPost[];
@@ -1152,7 +1179,11 @@ export interface SocialRadarMember {
1152
1179
  */
1153
1180
  visitorMedia?: true;
1154
1181
  }
1155
- /** One story on the radar. `breaking` is outlet corroboration inside a short window; `viral` is social velocity above its network's norm. */
1182
+ /**
1183
+ * One story on the radar. `breaking` is outlet corroboration inside a short window; `viral` is social velocity above its
1184
+ * network's norm, from two or more people, on a story still gaining engagement, reported by no outlet or by two or more.
1185
+ * `velocity.perHour` is the engagement its posts gained per hour since the last build, counting only posts read on both.
1186
+ */
1156
1187
  export interface SocialRadarCluster {
1157
1188
  id: string;
1158
1189
  /** Verbatim: a World Today headline, a provider topic name or the medoid outlet title. Never generated. */
@@ -1249,7 +1280,9 @@ export interface SocialRadarStoryArchive {
1249
1280
  }
1250
1281
  /**
1251
1282
  * One radar story by id, with its social member posts.
1252
- * `live`: on the current radar (`cluster` and `items` present). `aged_out`: not on this radar, but
1283
+ * `live`: on the current radar (`cluster` and `items` present), or left it sooner than its minimum lifetime after it was
1284
+ * first published (six hours by default), or within the last half hour: then `liveUntil` says until when, and `successor`
1285
+ * names the story on the radar that continues it, if one does. `aged_out`: not on this radar, but
1253
1286
  * held by the story archive (`archive`, and the live story it continues as in `successor`) or seen on
1254
1287
  * one of the last six radars (`archive` null). `unknown`: in neither (never existed, or left longer ago
1255
1288
  * than the archive keeps stories). A server without the story archive leaves `archive` and `successor` out.
@@ -1266,12 +1299,190 @@ export interface SocialRadarStory {
1266
1299
  id: string;
1267
1300
  label: string;
1268
1301
  } | null;
1302
+ /** On a `live` answer for a story no longer on the radar list: when it stops answering `live` and ages out. */
1303
+ liveUntil?: string;
1269
1304
  /**
1270
1305
  * Present when the story was asked for by a World Today edition story's id: the radar story that edition story
1271
1306
  * seeded, and `id` is its radar id, the address to link to. A server that cannot resolve edition story ids answers 400.
1272
1307
  */
1273
1308
  editionStory?: string;
1274
1309
  }
1310
+ /**
1311
+ * The Viral read (`getPublicViral`, `getSharedViral`): what is moving fastest on Bluesky, the Fediverse, Nostr and
1312
+ * Minds right now, each measured against its own network's pace, from a snapshot the server's rank tick rebuilds about
1313
+ * every five minutes. A read never reaches a provider or the model. These types mirror the server's
1314
+ * ViralSnapshotContract; a contract test compares them key by key.
1315
+ */
1316
+ export type ViralNetwork = 'all' | 'minds' | 'bluesky' | 'activitypub' | 'nostr';
1317
+ export type ViralProtocol = 'bluesky' | 'activitypub' | 'nostr' | 'minds';
1318
+ /** The networks whose posts the read serves as a `SocialPost`. */
1319
+ export type ViralOpenProtocol = 'bluesky' | 'activitypub' | 'nostr';
1320
+ /**
1321
+ * `viral`: a measured rate well above its network's baseline (z of 1 or more) with two or more kinds of engagement;
1322
+ * `rising`: a measured rate speeding up (accel of 0.7 or more) on a post under six hours old; `hot`: everything else,
1323
+ * including every post without a measured rate.
1324
+ */
1325
+ export type ViralLabel = 'viral' | 'rising' | 'hot';
1326
+ /**
1327
+ * `ok`: a build served. `building`: no build yet. `screening`: served, but fewer than 30% of the top 200 posts have
1328
+ * a safety verdict yet. `off`: the read is not enabled for this app (or, on `getSharedViral` only, the API predates it):
1329
+ * no items, and nothing to retry until it is turned on.
1330
+ */
1331
+ export type ViralReadStatus = 'ok' | 'building' | 'screening' | 'off';
1332
+ /** Every parameter is optional. The server rejects any other parameter with a 400, so only these travel. */
1333
+ export interface ViralReadParams {
1334
+ /** Default `all`: every network, with a per-network quota so no one network fills the page. */
1335
+ network?: ViralNetwork;
1336
+ /** A Discover category id: only posts judged into it. */
1337
+ category?: string;
1338
+ /**
1339
+ * `video`: only posts whose own media includes a video. A member's read serves every such video. A visitor's read
1340
+ * (`getSharedViral`) still serves a post's media only where the server cleared it, so some items can arrive with
1341
+ * the video withheld (the card's `media` empty): keep `items.filter(viralItemHasVideo)` for a video-only player.
1342
+ */
1343
+ media?: 'video';
1344
+ /** Page size, default 20. */
1345
+ limit?: 5 | 10 | 20 | 40;
1346
+ /** The previous page's `cursor`. A cursor names its build; one whose build expired (30 minutes) ends the list. */
1347
+ cursor?: string;
1348
+ /** A Politics choice. A visitor (`getSharedViral`) may send only `all` or `hide`; anything else is a 400. */
1349
+ compass?: string;
1350
+ }
1351
+ /** How fast a post is gathering reactions, reposts and replies (per hour), against its network's baseline. */
1352
+ export interface ViralItemVelocity {
1353
+ perHour1h: number;
1354
+ perHour6h: number;
1355
+ /** The 1-hour rate against its network's trailing baseline, from -2 to 4 (1 or more is well above). */
1356
+ z: number;
1357
+ /** ln((1 + perHour1h) / (1 + perHour6h)), from -1 to 2: above 0 is speeding up. */
1358
+ accel: number;
1359
+ /** Readings behind the rate; null for a Minds post. */
1360
+ samples: number | null;
1361
+ spanHours: number;
1362
+ /** The rate comes from two or more readings over time (a Minds post's always does). An unmeasured post is never viral or rising. */
1363
+ measured: boolean;
1364
+ /** Who counted: Bluesky's AppView, one Fediverse server, a third party's report (Primal, for Nostr), or Minds itself. */
1365
+ countsOrigin: 'appview' | 'instance_local' | 'provider_reported' | 'minds';
1366
+ /** Minutes since the counts were last read, when known. */
1367
+ countsAgeMin?: number;
1368
+ /** Present when a third party reported the counts (`Primal`): show them as reported by it, never as verified. */
1369
+ countsReportedBy?: string;
1370
+ }
1371
+ /** Another post in the same spread group: the same link posted on another network. */
1372
+ export interface ViralRef {
1373
+ protocol: ViralProtocol;
1374
+ nativeId: string;
1375
+ }
1376
+ export interface ViralItemSpread {
1377
+ /** The networks sharing the post's link (its own alone when no other network carries it). */
1378
+ protocols: ViralProtocol[];
1379
+ /** Fediverse: how many servers' trends lists carried the post. */
1380
+ instances?: number;
1381
+ clusterId: null;
1382
+ alsoOn: ViralRef[];
1383
+ }
1384
+ /** A Minds post's card as the read serves it; open it at /post/:id. A visitor's carries media only where the server cleared it. */
1385
+ export interface ViralMindsCard {
1386
+ id: string;
1387
+ title: string | null;
1388
+ text: string;
1389
+ createdAt: string;
1390
+ author: {
1391
+ name: string | null;
1392
+ username: string | null;
1393
+ } | null;
1394
+ media: Array<{
1395
+ type: string;
1396
+ url: string;
1397
+ width: number | null;
1398
+ height: number | null;
1399
+ thumbnailUrl: string | null;
1400
+ hlsUrl: string | null;
1401
+ }>;
1402
+ likeCount: number;
1403
+ repostCount: number;
1404
+ replyCount: number;
1405
+ }
1406
+ /** What every ranked post carries, whichever network it comes from. */
1407
+ export interface ViralItemFields {
1408
+ /** Stable across builds: use it to drop a post already shown. */
1409
+ key: string;
1410
+ label: ViralLabel;
1411
+ score: number;
1412
+ /** 1-based position in this read's ordering, across pages. */
1413
+ rank: number;
1414
+ category: string | null;
1415
+ velocity: ViralItemVelocity;
1416
+ spread: ViralItemSpread;
1417
+ }
1418
+ /** A ranked Minds post; open it at /post/:id. */
1419
+ export interface ViralMindsItem extends ViralItemFields {
1420
+ protocol: 'minds';
1421
+ post: ViralMindsCard;
1422
+ }
1423
+ /** A ranked Bluesky, Fediverse or Nostr post. */
1424
+ export interface ViralSocialItem extends ViralItemFields {
1425
+ protocol: ViralOpenProtocol;
1426
+ post: SocialPost;
1427
+ }
1428
+ /**
1429
+ * One ranked post: check `protocol` and TypeScript narrows `post` (a `ViralMindsCard` for `minds`, a `SocialPost`
1430
+ * otherwise). No item carries a compass value, an author id, a safety flag or its media kind. A visitor's card keeps
1431
+ * its own images and video only where the server cleared them; a withheld one has empty `media` (a `SocialPost` also
1432
+ * has `hasAdditionalContent: true`). Even under `media: 'video'` a visitor's item can arrive without its video, so
1433
+ * test each one with `viralItemHasVideo` before a video player.
1434
+ */
1435
+ export type ViralItem = ViralMindsItem | ViralSocialItem;
1436
+ /**
1437
+ * Whether the item's own card carries a playable video. Use it on a `media: 'video'` read before a Watch-style
1438
+ * player: a visitor's read can serve an item whose video the server withheld (its card's `media` is then empty).
1439
+ * A video only in a quoted post does not count, as the server never counts a quote's media.
1440
+ */
1441
+ export declare function viralItemHasVideo(item: ViralItem): boolean;
1442
+ /** A network's trailing pace that velocity is measured against. */
1443
+ export interface ViralBaseline {
1444
+ median: number;
1445
+ p90: number;
1446
+ n: number;
1447
+ source: 'trailing' | 'tick' | 'prior';
1448
+ }
1449
+ export interface ViralProvider {
1450
+ name: string;
1451
+ ok: boolean;
1452
+ lastOkAt: string | null;
1453
+ observedAt: string;
1454
+ }
1455
+ export interface ViralReadMeta {
1456
+ network: ViralNetwork;
1457
+ category_filter: {
1458
+ selected: string;
1459
+ } | null;
1460
+ media_filter: 'video' | null;
1461
+ /** Null unless the read named a filtering `compass`: what Hide politics left out (sides and Mix are not applied here). */
1462
+ compass_filter: CompassFilterMeta | null;
1463
+ safety_filter: {
1464
+ withheld: number;
1465
+ pending: number;
1466
+ };
1467
+ }
1468
+ export interface ViralReadResponse {
1469
+ items: ViralItem[];
1470
+ /** Pass as `cursor` for the next page; null at the end of this build's list. */
1471
+ cursor: string | null;
1472
+ builtAt: string | null;
1473
+ status: ViralReadStatus;
1474
+ window: {
1475
+ primary: '1h';
1476
+ context: '6h';
1477
+ };
1478
+ baselines: Record<string, ViralBaseline>;
1479
+ provenance: {
1480
+ providers: ViralProvider[];
1481
+ };
1482
+ meta: ViralReadMeta;
1483
+ /** True when no further page exists in this build (always on `off` and `building`). */
1484
+ exhausted: boolean;
1485
+ }
1275
1486
  /** Read options for callers that hold their own deadline, such as a discovery page. */
1276
1487
  export interface SocialReadOptions {
1277
1488
  retry?: boolean;
@@ -1302,6 +1513,13 @@ export interface SocialConversation {
1302
1513
  replies: SocialPost[];
1303
1514
  coverage: 'public_provider_window';
1304
1515
  limited: boolean;
1516
+ /**
1517
+ * The member hid this post, or blocks or mutes its author: `focus` is the post, so the app can show it on request,
1518
+ * `replies` is empty, and `viewerControls` are the member's controls to undo. Absent otherwise (a post the app's
1519
+ * moderation withholds is `post_unavailable`).
1520
+ */
1521
+ hiddenByViewer?: true;
1522
+ viewerControls?: Array<Pick<SocialSourceControl, 'source' | 'control'>>;
1305
1523
  }
1306
1524
  export interface SocialFollowingSource {
1307
1525
  subscriptionId: string;
@@ -1384,6 +1602,14 @@ export declare class ProtocolsResource {
1384
1602
  getSharedRadarStory(projectId: string, params: {
1385
1603
  cluster: string;
1386
1604
  }): Promise<SingleResponse<SocialRadarStory>>;
1605
+ /**
1606
+ * The Viral read without a key, for a signed-out visitor: posts whose authors allow wide reach only, and media only
1607
+ * where the server cleared it, so a `media: 'video'` read can carry items whose video was withheld (filter with
1608
+ * `viralItemHasVideo`). `status: 'off'` (no items, never thrown) until the server enables the read for the app;
1609
+ * an API that predates the read answers this path 401 `missing_api_key`, which resolves as off too. Every other
1610
+ * failure throws. Cacheable for a minute by a shared cache.
1611
+ */
1612
+ getSharedViral(projectId: string, params?: ViralReadParams, options?: SocialReadOptions): Promise<SingleResponse<ViralReadResponse>>;
1387
1613
  publicNetworks(): Promise<SingleResponse<SocialNetwork[]>>;
1388
1614
  /** Query the live public network. It does not import users or posts. */
1389
1615
  searchPublicPosts(params: SearchPublicPostsParams): Promise<SingleResponse<SocialPostSearch>>;
@@ -1452,6 +1678,12 @@ export declare class ProtocolsResource {
1452
1678
  getPublicRadarStory(params: {
1453
1679
  cluster: string;
1454
1680
  }, options?: SocialReadOptions): Promise<SingleResponse<SocialRadarStory>>;
1681
+ /**
1682
+ * The Viral read under the signed-in member's app key (scopes `users:read` and `posts:read`): every network's posts
1683
+ * the build keeps, less the member's own blocks, mutes and hidden posts. Never cached. `status: 'off'` resolves with
1684
+ * no items; a 401 or 403 always throws (a session or key failure is never reported as off).
1685
+ */
1686
+ getPublicViral(params?: ViralReadParams, options?: SocialReadOptions): Promise<SingleResponse<ViralReadResponse>>;
1455
1687
  /** Provider-ranked public accounts with explicit provenance; served from the app's index when a fresh snapshot exists. */
1456
1688
  getPublicPopularProfiles(params: {
1457
1689
  protocol: string;
@@ -1,4 +1,4 @@
1
- import { ValidationError } from '../errors.js';
1
+ import { AuthenticationError, ValidationError } from '../errors.js';
2
2
  /** Canonical protocol operation names returned by the adapter registry. */
3
3
  export const PROTOCOL_CAPABILITY_OPERATIONS = Object.freeze([
4
4
  'discover',
@@ -34,12 +34,43 @@ function hostedIdentityPath(id) {
34
34
  }
35
35
  return `/protocols/identities/${encodeURIComponent(id)}`;
36
36
  }
37
+ /**
38
+ * Whether the item's own card carries a playable video. Use it on a `media: 'video'` read before a Watch-style
39
+ * player: a visitor's read can serve an item whose video the server withheld (its card's `media` is then empty).
40
+ * A video only in a quoted post does not count, as the server never counts a quote's media.
41
+ */
42
+ export function viralItemHasVideo(item) {
43
+ return item.post.media.some(media => media.type === 'video');
44
+ }
37
45
  /** Only the two radar fields travel; caller scope or destination extras never reach the query string. */
38
46
  /** Forwarded only when given, so a default read makes exactly the call it always made. */
39
47
  const readOptions = (options) => options ? [options] : [];
40
48
  function radarQuery(params) {
41
49
  return { ...(params.category !== undefined ? { category: params.category } : {}), ...(params.limit !== undefined ? { limit: params.limit } : {}) };
42
50
  }
51
+ /** Only the six Viral read fields travel, each only when given: the read answers any other parameter with a 400. */
52
+ function viralQuery(params) {
53
+ const query = {};
54
+ for (const key of ['network', 'category', 'media', 'limit', 'cursor', 'compass'])
55
+ if (params[key] !== undefined)
56
+ query[key] = params[key];
57
+ return query;
58
+ }
59
+ /** An off read is empty whatever else its body carried. Every other status is passed through as served. */
60
+ function viralRead(response) {
61
+ if (response.data?.status !== 'off')
62
+ return response;
63
+ return { ...response, data: { ...response.data, items: [], cursor: null, exhausted: true } };
64
+ }
65
+ /** The server's own off answer (ViralReadService), built here for an API that predates the read. */
66
+ function viralOff(params) {
67
+ return { data: {
68
+ items: [], cursor: null, builtAt: null, status: 'off', window: { primary: '1h', context: '6h' }, baselines: {}, provenance: { providers: [] },
69
+ meta: { network: params.network ?? 'all', category_filter: params.category !== undefined ? { selected: params.category } : null,
70
+ media_filter: params.media ?? null, compass_filter: null, safety_filter: { withheld: 0, pending: 0 } },
71
+ exhausted: true,
72
+ } };
73
+ }
43
74
  /** Forward only the media contract fields; nothing else from the app's own media rows. */
44
75
  const nativeMediaInput = (media) => media.map(item => ({ url: item.url,
45
76
  ...(item.alt !== undefined ? { alt: item.alt } : {}), ...(item.width !== undefined ? { width: item.width } : {}),
@@ -117,6 +148,23 @@ export class ProtocolsResource {
117
148
  getSharedRadarStory(projectId, params) {
118
149
  return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/radar/story`, { cluster: params.cluster });
119
150
  }
151
+ /**
152
+ * The Viral read without a key, for a signed-out visitor: posts whose authors allow wide reach only, and media only
153
+ * where the server cleared it, so a `media: 'video'` read can carry items whose video was withheld (filter with
154
+ * `viralItemHasVideo`). `status: 'off'` (no items, never thrown) until the server enables the read for the app;
155
+ * an API that predates the read answers this path 401 `missing_api_key`, which resolves as off too. Every other
156
+ * failure throws. Cacheable for a minute by a shared cache.
157
+ */
158
+ async getSharedViral(projectId, params = {}, options) {
159
+ try {
160
+ return viralRead(await this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/viral`, viralQuery(params), ...readOptions(options)));
161
+ }
162
+ catch (error) {
163
+ if (error instanceof AuthenticationError && error.code === 'missing_api_key')
164
+ return viralOff(params);
165
+ throw error;
166
+ }
167
+ }
120
168
  publicNetworks() {
121
169
  return this.client.get('/protocols/social/networks');
122
170
  }
@@ -188,6 +236,14 @@ export class ProtocolsResource {
188
236
  getPublicRadarStory(params, options) {
189
237
  return this.client.get('/protocols/social/radar/story', { cluster: params.cluster }, ...readOptions(options));
190
238
  }
239
+ /**
240
+ * The Viral read under the signed-in member's app key (scopes `users:read` and `posts:read`): every network's posts
241
+ * the build keeps, less the member's own blocks, mutes and hidden posts. Never cached. `status: 'off'` resolves with
242
+ * no items; a 401 or 403 always throws (a session or key failure is never reported as off).
243
+ */
244
+ async getPublicViral(params = {}, options) {
245
+ return viralRead(await this.client.get('/protocols/social/viral', viralQuery(params), ...readOptions(options)));
246
+ }
191
247
  /** Provider-ranked public accounts with explicit provenance; served from the app's index when a fresh snapshot exists. */
192
248
  getPublicPopularProfiles(params) {
193
249
  return this.client.get('/protocols/social/popular-profiles', { protocol: params.protocol, limit: params.limit, cursor: params.cursor });
@@ -1,5 +1,5 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { Post } from '../types.js';
2
+ import type { Post, ViewerModeration } from '../types.js';
3
3
  import type { SocialPost } from './protocols.js';
4
4
  /** The sections a blended search can answer, in no particular order (see `SearchAnswer.order`). */
5
5
  export declare const SEARCH_SECTIONS: readonly ["top_posts", "latest_posts", "open_network", "people", "groups", "topics", "stories", "related", "trending"];
@@ -34,6 +34,14 @@ export interface SearchPerson {
34
34
  plus: boolean;
35
35
  pro: boolean;
36
36
  founder: boolean;
37
+ /**
38
+ * The account wears the display name of a verified or founder account it is not: it is listed after every account
39
+ * that is not such a copy. Label it as not the account at `name_matches_verified_handle`. Absent from an older API.
40
+ */
41
+ name_matches_verified?: boolean;
42
+ name_matches_verified_handle?: string | null;
43
+ /** Whether the caller mutes this account (people search never lists an account in a block with the caller). */
44
+ viewer?: ViewerModeration;
37
45
  }
38
46
  export interface SearchGroup {
39
47
  id: string;
package/dist/types.d.ts CHANGED
@@ -54,8 +54,41 @@ export type FalloverStep = {
54
54
  } | {
55
55
  kind: 'all';
56
56
  };
57
+ /**
58
+ * What a Politics choice (`compass`) did to a page, on the feeds that take one (`posts.list` on discovery, category
59
+ * and Minds' own lists, `curator.forYou`). Counts only: never a post's placement, never the cells asked for.
60
+ * `unavailable` means nothing was applied (the filter is not served to this caller yet, or the judgments are not
61
+ * there). Under Choose sides every post counts once as kept or left out; `non_political`, `pending` and `unplaced`
62
+ * (with posts judged maybe) are the not-placed rest. `mix` is Mix sides' band counts over the page served.
63
+ */
64
+ export interface CompassFilterMeta {
65
+ status: 'applied' | 'unavailable';
66
+ mode: 'all' | 'mix' | 'hide' | 'cells';
67
+ kept: number;
68
+ left_out: number;
69
+ unplaced: number;
70
+ pending: number;
71
+ non_political: number;
72
+ uncertain_shown: number;
73
+ truncated: boolean;
74
+ rubric?: string;
75
+ mix?: {
76
+ econ: {
77
+ l: number;
78
+ c: number;
79
+ r: number;
80
+ };
81
+ power: {
82
+ l: number;
83
+ m: number;
84
+ o: number;
85
+ };
86
+ };
87
+ }
57
88
  export interface PaginationMeta {
58
89
  discovery?: DiscoveryLayout;
90
+ /** Present when the request named `compass`: what the Politics choice did to this page. */
91
+ compass_filter?: CompassFilterMeta;
59
92
  scanned?: number;
60
93
  category_filter?: {
61
94
  selected: string;
@@ -118,6 +151,12 @@ export interface UserSummary {
118
151
  username: string;
119
152
  image: string | null;
120
153
  is_ai: boolean;
154
+ /**
155
+ * The account labels itself adult by its own handle, display name or bio:
156
+ * cover its avatar unless the member turned Show NSFW on. Absent from
157
+ * servers older than this field and from reads that do not label accounts.
158
+ */
159
+ is_nsfw?: boolean;
121
160
  }
122
161
  export interface User extends UserSummary {
123
162
  bio: string | null;
@@ -254,11 +293,24 @@ export interface RepostedPost {
254
293
  media: MediaItem[];
255
294
  created_at: string;
256
295
  }
296
+ /**
297
+ * What the signed-in caller has done about an account: they block it, they mute it. Never what the account did about
298
+ * the caller. Absent when the server could not read it, and from an older API.
299
+ */
300
+ export interface ViewerModeration {
301
+ blocking: boolean;
302
+ muting: boolean;
303
+ }
257
304
  export interface PostDetail extends Post {
258
305
  /** The newest replies (at most 50). */
259
306
  replies: PostReply[];
260
307
  /** Present when the thread has more replies; pass it to `posts.replies(id, { cursor })` for the next, older page. */
261
308
  replies_next_cursor?: string | null;
309
+ /**
310
+ * Whether the caller blocks or mutes the author, so a page can say "You blocked @x" (with View and Unblock) instead
311
+ * of offering a reply or a vote: reacting to or replying to a post across a block answers 403.
312
+ */
313
+ viewer?: ViewerModeration;
262
314
  }
263
315
  export interface PostReply {
264
316
  id: string;
@@ -329,6 +381,21 @@ export interface ListPostsParams extends PaginationParams {
329
381
  discovery?: boolean;
330
382
  /** Public-only broad category. Requires an enabled app experiment; limit <=25. */
331
383
  category?: string;
384
+ /**
385
+ * A Politics choice on a discovery, category or Minds list read: `all`, `mix`, `hide`, or cells in the canonical
386
+ * order `lo,co,ro,lm,cm,rm,ll,cl,rl`, optionally followed by `np`. Any other spelling is 400 `invalid_compass`; on a
387
+ * profile, group, Following, liked or saved read it is 400 `compass_not_supported`. Mix sides and Choose sides
388
+ * spend the member's hourly budget (429 `compass_rate_limited`). The page says what it did in
389
+ * `meta.compass_filter`. Only Minds posts are filtered.
390
+ */
391
+ compass?: string;
392
+ /**
393
+ * `false` asks once: the first 5xx or 429 is thrown, with no retry and no Retry-After sleep. For a read that holds its
394
+ * own deadline, such as a Discover feed session's Minds read. Never sent to the server. It rides in the params, not
395
+ * in a second argument, so a reader wrapped by a function that forwards only its params (as the Minds app wraps the
396
+ * `posts.list` it hands a feed session, to add a Politics choice) still asks once.
397
+ */
398
+ retry?: boolean;
332
399
  community_id?: string;
333
400
  author_id?: string;
334
401
  organization_id?: string;
@@ -336,6 +403,8 @@ export interface ListPostsParams extends PaginationParams {
336
403
  tag_ids?: string;
337
404
  /** Pass 'me' to return only posts where audience_user_id matches the caller (personal agent feed). */
338
405
  audience_user_id?: 'me' | string;
406
+ /** Return only posts by accounts the authenticated caller follows, newest first (the Following feed). */
407
+ following?: boolean;
339
408
  /** Return only posts the authenticated caller has upvoted. */
340
409
  liked?: boolean;
341
410
  /**
@@ -514,6 +583,12 @@ export interface CommunityMember {
514
583
  image: string | null;
515
584
  bio: string | null;
516
585
  is_ai: boolean;
586
+ /**
587
+ * The account labels itself adult by its own handle, display name or bio:
588
+ * cover its avatar unless the member turned Show NSFW on. Absent from
589
+ * servers older than this field and from reads that do not label accounts.
590
+ */
591
+ is_nsfw?: boolean;
517
592
  role: string;
518
593
  joined_at: string;
519
594
  }
@@ -1017,6 +1092,12 @@ export interface Profile {
1017
1092
  location: string | null;
1018
1093
  website: string | null;
1019
1094
  is_ai: boolean;
1095
+ /**
1096
+ * The account labels itself adult by its own handle, display name or bio:
1097
+ * cover its avatar unless the member turned Show NSFW on. Absent from
1098
+ * servers older than this field and from reads that do not label accounts.
1099
+ */
1100
+ is_nsfw?: boolean;
1020
1101
  created_at: string;
1021
1102
  }
1022
1103
  /** A row of a followers/following list: the profile plus the caller's relationship with it. */
@@ -1086,6 +1167,14 @@ export interface ProfileSearchResult extends Profile {
1086
1167
  plus: boolean;
1087
1168
  pro: boolean;
1088
1169
  founder: boolean;
1170
+ /**
1171
+ * The account wears the display name of a verified or founder account it is not: it is listed after every account
1172
+ * that is not such a copy. Label it as not the account at `name_matches_verified_handle`. Absent from an older API.
1173
+ */
1174
+ name_matches_verified?: boolean;
1175
+ name_matches_verified_handle?: string | null;
1176
+ /** Whether the caller mutes this account (a search never lists an account in a block with the caller). */
1177
+ viewer?: ViewerModeration;
1089
1178
  }
1090
1179
  export interface SearchMentionsParams {
1091
1180
  /** What the member typed after `@` (a leading `@` is ignored). */