@atproto/bsky 0.3.13 → 0.5.0
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/CHANGELOG.md +61 -0
- package/dist/api/app/bsky/actor/getProfile.d.ts.map +1 -1
- package/dist/api/app/bsky/actor/getProfile.js +1 -0
- package/dist/api/app/bsky/actor/getProfile.js.map +1 -1
- package/dist/api/app/bsky/actor/getProfiles.d.ts.map +1 -1
- package/dist/api/app/bsky/actor/getProfiles.js +1 -0
- package/dist/api/app/bsky/actor/getProfiles.js.map +1 -1
- package/dist/api/app/bsky/actor/getSuggestions.d.ts.map +1 -1
- package/dist/api/app/bsky/actor/getSuggestions.js +1 -0
- package/dist/api/app/bsky/actor/getSuggestions.js.map +1 -1
- package/dist/api/app/bsky/actor/searchActors.d.ts.map +1 -1
- package/dist/api/app/bsky/actor/searchActors.js +1 -0
- package/dist/api/app/bsky/actor/searchActors.js.map +1 -1
- package/dist/api/app/bsky/actor/searchActorsTypeahead.d.ts.map +1 -1
- package/dist/api/app/bsky/actor/searchActorsTypeahead.js +1 -0
- package/dist/api/app/bsky/actor/searchActorsTypeahead.js.map +1 -1
- package/dist/api/app/bsky/bookmark/getBookmarks.d.ts.map +1 -1
- package/dist/api/app/bsky/bookmark/getBookmarks.js +1 -0
- package/dist/api/app/bsky/bookmark/getBookmarks.js.map +1 -1
- package/dist/api/app/bsky/contact/getMatches.d.ts.map +1 -1
- package/dist/api/app/bsky/contact/getMatches.js +1 -0
- package/dist/api/app/bsky/contact/getMatches.js.map +1 -1
- package/dist/api/app/bsky/contact/importContacts.d.ts.map +1 -1
- package/dist/api/app/bsky/contact/importContacts.js +1 -0
- package/dist/api/app/bsky/contact/importContacts.js.map +1 -1
- package/dist/api/app/bsky/feed/getActorFeeds.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getActorFeeds.js +5 -1
- package/dist/api/app/bsky/feed/getActorFeeds.js.map +1 -1
- package/dist/api/app/bsky/feed/getActorLikes.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getActorLikes.js +1 -0
- package/dist/api/app/bsky/feed/getActorLikes.js.map +1 -1
- package/dist/api/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getAuthorFeed.js +1 -0
- package/dist/api/app/bsky/feed/getAuthorFeed.js.map +1 -1
- package/dist/api/app/bsky/feed/getFeed.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getFeed.js +1 -0
- package/dist/api/app/bsky/feed/getFeed.js.map +1 -1
- package/dist/api/app/bsky/feed/getFeedGenerator.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getFeedGenerator.js +5 -1
- package/dist/api/app/bsky/feed/getFeedGenerator.js.map +1 -1
- package/dist/api/app/bsky/feed/getFeedGenerators.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getFeedGenerators.js +5 -1
- package/dist/api/app/bsky/feed/getFeedGenerators.js.map +1 -1
- package/dist/api/app/bsky/feed/getLikes.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getLikes.js +1 -0
- package/dist/api/app/bsky/feed/getLikes.js.map +1 -1
- package/dist/api/app/bsky/feed/getListFeed.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getListFeed.js +1 -0
- package/dist/api/app/bsky/feed/getListFeed.js.map +1 -1
- package/dist/api/app/bsky/feed/getPostThread.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getPostThread.js +1 -0
- package/dist/api/app/bsky/feed/getPostThread.js.map +1 -1
- package/dist/api/app/bsky/feed/getPosts.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getPosts.js +5 -1
- package/dist/api/app/bsky/feed/getPosts.js.map +1 -1
- package/dist/api/app/bsky/feed/getQuotes.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getQuotes.js +1 -0
- package/dist/api/app/bsky/feed/getQuotes.js.map +1 -1
- package/dist/api/app/bsky/feed/getRepostedBy.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getRepostedBy.js +1 -0
- package/dist/api/app/bsky/feed/getRepostedBy.js.map +1 -1
- package/dist/api/app/bsky/feed/getSuggestedFeeds.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getSuggestedFeeds.js +5 -1
- package/dist/api/app/bsky/feed/getSuggestedFeeds.js.map +1 -1
- package/dist/api/app/bsky/feed/getTimeline.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getTimeline.js +1 -0
- package/dist/api/app/bsky/feed/getTimeline.js.map +1 -1
- package/dist/api/app/bsky/feed/searchPosts.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/searchPosts.js +1 -0
- package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
- package/dist/api/app/bsky/feed/searchPostsV2.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/searchPostsV2.js +1 -0
- package/dist/api/app/bsky/feed/searchPostsV2.js.map +1 -1
- package/dist/api/app/bsky/graph/getActorStarterPacks.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getActorStarterPacks.js +1 -0
- package/dist/api/app/bsky/graph/getActorStarterPacks.js.map +1 -1
- package/dist/api/app/bsky/graph/getBlocks.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getBlocks.js +5 -1
- package/dist/api/app/bsky/graph/getBlocks.js.map +1 -1
- package/dist/api/app/bsky/graph/getFollowers.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getFollowers.js +1 -0
- package/dist/api/app/bsky/graph/getFollowers.js.map +1 -1
- package/dist/api/app/bsky/graph/getFollows.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getFollows.js +1 -0
- package/dist/api/app/bsky/graph/getFollows.js.map +1 -1
- package/dist/api/app/bsky/graph/getKnownFollowers.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getKnownFollowers.js +1 -0
- package/dist/api/app/bsky/graph/getKnownFollowers.js.map +1 -1
- package/dist/api/app/bsky/graph/getList.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getList.js +1 -0
- package/dist/api/app/bsky/graph/getList.js.map +1 -1
- package/dist/api/app/bsky/graph/getListBlocks.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getListBlocks.js +5 -1
- package/dist/api/app/bsky/graph/getListBlocks.js.map +1 -1
- package/dist/api/app/bsky/graph/getListMutes.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getListMutes.js +5 -1
- package/dist/api/app/bsky/graph/getListMutes.js.map +1 -1
- package/dist/api/app/bsky/graph/getLists.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getLists.js +1 -0
- package/dist/api/app/bsky/graph/getLists.js.map +1 -1
- package/dist/api/app/bsky/graph/getListsWithMembership.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getListsWithMembership.js +1 -0
- package/dist/api/app/bsky/graph/getListsWithMembership.js.map +1 -1
- package/dist/api/app/bsky/graph/getMutes.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getMutes.js +5 -1
- package/dist/api/app/bsky/graph/getMutes.js.map +1 -1
- package/dist/api/app/bsky/graph/getStarterPack.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getStarterPack.js +1 -0
- package/dist/api/app/bsky/graph/getStarterPack.js.map +1 -1
- package/dist/api/app/bsky/graph/getStarterPacks.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getStarterPacks.js +1 -0
- package/dist/api/app/bsky/graph/getStarterPacks.js.map +1 -1
- package/dist/api/app/bsky/graph/getStarterPacksWithMembership.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getStarterPacksWithMembership.js +1 -0
- package/dist/api/app/bsky/graph/getStarterPacksWithMembership.js.map +1 -1
- package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js +1 -0
- package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js.map +1 -1
- package/dist/api/app/bsky/graph/searchStarterPacks.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/searchStarterPacks.js +1 -0
- package/dist/api/app/bsky/graph/searchStarterPacks.js.map +1 -1
- package/dist/api/app/bsky/graph/searchStarterPacksV2.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/searchStarterPacksV2.js +1 -0
- package/dist/api/app/bsky/graph/searchStarterPacksV2.js.map +1 -1
- package/dist/api/app/bsky/labeler/getServices.d.ts.map +1 -1
- package/dist/api/app/bsky/labeler/getServices.js +1 -0
- package/dist/api/app/bsky/labeler/getServices.js.map +1 -1
- package/dist/api/app/bsky/notification/getGroupedNotifications.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/getGroupedNotifications.js +41 -20
- package/dist/api/app/bsky/notification/getGroupedNotifications.js.map +1 -1
- package/dist/api/app/bsky/notification/grouping/algoGravity.d.ts +4 -3
- package/dist/api/app/bsky/notification/grouping/algoGravity.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/grouping/algoGravity.js +53 -42
- package/dist/api/app/bsky/notification/grouping/algoGravity.js.map +1 -1
- package/dist/api/app/bsky/notification/grouping/algoLookback.d.ts +4 -3
- package/dist/api/app/bsky/notification/grouping/algoLookback.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/grouping/algoLookback.js +56 -42
- package/dist/api/app/bsky/notification/grouping/algoLookback.js.map +1 -1
- package/dist/api/app/bsky/notification/grouping/grouping.d.ts +12 -2
- package/dist/api/app/bsky/notification/grouping/grouping.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/grouping/grouping.js +20 -6
- package/dist/api/app/bsky/notification/grouping/grouping.js.map +1 -1
- package/dist/api/app/bsky/notification/grouping/spotlight.d.ts +2 -2
- package/dist/api/app/bsky/notification/grouping/spotlight.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/grouping/spotlight.js +35 -22
- package/dist/api/app/bsky/notification/grouping/spotlight.js.map +1 -1
- package/dist/api/app/bsky/notification/listActivitySubscriptions.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/listActivitySubscriptions.js +1 -0
- package/dist/api/app/bsky/notification/listActivitySubscriptions.js.map +1 -1
- package/dist/api/app/bsky/notification/listNotifications.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/listNotifications.js +5 -1
- package/dist/api/app/bsky/notification/listNotifications.js.map +1 -1
- package/dist/api/app/bsky/unspecced/atmosphere-grouping.d.ts +16 -0
- package/dist/api/app/bsky/unspecced/atmosphere-grouping.d.ts.map +1 -0
- package/dist/api/app/bsky/unspecced/atmosphere-grouping.js +124 -0
- package/dist/api/app/bsky/unspecced/atmosphere-grouping.js.map +1 -0
- package/dist/api/app/bsky/unspecced/atmosphere-hydration.d.ts +32 -0
- package/dist/api/app/bsky/unspecced/atmosphere-hydration.d.ts.map +1 -0
- package/dist/api/app/bsky/unspecced/atmosphere-hydration.js +201 -0
- package/dist/api/app/bsky/unspecced/atmosphere-hydration.js.map +1 -0
- package/dist/api/app/bsky/unspecced/atmosphere-pagination.d.ts +6 -0
- package/dist/api/app/bsky/unspecced/atmosphere-pagination.d.ts.map +1 -0
- package/dist/api/app/bsky/unspecced/atmosphere-pagination.js +164 -0
- package/dist/api/app/bsky/unspecced/atmosphere-pagination.js.map +1 -0
- package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js +120 -55
- package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getAtmosphereTimeline.d.ts +4 -0
- package/dist/api/app/bsky/unspecced/getAtmosphereTimeline.d.ts.map +1 -0
- package/dist/api/app/bsky/unspecced/getAtmosphereTimeline.js +30 -0
- package/dist/api/app/bsky/unspecced/getAtmosphereTimeline.js.map +1 -0
- package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.js +4 -7
- package/dist/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getPopularFeedGenerators.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getPopularFeedGenerators.js +1 -0
- package/dist/api/app/bsky/unspecced/getPopularFeedGenerators.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getPostThreadOtherV2.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getPostThreadOtherV2.js +1 -0
- package/dist/api/app/bsky/unspecced/getPostThreadOtherV2.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getPostThreadV2.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getPostThreadV2.js +1 -0
- package/dist/api/app/bsky/unspecced/getPostThreadV2.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedFeeds.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedFeeds.js +4 -7
- package/dist/api/app/bsky/unspecced/getSuggestedFeeds.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.js +1 -0
- package/dist/api/app/bsky/unspecced/getSuggestedOnboardingUsers.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedStarterPacks.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedStarterPacks.js +4 -7
- package/dist/api/app/bsky/unspecced/getSuggestedStarterPacks.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedUsers.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedUsers.js +2 -19
- package/dist/api/app/bsky/unspecced/getSuggestedUsers.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedUsersForDiscover.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedUsersForDiscover.js +1 -0
- package/dist/api/app/bsky/unspecced/getSuggestedUsersForDiscover.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedUsersForExplore.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedUsersForExplore.js +1 -0
- package/dist/api/app/bsky/unspecced/getSuggestedUsersForExplore.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedUsersForSeeMore.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getSuggestedUsersForSeeMore.js +1 -0
- package/dist/api/app/bsky/unspecced/getSuggestedUsersForSeeMore.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getTrendingTopics.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getTrendingTopics.js +6 -2
- package/dist/api/app/bsky/unspecced/getTrendingTopics.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getTrends.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getTrends.js +6 -2
- package/dist/api/app/bsky/unspecced/getTrends.js.map +1 -1
- package/dist/api/blob-resolver.d.ts.map +1 -1
- package/dist/api/blob-resolver.js +9 -8
- package/dist/api/blob-resolver.js.map +1 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -0
- package/dist/api/index.js.map +1 -1
- package/dist/api/internal/bsky/actor/getProfiles.d.ts.map +1 -1
- package/dist/api/internal/bsky/actor/getProfiles.js +1 -0
- package/dist/api/internal/bsky/actor/getProfiles.js.map +1 -1
- package/dist/config.d.ts +2 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -16
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +0 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +0 -3
- package/dist/context.js.map +1 -1
- package/dist/feature-gates/gates.d.ts +0 -4
- package/dist/feature-gates/gates.d.ts.map +1 -1
- package/dist/feature-gates/gates.js +0 -4
- package/dist/feature-gates/gates.js.map +1 -1
- package/dist/hydration/external.d.ts +58 -45
- package/dist/hydration/external.d.ts.map +1 -1
- package/dist/hydration/external.js +134 -114
- package/dist/hydration/external.js.map +1 -1
- package/dist/hydration/hydrator.d.ts +15 -9
- package/dist/hydration/hydrator.d.ts.map +1 -1
- package/dist/hydration/hydrator.js +170 -210
- package/dist/hydration/hydrator.js.map +1 -1
- package/dist/image/server.d.ts.map +1 -1
- package/dist/image/server.js +12 -1
- package/dist/image/server.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -19
- package/dist/index.js.map +1 -1
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.d.ts +29 -0
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.js +2 -1
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereExploreTab.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.d.ts +3 -0
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.d.ts +85 -0
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.js +29 -0
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.defs.js.map +1 -0
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.js +6 -0
- package/dist/lexicons/app/bsky/unspecced/getAtmosphereTimeline.js.map +1 -0
- package/dist/lexicons/app/bsky/unspecced.d.ts +1 -0
- package/dist/lexicons/app/bsky/unspecced.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/unspecced.js +1 -0
- package/dist/lexicons/app/bsky/unspecced.js.map +1 -1
- package/dist/lexicons/tools/ozone/moderation/emitEvent.defs.d.ts +2 -2
- package/dist/lexicons/tools/ozone/moderation/emitEvent.defs.js.map +1 -1
- package/dist/lexicons/tools/ozone/report/closeReports.defs.d.ts +1 -1
- package/dist/lexicons/tools/ozone/report/closeReports.defs.d.ts.map +1 -1
- package/dist/lexicons/tools/ozone/report/closeReports.defs.js +1 -1
- package/dist/lexicons/tools/ozone/report/closeReports.defs.js.map +1 -1
- package/dist/telemetry/events.d.ts +14 -6
- package/dist/telemetry/events.d.ts.map +1 -1
- package/dist/telemetry/events.js +40 -5
- package/dist/telemetry/events.js.map +1 -1
- package/dist/util/external/standard-site.d.ts +2 -3
- package/dist/util/external/standard-site.d.ts.map +1 -1
- package/dist/util/external/standard-site.js +2 -3
- package/dist/util/external/standard-site.js.map +1 -1
- package/dist/util/uris.d.ts +5 -2
- package/dist/util/uris.d.ts.map +1 -1
- package/dist/util/uris.js +14 -1
- package/dist/util/uris.js.map +1 -1
- package/dist/views/external.d.ts +5 -0
- package/dist/views/external.d.ts.map +1 -1
- package/dist/views/external.js +38 -11
- package/dist/views/external.js.map +1 -1
- package/dist/views/index.d.ts +2 -2
- package/dist/views/index.d.ts.map +1 -1
- package/dist/views/index.js +17 -11
- package/dist/views/index.js.map +1 -1
- package/package.json +13 -14
|
@@ -3,19 +3,29 @@ import { l } from '@atproto/lex';
|
|
|
3
3
|
import { AtUri } from '@atproto/syntax';
|
|
4
4
|
import { ForbiddenError, MethodNotImplementedError, UpstreamFailureError, UpstreamTimeoutError, } from '@atproto/xrpc-server';
|
|
5
5
|
import { safeFetchWrap } from '@atproto-labs/fetch-node';
|
|
6
|
+
import { mergeStates, } from '../../../../hydration/hydrator.js';
|
|
6
7
|
import { app, site } from '../../../../lexicons/index.js';
|
|
7
8
|
import { httpLogger } from '../../../../logger.js';
|
|
8
9
|
import { createPipeline, noRules, } from '../../../../pipeline.js';
|
|
9
10
|
import { getAtprotoPassthroughHeaders } from '../../../../util/headers.js';
|
|
10
11
|
const MAX_CONFIG_BYTES = 512 * 1024;
|
|
11
12
|
const MAX_LIST_ITEMS = 10;
|
|
12
|
-
const
|
|
13
|
+
const groupFields = {
|
|
13
14
|
canonicalUri: l.optional(l.unknown()),
|
|
14
|
-
startsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),
|
|
15
|
-
endsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),
|
|
16
15
|
tags: l.optional(l.array(l.string({ maxLength: 128 }), { maxLength: 100 })),
|
|
17
16
|
locations: l.optional(l.array(l.string({ maxLength: 16 }), { maxLength: 100 })),
|
|
17
|
+
};
|
|
18
|
+
const groupSchema = l.object(groupFields);
|
|
19
|
+
// Only livestreams are scheduled; other groups ignore any schedule fields.
|
|
20
|
+
const optionalDatetime = l.optional(l.nullable(l.string({ format: 'datetime' })));
|
|
21
|
+
const livestreamSchema = l.object({
|
|
22
|
+
...groupFields,
|
|
23
|
+
previewAt: optionalDatetime,
|
|
24
|
+
startsAt: optionalDatetime,
|
|
25
|
+
endsAt: optionalDatetime,
|
|
18
26
|
});
|
|
27
|
+
// `did` hydrates the app's profile; `null` and absent both mean no profile.
|
|
28
|
+
const appDidSchema = l.optional(l.nullable(l.string({ format: 'did' })));
|
|
19
29
|
const bannerSchema = l.object({
|
|
20
30
|
status: l.string({ maxLength: 128 }),
|
|
21
31
|
startsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),
|
|
@@ -111,7 +121,7 @@ async function skeleton({ ctx, params, }) {
|
|
|
111
121
|
]);
|
|
112
122
|
const now = Date.now();
|
|
113
123
|
const announcementBanner = selectBanner(config.announcementBanners, params, now);
|
|
114
|
-
const select = createSelector(params, params.requestHeaders
|
|
124
|
+
const select = createSelector(params, params.requestHeaders);
|
|
115
125
|
return {
|
|
116
126
|
now,
|
|
117
127
|
announcementBanner,
|
|
@@ -120,15 +130,20 @@ async function skeleton({ ctx, params, }) {
|
|
|
120
130
|
const root = recordUri(uri);
|
|
121
131
|
return root &&
|
|
122
132
|
new AtUri(root).collection === site.standard.document.$type
|
|
123
|
-
? {
|
|
133
|
+
? { uri: root }
|
|
124
134
|
: undefined;
|
|
125
135
|
})
|
|
126
136
|
: completeGroups(select(config.articles))),
|
|
127
137
|
articleRecIdStr: recommendations?.recIdStr,
|
|
128
138
|
publications: orderGroups(completeGroups(select(config.publications))),
|
|
129
139
|
photos: orderGroups(completeGroups(select(config.photos))),
|
|
130
|
-
livestreams
|
|
131
|
-
//
|
|
140
|
+
// Expired and not-yet-previewed livestreams are dropped before selection,
|
|
141
|
+
// so they never consume the cap.
|
|
142
|
+
livestreams: orderGroups(mapDefined(select(config.livestreams.filter((entry) => isPreviewable(entry, now))), ({ canonicalUri, startsAt }) => {
|
|
143
|
+
const uri = recordUri(canonicalUri);
|
|
144
|
+
return uri ? { uri, startsAt } : undefined;
|
|
145
|
+
})),
|
|
146
|
+
// Apps are not record-backed: keep worker order.
|
|
132
147
|
apps: config.apps.slice(0, MAX_LIST_ITEMS),
|
|
133
148
|
};
|
|
134
149
|
}
|
|
@@ -141,49 +156,74 @@ async function hydration({ ctx, params, skeleton, }) {
|
|
|
141
156
|
...skeleton.livestreams,
|
|
142
157
|
].map(({ uri }) => uri)),
|
|
143
158
|
];
|
|
144
|
-
|
|
159
|
+
const dids = [...new Set(mapDefined(skeleton.apps, ({ did }) => did))];
|
|
160
|
+
const [externalState, appProfileState] = await Promise.all([
|
|
161
|
+
ctx.hydrator.hydrateEmbedExternalViewFromUris(uris, params.hydrateCtx),
|
|
162
|
+
dids.length
|
|
163
|
+
? ctx.hydrator.hydrateProfilesDetailed(dids, params.hydrateCtx)
|
|
164
|
+
: {},
|
|
165
|
+
]);
|
|
166
|
+
// Detailed profile state goes second so it wins any overlapping actor state.
|
|
167
|
+
return mergeStates(externalState, appProfileState);
|
|
145
168
|
}
|
|
146
169
|
function presentation({ ctx, skeleton, hydration: state, }) {
|
|
147
|
-
const { now, articles, publications, photos, livestreams } = skeleton;
|
|
170
|
+
const { now, articles, publications, photos, livestreams, apps } = skeleton;
|
|
148
171
|
return {
|
|
149
172
|
announcementBanner: skeleton.announcementBanner,
|
|
150
173
|
articles: {
|
|
151
|
-
items: mapDefined(articles, ({
|
|
174
|
+
items: mapDefined(articles, ({ uri }) => {
|
|
152
175
|
const view = ctx.views.externalRecordView(uri, state, now);
|
|
153
176
|
return view && app.bsky.embed.external.viewArticle.$isTypeOf(view)
|
|
154
|
-
? { featured, view }
|
|
177
|
+
? { featured: false, view }
|
|
155
178
|
: undefined;
|
|
156
179
|
}),
|
|
157
180
|
recIdStr: skeleton.articleRecIdStr,
|
|
158
181
|
},
|
|
159
182
|
publications: {
|
|
160
|
-
items: mapDefined(publications, ({
|
|
183
|
+
items: mapDefined(publications, ({ uri }) => {
|
|
161
184
|
const view = ctx.views.externalRecordView(uri, state, now);
|
|
162
185
|
return view &&
|
|
163
186
|
app.bsky.embed.external.viewArticlePublication.$isTypeOf(view)
|
|
164
|
-
? { featured, view }
|
|
187
|
+
? { featured: false, view }
|
|
165
188
|
: undefined;
|
|
166
189
|
}),
|
|
167
190
|
},
|
|
168
191
|
photos: {
|
|
169
|
-
items: mapDefined(photos, ({
|
|
192
|
+
items: mapDefined(photos, ({ uri }) => {
|
|
170
193
|
const view = ctx.views.externalRecordView(uri, state, now);
|
|
171
194
|
return view &&
|
|
172
195
|
app.bsky.embed.external.viewGallery.$isTypeOf(view) &&
|
|
173
196
|
view.items.length
|
|
174
|
-
? { featured, view }
|
|
197
|
+
? { featured: false, view }
|
|
175
198
|
: undefined;
|
|
176
199
|
}),
|
|
177
200
|
},
|
|
178
201
|
livestreams: {
|
|
179
|
-
items: mapDefined(livestreams, ({
|
|
202
|
+
items: mapDefined(livestreams, ({ uri, startsAt }) => {
|
|
180
203
|
const view = ctx.views.externalRecordView(uri, state, now);
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
204
|
+
if (!view || !app.bsky.embed.external.viewLivestream.$isTypeOf(view))
|
|
205
|
+
return;
|
|
206
|
+
// Previewed streams are never live; copy rather than mutate the view.
|
|
207
|
+
return startsAt != null && Date.parse(startsAt) > now
|
|
208
|
+
? { featured: false, view: { ...view, active: false } }
|
|
209
|
+
: { featured: false, view };
|
|
184
210
|
}),
|
|
185
211
|
},
|
|
186
|
-
apps:
|
|
212
|
+
apps: apps.map(({ card, did }) => {
|
|
213
|
+
const profile = did ? ctx.views.profileDetailed(did, state) : undefined;
|
|
214
|
+
return {
|
|
215
|
+
id: card.id,
|
|
216
|
+
title: card.title,
|
|
217
|
+
description: card.description,
|
|
218
|
+
logo: profile?.avatar ?? card.logo,
|
|
219
|
+
backgroundImage: profile?.banner ?? card.backgroundImage,
|
|
220
|
+
overlayColor: card.overlayColor,
|
|
221
|
+
textColor: card.textColor,
|
|
222
|
+
url: card.url,
|
|
223
|
+
category: card.category,
|
|
224
|
+
profile,
|
|
225
|
+
};
|
|
226
|
+
}),
|
|
187
227
|
};
|
|
188
228
|
}
|
|
189
229
|
function createFetchExploreTabSkeleton() {
|
|
@@ -240,13 +280,44 @@ function normalizeConfig(body) {
|
|
|
240
280
|
articles: valid(groupSchema, config.articles),
|
|
241
281
|
publications: valid(groupSchema, config.publications),
|
|
242
282
|
photos: valid(groupSchema, config.photos),
|
|
243
|
-
livestreams: valid(
|
|
244
|
-
apps:
|
|
283
|
+
livestreams: valid(livestreamSchema, config.livestreams),
|
|
284
|
+
apps: entries(config.apps).flatMap((entry) => {
|
|
285
|
+
const parsed = parseApp(entry);
|
|
286
|
+
return parsed ? [parsed] : [];
|
|
287
|
+
}),
|
|
245
288
|
};
|
|
246
289
|
}
|
|
290
|
+
const APP_CARD_FIELDS = [
|
|
291
|
+
'id',
|
|
292
|
+
'title',
|
|
293
|
+
'description',
|
|
294
|
+
'logo',
|
|
295
|
+
'backgroundImage',
|
|
296
|
+
'overlayColor',
|
|
297
|
+
'textColor',
|
|
298
|
+
'url',
|
|
299
|
+
'category',
|
|
300
|
+
];
|
|
301
|
+
/**
|
|
302
|
+
* Validates an app entry. Only the public card fields are copied (so a
|
|
303
|
+
* supplied `profile` or `profileUrl` is never read) and only `did` is kept
|
|
304
|
+
* beyond them. A present but malformed `did` drops the app.
|
|
305
|
+
*/
|
|
306
|
+
function parseApp(entry) {
|
|
307
|
+
if (typeof entry !== 'object' || entry === null || Array.isArray(entry))
|
|
308
|
+
return;
|
|
309
|
+
const raw = entry;
|
|
310
|
+
const did = appDidSchema.safeParse(raw.did);
|
|
311
|
+
if (!did.success)
|
|
312
|
+
return;
|
|
313
|
+
const card = app.bsky.unspecced.getAtmosphereExploreTab.appCard.safeParse(noUndefinedVals(Object.fromEntries(APP_CARD_FIELDS.map((key) => [key, raw[key]]))));
|
|
314
|
+
if (!card.success)
|
|
315
|
+
return;
|
|
316
|
+
return { card: card.value, did: did.value ?? undefined };
|
|
317
|
+
}
|
|
247
318
|
/**
|
|
248
|
-
* Randomizes a selected list (Fisher-Yates)
|
|
249
|
-
*
|
|
319
|
+
* Randomizes a selected list (Fisher-Yates) and caps it. Runs before hydration,
|
|
320
|
+
* so presentation keeps this order.
|
|
250
321
|
*/
|
|
251
322
|
function orderGroups(groups) {
|
|
252
323
|
const shuffled = [...groups];
|
|
@@ -254,19 +325,25 @@ function orderGroups(groups) {
|
|
|
254
325
|
const j = Math.floor(Math.random() * (i + 1));
|
|
255
326
|
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
256
327
|
}
|
|
257
|
-
return
|
|
258
|
-
...shuffled.filter(({ featured }) => featured),
|
|
259
|
-
...shuffled.filter(({ featured }) => !featured),
|
|
260
|
-
].slice(0, MAX_LIST_ITEMS);
|
|
328
|
+
return shuffled.slice(0, MAX_LIST_ITEMS);
|
|
261
329
|
}
|
|
262
330
|
function completeGroups(groups = []) {
|
|
263
|
-
return mapDefined(groups, ({
|
|
331
|
+
return mapDefined(groups, ({ canonicalUri }) => {
|
|
264
332
|
const uri = recordUri(canonicalUri);
|
|
265
|
-
|
|
266
|
-
return;
|
|
267
|
-
return { featured, uri };
|
|
333
|
+
return uri ? { uri } : undefined;
|
|
268
334
|
});
|
|
269
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Editorial window for a livestream: shown from `previewAt` (falling back to
|
|
338
|
+
* `startsAt`) inclusive until `endsAt` exclusive. Absent or null bounds are
|
|
339
|
+
* open. This is independent of the view's heartbeat-based `active` flag.
|
|
340
|
+
*/
|
|
341
|
+
function isPreviewable({ previewAt, startsAt, endsAt }, now) {
|
|
342
|
+
const start = previewAt ?? startsAt;
|
|
343
|
+
if (start != null && Date.parse(start) > now)
|
|
344
|
+
return false;
|
|
345
|
+
return endsAt == null || now < Date.parse(endsAt);
|
|
346
|
+
}
|
|
270
347
|
/**
|
|
271
348
|
* Returns the single (already validated) banner to show. Eligible (enabled, in window) banners targeted at a matching location
|
|
272
349
|
* win, first in worker order; otherwise the unrestricted eligible banner with
|
|
@@ -304,11 +381,9 @@ function selectBanner(banners, params, now) {
|
|
|
304
381
|
/**
|
|
305
382
|
* Builds a per-list selector that drops location-ineligible entries, orders
|
|
306
383
|
* the rest by priority bucket (location, requested topic, `general`, untagged;
|
|
307
|
-
* stable within a bucket),
|
|
308
|
-
* of the rest (stable, so bucket order holds within each side), and keeps the
|
|
309
|
-
* first `MAX_LIST_ITEMS`. A worker-supplied `featured` flag is ignored.
|
|
384
|
+
* stable within a bucket), and keeps the first `MAX_LIST_ITEMS`.
|
|
310
385
|
*/
|
|
311
|
-
function createSelector(params, headers
|
|
386
|
+
function createSelector(params, headers) {
|
|
312
387
|
const topics = parseTopics(headers['x-atproto-bsky-topics']);
|
|
313
388
|
return (groups = []) => {
|
|
314
389
|
const buckets = [[], [], [], []];
|
|
@@ -317,22 +392,9 @@ function createSelector(params, headers, now) {
|
|
|
317
392
|
if (bucket !== undefined)
|
|
318
393
|
buckets[bucket].push(group);
|
|
319
394
|
}
|
|
320
|
-
|
|
321
|
-
const ordinary = [];
|
|
322
|
-
for (const group of buckets.flat()) {
|
|
323
|
-
const active = isFeatured(group, now);
|
|
324
|
-
(active ? featured : ordinary).push({ ...group, featured: active });
|
|
325
|
-
}
|
|
326
|
-
return [...featured, ...ordinary].slice(0, MAX_LIST_ITEMS);
|
|
395
|
+
return buckets.flat().slice(0, MAX_LIST_ITEMS);
|
|
327
396
|
};
|
|
328
397
|
}
|
|
329
|
-
/** Unlike banners, content needs both bounds to define a featured window. */
|
|
330
|
-
function isFeatured({ startsAt, endsAt }, now) {
|
|
331
|
-
return (startsAt != null &&
|
|
332
|
-
endsAt != null &&
|
|
333
|
-
Date.parse(startsAt) <= now &&
|
|
334
|
-
now < Date.parse(endsAt));
|
|
335
|
-
}
|
|
336
398
|
function selectionBucket({ tags, locations }, params, topics) {
|
|
337
399
|
if (locations?.length) {
|
|
338
400
|
if (!matchesLocation(locations, params))
|
|
@@ -348,11 +410,14 @@ function selectionBucket({ tags, locations }, params, topics) {
|
|
|
348
410
|
}
|
|
349
411
|
function parseTopics(header) {
|
|
350
412
|
const topics = new Set();
|
|
351
|
-
const value
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
413
|
+
for (const value of Array.isArray(header) ? header : [header ?? '']) {
|
|
414
|
+
// @NOTE Clients may append `;<updatedAt>` metadata to the tag list. Ignore it
|
|
415
|
+
// for matching; the raw header is still forwarded to Iris.
|
|
416
|
+
for (const token of value.split(';', 1)[0].split(',')) {
|
|
417
|
+
const topic = token.trim();
|
|
418
|
+
if (topic)
|
|
419
|
+
topics.add(topic);
|
|
420
|
+
}
|
|
356
421
|
}
|
|
357
422
|
return topics;
|
|
358
423
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getAtmosphereExploreTab.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereExploreTab.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingHttpHeaders } from 'node:http'\nimport { mapDefined, noUndefinedVals } from '@atproto/common'\nimport { type AtUriString, type Client, l } from '@atproto/lex'\nimport { AtUri } from '@atproto/syntax'\nimport {\n ForbiddenError,\n MethodNotImplementedError,\n type Server,\n UpstreamFailureError,\n UpstreamTimeoutError,\n} from '@atproto/xrpc-server'\nimport { safeFetchWrap } from '@atproto-labs/fetch-node'\nimport type { AppContext } from '../../../../context.js'\nimport type { HydrateCtx, Hydrator } from '../../../../hydration/hydrator.js'\nimport { app, site } from '../../../../lexicons/index.js'\nimport { httpLogger } from '../../../../logger.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type SkeletonFnInput,\n createPipeline,\n noRules,\n} from '../../../../pipeline.js'\nimport { getAtprotoPassthroughHeaders } from '../../../../util/headers.js'\nimport type { Views } from '../../../../views/index.js'\n\nconst MAX_CONFIG_BYTES = 512 * 1024\nconst MAX_LIST_ITEMS = 10\n\nconst groupSchema = l.object({\n canonicalUri: l.optional(l.unknown()),\n startsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n endsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n tags: l.optional(l.array(l.string({ maxLength: 128 }), { maxLength: 100 })),\n locations: l.optional(\n l.array(l.string({ maxLength: 16 }), { maxLength: 100 }),\n ),\n})\nconst bannerSchema = l.object({\n status: l.string({ maxLength: 128 }),\n startsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n endsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n locations: l.optional(\n l.array(l.string({ maxLength: 16 }), { maxLength: 100 }),\n ),\n})\n// Ingestion is best-effort per item: each section is an unvalidated value, and\n// every entry is validated on its own so one bad entry never fails the rest.\nconst configSchema = l.object({\n announcementBanners: l.optional(l.unknown()),\n articles: l.optional(l.unknown()),\n publications: l.optional(l.unknown()),\n photos: l.optional(l.unknown()),\n livestreams: l.optional(l.unknown()),\n apps: l.optional(l.unknown()),\n})\nconst MAX_INPUT_ENTRIES = 100\n\nexport default function (server: Server, ctx: AppContext) {\n const getExplore = createPipeline(skeleton, hydration, noRules, presentation)\n const fetchExploreTabSkeleton = createFetchExploreTabSkeleton()\n server.add(app.bsky.unspecced.getAtmosphereExploreTab, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ auth, params, req, signal }) => {\n const viewer = auth.credentials.iss\n const features = ctx.featureGatesClient.scope(\n ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }),\n )\n if (!features.checkGate(features.Gate.AtmosphereScreenEndpointsEnable)) {\n throw new ForbiddenError('Atmosphere Explore is not enabled')\n }\n const service = ctx.cfg.atmosphereExploreTabService\n if (!service)\n throw new MethodNotImplementedError(\n 'Atmosphere Explore service not configured',\n )\n let workerUrl: URL\n try {\n // Location filtering happens locally, so fetch the full config as-is.\n workerUrl = new URL(service)\n } catch {\n throw new UpstreamFailureError(\n 'Unable to fetch Atmosphere Explore configuration',\n )\n }\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer,\n labelers: ctx.reqLabelers(req),\n features,\n xrpcEndpoint: app.bsky.unspecced.getAtmosphereExploreTab.$lxm,\n })\n const useIris = hydrateCtx.features.checkGate(\n hydrateCtx.features.Gate.AtmosphereExploreArticlesIrisEnable,\n )\n // Use 501 instead of 500 as these are not considered retry-able by clients\n if (useIris && !ctx.irisClient)\n throw new MethodNotImplementedError('Iris agent not available')\n const result = await getExplore(\n {\n ...params,\n hydrateCtx,\n workerUrl,\n requestHeaders: req.headers,\n headers: noUndefinedVals({\n 'accept-language': params.langs?.length\n ? params.langs.join(',')\n : req.headers['accept-language'],\n ...getAtprotoPassthroughHeaders(req),\n }),\n signal,\n },\n {\n hydrator: ctx.hydrator,\n views: ctx.views,\n irisClient: useIris ? ctx.irisClient : undefined,\n fetchExploreTabSkeleton,\n },\n )\n return { encoding: 'application/json', body: result }\n },\n })\n}\n\nasync function skeleton({\n ctx,\n params,\n}: SkeletonFnInput<Context, Params>): Promise<SkeletonState> {\n const [config, recommendations] = await Promise.all([\n ctx.fetchExploreTabSkeleton(params.workerUrl, params.signal),\n ctx.irisClient\n ? ctx.irisClient\n .call(\n app.bsky.unspecced.getAtmosphereExploreArticleRecommendations,\n {\n limit: MAX_LIST_ITEMS,\n viewer: params.hydrateCtx.viewer ?? undefined,\n },\n {\n headers: params.headers,\n signal: params.signal,\n validateResponse: true,\n },\n )\n // @NOTE Request and response-validation failures use curated articles.\n .catch((err) => {\n if (params.signal.aborted) throw err\n httpLogger.warn(\n { err },\n 'Unable to fetch Atmosphere Explore article recommendations',\n )\n return undefined\n })\n : undefined,\n ])\n const now = Date.now()\n const announcementBanner = selectBanner(\n config.announcementBanners,\n params,\n now,\n )\n const select = createSelector(params, params.requestHeaders, now)\n return {\n now,\n announcementBanner,\n articles: orderGroups(\n recommendations && recommendations.articles.length > 0\n ? mapDefined(\n recommendations.articles.slice(0, MAX_LIST_ITEMS),\n ({ uri }) => {\n const root = recordUri(uri)\n return root &&\n new AtUri(root).collection === site.standard.document.$type\n ? { featured: false, uri: root }\n : undefined\n },\n )\n : completeGroups(select(config.articles)),\n ),\n articleRecIdStr: recommendations?.recIdStr,\n publications: orderGroups(completeGroups(select(config.publications))),\n photos: orderGroups(completeGroups(select(config.photos))),\n livestreams: orderGroups(completeGroups(select(config.livestreams))),\n // Apps have no hydration root or featured flag: keep worker order.\n apps: config.apps.slice(0, MAX_LIST_ITEMS),\n }\n}\n\nasync function hydration({\n ctx,\n params,\n skeleton,\n}: HydrationFnInput<Context, Params, SkeletonState>) {\n const uris = [\n ...new Set(\n [\n ...skeleton.articles,\n ...skeleton.publications,\n ...skeleton.photos,\n ...skeleton.livestreams,\n ].map(({ uri }) => uri),\n ),\n ]\n return ctx.hydrator.hydrateEmbedExternalViewFromUris(uris, params.hydrateCtx)\n}\n\nfunction presentation({\n ctx,\n skeleton,\n hydration: state,\n}: PresentationFnInput<\n Context,\n Params,\n SkeletonState\n>): app.bsky.unspecced.getAtmosphereExploreTab.$OutputBody {\n const { now, articles, publications, photos, livestreams } = skeleton\n return {\n announcementBanner: skeleton.announcementBanner,\n articles: {\n items: mapDefined(articles, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view && app.bsky.embed.external.viewArticle.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n recIdStr: skeleton.articleRecIdStr,\n },\n publications: {\n items: mapDefined(publications, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view &&\n app.bsky.embed.external.viewArticlePublication.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n },\n photos: {\n items: mapDefined(photos, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view &&\n app.bsky.embed.external.viewGallery.$isTypeOf(view) &&\n view.items.length\n ? { featured, view }\n : undefined\n }),\n },\n livestreams: {\n items: mapDefined(livestreams, ({ featured, uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view && app.bsky.embed.external.viewLivestream.$isTypeOf(view)\n ? { featured, view }\n : undefined\n }),\n },\n apps: skeleton.apps,\n }\n}\n\nfunction createFetchExploreTabSkeleton() {\n // @NOTE The wrapper caps decoded response bytes, including compressed bodies.\n const fetchConfig = safeFetchWrap({\n timeout: 5000,\n responseMaxSize: MAX_CONFIG_BYTES,\n })\n\n return async function fetchExploreTabSkeleton(\n url: URL,\n signal: AbortSignal,\n ): Promise<ExploreConfig> {\n try {\n const response = await fetchConfig(url, {\n headers: { accept: 'application/json' },\n redirect: 'error',\n signal,\n })\n if (!response.ok) {\n await response.body?.cancel()\n throw new UpstreamFailureError(\n `Atmosphere Explore service returned HTTP ${response.status}`,\n )\n }\n return normalizeConfig(await response.json())\n } catch (err) {\n if (err instanceof UpstreamFailureError) throw err\n if (\n err instanceof Error &&\n (err.name === 'TimeoutError' || err.name === 'AbortError')\n ) {\n throw new UpstreamTimeoutError('Atmosphere Explore request timed out')\n }\n throw new UpstreamFailureError(\n 'Unable to fetch Atmosphere Explore configuration',\n )\n }\n }\n}\n\ntype Group = l.Infer<typeof groupSchema>\ntype AppCard = app.bsky.unspecced.getAtmosphereExploreTab.AppCard\ntype ValidBanner = {\n banner: l.Infer<typeof bannerSchema>\n presentation: AnnouncementBanner\n}\ntype ExploreConfig = {\n announcementBanners: ValidBanner[]\n articles: Group[]\n publications: Group[]\n photos: Group[]\n livestreams: Group[]\n apps: AppCard[]\n}\n\n/**\n * Keeps only the well-formed items of a successfully decoded worker body. Only\n * the first `MAX_INPUT_ENTRIES` entries of each section are examined, and\n * malformed entries are skipped whole, never coerced or repaired.\n */\nfunction normalizeConfig(body: unknown): ExploreConfig {\n const raw = configSchema.safeParse(body)\n const config = raw.success ? raw.value : {}\n const entries = (value: unknown): unknown[] =>\n Array.isArray(value) ? value.slice(0, MAX_INPUT_ENTRIES) : []\n const valid = <T>(schema: l.Schema<T>, value: unknown): T[] =>\n entries(value).flatMap((entry) => {\n const result = schema.safeParse(entry)\n return result.success ? [result.value] : []\n })\n return {\n announcementBanners: entries(config.announcementBanners).flatMap(\n (entry) => {\n const banner = bannerSchema.safeParse(entry)\n const presentation =\n app.bsky.unspecced.getAtmosphereExploreTab.announcementBanner.$safeValidate(\n entry,\n )\n return banner.success && presentation.success\n ? [{ banner: banner.value, presentation: presentation.value }]\n : []\n },\n ),\n articles: valid(groupSchema, config.articles),\n publications: valid(groupSchema, config.publications),\n photos: valid(groupSchema, config.photos),\n livestreams: valid(groupSchema, config.livestreams),\n apps: valid(\n app.bsky.unspecced.getAtmosphereExploreTab.appCard,\n config.apps,\n ),\n }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n irisClient: Client | undefined\n fetchExploreTabSkeleton: ReturnType<typeof createFetchExploreTabSkeleton>\n}\n\ntype Params = app.bsky.unspecced.getAtmosphereExploreTab.$Params & {\n hydrateCtx: HydrateCtx\n workerUrl: URL\n /** Recommendation headers for Iris only. */\n headers: Record<string, string>\n requestHeaders: IncomingHttpHeaders\n signal: AbortSignal\n}\n\ntype RecordGroup = { featured: boolean; uri: AtUriString }\n\ntype SkeletonState = {\n now: number\n announcementBanner?: app.bsky.unspecced.getAtmosphereExploreTab.AnnouncementBanner\n articles: RecordGroup[]\n articleRecIdStr?: string\n publications: RecordGroup[]\n photos: RecordGroup[]\n livestreams: RecordGroup[]\n apps: app.bsky.unspecced.getAtmosphereExploreTab.AppCard[]\n}\n\n/**\n * Randomizes a selected list (Fisher-Yates), then places featured items ahead\n * of ordinary ones. Runs before hydration, so presentation keeps this order.\n */\nfunction orderGroups(groups: RecordGroup[]): RecordGroup[] {\n const shuffled = [...groups]\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n }\n return [\n ...shuffled.filter(({ featured }) => featured),\n ...shuffled.filter(({ featured }) => !featured),\n ].slice(0, MAX_LIST_ITEMS)\n}\n\nfunction completeGroups(groups: SelectedGroup[] = []): RecordGroup[] {\n return mapDefined(groups, ({ featured, canonicalUri }) => {\n const uri = recordUri(canonicalUri)\n if (!uri) return\n return { featured, uri }\n })\n}\n\ntype AnnouncementBanner =\n app.bsky.unspecced.getAtmosphereExploreTab.AnnouncementBanner\n\n/**\n * Returns the single (already validated) banner to show. Eligible (enabled, in window) banners targeted at a matching location\n * win, first in worker order; otherwise the unrestricted eligible banner with\n * the furthest-out `endsAt` (absent means no expiration; ties keep worker order).\n */\nfunction selectBanner(\n banners: ValidBanner[],\n params: { countryCode?: string; regionCode?: string },\n now: number,\n): AnnouncementBanner | undefined {\n let matched: AnnouncementBanner | undefined\n let fallback: AnnouncementBanner | undefined\n let fallbackEnd = -Infinity\n for (const { banner, presentation } of banners) {\n const { status, startsAt, endsAt, locations } = banner\n if (status !== 'enabled') continue\n if (startsAt != null && Date.parse(startsAt) > now) continue\n const end = endsAt == null ? Infinity : Date.parse(endsAt)\n if (end <= now) continue\n if (locations?.length) {\n if (!matched && matchesLocation(locations, params)) {\n matched = presentation\n }\n } else if (end > fallbackEnd) {\n fallback = presentation\n fallbackEnd = end\n }\n }\n const selected = matched ?? fallback\n if (!selected) return\n const { id, title, description, url, image, overlayColor, textColor } =\n selected\n return { id, title, description, url, image, overlayColor, textColor }\n}\n\ntype Selectable = {\n tags?: string[]\n locations?: string[]\n startsAt?: string | null\n endsAt?: string | null\n}\ntype SelectedGroup = l.Infer<typeof groupSchema> & { featured: boolean }\n\n/**\n * Builds a per-list selector that drops location-ineligible entries, orders\n * the rest by priority bucket (location, requested topic, `general`, untagged;\n * stable within a bucket), moves entries with an active schedule window ahead\n * of the rest (stable, so bucket order holds within each side), and keeps the\n * first `MAX_LIST_ITEMS`. A worker-supplied `featured` flag is ignored.\n */\nfunction createSelector(\n params: { countryCode?: string; regionCode?: string },\n headers: IncomingHttpHeaders,\n now: number,\n) {\n const topics = parseTopics(headers['x-atproto-bsky-topics'])\n return (groups: l.Infer<typeof groupSchema>[] = []): SelectedGroup[] => {\n const buckets: l.Infer<typeof groupSchema>[][] = [[], [], [], []]\n for (const group of groups) {\n const bucket = selectionBucket(group, params, topics)\n if (bucket !== undefined) buckets[bucket].push(group)\n }\n const featured: SelectedGroup[] = []\n const ordinary: SelectedGroup[] = []\n for (const group of buckets.flat()) {\n const active = isFeatured(group, now)\n ;(active ? featured : ordinary).push({ ...group, featured: active })\n }\n return [...featured, ...ordinary].slice(0, MAX_LIST_ITEMS)\n }\n}\n\n/** Unlike banners, content needs both bounds to define a featured window. */\nfunction isFeatured({ startsAt, endsAt }: Selectable, now: number): boolean {\n return (\n startsAt != null &&\n endsAt != null &&\n Date.parse(startsAt) <= now &&\n now < Date.parse(endsAt)\n )\n}\n\nfunction selectionBucket(\n { tags, locations }: Selectable,\n params: { countryCode?: string; regionCode?: string },\n topics: Set<string>,\n): 0 | 1 | 2 | 3 | undefined {\n if (locations?.length) {\n if (!matchesLocation(locations, params)) return\n return 0\n }\n if (tags?.some((tag) => topics.has(tag))) return 1\n if (tags?.includes('general')) return 2\n if (!tags?.length) return 3\n}\n\nfunction parseTopics(header: string | string[] | undefined): Set<string> {\n const topics = new Set<string>()\n const value = Array.isArray(header) ? header.join(',') : header\n for (const token of value?.split(',') ?? []) {\n const topic = token.trim()\n if (topic) topics.add(topic)\n }\n return topics\n}\n\n/**\n * True only when a nonempty `locations` list matches the request. Entries are\n * a country (`JP`) or country and region (`US-WI`); a region alone never\n * matches. A full `regionCode` (`US-WI`) is honored only when its country\n * agrees with `countryCode`.\n */\nfunction matchesLocation(\n locations: string[] | undefined,\n { countryCode, regionCode }: { countryCode?: string; regionCode?: string },\n): boolean {\n const country = countryCode?.toUpperCase()\n if (!locations?.length || !country) return false\n let region = regionCode?.toUpperCase() || undefined\n if (region?.includes('-')) {\n const [regionCountry, subdivision] = region.split('-')\n region = regionCountry === country ? subdivision : undefined\n }\n return locations.some((location) => {\n const [locationCountry, locationRegion, ...rest] = location\n .toUpperCase()\n .split('-')\n if (rest.length || locationCountry !== country) return false\n return (\n locationRegion === undefined || (!!region && locationRegion === region)\n )\n })\n}\n\nfunction recordUri(value: unknown): AtUriString | undefined {\n if (!l.isAtUriString(value)) return\n const uri = new AtUri(value)\n return uri.collection && uri.rkey && l.isDidString(uri.hostname)\n ? value\n : undefined\n}\n"]}
|
|
1
|
+
{"version":3,"file":"getAtmosphereExploreTab.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereExploreTab.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingHttpHeaders } from 'node:http'\nimport { mapDefined, noUndefinedVals } from '@atproto/common'\nimport { type AtUriString, type Client, type DidString, l } from '@atproto/lex'\nimport { AtUri } from '@atproto/syntax'\nimport {\n ForbiddenError,\n MethodNotImplementedError,\n type Server,\n UpstreamFailureError,\n UpstreamTimeoutError,\n} from '@atproto/xrpc-server'\nimport { safeFetchWrap } from '@atproto-labs/fetch-node'\nimport type { AppContext } from '../../../../context.js'\nimport {\n type HydrateCtx,\n type Hydrator,\n mergeStates,\n} from '../../../../hydration/hydrator.js'\nimport { app, site } from '../../../../lexicons/index.js'\nimport { httpLogger } from '../../../../logger.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type SkeletonFnInput,\n createPipeline,\n noRules,\n} from '../../../../pipeline.js'\nimport { getAtprotoPassthroughHeaders } from '../../../../util/headers.js'\nimport type { Views } from '../../../../views/index.js'\n\nconst MAX_CONFIG_BYTES = 512 * 1024\nconst MAX_LIST_ITEMS = 10\n\nconst groupFields = {\n canonicalUri: l.optional(l.unknown()),\n tags: l.optional(l.array(l.string({ maxLength: 128 }), { maxLength: 100 })),\n locations: l.optional(\n l.array(l.string({ maxLength: 16 }), { maxLength: 100 }),\n ),\n}\nconst groupSchema = l.object(groupFields)\n// Only livestreams are scheduled; other groups ignore any schedule fields.\nconst optionalDatetime = l.optional(\n l.nullable(l.string({ format: 'datetime' })),\n)\nconst livestreamSchema = l.object({\n ...groupFields,\n previewAt: optionalDatetime,\n startsAt: optionalDatetime,\n endsAt: optionalDatetime,\n})\n// `did` hydrates the app's profile; `null` and absent both mean no profile.\nconst appDidSchema = l.optional(l.nullable(l.string({ format: 'did' })))\nconst bannerSchema = l.object({\n status: l.string({ maxLength: 128 }),\n startsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n endsAt: l.optional(l.nullable(l.string({ format: 'datetime' }))),\n locations: l.optional(\n l.array(l.string({ maxLength: 16 }), { maxLength: 100 }),\n ),\n})\n// Ingestion is best-effort per item: each section is an unvalidated value, and\n// every entry is validated on its own so one bad entry never fails the rest.\nconst configSchema = l.object({\n announcementBanners: l.optional(l.unknown()),\n articles: l.optional(l.unknown()),\n publications: l.optional(l.unknown()),\n photos: l.optional(l.unknown()),\n livestreams: l.optional(l.unknown()),\n apps: l.optional(l.unknown()),\n})\nconst MAX_INPUT_ENTRIES = 100\n\nexport default function (server: Server, ctx: AppContext) {\n const getExplore = createPipeline(skeleton, hydration, noRules, presentation)\n const fetchExploreTabSkeleton = createFetchExploreTabSkeleton()\n server.add(app.bsky.unspecced.getAtmosphereExploreTab, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ auth, params, req, signal }) => {\n const viewer = auth.credentials.iss\n const features = ctx.featureGatesClient.scope(\n ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }),\n )\n if (!features.checkGate(features.Gate.AtmosphereScreenEndpointsEnable)) {\n throw new ForbiddenError('Atmosphere Explore is not enabled')\n }\n const service = ctx.cfg.atmosphereExploreTabService\n if (!service)\n throw new MethodNotImplementedError(\n 'Atmosphere Explore service not configured',\n )\n let workerUrl: URL\n try {\n // Location filtering happens locally, so fetch the full config as-is.\n workerUrl = new URL(service)\n } catch {\n throw new UpstreamFailureError(\n 'Unable to fetch Atmosphere Explore configuration',\n )\n }\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer,\n labelers: ctx.reqLabelers(req),\n features,\n xrpcEndpoint: app.bsky.unspecced.getAtmosphereExploreTab.$lxm,\n })\n const useIris = hydrateCtx.features.checkGate(\n hydrateCtx.features.Gate.AtmosphereExploreArticlesIrisEnable,\n )\n // Use 501 instead of 500 as these are not considered retry-able by clients\n if (useIris && !ctx.irisClient)\n throw new MethodNotImplementedError('Iris agent not available')\n const result = await getExplore(\n {\n ...params,\n hydrateCtx,\n workerUrl,\n requestHeaders: req.headers,\n headers: noUndefinedVals({\n 'accept-language': params.langs?.length\n ? params.langs.join(',')\n : req.headers['accept-language'],\n ...getAtprotoPassthroughHeaders(req),\n }),\n signal,\n },\n {\n hydrator: ctx.hydrator,\n views: ctx.views,\n irisClient: useIris ? ctx.irisClient : undefined,\n fetchExploreTabSkeleton,\n },\n )\n return { encoding: 'application/json', body: result }\n },\n })\n}\n\nasync function skeleton({\n ctx,\n params,\n}: SkeletonFnInput<Context, Params>): Promise<SkeletonState> {\n const [config, recommendations] = await Promise.all([\n ctx.fetchExploreTabSkeleton(params.workerUrl, params.signal),\n ctx.irisClient\n ? ctx.irisClient\n .call(\n app.bsky.unspecced.getAtmosphereExploreArticleRecommendations,\n {\n limit: MAX_LIST_ITEMS,\n viewer: params.hydrateCtx.viewer ?? undefined,\n },\n {\n headers: params.headers,\n signal: params.signal,\n validateResponse: true,\n },\n )\n // @NOTE Request and response-validation failures use curated articles.\n .catch((err) => {\n if (params.signal.aborted) throw err\n httpLogger.warn(\n { err },\n 'Unable to fetch Atmosphere Explore article recommendations',\n )\n return undefined\n })\n : undefined,\n ])\n const now = Date.now()\n const announcementBanner = selectBanner(\n config.announcementBanners,\n params,\n now,\n )\n const select = createSelector(params, params.requestHeaders)\n return {\n now,\n announcementBanner,\n articles: orderGroups(\n recommendations && recommendations.articles.length > 0\n ? mapDefined(\n recommendations.articles.slice(0, MAX_LIST_ITEMS),\n ({ uri }) => {\n const root = recordUri(uri)\n return root &&\n new AtUri(root).collection === site.standard.document.$type\n ? { uri: root }\n : undefined\n },\n )\n : completeGroups(select(config.articles)),\n ),\n articleRecIdStr: recommendations?.recIdStr,\n publications: orderGroups(completeGroups(select(config.publications))),\n photos: orderGroups(completeGroups(select(config.photos))),\n // Expired and not-yet-previewed livestreams are dropped before selection,\n // so they never consume the cap.\n livestreams: orderGroups(\n mapDefined(\n select(config.livestreams.filter((entry) => isPreviewable(entry, now))),\n ({ canonicalUri, startsAt }) => {\n const uri = recordUri(canonicalUri)\n return uri ? { uri, startsAt } : undefined\n },\n ),\n ),\n // Apps are not record-backed: keep worker order.\n apps: config.apps.slice(0, MAX_LIST_ITEMS),\n }\n}\n\nasync function hydration({\n ctx,\n params,\n skeleton,\n}: HydrationFnInput<Context, Params, SkeletonState>) {\n const uris = [\n ...new Set(\n [\n ...skeleton.articles,\n ...skeleton.publications,\n ...skeleton.photos,\n ...skeleton.livestreams,\n ].map(({ uri }) => uri),\n ),\n ]\n const dids = [...new Set(mapDefined(skeleton.apps, ({ did }) => did))]\n const [externalState, appProfileState] = await Promise.all([\n ctx.hydrator.hydrateEmbedExternalViewFromUris(uris, params.hydrateCtx),\n dids.length\n ? ctx.hydrator.hydrateProfilesDetailed(dids, params.hydrateCtx)\n : {},\n ])\n // Detailed profile state goes second so it wins any overlapping actor state.\n return mergeStates(externalState, appProfileState)\n}\n\nfunction presentation({\n ctx,\n skeleton,\n hydration: state,\n}: PresentationFnInput<\n Context,\n Params,\n SkeletonState\n>): app.bsky.unspecced.getAtmosphereExploreTab.$OutputBody {\n const { now, articles, publications, photos, livestreams, apps } = skeleton\n return {\n announcementBanner: skeleton.announcementBanner,\n articles: {\n items: mapDefined(articles, ({ uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view && app.bsky.embed.external.viewArticle.$isTypeOf(view)\n ? { featured: false, view }\n : undefined\n }),\n recIdStr: skeleton.articleRecIdStr,\n },\n publications: {\n items: mapDefined(publications, ({ uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view &&\n app.bsky.embed.external.viewArticlePublication.$isTypeOf(view)\n ? { featured: false, view }\n : undefined\n }),\n },\n photos: {\n items: mapDefined(photos, ({ uri }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n return view &&\n app.bsky.embed.external.viewGallery.$isTypeOf(view) &&\n view.items.length\n ? { featured: false, view }\n : undefined\n }),\n },\n livestreams: {\n items: mapDefined(livestreams, ({ uri, startsAt }) => {\n const view = ctx.views.externalRecordView(uri, state, now)\n if (!view || !app.bsky.embed.external.viewLivestream.$isTypeOf(view))\n return\n // Previewed streams are never live; copy rather than mutate the view.\n return startsAt != null && Date.parse(startsAt) > now\n ? { featured: false, view: { ...view, active: false } }\n : { featured: false, view }\n }),\n },\n apps: apps.map(({ card, did }) => {\n const profile = did ? ctx.views.profileDetailed(did, state) : undefined\n return {\n id: card.id,\n title: card.title,\n description: card.description,\n logo: profile?.avatar ?? card.logo,\n backgroundImage: profile?.banner ?? card.backgroundImage,\n overlayColor: card.overlayColor,\n textColor: card.textColor,\n url: card.url,\n category: card.category,\n profile,\n }\n }),\n }\n}\n\nfunction createFetchExploreTabSkeleton() {\n // @NOTE The wrapper caps decoded response bytes, including compressed bodies.\n const fetchConfig = safeFetchWrap({\n timeout: 5000,\n responseMaxSize: MAX_CONFIG_BYTES,\n })\n\n return async function fetchExploreTabSkeleton(\n url: URL,\n signal: AbortSignal,\n ): Promise<ExploreConfig> {\n try {\n const response = await fetchConfig(url, {\n headers: { accept: 'application/json' },\n redirect: 'error',\n signal,\n })\n if (!response.ok) {\n await response.body?.cancel()\n throw new UpstreamFailureError(\n `Atmosphere Explore service returned HTTP ${response.status}`,\n )\n }\n return normalizeConfig(await response.json())\n } catch (err) {\n if (err instanceof UpstreamFailureError) throw err\n if (\n err instanceof Error &&\n (err.name === 'TimeoutError' || err.name === 'AbortError')\n ) {\n throw new UpstreamTimeoutError('Atmosphere Explore request timed out')\n }\n throw new UpstreamFailureError(\n 'Unable to fetch Atmosphere Explore configuration',\n )\n }\n }\n}\n\ntype Group = l.Infer<typeof groupSchema>\ntype LivestreamGroup = l.Infer<typeof livestreamSchema>\ntype AppCard = app.bsky.unspecced.getAtmosphereExploreTab.AppCard\n// A validated public card plus the internal DID used to hydrate its profile.\ntype AppEntry = { card: AppCard; did?: DidString }\ntype ValidBanner = {\n banner: l.Infer<typeof bannerSchema>\n presentation: AnnouncementBanner\n}\ntype ExploreConfig = {\n announcementBanners: ValidBanner[]\n articles: Group[]\n publications: Group[]\n photos: Group[]\n livestreams: LivestreamGroup[]\n apps: AppEntry[]\n}\n\n/**\n * Keeps only the well-formed items of a successfully decoded worker body. Only\n * the first `MAX_INPUT_ENTRIES` entries of each section are examined, and\n * malformed entries are skipped whole, never coerced or repaired.\n */\nfunction normalizeConfig(body: unknown): ExploreConfig {\n const raw = configSchema.safeParse(body)\n const config = raw.success ? raw.value : {}\n const entries = (value: unknown): unknown[] =>\n Array.isArray(value) ? value.slice(0, MAX_INPUT_ENTRIES) : []\n const valid = <T>(schema: l.Schema<T>, value: unknown): T[] =>\n entries(value).flatMap((entry) => {\n const result = schema.safeParse(entry)\n return result.success ? [result.value] : []\n })\n return {\n announcementBanners: entries(config.announcementBanners).flatMap(\n (entry) => {\n const banner = bannerSchema.safeParse(entry)\n const presentation =\n app.bsky.unspecced.getAtmosphereExploreTab.announcementBanner.$safeValidate(\n entry,\n )\n return banner.success && presentation.success\n ? [{ banner: banner.value, presentation: presentation.value }]\n : []\n },\n ),\n articles: valid(groupSchema, config.articles),\n publications: valid(groupSchema, config.publications),\n photos: valid(groupSchema, config.photos),\n livestreams: valid(livestreamSchema, config.livestreams),\n apps: entries(config.apps).flatMap((entry) => {\n const parsed = parseApp(entry)\n return parsed ? [parsed] : []\n }),\n }\n}\n\nconst APP_CARD_FIELDS = [\n 'id',\n 'title',\n 'description',\n 'logo',\n 'backgroundImage',\n 'overlayColor',\n 'textColor',\n 'url',\n 'category',\n] as const\n\n/**\n * Validates an app entry. Only the public card fields are copied (so a\n * supplied `profile` or `profileUrl` is never read) and only `did` is kept\n * beyond them. A present but malformed `did` drops the app.\n */\nfunction parseApp(entry: unknown): AppEntry | undefined {\n if (typeof entry !== 'object' || entry === null || Array.isArray(entry))\n return\n const raw = entry as Record<string, unknown>\n const did = appDidSchema.safeParse(raw.did)\n if (!did.success) return\n const card = app.bsky.unspecced.getAtmosphereExploreTab.appCard.safeParse(\n noUndefinedVals(\n Object.fromEntries(APP_CARD_FIELDS.map((key) => [key, raw[key]])),\n ),\n )\n if (!card.success) return\n return { card: card.value, did: did.value ?? undefined }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n irisClient: Client | undefined\n fetchExploreTabSkeleton: ReturnType<typeof createFetchExploreTabSkeleton>\n}\n\ntype Params = app.bsky.unspecced.getAtmosphereExploreTab.$Params & {\n hydrateCtx: HydrateCtx\n workerUrl: URL\n /** Recommendation headers for Iris only. */\n headers: Record<string, string>\n requestHeaders: IncomingHttpHeaders\n signal: AbortSignal\n}\n\ntype RecordGroup = { uri: AtUriString }\ntype LivestreamRecordGroup = RecordGroup & { startsAt?: string | null }\n\ntype SkeletonState = {\n now: number\n announcementBanner?: app.bsky.unspecced.getAtmosphereExploreTab.AnnouncementBanner\n articles: RecordGroup[]\n articleRecIdStr?: string\n publications: RecordGroup[]\n photos: RecordGroup[]\n livestreams: LivestreamRecordGroup[]\n apps: AppEntry[]\n}\n\n/**\n * Randomizes a selected list (Fisher-Yates) and caps it. Runs before hydration,\n * so presentation keeps this order.\n */\nfunction orderGroups<T extends RecordGroup>(groups: T[]): T[] {\n const shuffled = [...groups]\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n }\n return shuffled.slice(0, MAX_LIST_ITEMS)\n}\n\nfunction completeGroups(groups: Group[] = []): RecordGroup[] {\n return mapDefined(groups, ({ canonicalUri }) => {\n const uri = recordUri(canonicalUri)\n return uri ? { uri } : undefined\n })\n}\n\n/**\n * Editorial window for a livestream: shown from `previewAt` (falling back to\n * `startsAt`) inclusive until `endsAt` exclusive. Absent or null bounds are\n * open. This is independent of the view's heartbeat-based `active` flag.\n */\nfunction isPreviewable(\n { previewAt, startsAt, endsAt }: LivestreamGroup,\n now: number,\n): boolean {\n const start = previewAt ?? startsAt\n if (start != null && Date.parse(start) > now) return false\n return endsAt == null || now < Date.parse(endsAt)\n}\n\ntype AnnouncementBanner =\n app.bsky.unspecced.getAtmosphereExploreTab.AnnouncementBanner\n\n/**\n * Returns the single (already validated) banner to show. Eligible (enabled, in window) banners targeted at a matching location\n * win, first in worker order; otherwise the unrestricted eligible banner with\n * the furthest-out `endsAt` (absent means no expiration; ties keep worker order).\n */\nfunction selectBanner(\n banners: ValidBanner[],\n params: { countryCode?: string; regionCode?: string },\n now: number,\n): AnnouncementBanner | undefined {\n let matched: AnnouncementBanner | undefined\n let fallback: AnnouncementBanner | undefined\n let fallbackEnd = -Infinity\n for (const { banner, presentation } of banners) {\n const { status, startsAt, endsAt, locations } = banner\n if (status !== 'enabled') continue\n if (startsAt != null && Date.parse(startsAt) > now) continue\n const end = endsAt == null ? Infinity : Date.parse(endsAt)\n if (end <= now) continue\n if (locations?.length) {\n if (!matched && matchesLocation(locations, params)) {\n matched = presentation\n }\n } else if (end > fallbackEnd) {\n fallback = presentation\n fallbackEnd = end\n }\n }\n const selected = matched ?? fallback\n if (!selected) return\n const { id, title, description, url, image, overlayColor, textColor } =\n selected\n return { id, title, description, url, image, overlayColor, textColor }\n}\n\ntype Selectable = {\n tags?: string[]\n locations?: string[]\n}\n\n/**\n * Builds a per-list selector that drops location-ineligible entries, orders\n * the rest by priority bucket (location, requested topic, `general`, untagged;\n * stable within a bucket), and keeps the first `MAX_LIST_ITEMS`.\n */\nfunction createSelector(\n params: { countryCode?: string; regionCode?: string },\n headers: IncomingHttpHeaders,\n) {\n const topics = parseTopics(headers['x-atproto-bsky-topics'])\n return <T extends Group>(groups: T[] = []): T[] => {\n const buckets: T[][] = [[], [], [], []]\n for (const group of groups) {\n const bucket = selectionBucket(group, params, topics)\n if (bucket !== undefined) buckets[bucket].push(group)\n }\n return buckets.flat().slice(0, MAX_LIST_ITEMS)\n }\n}\n\nfunction selectionBucket(\n { tags, locations }: Selectable,\n params: { countryCode?: string; regionCode?: string },\n topics: Set<string>,\n): 0 | 1 | 2 | 3 | undefined {\n if (locations?.length) {\n if (!matchesLocation(locations, params)) return\n return 0\n }\n if (tags?.some((tag) => topics.has(tag))) return 1\n if (tags?.includes('general')) return 2\n if (!tags?.length) return 3\n}\n\nfunction parseTopics(header: string | string[] | undefined): Set<string> {\n const topics = new Set<string>()\n for (const value of Array.isArray(header) ? header : [header ?? '']) {\n // @NOTE Clients may append `;<updatedAt>` metadata to the tag list. Ignore it\n // for matching; the raw header is still forwarded to Iris.\n for (const token of value.split(';', 1)[0].split(',')) {\n const topic = token.trim()\n if (topic) topics.add(topic)\n }\n }\n return topics\n}\n\n/**\n * True only when a nonempty `locations` list matches the request. Entries are\n * a country (`JP`) or country and region (`US-WI`); a region alone never\n * matches. A full `regionCode` (`US-WI`) is honored only when its country\n * agrees with `countryCode`.\n */\nfunction matchesLocation(\n locations: string[] | undefined,\n { countryCode, regionCode }: { countryCode?: string; regionCode?: string },\n): boolean {\n const country = countryCode?.toUpperCase()\n if (!locations?.length || !country) return false\n let region = regionCode?.toUpperCase() || undefined\n if (region?.includes('-')) {\n const [regionCountry, subdivision] = region.split('-')\n region = regionCountry === country ? subdivision : undefined\n }\n return locations.some((location) => {\n const [locationCountry, locationRegion, ...rest] = location\n .toUpperCase()\n .split('-')\n if (rest.length || locationCountry !== country) return false\n return (\n locationRegion === undefined || (!!region && locationRegion === region)\n )\n })\n}\n\nfunction recordUri(value: unknown): AtUriString | undefined {\n if (!l.isAtUriString(value)) return\n const uri = new AtUri(value)\n return uri.collection && uri.rkey && l.isDidString(uri.hostname)\n ? value\n : undefined\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getAtmosphereTimeline.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereTimeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAKxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QA+BvD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ForbiddenError } from '@atproto/xrpc-server';
|
|
2
|
+
import { app } from '../../../../lexicons/index.js';
|
|
3
|
+
import { resHeaders } from '../../../util.js';
|
|
4
|
+
import { loadAtmosphereTimeline } from './atmosphere-pagination.js';
|
|
5
|
+
export default function (server, ctx) {
|
|
6
|
+
server.add(app.bsky.unspecced.getAtmosphereTimeline, {
|
|
7
|
+
auth: ctx.authVerifier.standard,
|
|
8
|
+
handler: async ({ auth, params, req }) => {
|
|
9
|
+
const now = Date.now();
|
|
10
|
+
const viewer = auth.credentials.iss;
|
|
11
|
+
const features = ctx.featureGatesClient.scope(ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }));
|
|
12
|
+
if (!features.checkGate(features.Gate.AtmosphereScreenEndpointsEnable)) {
|
|
13
|
+
throw new ForbiddenError('Atmosphere timeline is not enabled');
|
|
14
|
+
}
|
|
15
|
+
const hydrateCtx = await ctx.hydrator.createContext({
|
|
16
|
+
viewer,
|
|
17
|
+
features,
|
|
18
|
+
labelers: ctx.reqLabelers(req),
|
|
19
|
+
xrpcEndpoint: app.bsky.unspecced.getAtmosphereTimeline.$lxm,
|
|
20
|
+
});
|
|
21
|
+
const body = await loadAtmosphereTimeline(ctx, hydrateCtx, params.cursor, now);
|
|
22
|
+
return {
|
|
23
|
+
encoding: 'application/json',
|
|
24
|
+
body,
|
|
25
|
+
headers: resHeaders({ labelers: hydrateCtx.labelers }),
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=getAtmosphereTimeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getAtmosphereTimeline.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getAtmosphereTimeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAe,MAAM,sBAAsB,CAAA;AAElE,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAA;AAEnE,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,EAAE;QACnD,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,QAAQ;QAC/B,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE;YACvC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACtB,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAA;YACnC,MAAM,QAAQ,GAAG,GAAG,CAAC,kBAAkB,CAAC,KAAK,CAC3C,GAAG,CAAC,kBAAkB,CAAC,2BAA2B,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CACpE,CAAA;YACD,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,+BAA+B,CAAC,EAAE,CAAC;gBACvE,MAAM,IAAI,cAAc,CAAC,oCAAoC,CAAC,CAAA;YAChE,CAAC;YACD,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC;gBAClD,MAAM;gBACN,QAAQ;gBACR,QAAQ,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC;gBAC9B,YAAY,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAAC,IAAI;aAC5D,CAAC,CAAA;YACF,MAAM,IAAI,GAAG,MAAM,sBAAsB,CACvC,GAAG,EACH,UAAU,EACV,MAAM,CAAC,MAAM,EACb,GAAG,CACJ,CAAA;YACD,OAAO;gBACL,QAAQ,EAAE,kBAAkB;gBAC5B,IAAI;gBACJ,OAAO,EAAE,UAAU,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC;aACvD,CAAA;QACH,CAAC;KACF,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { ForbiddenError, type Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport { app } from '../../../../lexicons/index.js'\nimport { resHeaders } from '../../../util.js'\nimport { loadAtmosphereTimeline } from './atmosphere-pagination.js'\n\nexport default function (server: Server, ctx: AppContext) {\n server.add(app.bsky.unspecced.getAtmosphereTimeline, {\n auth: ctx.authVerifier.standard,\n handler: async ({ auth, params, req }) => {\n const now = Date.now()\n const viewer = auth.credentials.iss\n const features = ctx.featureGatesClient.scope(\n ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }),\n )\n if (!features.checkGate(features.Gate.AtmosphereScreenEndpointsEnable)) {\n throw new ForbiddenError('Atmosphere timeline is not enabled')\n }\n const hydrateCtx = await ctx.hydrator.createContext({\n viewer,\n features,\n labelers: ctx.reqLabelers(req),\n xrpcEndpoint: app.bsky.unspecced.getAtmosphereTimeline.$lxm,\n })\n const body = await loadAtmosphereTimeline(\n ctx,\n hydrateCtx,\n params.cursor,\n now,\n )\n return {\n encoding: 'application/json',\n body,\n headers: resHeaders({ labelers: hydrateCtx.labelers }),\n }\n },\n })\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getOnboardingSuggestedStarterPacks.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts"],"names":[],"mappings":"AAGA,OAAO,EAA6B,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAiBxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"getOnboardingSuggestedStarterPacks.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts"],"names":[],"mappings":"AAGA,OAAO,EAA6B,KAAK,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAiBxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAqCvD"}
|
|
@@ -13,9 +13,9 @@ export default function (server, ctx) {
|
|
|
13
13
|
const viewer = auth.credentials.iss;
|
|
14
14
|
const labelers = ctx.reqLabelers(req);
|
|
15
15
|
const hydrateCtx = await ctx.hydrator.createContext({
|
|
16
|
+
xrpcEndpoint: app.bsky.unspecced.getOnboardingSuggestedStarterPacks.$lxm,
|
|
16
17
|
labelers,
|
|
17
18
|
viewer,
|
|
18
|
-
features: ctx.featureGatesClient.scope(ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req })),
|
|
19
19
|
});
|
|
20
20
|
const headers = noUndefinedVals({
|
|
21
21
|
'accept-language': req.headers['accept-language'],
|
|
@@ -36,14 +36,11 @@ export default function (server, ctx) {
|
|
|
36
36
|
}
|
|
37
37
|
const skeleton = async (input) => {
|
|
38
38
|
const { params, ctx } = input;
|
|
39
|
-
|
|
40
|
-
const client = useIris ? ctx.irisClient : ctx.topicsClient;
|
|
41
|
-
if (!client) {
|
|
39
|
+
if (!ctx.irisClient) {
|
|
42
40
|
// Use 501 instead of 500 as these are not considered retry-able by clients
|
|
43
|
-
|
|
44
|
-
throw new MethodNotImplementedError(`${agent} agent not available`);
|
|
41
|
+
throw new MethodNotImplementedError('Iris agent not available');
|
|
45
42
|
}
|
|
46
|
-
const skeleton = await
|
|
43
|
+
const skeleton = await ctx.irisClient.call(app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton, { limit: params.limit, viewer: params.hydrateCtx.viewer ?? undefined }, { headers: params.headers, signal: params.signal });
|
|
47
44
|
// @TODO Make sure upstream always provides this
|
|
48
45
|
skeleton.starterPacks ??= [];
|
|
49
46
|
return skeleton;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getOnboardingSuggestedStarterPacks.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAE7D,OAAO,EAAE,KAAK,EAAoC,MAAM,iBAAiB,CAAA;AACzE,OAAO,EAAE,yBAAyB,EAAe,MAAM,sBAAsB,CAAA;AAE7E,OAAO,EAGL,eAAe,GAChB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,+BAA+B,CAAA;AACnD,OAAO,EAKL,cAAc,GACf,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAA;AAG1E,MAAM,CAAC,OAAO,WAAW,MAAc,EAAE,GAAe;IACtD,MAAM,kCAAkC,GAAG,cAAc,CACvD,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,YAAY,CACb,CAAA;IACD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,kCAAkC,EAAE;QAChE,IAAI,EAAE,GAAG,CAAC,YAAY,CAAC,gBAAgB;QACvC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE;YAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAA;YACnC,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;YACrC,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC;gBAClD,
|
|
1
|
+
{"version":3,"file":"getOnboardingSuggestedStarterPacks.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined, noUndefinedVals } from '@atproto/common'\nimport type { Client } from '@atproto/lex'\nimport { AtUri, type AtUriString, type DidString } from '@atproto/syntax'\nimport { MethodNotImplementedError, type Server } from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport {\n type HydrateCtx,\n type Hydrator,\n mergeManyStates,\n} from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport {\n type HydrationFn,\n type PresentationFn,\n type RulesFn,\n type SkeletonFn,\n createPipeline,\n} from '../../../../pipeline.js'\nimport { getAtprotoPassthroughHeaders } from '../../../../util/headers.js'\nimport type { Views } from '../../../../views/index.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getOnboardingSuggestedStarterPacks = createPipeline(\n skeleton,\n hydration,\n noBlocks,\n presentation,\n )\n server.add(app.bsky.unspecced.getOnboardingSuggestedStarterPacks, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ auth, params, req, signal }) => {\n const viewer = auth.credentials.iss\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n xrpcEndpoint:\n app.bsky.unspecced.getOnboardingSuggestedStarterPacks.$lxm,\n labelers,\n viewer,\n })\n const headers = noUndefinedVals({\n 'accept-language': req.headers['accept-language'],\n ...getAtprotoPassthroughHeaders(req),\n })\n const result = await getOnboardingSuggestedStarterPacks(\n {\n ...params,\n hydrateCtx,\n headers,\n signal,\n },\n ctx,\n )\n return {\n encoding: 'application/json',\n body: result,\n }\n },\n })\n}\n\nconst skeleton: SkeletonFn<Context, Params, SkeletonState> = async (\n input,\n): Promise<SkeletonState> => {\n const { params, ctx } = input\n\n if (!ctx.irisClient) {\n // Use 501 instead of 500 as these are not considered retry-able by clients\n throw new MethodNotImplementedError('Iris agent not available')\n }\n\n const skeleton = await ctx.irisClient.call(\n app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton,\n { limit: params.limit, viewer: params.hydrateCtx.viewer ?? undefined },\n { headers: params.headers, signal: params.signal },\n )\n\n // @TODO Make sure upstream always provides this\n skeleton.starterPacks ??= []\n\n return skeleton\n}\n\nconst hydration: HydrationFn<Context, Params, SkeletonState> = async (\n input,\n) => {\n const { ctx, params, skeleton } = input\n\n const pairs: Map<DidString, DidString[]> = new Map()\n const viewer = params.hydrateCtx.viewer\n if (viewer) {\n pairs.set(viewer, getUniqueDidsFromStarterPacks(skeleton.starterPacks))\n }\n const [starterPacksState, bidirectionalBlocks] = await Promise.all([\n ctx.hydrator.hydrateStarterPacks(skeleton.starterPacks, params.hydrateCtx),\n ctx.hydrator.hydrateBidirectionalBlocks(pairs, params.hydrateCtx),\n ])\n\n return mergeManyStates(starterPacksState, { bidirectionalBlocks })\n}\n\nconst noBlocks: RulesFn<Context, Params, SkeletonState> = (input) => {\n const { skeleton, params, hydration } = input\n const viewer = params.hydrateCtx.viewer\n if (!viewer) {\n return skeleton\n }\n\n const blocks = hydration.bidirectionalBlocks?.get(viewer)\n const filteredSkeleton: SkeletonState = {\n ...skeleton,\n starterPacks: skeleton.starterPacks.filter((uri) => {\n try {\n return !blocks?.get(new AtUri(uri).did)\n } catch {\n return false\n }\n }),\n }\n\n return filteredSkeleton\n}\n\nconst presentation: PresentationFn<\n Context,\n Params,\n SkeletonState,\n app.bsky.unspecced.getOnboardingSuggestedStarterPacks.$OutputBody\n> = (input) => {\n const { ctx, skeleton, hydration } = input\n\n return {\n starterPacks: mapDefined(skeleton.starterPacks, (uri) =>\n ctx.views.starterPack(uri, hydration),\n ),\n recIdStr: skeleton.recIdStr,\n }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n irisClient: Client | undefined\n}\n\ntype Params = app.bsky.unspecced.getOnboardingSuggestedStarterPacks.$Params & {\n hydrateCtx: HydrateCtx\n headers: Record<string, string>\n signal: AbortSignal\n}\n\ntype SkeletonState =\n app.bsky.unspecced.getOnboardingSuggestedStarterPacksSkeleton.$OutputBody\n\nfunction getUniqueDidsFromStarterPacks(\n starterPacks?: AtUriString[],\n): DidString[] {\n if (!starterPacks) return []\n\n const dids = new Set<DidString>()\n\n for (const uri of starterPacks) {\n try {\n dids.add(new AtUri(uri).did)\n } catch {\n continue\n }\n }\n\n return Array.from(dids)\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getPopularFeedGenerators.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getPopularFeedGenerators.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAcxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"getPopularFeedGenerators.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/unspecced/getPopularFeedGenerators.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAcxD,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAsFvD"}
|