@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.
- package/README.md +16 -4
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +12 -0
- package/dist/index.d.ts +7 -6
- package/dist/index.js +2 -2
- package/dist/resources/agents.d.ts +70 -0
- package/dist/resources/agents.js +10 -0
- package/dist/resources/curator.d.ts +5 -1
- package/dist/resources/curator.js +6 -2
- package/dist/resources/discovery.d.ts +83 -1
- package/dist/resources/discovery.js +36 -6
- package/dist/resources/e2ee.d.ts +115 -2
- package/dist/resources/e2ee.js +41 -3
- package/dist/resources/email.d.ts +27 -3
- package/dist/resources/email.js +18 -3
- package/dist/resources/env-vars.d.ts +25 -0
- package/dist/resources/env-vars.js +8 -0
- package/dist/resources/moderation.d.ts +22 -0
- package/dist/resources/moderation.js +16 -0
- package/dist/resources/posts.d.ts +1 -1
- package/dist/resources/posts.js +6 -2
- package/dist/resources/protocols.d.ts +237 -5
- package/dist/resources/protocols.js +57 -1
- package/dist/resources/search.d.ts +9 -1
- package/dist/types.d.ts +89 -0
- package/llm.md +17 -4
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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)
|
|
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). */
|