@singi-labs/sifa-sdk 0.19.49 → 0.19.50

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/dist/index.cjs CHANGED
@@ -7128,7 +7128,7 @@ function describeSifaRecord(collection, value) {
7128
7128
  }
7129
7129
 
7130
7130
  // src/index.ts
7131
- var SIFA_SDK_VERSION = "0.19.49";
7131
+ var SIFA_SDK_VERSION = "0.19.50";
7132
7132
 
7133
7133
  exports.ACTIVITY_TIERS = ACTIVITY_TIERS;
7134
7134
  exports.ACTIVITY_VERBS = ACTIVITY_VERBS;
package/dist/index.js CHANGED
@@ -347,7 +347,7 @@ import {
347
347
  roadmapIssueUrl
348
348
  } from "./roadmap/index.js";
349
349
  import { SIFA_REPO_GROUPS, describeSifaRecord, repoGroupForCollection } from "./repo/index.js";
350
- var SIFA_SDK_VERSION = "0.19.49";
350
+ var SIFA_SDK_VERSION = "0.19.50";
351
351
  export {
352
352
  ACTIVITY_TIERS,
353
353
  ACTIVITY_VERBS,
@@ -1050,7 +1050,9 @@ interface FollowingFeedApp {
1050
1050
  * Why a following-feed page came back empty. Lets a client say something true
1051
1051
  * per case instead of one catch-all sentence, and point at the right next step.
1052
1052
  */
1053
- type FollowingFeedEmptyReason = 'no_follows' | 'no_visible_follows' | 'no_cross_app_activity' | 'no_recent_records' | 'all_hidden' | 'nothing_renderable' | 'hydration_failed';
1053
+ type FollowingFeedEmptyReason = 'no_follows' | 'no_visible_follows' | 'no_cross_app_activity' | 'no_recent_records' | 'all_hidden' | 'nothing_renderable' | 'hydration_failed'
1054
+ /** The feed is still being built. Nothing is wrong: ask again shortly. */
1055
+ | 'building';
1054
1056
  /** Options for {@link fetchFollowingFeed}. */
1055
1057
  interface FetchFollowingFeedOptions extends ApiFetchOptions {
1056
1058
  limit?: number;
@@ -1079,6 +1081,14 @@ interface FollowingFeedResponse extends ActivityFeedResponse {
1079
1081
  apps?: FollowingFeedApp[];
1080
1082
  /** Set only when `items` is empty. */
1081
1083
  reason?: FollowingFeedEmptyReason;
1084
+ /** When this feed was built (ISO 8601). Absent on a `building` answer. */
1085
+ builtAt?: string;
1086
+ /**
1087
+ * True when this is an older build, served while a newer one is built in
1088
+ * the background. Poll {@link fetchFollowingFeedVersion} to learn when the
1089
+ * newer one is ready.
1090
+ */
1091
+ stale?: boolean;
1082
1092
  }
1083
1093
  /**
1084
1094
  * The viewer's cross-app following feed: what OTHER apps their connections use
@@ -1092,6 +1102,19 @@ interface FollowingFeedResponse extends ActivityFeedResponse {
1092
1102
  * finite first page (no cursor yet).
1093
1103
  */
1094
1104
  declare function fetchFollowingFeed(config: SifaApiConfig, opts?: FetchFollowingFeedOptions): Promise<FollowingFeedResponse | null>;
1105
+ /** When the cached following feed for a query was built. */
1106
+ interface FollowingFeedVersion {
1107
+ /** ISO 8601, or null when nothing is cached for this query. */
1108
+ builtAt: string | null;
1109
+ }
1110
+ /**
1111
+ * When the cached following feed for the same query was built. Cheap: it never
1112
+ * builds a feed. A client showing a `stale` feed polls this and offers the
1113
+ * newer one once {@link isNewerFollowingFeed} says so. Returns `null` on error.
1114
+ */
1115
+ declare function fetchFollowingFeedVersion(config: SifaApiConfig, opts?: FetchFollowingFeedOptions): Promise<FollowingFeedVersion | null>;
1116
+ /** True when `latestBuiltAt` is a newer build than the one shown (`shownBuiltAt`). */
1117
+ declare function isNewerFollowingFeed(shownBuiltAt: string | null | undefined, latestBuiltAt: string | null | undefined): boolean;
1095
1118
 
1096
1119
  /**
1097
1120
  * Options shared by the cursor-paginated follow-graph endpoints introduced in
@@ -1553,6 +1576,7 @@ declare const sifaQueryKeys: {
1553
1576
  readonly followers: (handle: string) => readonly ["sifa", "follow", "followers", string];
1554
1577
  readonly followingOf: (handle: string) => readonly ["sifa", "follow", "following-of", string];
1555
1578
  readonly feed: (opts: Record<string, unknown>) => readonly ["sifa", "follow", "feed", Record<string, unknown>];
1579
+ readonly feedVersion: (opts: Record<string, unknown>) => readonly ["sifa", "follow", "feed-version", Record<string, unknown>];
1556
1580
  readonly mutuals: (handle: string) => readonly ["sifa", "follow", "mutuals", string];
1557
1581
  readonly blueskySuggestions: () => readonly ["sifa", "follow", "bluesky-suggestions"];
1558
1582
  };
@@ -1718,4 +1742,4 @@ declare function addOrgNotificationEmail(config: SifaApiConfig, body: OrgNotific
1718
1742
  */
1719
1743
  declare function removeOrgNotificationEmail(config: SifaApiConfig, body: OrgNotificationEmailRequestInput, options?: Omit<ApiFetchOptions, 'method' | 'body'>): Promise<WriteResult & Partial<OrgNotificationEmailRemoveResult>>;
1720
1744
 
1721
- export { type HeatmapResponse as $, type AccountCheckResult as A, type BulkHideProfileItemInput as B, type ConfirmEndorsementInput as C, type DeleteAccountResult as D, type EndorsementInput as E, type FeatureAllowlistResponse as F, type FetchInboxCountsOptions as G, type FetchMyGithubPullRequestsOptions as H, type FetchMyRoadmapVotesOptions as I, type FetchProfileCompletenessOptions as J, type FetchReactionStatusOptions as K, type FetchSuggestionsOptions as L, type FetchUnlinkedPositionsOptions as M, type FollowListPage as N, type FollowProfile as O, type FollowProfilePageResponse as P, type FollowUserResult as Q, type FollowingFeedApp as R, type FollowingFeedEmptyReason as S, type FollowingFeedResponse as T, type FollowingResponse as U, type GetAdminReviewQueuesOptions as V, type GithubPullRequest as W, type GivenConfirmation as X, HIDDEN_ITEM_SOURCES as Y, HIDDEN_ITEM_TYPES as Z, type HeatmapDay as _, type ConfirmEndorsementResult as a, bulkHideStandardPublications as a$, type HiddenActivityItem as a0, type HiddenApp as a1, type HiddenItemSource as a2, type HiddenItemType as a3, type HideActivityItemInput as a4, type HideProfileItemInput as a5, type InboxCounts as a6, type ListFeatureAllowlistOptions as a7, type MyGithubPullRequestsResponse as a8, type OrgClaimBinding as a9, type RefreshOrcidPublicationsResult as aA, type RefreshPdsResult as aB, type RepoDeleteInput as aC, type ResetProfileResult as aD, type ResolveQuotedPostsOptions as aE, type RoadmapVoteError as aF, type RoadmapVoteResult as aG, type RoadmapVoter as aH, type RoadmapVotesResponse as aI, type SifaQueryKey as aJ, type SimilarProfile as aK, type StatsResponse as aL, type SubCategoryBulkResult as aM, type SuggestionProfile as aN, type SuggestionsResponse as aO, type UnhideActivityItemInput as aP, type UnlinkedPosition as aQ, type UnlinkedPositionsResult as aR, type UpdateProfileOverrideInput as aS, type UpdateProfileSelfInput as aT, type UploadAvatarResult as aU, type UploadPronunciationAudioResult as aV, type VerifyExternalAccountResult as aW, type WipePreview as aX, addFeatureAllowlist as aY, addOrgNotificationEmail as aZ, bulkHideProfileItems as a_, type OrgClaimResult as aa, type OrgDomainChallengeResult as ab, type OrgDomainVerifyResult as ac, type OrgNotificationEmailAddResult as ad, type OrgNotificationEmailRemoveResult as ae, type OrgProfileEcho as af, type OrgProfileUpdateResult as ag, PROFILE_COMPLETENESS_SIGNALS as ah, type PdsWipeOutcome as ai, type PendingConfirmation as aj, type PendingConfirmationsPage as ak, type PrimarySection as al, type ProfileCompleteness as am, type ProfileCompletenessSignal as an, type ProfileIndustryInput as ao, type ProfileLocationAddress as ap, type ProfileLocationInput as aq, type ProfileSelfLocation as ar, QUOTED_POSTS_BATCH_MAX as as, type QuotedPostAuthor as at, type QuotedPostImage as au, type QuotedPostResult as av, type QuotedPostView as aw, type ReactionError as ax, type ReactionResult as ay, type ReactionStatus as az, type AccountSummary as b, repoExportUrl as b$, bulkUnhideProfileItems as b0, bulkUnhideStandardPublications as b1, castRoadmapVote as b2, checkAppAccount as b3, confirmEndorsement as b4, createConfirmation as b5, createEndorsement as b6, createExternalAccount as b7, createProfileLocation as b8, createReaction as b9, fetchPendingConfirmations as bA, fetchProfileCompleteness as bB, fetchReactionStatus as bC, fetchRepoInventory as bD, fetchRoadmapVotes as bE, fetchSimilarProfiles as bF, fetchStats as bG, fetchSuggestionCount as bH, fetchSuggestions as bI, fetchUnlinkedPositions as bJ, fetchWipePreview as bK, followUser as bL, getAdminReviewQueues as bM, getBlueskySuggestions as bN, getFollowers as bO, getFollowing as bP, getMutuals as bQ, hideActivityItem as bR, hideOrcidPublication as bS, hideProfileItem as bT, hideSifaPublication as bU, hideStandardPublication as bV, listFeatureAllowlist as bW, refreshOrcidPublications as bX, refreshPds as bY, removeFeatureAllowlist as bZ, removeOrgNotificationEmail as b_, createSkill as ba, deleteAccount as bb, deleteAvatarOverride as bc, deleteExternalAccount as bd, deleteNamePronunciationAudio as be, deleteProfileLocation as bf, deleteReaction as bg, deleteRepoRecords as bh, deleteSkill as bi, dismissConfirmation as bj, dismissUnlinkedPosition as bk, fetchAccounts as bl, fetchActivityFeed as bm, fetchActivityTeaser as bn, fetchAppsRegistry as bo, fetchExternalAccounts as bp, fetchFeaturedProfile as bq, fetchFollowing as br, fetchFollowingFeed as bs, fetchGivenConfirmations as bt, fetchHeatmapData as bu, fetchHiddenActivityItems as bv, fetchHiddenApps as bw, fetchInboxCounts as bx, fetchMyGithubPullRequests as by, fetchMyRoadmapVotes as bz, type ActivityFeedResponse as c, requestOrgDomainChallenge as c0, resetProfile as c1, resolveQuotedPosts as c2, retractRoadmapVote as c3, revokeConfirmation as c4, setExternalAccountPrimary as c5, setSectionPrimary as c6, submitOrgClaim as c7, switchAccount as c8, unfollowUser as c9, unhideActivityItem as ca, unhideOrcidPublication as cb, unhideProfileItem as cc, unhideSifaPublication as cd, unhideStandardPublication as ce, unsetExternalAccountPrimary as cf, unsetSectionPrimary as cg, updateExternalAccount as ch, updateOrgProfile as ci, updateProfileLocation as cj, updateProfileOverride as ck, updateProfileSelf as cl, updateSkill as cm, updateSkillSubCategories as cn, uploadAvatar as co, uploadNamePronunciationAudio as cp, verifyExternalAccount as cq, verifyOrgDomain as cr, type ActivityItem as d, type ActivityItemLinkHealth as e, type ActivityTeaserResponse as f, type AdminReviewQueues as g, type AppRegistryEntry as h, type CastRoadmapVoteResult as i, type CheckAppAccountOptions as j, type ConfirmationInput as k, type ConfirmationSubjectInput as l, type CreateExternalAccountResult as m, type DismissUnlinkedPositionInput as n, type ExternalAccountInput as o, type FeaturedProfile as p, type FetchAccountsOptions as q, type FetchActivityFeedOptions as r, sifaQueryKeys as s, type FetchActivityTeaserOptions as t, type FetchConfirmationsOptions as u, type FetchFollowListOptions as v, type FetchFollowProfilePageOptions as w, type FetchFollowingFeedOptions as x, type FetchHiddenActivityItemsOptions as y, type FetchHiddenAppsOptions as z };
1745
+ export { type HeatmapDay as $, type AccountCheckResult as A, type BulkHideProfileItemInput as B, type ConfirmEndorsementInput as C, type DeleteAccountResult as D, type EndorsementInput as E, type FeatureAllowlistResponse as F, type FetchInboxCountsOptions as G, type FetchMyGithubPullRequestsOptions as H, type FetchMyRoadmapVotesOptions as I, type FetchProfileCompletenessOptions as J, type FetchReactionStatusOptions as K, type FetchSuggestionsOptions as L, type FetchUnlinkedPositionsOptions as M, type FollowListPage as N, type FollowProfile as O, type FollowProfilePageResponse as P, type FollowUserResult as Q, type FollowingFeedApp as R, type FollowingFeedEmptyReason as S, type FollowingFeedResponse as T, type FollowingFeedVersion as U, type FollowingResponse as V, type GetAdminReviewQueuesOptions as W, type GithubPullRequest as X, type GivenConfirmation as Y, HIDDEN_ITEM_SOURCES as Z, HIDDEN_ITEM_TYPES as _, type ConfirmEndorsementResult as a, bulkHideProfileItems as a$, type HeatmapResponse as a0, type HiddenActivityItem as a1, type HiddenApp as a2, type HiddenItemSource as a3, type HiddenItemType as a4, type HideActivityItemInput as a5, type HideProfileItemInput as a6, type InboxCounts as a7, type ListFeatureAllowlistOptions as a8, type MyGithubPullRequestsResponse as a9, type ReactionStatus as aA, type RefreshOrcidPublicationsResult as aB, type RefreshPdsResult as aC, type RepoDeleteInput as aD, type ResetProfileResult as aE, type ResolveQuotedPostsOptions as aF, type RoadmapVoteError as aG, type RoadmapVoteResult as aH, type RoadmapVoter as aI, type RoadmapVotesResponse as aJ, type SifaQueryKey as aK, type SimilarProfile as aL, type StatsResponse as aM, type SubCategoryBulkResult as aN, type SuggestionProfile as aO, type SuggestionsResponse as aP, type UnhideActivityItemInput as aQ, type UnlinkedPosition as aR, type UnlinkedPositionsResult as aS, type UpdateProfileOverrideInput as aT, type UpdateProfileSelfInput as aU, type UploadAvatarResult as aV, type UploadPronunciationAudioResult as aW, type VerifyExternalAccountResult as aX, type WipePreview as aY, addFeatureAllowlist as aZ, addOrgNotificationEmail as a_, type OrgClaimBinding as aa, type OrgClaimResult as ab, type OrgDomainChallengeResult as ac, type OrgDomainVerifyResult as ad, type OrgNotificationEmailAddResult as ae, type OrgNotificationEmailRemoveResult as af, type OrgProfileEcho as ag, type OrgProfileUpdateResult as ah, PROFILE_COMPLETENESS_SIGNALS as ai, type PdsWipeOutcome as aj, type PendingConfirmation as ak, type PendingConfirmationsPage as al, type PrimarySection as am, type ProfileCompleteness as an, type ProfileCompletenessSignal as ao, type ProfileIndustryInput as ap, type ProfileLocationAddress as aq, type ProfileLocationInput as ar, type ProfileSelfLocation as as, QUOTED_POSTS_BATCH_MAX as at, type QuotedPostAuthor as au, type QuotedPostImage as av, type QuotedPostResult as aw, type QuotedPostView as ax, type ReactionError as ay, type ReactionResult as az, type AccountSummary as b, refreshPds as b$, bulkHideStandardPublications as b0, bulkUnhideProfileItems as b1, bulkUnhideStandardPublications as b2, castRoadmapVote as b3, checkAppAccount as b4, confirmEndorsement as b5, createConfirmation as b6, createEndorsement as b7, createExternalAccount as b8, createProfileLocation as b9, fetchMyGithubPullRequests as bA, fetchMyRoadmapVotes as bB, fetchPendingConfirmations as bC, fetchProfileCompleteness as bD, fetchReactionStatus as bE, fetchRepoInventory as bF, fetchRoadmapVotes as bG, fetchSimilarProfiles as bH, fetchStats as bI, fetchSuggestionCount as bJ, fetchSuggestions as bK, fetchUnlinkedPositions as bL, fetchWipePreview as bM, followUser as bN, getAdminReviewQueues as bO, getBlueskySuggestions as bP, getFollowers as bQ, getFollowing as bR, getMutuals as bS, hideActivityItem as bT, hideOrcidPublication as bU, hideProfileItem as bV, hideSifaPublication as bW, hideStandardPublication as bX, isNewerFollowingFeed as bY, listFeatureAllowlist as bZ, refreshOrcidPublications as b_, createReaction as ba, createSkill as bb, deleteAccount as bc, deleteAvatarOverride as bd, deleteExternalAccount as be, deleteNamePronunciationAudio as bf, deleteProfileLocation as bg, deleteReaction as bh, deleteRepoRecords as bi, deleteSkill as bj, dismissConfirmation as bk, dismissUnlinkedPosition as bl, fetchAccounts as bm, fetchActivityFeed as bn, fetchActivityTeaser as bo, fetchAppsRegistry as bp, fetchExternalAccounts as bq, fetchFeaturedProfile as br, fetchFollowing as bs, fetchFollowingFeed as bt, fetchFollowingFeedVersion as bu, fetchGivenConfirmations as bv, fetchHeatmapData as bw, fetchHiddenActivityItems as bx, fetchHiddenApps as by, fetchInboxCounts as bz, type ActivityFeedResponse as c, removeFeatureAllowlist as c0, removeOrgNotificationEmail as c1, repoExportUrl as c2, requestOrgDomainChallenge as c3, resetProfile as c4, resolveQuotedPosts as c5, retractRoadmapVote as c6, revokeConfirmation as c7, setExternalAccountPrimary as c8, setSectionPrimary as c9, submitOrgClaim as ca, switchAccount as cb, unfollowUser as cc, unhideActivityItem as cd, unhideOrcidPublication as ce, unhideProfileItem as cf, unhideSifaPublication as cg, unhideStandardPublication as ch, unsetExternalAccountPrimary as ci, unsetSectionPrimary as cj, updateExternalAccount as ck, updateOrgProfile as cl, updateProfileLocation as cm, updateProfileOverride as cn, updateProfileSelf as co, updateSkill as cp, updateSkillSubCategories as cq, uploadAvatar as cr, uploadNamePronunciationAudio as cs, verifyExternalAccount as ct, verifyOrgDomain as cu, type ActivityItem as d, type ActivityItemLinkHealth as e, type ActivityTeaserResponse as f, type AdminReviewQueues as g, type AppRegistryEntry as h, type CastRoadmapVoteResult as i, type CheckAppAccountOptions as j, type ConfirmationInput as k, type ConfirmationSubjectInput as l, type CreateExternalAccountResult as m, type DismissUnlinkedPositionInput as n, type ExternalAccountInput as o, type FeaturedProfile as p, type FetchAccountsOptions as q, type FetchActivityFeedOptions as r, sifaQueryKeys as s, type FetchActivityTeaserOptions as t, type FetchConfirmationsOptions as u, type FetchFollowListOptions as v, type FetchFollowProfilePageOptions as w, type FetchFollowingFeedOptions as x, type FetchHiddenActivityItemsOptions as y, type FetchHiddenAppsOptions as z };
@@ -1050,7 +1050,9 @@ interface FollowingFeedApp {
1050
1050
  * Why a following-feed page came back empty. Lets a client say something true
1051
1051
  * per case instead of one catch-all sentence, and point at the right next step.
1052
1052
  */
1053
- type FollowingFeedEmptyReason = 'no_follows' | 'no_visible_follows' | 'no_cross_app_activity' | 'no_recent_records' | 'all_hidden' | 'nothing_renderable' | 'hydration_failed';
1053
+ type FollowingFeedEmptyReason = 'no_follows' | 'no_visible_follows' | 'no_cross_app_activity' | 'no_recent_records' | 'all_hidden' | 'nothing_renderable' | 'hydration_failed'
1054
+ /** The feed is still being built. Nothing is wrong: ask again shortly. */
1055
+ | 'building';
1054
1056
  /** Options for {@link fetchFollowingFeed}. */
1055
1057
  interface FetchFollowingFeedOptions extends ApiFetchOptions {
1056
1058
  limit?: number;
@@ -1079,6 +1081,14 @@ interface FollowingFeedResponse extends ActivityFeedResponse {
1079
1081
  apps?: FollowingFeedApp[];
1080
1082
  /** Set only when `items` is empty. */
1081
1083
  reason?: FollowingFeedEmptyReason;
1084
+ /** When this feed was built (ISO 8601). Absent on a `building` answer. */
1085
+ builtAt?: string;
1086
+ /**
1087
+ * True when this is an older build, served while a newer one is built in
1088
+ * the background. Poll {@link fetchFollowingFeedVersion} to learn when the
1089
+ * newer one is ready.
1090
+ */
1091
+ stale?: boolean;
1082
1092
  }
1083
1093
  /**
1084
1094
  * The viewer's cross-app following feed: what OTHER apps their connections use
@@ -1092,6 +1102,19 @@ interface FollowingFeedResponse extends ActivityFeedResponse {
1092
1102
  * finite first page (no cursor yet).
1093
1103
  */
1094
1104
  declare function fetchFollowingFeed(config: SifaApiConfig, opts?: FetchFollowingFeedOptions): Promise<FollowingFeedResponse | null>;
1105
+ /** When the cached following feed for a query was built. */
1106
+ interface FollowingFeedVersion {
1107
+ /** ISO 8601, or null when nothing is cached for this query. */
1108
+ builtAt: string | null;
1109
+ }
1110
+ /**
1111
+ * When the cached following feed for the same query was built. Cheap: it never
1112
+ * builds a feed. A client showing a `stale` feed polls this and offers the
1113
+ * newer one once {@link isNewerFollowingFeed} says so. Returns `null` on error.
1114
+ */
1115
+ declare function fetchFollowingFeedVersion(config: SifaApiConfig, opts?: FetchFollowingFeedOptions): Promise<FollowingFeedVersion | null>;
1116
+ /** True when `latestBuiltAt` is a newer build than the one shown (`shownBuiltAt`). */
1117
+ declare function isNewerFollowingFeed(shownBuiltAt: string | null | undefined, latestBuiltAt: string | null | undefined): boolean;
1095
1118
 
1096
1119
  /**
1097
1120
  * Options shared by the cursor-paginated follow-graph endpoints introduced in
@@ -1553,6 +1576,7 @@ declare const sifaQueryKeys: {
1553
1576
  readonly followers: (handle: string) => readonly ["sifa", "follow", "followers", string];
1554
1577
  readonly followingOf: (handle: string) => readonly ["sifa", "follow", "following-of", string];
1555
1578
  readonly feed: (opts: Record<string, unknown>) => readonly ["sifa", "follow", "feed", Record<string, unknown>];
1579
+ readonly feedVersion: (opts: Record<string, unknown>) => readonly ["sifa", "follow", "feed-version", Record<string, unknown>];
1556
1580
  readonly mutuals: (handle: string) => readonly ["sifa", "follow", "mutuals", string];
1557
1581
  readonly blueskySuggestions: () => readonly ["sifa", "follow", "bluesky-suggestions"];
1558
1582
  };
@@ -1718,4 +1742,4 @@ declare function addOrgNotificationEmail(config: SifaApiConfig, body: OrgNotific
1718
1742
  */
1719
1743
  declare function removeOrgNotificationEmail(config: SifaApiConfig, body: OrgNotificationEmailRequestInput, options?: Omit<ApiFetchOptions, 'method' | 'body'>): Promise<WriteResult & Partial<OrgNotificationEmailRemoveResult>>;
1720
1744
 
1721
- export { type HeatmapResponse as $, type AccountCheckResult as A, type BulkHideProfileItemInput as B, type ConfirmEndorsementInput as C, type DeleteAccountResult as D, type EndorsementInput as E, type FeatureAllowlistResponse as F, type FetchInboxCountsOptions as G, type FetchMyGithubPullRequestsOptions as H, type FetchMyRoadmapVotesOptions as I, type FetchProfileCompletenessOptions as J, type FetchReactionStatusOptions as K, type FetchSuggestionsOptions as L, type FetchUnlinkedPositionsOptions as M, type FollowListPage as N, type FollowProfile as O, type FollowProfilePageResponse as P, type FollowUserResult as Q, type FollowingFeedApp as R, type FollowingFeedEmptyReason as S, type FollowingFeedResponse as T, type FollowingResponse as U, type GetAdminReviewQueuesOptions as V, type GithubPullRequest as W, type GivenConfirmation as X, HIDDEN_ITEM_SOURCES as Y, HIDDEN_ITEM_TYPES as Z, type HeatmapDay as _, type ConfirmEndorsementResult as a, bulkHideStandardPublications as a$, type HiddenActivityItem as a0, type HiddenApp as a1, type HiddenItemSource as a2, type HiddenItemType as a3, type HideActivityItemInput as a4, type HideProfileItemInput as a5, type InboxCounts as a6, type ListFeatureAllowlistOptions as a7, type MyGithubPullRequestsResponse as a8, type OrgClaimBinding as a9, type RefreshOrcidPublicationsResult as aA, type RefreshPdsResult as aB, type RepoDeleteInput as aC, type ResetProfileResult as aD, type ResolveQuotedPostsOptions as aE, type RoadmapVoteError as aF, type RoadmapVoteResult as aG, type RoadmapVoter as aH, type RoadmapVotesResponse as aI, type SifaQueryKey as aJ, type SimilarProfile as aK, type StatsResponse as aL, type SubCategoryBulkResult as aM, type SuggestionProfile as aN, type SuggestionsResponse as aO, type UnhideActivityItemInput as aP, type UnlinkedPosition as aQ, type UnlinkedPositionsResult as aR, type UpdateProfileOverrideInput as aS, type UpdateProfileSelfInput as aT, type UploadAvatarResult as aU, type UploadPronunciationAudioResult as aV, type VerifyExternalAccountResult as aW, type WipePreview as aX, addFeatureAllowlist as aY, addOrgNotificationEmail as aZ, bulkHideProfileItems as a_, type OrgClaimResult as aa, type OrgDomainChallengeResult as ab, type OrgDomainVerifyResult as ac, type OrgNotificationEmailAddResult as ad, type OrgNotificationEmailRemoveResult as ae, type OrgProfileEcho as af, type OrgProfileUpdateResult as ag, PROFILE_COMPLETENESS_SIGNALS as ah, type PdsWipeOutcome as ai, type PendingConfirmation as aj, type PendingConfirmationsPage as ak, type PrimarySection as al, type ProfileCompleteness as am, type ProfileCompletenessSignal as an, type ProfileIndustryInput as ao, type ProfileLocationAddress as ap, type ProfileLocationInput as aq, type ProfileSelfLocation as ar, QUOTED_POSTS_BATCH_MAX as as, type QuotedPostAuthor as at, type QuotedPostImage as au, type QuotedPostResult as av, type QuotedPostView as aw, type ReactionError as ax, type ReactionResult as ay, type ReactionStatus as az, type AccountSummary as b, repoExportUrl as b$, bulkUnhideProfileItems as b0, bulkUnhideStandardPublications as b1, castRoadmapVote as b2, checkAppAccount as b3, confirmEndorsement as b4, createConfirmation as b5, createEndorsement as b6, createExternalAccount as b7, createProfileLocation as b8, createReaction as b9, fetchPendingConfirmations as bA, fetchProfileCompleteness as bB, fetchReactionStatus as bC, fetchRepoInventory as bD, fetchRoadmapVotes as bE, fetchSimilarProfiles as bF, fetchStats as bG, fetchSuggestionCount as bH, fetchSuggestions as bI, fetchUnlinkedPositions as bJ, fetchWipePreview as bK, followUser as bL, getAdminReviewQueues as bM, getBlueskySuggestions as bN, getFollowers as bO, getFollowing as bP, getMutuals as bQ, hideActivityItem as bR, hideOrcidPublication as bS, hideProfileItem as bT, hideSifaPublication as bU, hideStandardPublication as bV, listFeatureAllowlist as bW, refreshOrcidPublications as bX, refreshPds as bY, removeFeatureAllowlist as bZ, removeOrgNotificationEmail as b_, createSkill as ba, deleteAccount as bb, deleteAvatarOverride as bc, deleteExternalAccount as bd, deleteNamePronunciationAudio as be, deleteProfileLocation as bf, deleteReaction as bg, deleteRepoRecords as bh, deleteSkill as bi, dismissConfirmation as bj, dismissUnlinkedPosition as bk, fetchAccounts as bl, fetchActivityFeed as bm, fetchActivityTeaser as bn, fetchAppsRegistry as bo, fetchExternalAccounts as bp, fetchFeaturedProfile as bq, fetchFollowing as br, fetchFollowingFeed as bs, fetchGivenConfirmations as bt, fetchHeatmapData as bu, fetchHiddenActivityItems as bv, fetchHiddenApps as bw, fetchInboxCounts as bx, fetchMyGithubPullRequests as by, fetchMyRoadmapVotes as bz, type ActivityFeedResponse as c, requestOrgDomainChallenge as c0, resetProfile as c1, resolveQuotedPosts as c2, retractRoadmapVote as c3, revokeConfirmation as c4, setExternalAccountPrimary as c5, setSectionPrimary as c6, submitOrgClaim as c7, switchAccount as c8, unfollowUser as c9, unhideActivityItem as ca, unhideOrcidPublication as cb, unhideProfileItem as cc, unhideSifaPublication as cd, unhideStandardPublication as ce, unsetExternalAccountPrimary as cf, unsetSectionPrimary as cg, updateExternalAccount as ch, updateOrgProfile as ci, updateProfileLocation as cj, updateProfileOverride as ck, updateProfileSelf as cl, updateSkill as cm, updateSkillSubCategories as cn, uploadAvatar as co, uploadNamePronunciationAudio as cp, verifyExternalAccount as cq, verifyOrgDomain as cr, type ActivityItem as d, type ActivityItemLinkHealth as e, type ActivityTeaserResponse as f, type AdminReviewQueues as g, type AppRegistryEntry as h, type CastRoadmapVoteResult as i, type CheckAppAccountOptions as j, type ConfirmationInput as k, type ConfirmationSubjectInput as l, type CreateExternalAccountResult as m, type DismissUnlinkedPositionInput as n, type ExternalAccountInput as o, type FeaturedProfile as p, type FetchAccountsOptions as q, type FetchActivityFeedOptions as r, sifaQueryKeys as s, type FetchActivityTeaserOptions as t, type FetchConfirmationsOptions as u, type FetchFollowListOptions as v, type FetchFollowProfilePageOptions as w, type FetchFollowingFeedOptions as x, type FetchHiddenActivityItemsOptions as y, type FetchHiddenAppsOptions as z };
1745
+ export { type HeatmapDay as $, type AccountCheckResult as A, type BulkHideProfileItemInput as B, type ConfirmEndorsementInput as C, type DeleteAccountResult as D, type EndorsementInput as E, type FeatureAllowlistResponse as F, type FetchInboxCountsOptions as G, type FetchMyGithubPullRequestsOptions as H, type FetchMyRoadmapVotesOptions as I, type FetchProfileCompletenessOptions as J, type FetchReactionStatusOptions as K, type FetchSuggestionsOptions as L, type FetchUnlinkedPositionsOptions as M, type FollowListPage as N, type FollowProfile as O, type FollowProfilePageResponse as P, type FollowUserResult as Q, type FollowingFeedApp as R, type FollowingFeedEmptyReason as S, type FollowingFeedResponse as T, type FollowingFeedVersion as U, type FollowingResponse as V, type GetAdminReviewQueuesOptions as W, type GithubPullRequest as X, type GivenConfirmation as Y, HIDDEN_ITEM_SOURCES as Z, HIDDEN_ITEM_TYPES as _, type ConfirmEndorsementResult as a, bulkHideProfileItems as a$, type HeatmapResponse as a0, type HiddenActivityItem as a1, type HiddenApp as a2, type HiddenItemSource as a3, type HiddenItemType as a4, type HideActivityItemInput as a5, type HideProfileItemInput as a6, type InboxCounts as a7, type ListFeatureAllowlistOptions as a8, type MyGithubPullRequestsResponse as a9, type ReactionStatus as aA, type RefreshOrcidPublicationsResult as aB, type RefreshPdsResult as aC, type RepoDeleteInput as aD, type ResetProfileResult as aE, type ResolveQuotedPostsOptions as aF, type RoadmapVoteError as aG, type RoadmapVoteResult as aH, type RoadmapVoter as aI, type RoadmapVotesResponse as aJ, type SifaQueryKey as aK, type SimilarProfile as aL, type StatsResponse as aM, type SubCategoryBulkResult as aN, type SuggestionProfile as aO, type SuggestionsResponse as aP, type UnhideActivityItemInput as aQ, type UnlinkedPosition as aR, type UnlinkedPositionsResult as aS, type UpdateProfileOverrideInput as aT, type UpdateProfileSelfInput as aU, type UploadAvatarResult as aV, type UploadPronunciationAudioResult as aW, type VerifyExternalAccountResult as aX, type WipePreview as aY, addFeatureAllowlist as aZ, addOrgNotificationEmail as a_, type OrgClaimBinding as aa, type OrgClaimResult as ab, type OrgDomainChallengeResult as ac, type OrgDomainVerifyResult as ad, type OrgNotificationEmailAddResult as ae, type OrgNotificationEmailRemoveResult as af, type OrgProfileEcho as ag, type OrgProfileUpdateResult as ah, PROFILE_COMPLETENESS_SIGNALS as ai, type PdsWipeOutcome as aj, type PendingConfirmation as ak, type PendingConfirmationsPage as al, type PrimarySection as am, type ProfileCompleteness as an, type ProfileCompletenessSignal as ao, type ProfileIndustryInput as ap, type ProfileLocationAddress as aq, type ProfileLocationInput as ar, type ProfileSelfLocation as as, QUOTED_POSTS_BATCH_MAX as at, type QuotedPostAuthor as au, type QuotedPostImage as av, type QuotedPostResult as aw, type QuotedPostView as ax, type ReactionError as ay, type ReactionResult as az, type AccountSummary as b, refreshPds as b$, bulkHideStandardPublications as b0, bulkUnhideProfileItems as b1, bulkUnhideStandardPublications as b2, castRoadmapVote as b3, checkAppAccount as b4, confirmEndorsement as b5, createConfirmation as b6, createEndorsement as b7, createExternalAccount as b8, createProfileLocation as b9, fetchMyGithubPullRequests as bA, fetchMyRoadmapVotes as bB, fetchPendingConfirmations as bC, fetchProfileCompleteness as bD, fetchReactionStatus as bE, fetchRepoInventory as bF, fetchRoadmapVotes as bG, fetchSimilarProfiles as bH, fetchStats as bI, fetchSuggestionCount as bJ, fetchSuggestions as bK, fetchUnlinkedPositions as bL, fetchWipePreview as bM, followUser as bN, getAdminReviewQueues as bO, getBlueskySuggestions as bP, getFollowers as bQ, getFollowing as bR, getMutuals as bS, hideActivityItem as bT, hideOrcidPublication as bU, hideProfileItem as bV, hideSifaPublication as bW, hideStandardPublication as bX, isNewerFollowingFeed as bY, listFeatureAllowlist as bZ, refreshOrcidPublications as b_, createReaction as ba, createSkill as bb, deleteAccount as bc, deleteAvatarOverride as bd, deleteExternalAccount as be, deleteNamePronunciationAudio as bf, deleteProfileLocation as bg, deleteReaction as bh, deleteRepoRecords as bi, deleteSkill as bj, dismissConfirmation as bk, dismissUnlinkedPosition as bl, fetchAccounts as bm, fetchActivityFeed as bn, fetchActivityTeaser as bo, fetchAppsRegistry as bp, fetchExternalAccounts as bq, fetchFeaturedProfile as br, fetchFollowing as bs, fetchFollowingFeed as bt, fetchFollowingFeedVersion as bu, fetchGivenConfirmations as bv, fetchHeatmapData as bw, fetchHiddenActivityItems as bx, fetchHiddenApps as by, fetchInboxCounts as bz, type ActivityFeedResponse as c, removeFeatureAllowlist as c0, removeOrgNotificationEmail as c1, repoExportUrl as c2, requestOrgDomainChallenge as c3, resetProfile as c4, resolveQuotedPosts as c5, retractRoadmapVote as c6, revokeConfirmation as c7, setExternalAccountPrimary as c8, setSectionPrimary as c9, submitOrgClaim as ca, switchAccount as cb, unfollowUser as cc, unhideActivityItem as cd, unhideOrcidPublication as ce, unhideProfileItem as cf, unhideSifaPublication as cg, unhideStandardPublication as ch, unsetExternalAccountPrimary as ci, unsetSectionPrimary as cj, updateExternalAccount as ck, updateOrgProfile as cl, updateProfileLocation as cm, updateProfileOverride as cn, updateProfileSelf as co, updateSkill as cp, updateSkillSubCategories as cq, uploadAvatar as cr, uploadNamePronunciationAudio as cs, verifyExternalAccount as ct, verifyOrgDomain as cu, type ActivityItem as d, type ActivityItemLinkHealth as e, type ActivityTeaserResponse as f, type AdminReviewQueues as g, type AppRegistryEntry as h, type CastRoadmapVoteResult as i, type CheckAppAccountOptions as j, type ConfirmationInput as k, type ConfirmationSubjectInput as l, type CreateExternalAccountResult as m, type DismissUnlinkedPositionInput as n, type ExternalAccountInput as o, type FeaturedProfile as p, type FetchAccountsOptions as q, type FetchActivityFeedOptions as r, sifaQueryKeys as s, type FetchActivityTeaserOptions as t, type FetchConfirmationsOptions as u, type FetchFollowListOptions as v, type FetchFollowProfilePageOptions as w, type FetchFollowingFeedOptions as x, type FetchHiddenActivityItemsOptions as y, type FetchHiddenAppsOptions as z };
@@ -71,7 +71,7 @@ async function getFollowing(config, handle, opts = {}) {
71
71
  }
72
72
  }
73
73
  var FOLLOWING_FEED_LXM = "id.sifa.feed.getFollowingFeed";
74
- async function fetchFollowingFeed(config, opts = {}) {
74
+ async function followingFeedRequest(config, opts) {
75
75
  const params = new URLSearchParams();
76
76
  if (opts.limit) params.set("limit", String(opts.limit));
77
77
  if (opts.app) params.set("app", opts.app);
@@ -83,23 +83,47 @@ async function fetchFollowingFeed(config, opts = {}) {
83
83
  const token = await config.getAuthToken(FOLLOWING_FEED_LXM);
84
84
  if (token) headers.Authorization = `Bearer ${token}`;
85
85
  }
86
+ return { query: qs ? `?${qs}` : "", headers };
87
+ }
88
+ async function fetchFollowingFeed(config, opts = {}) {
89
+ const { query, headers } = await followingFeedRequest(config, opts);
86
90
  try {
87
- return await apiFetch(
88
- config,
89
- `/api/following/feed${qs ? `?${qs}` : ""}`,
90
- { credentials: "include", cache: "no-store", ...opts, headers }
91
- );
91
+ return await apiFetch(config, `/api/following/feed${query}`, {
92
+ credentials: "include",
93
+ cache: "no-store",
94
+ ...opts,
95
+ headers
96
+ });
92
97
  } catch {
93
98
  return null;
94
99
  }
95
100
  }
101
+ async function fetchFollowingFeedVersion(config, opts = {}) {
102
+ const { query, headers } = await followingFeedRequest(config, opts);
103
+ try {
104
+ return await apiFetch(config, `/api/following/feed/version${query}`, {
105
+ credentials: "include",
106
+ cache: "no-store",
107
+ ...opts,
108
+ headers
109
+ });
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+ function isNewerFollowingFeed(shownBuiltAt, latestBuiltAt) {
115
+ if (!shownBuiltAt || !latestBuiltAt) return false;
116
+ return Date.parse(latestBuiltAt) > Date.parse(shownBuiltAt);
117
+ }
96
118
  export {
97
119
  FOLLOWING_FEED_LXM,
98
120
  fetchFollowing,
99
121
  fetchFollowingFeed,
122
+ fetchFollowingFeedVersion,
100
123
  followUser,
101
124
  getFollowers,
102
125
  getFollowing,
126
+ isNewerFollowingFeed,
103
127
  unfollowUser
104
128
  };
105
129
  //# sourceMappingURL=follow.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/query/fetchers/follow.ts"],"sourcesContent":["import {\n apiFetch,\n encodeIdentifier,\n apiWrite,\n type ApiFetchOptions,\n type SifaApiConfig,\n type WriteResult,\n} from '../client.js';\nimport type { ActivityFeedResponse } from './activity.js';\n\nexport interface FollowProfile {\n did: string;\n handle: string;\n displayName?: string;\n headline?: string;\n avatarUrl?: string;\n source: string;\n claimed: boolean;\n followedAt: string;\n blueskyVerified?: boolean;\n blueskyVerifiedAt?: string | null;\n}\n\nexport interface FollowingResponse {\n follows: FollowProfile[];\n cursor?: string;\n}\n\n/** People the authenticated user follows. Empty on error. */\nexport async function fetchFollowing(\n config: SifaApiConfig,\n opts: { source?: string; cursor?: string; limit?: number } & ApiFetchOptions = {},\n): Promise<FollowingResponse> {\n const params = new URLSearchParams();\n if (opts.source) params.set('source', opts.source);\n if (opts.cursor) params.set('cursor', opts.cursor);\n if (opts.limit) params.set('limit', String(opts.limit));\n const qs = params.toString();\n\n try {\n return await apiFetch<FollowingResponse>(config, `/api/following${qs ? `?${qs}` : ''}`, {\n credentials: 'include',\n cache: 'no-store',\n ...opts,\n });\n } catch {\n return { follows: [] };\n }\n}\n\n/**\n * Result of {@link followUser}. Extends {@link WriteResult} with the\n * follow `rkey` returned by sifa-api on success. Self-follow + invalid\n * handle surface as `success: false` with the server-provided message;\n * dup-follow is idempotent (sifa-api E7) and resolves as `success: true`.\n */\nexport interface FollowUserResult extends WriteResult {\n rkey?: string;\n /** DID of the followed subject (server-resolved from the handle). */\n subjectDid?: string;\n}\n\n/**\n * Create an `id.sifa.graph.follow` record on the caller's PDS via the\n * AppView. Idempotent on duplicate (server catches the unique-violation\n * and returns 200, per sifa-api#673 E7).\n */\nexport function followUser(\n config: SifaApiConfig,\n handle: string,\n opts: { note?: string } & Omit<ApiFetchOptions, 'method' | 'body'> = {},\n): Promise<FollowUserResult> {\n const { note, ...rest } = opts;\n return apiWrite<{ rkey?: string; subjectDid?: string }>(\n config,\n `/api/follow/${encodeIdentifier(handle)}`,\n 'POST',\n {\n body: note !== undefined ? { note } : undefined,\n ...rest,\n },\n );\n}\n\n/** Delete the authenticated viewer's `id.sifa.graph.follow` for `handle`. */\nexport function unfollowUser(\n config: SifaApiConfig,\n handle: string,\n opts: Omit<ApiFetchOptions, 'method' | 'body'> = {},\n): Promise<WriteResult> {\n return apiWrite(config, `/api/follow/${encodeIdentifier(handle)}`, 'DELETE', opts);\n}\n\nexport interface FollowListPage {\n follows: FollowProfile[];\n cursor: string | null;\n}\n\nexport interface FetchFollowListOptions extends ApiFetchOptions {\n cursor?: string;\n limit?: number;\n /**\n * Pass the caller's `Cookie` header on Next.js RSC server-side calls\n * (mirrors {@link FetchActivityFeedOptions}; required for authenticated\n * server fetches because `credentials: 'include'` does not propagate\n * cookies from RSC).\n */\n cookieHeader?: string;\n}\n\nfunction buildListPath(prefix: string, opts: FetchFollowListOptions): string {\n const params = new URLSearchParams();\n if (opts.cursor) params.set('cursor', opts.cursor);\n if (opts.limit) params.set('limit', String(opts.limit));\n const qs = params.toString();\n return `${prefix}${qs ? `?${qs}` : ''}`;\n}\n\n/**\n * Paginated list of `handle`'s followers. Returns an empty page on error\n * so the UI can render a graceful \"no followers yet\" state.\n */\nexport async function getFollowers(\n config: SifaApiConfig,\n handle: string,\n opts: FetchFollowListOptions = {},\n): Promise<FollowListPage> {\n const headers: Record<string, string> = { ...(opts.headers ?? {}) };\n if (opts.cookieHeader) headers.cookie = opts.cookieHeader;\n try {\n const res = await apiFetch<{ follows: FollowProfile[]; cursor?: string | null }>(\n config,\n buildListPath(`/api/profile/${encodeIdentifier(handle)}/followers`, opts),\n { credentials: 'include', cache: 'no-store', ...opts, headers },\n );\n return { follows: res.follows, cursor: res.cursor ?? null };\n } catch {\n return { follows: [], cursor: null };\n }\n}\n\n/** Paginated list of who `handle` follows. */\nexport async function getFollowing(\n config: SifaApiConfig,\n handle: string,\n opts: FetchFollowListOptions = {},\n): Promise<FollowListPage> {\n const headers: Record<string, string> = { ...(opts.headers ?? {}) };\n if (opts.cookieHeader) headers.cookie = opts.cookieHeader;\n try {\n const res = await apiFetch<{ follows: FollowProfile[]; cursor?: string | null }>(\n config,\n buildListPath(`/api/profile/${encodeIdentifier(handle)}/following`, opts),\n { credentials: 'include', cache: 'no-store', ...opts, headers },\n );\n return { follows: res.follows, cursor: res.cursor ?? null };\n } catch {\n return { follows: [], cursor: null };\n }\n}\n\n/** The method binding for the following-feed service-auth token; MUST match the\n * sifa-api endpoint's `lxm`. */\nexport const FOLLOWING_FEED_LXM = 'id.sifa.feed.getFollowingFeed';\n\n/** One app the viewer's network is active on, for the feed's source tabs. */\nexport interface FollowingFeedApp {\n /** Registry app id, e.g. `tangled`. Pass back as {@link FetchFollowingFeedOptions.app}. */\n id: string;\n name: string;\n /** Summed recent activity across the network; the tab ordering. */\n count: number;\n}\n\n/**\n * Why a following-feed page came back empty. Lets a client say something true\n * per case instead of one catch-all sentence, and point at the right next step.\n */\nexport type FollowingFeedEmptyReason =\n | 'no_follows'\n | 'no_visible_follows'\n | 'no_cross_app_activity'\n | 'no_recent_records'\n | 'all_hidden'\n | 'nothing_renderable'\n | 'hydration_failed';\n\n/** Options for {@link fetchFollowingFeed}. */\nexport interface FetchFollowingFeedOptions extends ApiFetchOptions {\n limit?: number;\n /**\n * Opt in to the viewer's Bluesky activity too. Off by default: the point of\n * the feed is what connections do on OTHER apps.\n *\n * Ignored when {@link app} is set, since a source tab selects one app.\n */\n includeBluesky?: boolean;\n /**\n * Restrict the page to one app (a registry id from the response's `apps`).\n * This is what a source tab selects. `app: 'bluesky'` selects Bluesky even\n * though it is excluded from the default mixed view.\n */\n app?: string;\n /** Forward a `Cookie` header on Next.js RSC server-side calls (web). */\n cookieHeader?: string;\n}\n\n/** The following feed's response: an activity feed plus its tab metadata. */\nexport interface FollowingFeedResponse extends ActivityFeedResponse {\n /**\n * Apps the viewer's network is actually active on, most active first.\n * Present on empty pages too, so a client's tabs survive a quiet tab.\n */\n apps?: FollowingFeedApp[];\n /** Set only when `items` is empty. */\n reason?: FollowingFeedEmptyReason;\n}\n\n/**\n * The viewer's cross-app following feed: what OTHER apps their connections use\n * (Tangled, WhiteWind, Smoke Signal, ...) — Bluesky is excluded server-side by\n * default, as it is the least interesting part and other apps already surface\n * it. Pass `includeBluesky: true` to bring it back in.\n *\n * Auth is identity-only server-side, so the NATIVE app supplies\n * `config.getAuthToken` to mint a service-auth Bearer, while WEB relies on its\n * session cookie (`credentials: 'include'`). Returns `null` on error. v1 is a\n * finite first page (no cursor yet).\n */\nexport async function fetchFollowingFeed(\n config: SifaApiConfig,\n opts: FetchFollowingFeedOptions = {},\n): Promise<FollowingFeedResponse | null> {\n const params = new URLSearchParams();\n if (opts.limit) params.set('limit', String(opts.limit));\n // A selected tab decides Bluesky on its own, so the flag is not also sent.\n if (opts.app) params.set('app', opts.app);\n else if (opts.includeBluesky) params.set('includeBluesky', 'true');\n const qs = params.toString();\n\n const headers: Record<string, string> = { ...(opts.headers ?? {}) };\n if (opts.cookieHeader) headers.cookie = opts.cookieHeader;\n if (config.getAuthToken) {\n const token = await config.getAuthToken(FOLLOWING_FEED_LXM);\n if (token) headers.Authorization = `Bearer ${token}`;\n }\n\n try {\n return await apiFetch<FollowingFeedResponse>(\n config,\n `/api/following/feed${qs ? `?${qs}` : ''}`,\n { credentials: 'include', cache: 'no-store', ...opts, headers },\n );\n } catch {\n return null;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/query/fetchers/follow.ts"],"sourcesContent":["import {\n apiFetch,\n encodeIdentifier,\n apiWrite,\n type ApiFetchOptions,\n type SifaApiConfig,\n type WriteResult,\n} from '../client.js';\nimport type { ActivityFeedResponse } from './activity.js';\n\nexport interface FollowProfile {\n did: string;\n handle: string;\n displayName?: string;\n headline?: string;\n avatarUrl?: string;\n source: string;\n claimed: boolean;\n followedAt: string;\n blueskyVerified?: boolean;\n blueskyVerifiedAt?: string | null;\n}\n\nexport interface FollowingResponse {\n follows: FollowProfile[];\n cursor?: string;\n}\n\n/** People the authenticated user follows. Empty on error. */\nexport async function fetchFollowing(\n config: SifaApiConfig,\n opts: { source?: string; cursor?: string; limit?: number } & ApiFetchOptions = {},\n): Promise<FollowingResponse> {\n const params = new URLSearchParams();\n if (opts.source) params.set('source', opts.source);\n if (opts.cursor) params.set('cursor', opts.cursor);\n if (opts.limit) params.set('limit', String(opts.limit));\n const qs = params.toString();\n\n try {\n return await apiFetch<FollowingResponse>(config, `/api/following${qs ? `?${qs}` : ''}`, {\n credentials: 'include',\n cache: 'no-store',\n ...opts,\n });\n } catch {\n return { follows: [] };\n }\n}\n\n/**\n * Result of {@link followUser}. Extends {@link WriteResult} with the\n * follow `rkey` returned by sifa-api on success. Self-follow + invalid\n * handle surface as `success: false` with the server-provided message;\n * dup-follow is idempotent (sifa-api E7) and resolves as `success: true`.\n */\nexport interface FollowUserResult extends WriteResult {\n rkey?: string;\n /** DID of the followed subject (server-resolved from the handle). */\n subjectDid?: string;\n}\n\n/**\n * Create an `id.sifa.graph.follow` record on the caller's PDS via the\n * AppView. Idempotent on duplicate (server catches the unique-violation\n * and returns 200, per sifa-api#673 E7).\n */\nexport function followUser(\n config: SifaApiConfig,\n handle: string,\n opts: { note?: string } & Omit<ApiFetchOptions, 'method' | 'body'> = {},\n): Promise<FollowUserResult> {\n const { note, ...rest } = opts;\n return apiWrite<{ rkey?: string; subjectDid?: string }>(\n config,\n `/api/follow/${encodeIdentifier(handle)}`,\n 'POST',\n {\n body: note !== undefined ? { note } : undefined,\n ...rest,\n },\n );\n}\n\n/** Delete the authenticated viewer's `id.sifa.graph.follow` for `handle`. */\nexport function unfollowUser(\n config: SifaApiConfig,\n handle: string,\n opts: Omit<ApiFetchOptions, 'method' | 'body'> = {},\n): Promise<WriteResult> {\n return apiWrite(config, `/api/follow/${encodeIdentifier(handle)}`, 'DELETE', opts);\n}\n\nexport interface FollowListPage {\n follows: FollowProfile[];\n cursor: string | null;\n}\n\nexport interface FetchFollowListOptions extends ApiFetchOptions {\n cursor?: string;\n limit?: number;\n /**\n * Pass the caller's `Cookie` header on Next.js RSC server-side calls\n * (mirrors {@link FetchActivityFeedOptions}; required for authenticated\n * server fetches because `credentials: 'include'` does not propagate\n * cookies from RSC).\n */\n cookieHeader?: string;\n}\n\nfunction buildListPath(prefix: string, opts: FetchFollowListOptions): string {\n const params = new URLSearchParams();\n if (opts.cursor) params.set('cursor', opts.cursor);\n if (opts.limit) params.set('limit', String(opts.limit));\n const qs = params.toString();\n return `${prefix}${qs ? `?${qs}` : ''}`;\n}\n\n/**\n * Paginated list of `handle`'s followers. Returns an empty page on error\n * so the UI can render a graceful \"no followers yet\" state.\n */\nexport async function getFollowers(\n config: SifaApiConfig,\n handle: string,\n opts: FetchFollowListOptions = {},\n): Promise<FollowListPage> {\n const headers: Record<string, string> = { ...(opts.headers ?? {}) };\n if (opts.cookieHeader) headers.cookie = opts.cookieHeader;\n try {\n const res = await apiFetch<{ follows: FollowProfile[]; cursor?: string | null }>(\n config,\n buildListPath(`/api/profile/${encodeIdentifier(handle)}/followers`, opts),\n { credentials: 'include', cache: 'no-store', ...opts, headers },\n );\n return { follows: res.follows, cursor: res.cursor ?? null };\n } catch {\n return { follows: [], cursor: null };\n }\n}\n\n/** Paginated list of who `handle` follows. */\nexport async function getFollowing(\n config: SifaApiConfig,\n handle: string,\n opts: FetchFollowListOptions = {},\n): Promise<FollowListPage> {\n const headers: Record<string, string> = { ...(opts.headers ?? {}) };\n if (opts.cookieHeader) headers.cookie = opts.cookieHeader;\n try {\n const res = await apiFetch<{ follows: FollowProfile[]; cursor?: string | null }>(\n config,\n buildListPath(`/api/profile/${encodeIdentifier(handle)}/following`, opts),\n { credentials: 'include', cache: 'no-store', ...opts, headers },\n );\n return { follows: res.follows, cursor: res.cursor ?? null };\n } catch {\n return { follows: [], cursor: null };\n }\n}\n\n/** The method binding for the following-feed service-auth token; MUST match the\n * sifa-api endpoint's `lxm`. */\nexport const FOLLOWING_FEED_LXM = 'id.sifa.feed.getFollowingFeed';\n\n/** One app the viewer's network is active on, for the feed's source tabs. */\nexport interface FollowingFeedApp {\n /** Registry app id, e.g. `tangled`. Pass back as {@link FetchFollowingFeedOptions.app}. */\n id: string;\n name: string;\n /** Summed recent activity across the network; the tab ordering. */\n count: number;\n}\n\n/**\n * Why a following-feed page came back empty. Lets a client say something true\n * per case instead of one catch-all sentence, and point at the right next step.\n */\nexport type FollowingFeedEmptyReason =\n | 'no_follows'\n | 'no_visible_follows'\n | 'no_cross_app_activity'\n | 'no_recent_records'\n | 'all_hidden'\n | 'nothing_renderable'\n | 'hydration_failed'\n /** The feed is still being built. Nothing is wrong: ask again shortly. */\n | 'building';\n\n/** Options for {@link fetchFollowingFeed}. */\nexport interface FetchFollowingFeedOptions extends ApiFetchOptions {\n limit?: number;\n /**\n * Opt in to the viewer's Bluesky activity too. Off by default: the point of\n * the feed is what connections do on OTHER apps.\n *\n * Ignored when {@link app} is set, since a source tab selects one app.\n */\n includeBluesky?: boolean;\n /**\n * Restrict the page to one app (a registry id from the response's `apps`).\n * This is what a source tab selects. `app: 'bluesky'` selects Bluesky even\n * though it is excluded from the default mixed view.\n */\n app?: string;\n /** Forward a `Cookie` header on Next.js RSC server-side calls (web). */\n cookieHeader?: string;\n}\n\n/** The following feed's response: an activity feed plus its tab metadata. */\nexport interface FollowingFeedResponse extends ActivityFeedResponse {\n /**\n * Apps the viewer's network is actually active on, most active first.\n * Present on empty pages too, so a client's tabs survive a quiet tab.\n */\n apps?: FollowingFeedApp[];\n /** Set only when `items` is empty. */\n reason?: FollowingFeedEmptyReason;\n /** When this feed was built (ISO 8601). Absent on a `building` answer. */\n builtAt?: string;\n /**\n * True when this is an older build, served while a newer one is built in\n * the background. Poll {@link fetchFollowingFeedVersion} to learn when the\n * newer one is ready.\n */\n stale?: boolean;\n}\n\n/** Query string and auth headers shared by the feed and its version check. */\nasync function followingFeedRequest(config: SifaApiConfig, opts: FetchFollowingFeedOptions) {\n const params = new URLSearchParams();\n if (opts.limit) params.set('limit', String(opts.limit));\n // A selected tab decides Bluesky on its own, so the flag is not also sent.\n if (opts.app) params.set('app', opts.app);\n else if (opts.includeBluesky) params.set('includeBluesky', 'true');\n const qs = params.toString();\n\n const headers: Record<string, string> = { ...(opts.headers ?? {}) };\n if (opts.cookieHeader) headers.cookie = opts.cookieHeader;\n if (config.getAuthToken) {\n const token = await config.getAuthToken(FOLLOWING_FEED_LXM);\n if (token) headers.Authorization = `Bearer ${token}`;\n }\n return { query: qs ? `?${qs}` : '', headers };\n}\n\n/**\n * The viewer's cross-app following feed: what OTHER apps their connections use\n * (Tangled, WhiteWind, Smoke Signal, ...) — Bluesky is excluded server-side by\n * default, as it is the least interesting part and other apps already surface\n * it. Pass `includeBluesky: true` to bring it back in.\n *\n * Auth is identity-only server-side, so the NATIVE app supplies\n * `config.getAuthToken` to mint a service-auth Bearer, while WEB relies on its\n * session cookie (`credentials: 'include'`). Returns `null` on error. v1 is a\n * finite first page (no cursor yet).\n */\nexport async function fetchFollowingFeed(\n config: SifaApiConfig,\n opts: FetchFollowingFeedOptions = {},\n): Promise<FollowingFeedResponse | null> {\n const { query, headers } = await followingFeedRequest(config, opts);\n try {\n return await apiFetch<FollowingFeedResponse>(config, `/api/following/feed${query}`, {\n credentials: 'include',\n cache: 'no-store',\n ...opts,\n headers,\n });\n } catch {\n return null;\n }\n}\n\n/** When the cached following feed for a query was built. */\nexport interface FollowingFeedVersion {\n /** ISO 8601, or null when nothing is cached for this query. */\n builtAt: string | null;\n}\n\n/**\n * When the cached following feed for the same query was built. Cheap: it never\n * builds a feed. A client showing a `stale` feed polls this and offers the\n * newer one once {@link isNewerFollowingFeed} says so. Returns `null` on error.\n */\nexport async function fetchFollowingFeedVersion(\n config: SifaApiConfig,\n opts: FetchFollowingFeedOptions = {},\n): Promise<FollowingFeedVersion | null> {\n const { query, headers } = await followingFeedRequest(config, opts);\n try {\n return await apiFetch<FollowingFeedVersion>(config, `/api/following/feed/version${query}`, {\n credentials: 'include',\n cache: 'no-store',\n ...opts,\n headers,\n });\n } catch {\n return null;\n }\n}\n\n/** True when `latestBuiltAt` is a newer build than the one shown (`shownBuiltAt`). */\nexport function isNewerFollowingFeed(\n shownBuiltAt: string | null | undefined,\n latestBuiltAt: string | null | undefined,\n): boolean {\n if (!shownBuiltAt || !latestBuiltAt) return false;\n return Date.parse(latestBuiltAt) > Date.parse(shownBuiltAt);\n}\n"],"mappings":"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","names":[]}
@@ -1063,7 +1063,7 @@ async function getFollowing(config, handle, opts = {}) {
1063
1063
  }
1064
1064
  }
1065
1065
  var FOLLOWING_FEED_LXM = "id.sifa.feed.getFollowingFeed";
1066
- async function fetchFollowingFeed(config, opts = {}) {
1066
+ async function followingFeedRequest(config, opts) {
1067
1067
  const params = new URLSearchParams();
1068
1068
  if (opts.limit) params.set("limit", String(opts.limit));
1069
1069
  if (opts.app) params.set("app", opts.app);
@@ -1075,16 +1075,38 @@ async function fetchFollowingFeed(config, opts = {}) {
1075
1075
  const token = await config.getAuthToken(FOLLOWING_FEED_LXM);
1076
1076
  if (token) headers.Authorization = `Bearer ${token}`;
1077
1077
  }
1078
+ return { query: qs ? `?${qs}` : "", headers };
1079
+ }
1080
+ async function fetchFollowingFeed(config, opts = {}) {
1081
+ const { query, headers } = await followingFeedRequest(config, opts);
1078
1082
  try {
1079
- return await apiFetch(
1080
- config,
1081
- `/api/following/feed${qs ? `?${qs}` : ""}`,
1082
- { credentials: "include", cache: "no-store", ...opts, headers }
1083
- );
1083
+ return await apiFetch(config, `/api/following/feed${query}`, {
1084
+ credentials: "include",
1085
+ cache: "no-store",
1086
+ ...opts,
1087
+ headers
1088
+ });
1084
1089
  } catch {
1085
1090
  return null;
1086
1091
  }
1087
1092
  }
1093
+ async function fetchFollowingFeedVersion(config, opts = {}) {
1094
+ const { query, headers } = await followingFeedRequest(config, opts);
1095
+ try {
1096
+ return await apiFetch(config, `/api/following/feed/version${query}`, {
1097
+ credentials: "include",
1098
+ cache: "no-store",
1099
+ ...opts,
1100
+ headers
1101
+ });
1102
+ } catch {
1103
+ return null;
1104
+ }
1105
+ }
1106
+ function isNewerFollowingFeed(shownBuiltAt, latestBuiltAt) {
1107
+ if (!shownBuiltAt || !latestBuiltAt) return false;
1108
+ return Date.parse(latestBuiltAt) > Date.parse(shownBuiltAt);
1109
+ }
1088
1110
 
1089
1111
  // src/query/fetchers/follow-extras.ts
1090
1112
  function buildListPath2(prefix, opts) {
@@ -1816,6 +1838,7 @@ var sifaQueryKeys = {
1816
1838
  followers: (handle) => ["sifa", "follow", "followers", handle],
1817
1839
  followingOf: (handle) => ["sifa", "follow", "following-of", handle],
1818
1840
  feed: (opts) => ["sifa", "follow", "feed", opts],
1841
+ feedVersion: (opts) => ["sifa", "follow", "feed-version", opts],
1819
1842
  mutuals: (handle) => ["sifa", "follow", "mutuals", handle],
1820
1843
  blueskySuggestions: () => ["sifa", "follow", "bluesky-suggestions"]
1821
1844
  },
@@ -2101,6 +2124,7 @@ exports.fetchExternalAccounts = fetchExternalAccounts;
2101
2124
  exports.fetchFeaturedProfile = fetchFeaturedProfile;
2102
2125
  exports.fetchFollowing = fetchFollowing;
2103
2126
  exports.fetchFollowingFeed = fetchFollowingFeed;
2127
+ exports.fetchFollowingFeedVersion = fetchFollowingFeedVersion;
2104
2128
  exports.fetchGetProfileView = fetchGetProfileView;
2105
2129
  exports.fetchGivenConfirmations = fetchGivenConfirmations;
2106
2130
  exports.fetchHeatmapData = fetchHeatmapData;
@@ -2149,6 +2173,7 @@ exports.hideStandardPublication = hideStandardPublication;
2149
2173
  exports.importSearchEntities = importSearchEntities;
2150
2174
  exports.initiateNetworkMapGeneration = initiateNetworkMapGeneration;
2151
2175
  exports.isNetworkMapResponse = isNetworkMapResponse;
2176
+ exports.isNewerFollowingFeed = isNewerFollowingFeed;
2152
2177
  exports.linkSkillToPosition = linkSkillToPosition;
2153
2178
  exports.listFeatureAllowlist = listFeatureAllowlist;
2154
2179
  exports.refreshOrcidPublications = refreshOrcidPublications;