@atproto/bsky 0.0.251 → 0.0.253
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 +35 -0
- package/dist/api/age-assurance/const.js +19 -19
- package/dist/api/age-assurance/const.js.map +1 -1
- package/dist/kws.d.ts +8 -0
- package/dist/kws.d.ts.map +1 -1
- package/dist/kws.js +53 -4
- package/dist/kws.js.map +1 -1
- package/package.json +28 -23
- package/bin/migration-create.ts +0 -46
- package/buf.gen.yaml +0 -12
- package/proto/bsky.proto +0 -1907
- package/proto/courier.proto +0 -84
- package/proto/rolodex.proto +0 -116
- package/src/api/age-assurance/const.ts +0 -203
- package/src/api/age-assurance/index.ts +0 -34
- package/src/api/age-assurance/kws/age-verified.test.ts +0 -98
- package/src/api/age-assurance/kws/age-verified.ts +0 -81
- package/src/api/age-assurance/kws/const.ts +0 -33
- package/src/api/age-assurance/kws/external-payload.test.ts +0 -72
- package/src/api/age-assurance/kws/external-payload.ts +0 -149
- package/src/api/age-assurance/redirects/kws-age-verified.ts +0 -107
- package/src/api/age-assurance/stash.ts +0 -23
- package/src/api/age-assurance/types.ts +0 -10
- package/src/api/age-assurance/util.ts +0 -68
- package/src/api/age-assurance/webhooks/kws-age-verified.ts +0 -75
- package/src/api/app/bsky/actor/getProfile.ts +0 -106
- package/src/api/app/bsky/actor/getProfiles.ts +0 -99
- package/src/api/app/bsky/actor/getSuggestions.ts +0 -163
- package/src/api/app/bsky/actor/searchActors.ts +0 -171
- package/src/api/app/bsky/actor/searchActorsTypeahead.ts +0 -164
- package/src/api/app/bsky/ageassurance/begin.ts +0 -175
- package/src/api/app/bsky/ageassurance/getConfig.ts +0 -16
- package/src/api/app/bsky/ageassurance/getState.ts +0 -58
- package/src/api/app/bsky/bookmark/createBookmark.ts +0 -39
- package/src/api/app/bsky/bookmark/deleteBookmark.ts +0 -32
- package/src/api/app/bsky/bookmark/getBookmarks.ts +0 -97
- package/src/api/app/bsky/bookmark/util.ts +0 -13
- package/src/api/app/bsky/contact/dismissMatch.ts +0 -26
- package/src/api/app/bsky/contact/getMatches.ts +0 -111
- package/src/api/app/bsky/contact/getSyncStatus.ts +0 -38
- package/src/api/app/bsky/contact/importContacts.ts +0 -119
- package/src/api/app/bsky/contact/removeData.ts +0 -25
- package/src/api/app/bsky/contact/sendNotification.ts +0 -33
- package/src/api/app/bsky/contact/startPhoneVerification.ts +0 -26
- package/src/api/app/bsky/contact/util.ts +0 -92
- package/src/api/app/bsky/contact/verifyPhone.ts +0 -29
- package/src/api/app/bsky/draft/createDraft.ts +0 -45
- package/src/api/app/bsky/draft/deleteDraft.ts +0 -20
- package/src/api/app/bsky/draft/getDrafts.ts +0 -44
- package/src/api/app/bsky/draft/updateDraft.ts +0 -26
- package/src/api/app/bsky/embed/getEmbedExternalView.ts +0 -154
- package/src/api/app/bsky/feed/getActorFeeds.ts +0 -101
- package/src/api/app/bsky/feed/getActorLikes.ts +0 -128
- package/src/api/app/bsky/feed/getAuthorFeed.ts +0 -279
- package/src/api/app/bsky/feed/getFeed.ts +0 -282
- package/src/api/app/bsky/feed/getFeedGenerator.ts +0 -68
- package/src/api/app/bsky/feed/getFeedGenerators.ts +0 -86
- package/src/api/app/bsky/feed/getLikes.ts +0 -151
- package/src/api/app/bsky/feed/getListFeed.ts +0 -147
- package/src/api/app/bsky/feed/getPostThread.ts +0 -156
- package/src/api/app/bsky/feed/getPosts.ts +0 -100
- package/src/api/app/bsky/feed/getQuotes.ts +0 -122
- package/src/api/app/bsky/feed/getRepostedBy.ts +0 -121
- package/src/api/app/bsky/feed/getSuggestedFeeds.ts +0 -39
- package/src/api/app/bsky/feed/getTimeline.ts +0 -127
- package/src/api/app/bsky/feed/searchPosts.ts +0 -274
- package/src/api/app/bsky/feed/searchPostsV2.ts +0 -243
- package/src/api/app/bsky/graph/getActorStarterPacks.ts +0 -103
- package/src/api/app/bsky/graph/getBlocks.ts +0 -94
- package/src/api/app/bsky/graph/getFollowers.ts +0 -153
- package/src/api/app/bsky/graph/getFollows.ts +0 -151
- package/src/api/app/bsky/graph/getKnownFollowers.ts +0 -123
- package/src/api/app/bsky/graph/getList.ts +0 -152
- package/src/api/app/bsky/graph/getListBlocks.ts +0 -87
- package/src/api/app/bsky/graph/getListMutes.ts +0 -87
- package/src/api/app/bsky/graph/getLists.ts +0 -125
- package/src/api/app/bsky/graph/getListsWithMembership.ts +0 -146
- package/src/api/app/bsky/graph/getMutes.ts +0 -87
- package/src/api/app/bsky/graph/getRelationships.ts +0 -58
- package/src/api/app/bsky/graph/getStarterPack.ts +0 -82
- package/src/api/app/bsky/graph/getStarterPacks.ts +0 -86
- package/src/api/app/bsky/graph/getStarterPacksWithMembership.ts +0 -136
- package/src/api/app/bsky/graph/getSuggestedFollowsByActor.ts +0 -171
- package/src/api/app/bsky/graph/muteActor.ts +0 -24
- package/src/api/app/bsky/graph/muteActorList.ts +0 -19
- package/src/api/app/bsky/graph/muteThread.ts +0 -19
- package/src/api/app/bsky/graph/searchStarterPacks.ts +0 -169
- package/src/api/app/bsky/graph/unmuteActor.ts +0 -21
- package/src/api/app/bsky/graph/unmuteActorList.ts +0 -19
- package/src/api/app/bsky/graph/unmuteThread.ts +0 -19
- package/src/api/app/bsky/labeler/getServices.ts +0 -41
- package/src/api/app/bsky/notification/getPreferences.ts +0 -49
- package/src/api/app/bsky/notification/getUnreadCount.ts +0 -83
- package/src/api/app/bsky/notification/listActivitySubscriptions.ts +0 -116
- package/src/api/app/bsky/notification/listNotifications.ts +0 -272
- package/src/api/app/bsky/notification/putActivitySubscription.ts +0 -69
- package/src/api/app/bsky/notification/putPreferences.ts +0 -17
- package/src/api/app/bsky/notification/putPreferencesV2.ts +0 -64
- package/src/api/app/bsky/notification/registerPush.ts +0 -40
- package/src/api/app/bsky/notification/unregisterPush.ts +0 -39
- package/src/api/app/bsky/notification/updateSeen.ts +0 -52
- package/src/api/app/bsky/notification/util.ts +0 -134
- package/src/api/app/bsky/unspecced/getAgeAssuranceState.ts +0 -48
- package/src/api/app/bsky/unspecced/getConfig.ts +0 -20
- package/src/api/app/bsky/unspecced/getOnboardingSuggestedStarterPacks.ts +0 -162
- package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +0 -91
- package/src/api/app/bsky/unspecced/getPostThreadOtherV2.ts +0 -126
- package/src/api/app/bsky/unspecced/getPostThreadV2.ts +0 -148
- package/src/api/app/bsky/unspecced/getSuggestedFeeds.ts +0 -100
- package/src/api/app/bsky/unspecced/getSuggestedOnboardingUsers.ts +0 -143
- package/src/api/app/bsky/unspecced/getSuggestedStarterPacks.ts +0 -165
- package/src/api/app/bsky/unspecced/getSuggestedUsers.ts +0 -183
- package/src/api/app/bsky/unspecced/getSuggestedUsersForDiscover.ts +0 -175
- package/src/api/app/bsky/unspecced/getSuggestedUsersForExplore.ts +0 -178
- package/src/api/app/bsky/unspecced/getSuggestedUsersForSeeMore.ts +0 -178
- package/src/api/app/bsky/unspecced/getTaggedSuggestions.ts +0 -27
- package/src/api/app/bsky/unspecced/getTrendingTopics.ts +0 -96
- package/src/api/app/bsky/unspecced/getTrends.ts +0 -162
- package/src/api/app/bsky/unspecced/initAgeAssurance.ts +0 -150
- package/src/api/blob-dispatcher.ts +0 -41
- package/src/api/blob-resolver.ts +0 -401
- package/src/api/com/atproto/admin/getAccountInfos.ts +0 -49
- package/src/api/com/atproto/admin/getSubjectStatus.ts +0 -79
- package/src/api/com/atproto/admin/updateSubjectStatus.ts +0 -80
- package/src/api/com/atproto/identity/resolveHandle.ts +0 -19
- package/src/api/com/atproto/label/queryLabels.ts +0 -42
- package/src/api/com/atproto/repo/getRecord.ts +0 -44
- package/src/api/com/atproto/temp/fetchLabels.ts +0 -9
- package/src/api/external.ts +0 -15
- package/src/api/health.ts +0 -47
- package/src/api/index.ts +0 -216
- package/src/api/internal/bsky/actor/getProfiles.ts +0 -87
- package/src/api/kws/api.ts +0 -113
- package/src/api/kws/index.ts +0 -29
- package/src/api/kws/types.ts +0 -78
- package/src/api/kws/util.test.ts +0 -50
- package/src/api/kws/util.ts +0 -109
- package/src/api/kws/webhook.ts +0 -134
- package/src/api/sitemap.ts +0 -76
- package/src/api/util.ts +0 -45
- package/src/api/well-known.ts +0 -44
- package/src/auth-verifier.ts +0 -522
- package/src/bsync.ts +0 -41
- package/src/cache/read-through.ts +0 -157
- package/src/config.ts +0 -719
- package/src/context.ts +0 -143
- package/src/courier.ts +0 -41
- package/src/data-plane/client/hosts.ts +0 -103
- package/src/data-plane/client/index.ts +0 -125
- package/src/data-plane/client/util.test.ts +0 -29
- package/src/data-plane/client/util.ts +0 -74
- package/src/data-plane/index.ts +0 -2
- package/src/data-plane/server/background.ts +0 -66
- package/src/data-plane/server/bsync-subscription.ts +0 -590
- package/src/data-plane/server/db/database-schema.ts +0 -90
- package/src/data-plane/server/db/db.ts +0 -204
- package/src/data-plane/server/db/index.ts +0 -2
- package/src/data-plane/server/db/migrations/20230309T045948368Z-init.ts +0 -305
- package/src/data-plane/server/db/migrations/20230408T152211201Z-notification-init.ts +0 -25
- package/src/data-plane/server/db/migrations/20230417T210628672Z-moderation-init.ts +0 -127
- package/src/data-plane/server/db/migrations/20230420T211446071Z-did-cache.ts +0 -14
- package/src/data-plane/server/db/migrations/20230427T194702079Z-notif-record-index.ts +0 -14
- package/src/data-plane/server/db/migrations/20230605T144730094Z-post-profile-aggs.ts +0 -23
- package/src/data-plane/server/db/migrations/20230607T211442112Z-feed-generator-init.ts +0 -34
- package/src/data-plane/server/db/migrations/20230608T155101190Z-algo-whats-hot-view.ts +0 -85
- package/src/data-plane/server/db/migrations/20230608T201813132Z-mute-lists.ts +0 -78
- package/src/data-plane/server/db/migrations/20230608T205147239Z-mutes.ts +0 -15
- package/src/data-plane/server/db/migrations/20230609T153623961Z-blocks.ts +0 -33
- package/src/data-plane/server/db/migrations/20230609T232122649Z-actor-deletion-indexes.ts +0 -27
- package/src/data-plane/server/db/migrations/20230610T203555962Z-suggested-follows.ts +0 -13
- package/src/data-plane/server/db/migrations/20230611T215300060Z-actor-state.ts +0 -13
- package/src/data-plane/server/db/migrations/20230620T161134972Z-post-langs.ts +0 -9
- package/src/data-plane/server/db/migrations/20230627T212437895Z-optional-handle.ts +0 -15
- package/src/data-plane/server/db/migrations/20230629T220835893Z-remove-post-hierarchy.ts +0 -32
- package/src/data-plane/server/db/migrations/20230703T045536691Z-feed-and-label-indices.ts +0 -31
- package/src/data-plane/server/db/migrations/20230720T164800037Z-posts-cursor-idx.ts +0 -19
- package/src/data-plane/server/db/migrations/20230807T035309811Z-feed-item-delete-invite-for-user-idx.ts +0 -14
- package/src/data-plane/server/db/migrations/20230808T172902639Z-repo-rev.ts +0 -12
- package/src/data-plane/server/db/migrations/20230810T203349843Z-action-duration.ts +0 -23
- package/src/data-plane/server/db/migrations/20230817T195936007Z-native-notifications.ts +0 -16
- package/src/data-plane/server/db/migrations/20230830T205507322Z-suggested-feeds.ts +0 -13
- package/src/data-plane/server/db/migrations/20230904T211011773Z-block-lists.ts +0 -24
- package/src/data-plane/server/db/migrations/20230906T222220386Z-thread-gating.ts +0 -27
- package/src/data-plane/server/db/migrations/20230920T213858047Z-add-tags-to-post.ts +0 -9
- package/src/data-plane/server/db/migrations/20230929T192920807Z-record-cursor-indexes.ts +0 -40
- package/src/data-plane/server/db/migrations/20231003T202833377Z-create-moderation-subject-status.ts +0 -123
- package/src/data-plane/server/db/migrations/20231220T225126090Z-blob-takedowns.ts +0 -66
- package/src/data-plane/server/db/migrations/20240124T023719200Z-tagged-suggestions.ts +0 -15
- package/src/data-plane/server/db/migrations/20240226T225725627Z-labelers.ts +0 -27
- package/src/data-plane/server/db/migrations/20240530T170337073Z-account-deactivation.ts +0 -12
- package/src/data-plane/server/db/migrations/20240606T171229898Z-thread-mutes.ts +0 -15
- package/src/data-plane/server/db/migrations/20240606T222548219Z-starter-packs.ts +0 -45
- package/src/data-plane/server/db/migrations/20240719T203853939Z-priority-notifs.ts +0 -25
- package/src/data-plane/server/db/migrations/20240723T220700077Z-quotes-post-aggs.ts +0 -12
- package/src/data-plane/server/db/migrations/20240723T220703655Z-quotes.ts +0 -28
- package/src/data-plane/server/db/migrations/20240801T193939827Z-post-gate.ts +0 -17
- package/src/data-plane/server/db/migrations/20240808T224251220Z-post-gate-flags.ts +0 -25
- package/src/data-plane/server/db/migrations/20240829T211238293Z-simplify-actor-sync.ts +0 -23
- package/src/data-plane/server/db/migrations/20240831T134810923Z-pinned-posts.ts +0 -17
- package/src/data-plane/server/db/migrations/20241114T153108102Z-add-starter-packs-name.ts +0 -19
- package/src/data-plane/server/db/migrations/20250116T222618297Z-post-embed-video.ts +0 -17
- package/src/data-plane/server/db/migrations/20250207T174822012Z-add-label-exp.ts +0 -9
- package/src/data-plane/server/db/migrations/20250404T163421487Z-verifications.ts +0 -39
- package/src/data-plane/server/db/migrations/20250526T023712742Z-like-repost-via.ts +0 -17
- package/src/data-plane/server/db/migrations/20250528T221913281Z-add-record-tags.ts +0 -9
- package/src/data-plane/server/db/migrations/20250602T190357447Z-add-private-data.ts +0 -22
- package/src/data-plane/server/db/migrations/20250611T140649895Z-add-activity-subscription.ts +0 -22
- package/src/data-plane/server/db/migrations/20250627T025331240Z-add-actor-age-assurance-columns.ts +0 -22
- package/src/data-plane/server/db/migrations/20250812T183735692Z-add-bookmarks.ts +0 -23
- package/src/data-plane/server/db/migrations/20250813T174955711Z-add-post-agg-bookmarks.ts +0 -12
- package/src/data-plane/server/db/migrations/20251120T004738098Z-update-actor-age-assurance-v2.ts +0 -28
- package/src/data-plane/server/db/migrations/20260112T133951271Z-add-drafts.ts +0 -24
- package/src/data-plane/server/db/migrations/20260604T224952774Z-post-embed-gallery-image.ts +0 -19
- package/src/data-plane/server/db/migrations/index.ts +0 -60
- package/src/data-plane/server/db/migrations/provider.ts +0 -26
- package/src/data-plane/server/db/pagination.ts +0 -341
- package/src/data-plane/server/db/tables/activity-subscription.ts +0 -12
- package/src/data-plane/server/db/tables/actor-block.ts +0 -14
- package/src/data-plane/server/db/tables/actor-state.ts +0 -10
- package/src/data-plane/server/db/tables/actor-sync.ts +0 -9
- package/src/data-plane/server/db/tables/actor.ts +0 -19
- package/src/data-plane/server/db/tables/algo.ts +0 -11
- package/src/data-plane/server/db/tables/blob-takedown.ts +0 -9
- package/src/data-plane/server/db/tables/bookmark.ts +0 -11
- package/src/data-plane/server/db/tables/did-cache.ts +0 -13
- package/src/data-plane/server/db/tables/draft.ts +0 -11
- package/src/data-plane/server/db/tables/duplicate-record.ts +0 -12
- package/src/data-plane/server/db/tables/feed-generator.ts +0 -21
- package/src/data-plane/server/db/tables/feed-item.ts +0 -12
- package/src/data-plane/server/db/tables/follow.ts +0 -15
- package/src/data-plane/server/db/tables/label.ts +0 -13
- package/src/data-plane/server/db/tables/labeler.ts +0 -16
- package/src/data-plane/server/db/tables/like.ts +0 -18
- package/src/data-plane/server/db/tables/list-block.ts +0 -15
- package/src/data-plane/server/db/tables/list-item.ts +0 -16
- package/src/data-plane/server/db/tables/list-mute.ts +0 -9
- package/src/data-plane/server/db/tables/list.ts +0 -19
- package/src/data-plane/server/db/tables/mute.ts +0 -9
- package/src/data-plane/server/db/tables/notification-push-token.ts +0 -10
- package/src/data-plane/server/db/tables/notification.ts +0 -16
- package/src/data-plane/server/db/tables/post-agg.ts +0 -16
- package/src/data-plane/server/db/tables/post-embed.ts +0 -47
- package/src/data-plane/server/db/tables/post-gate.ts +0 -12
- package/src/data-plane/server/db/tables/post.ts +0 -28
- package/src/data-plane/server/db/tables/private-data.ts +0 -13
- package/src/data-plane/server/db/tables/profile-agg.ts +0 -14
- package/src/data-plane/server/db/tables/profile.ts +0 -17
- package/src/data-plane/server/db/tables/quote.ts +0 -15
- package/src/data-plane/server/db/tables/record.ts +0 -15
- package/src/data-plane/server/db/tables/repost.ts +0 -18
- package/src/data-plane/server/db/tables/starter-pack.ts +0 -17
- package/src/data-plane/server/db/tables/subscription.ts +0 -9
- package/src/data-plane/server/db/tables/suggested-feed.ts +0 -10
- package/src/data-plane/server/db/tables/suggested-follow.ts +0 -10
- package/src/data-plane/server/db/tables/tagged-suggestion.ts +0 -11
- package/src/data-plane/server/db/tables/thread-gate.ts +0 -12
- package/src/data-plane/server/db/tables/thread-mute.ts +0 -9
- package/src/data-plane/server/db/tables/verification.ts +0 -20
- package/src/data-plane/server/db/tables/view-param.ts +0 -12
- package/src/data-plane/server/db/types.ts +0 -12
- package/src/data-plane/server/db/util.ts +0 -67
- package/src/data-plane/server/index.ts +0 -44
- package/src/data-plane/server/indexing/index.ts +0 -458
- package/src/data-plane/server/indexing/plugins/block.ts +0 -84
- package/src/data-plane/server/indexing/plugins/chat-declaration.ts +0 -60
- package/src/data-plane/server/indexing/plugins/feed-generator.ts +0 -80
- package/src/data-plane/server/indexing/plugins/follow.ts +0 -132
- package/src/data-plane/server/indexing/plugins/germ-declaration.ts +0 -57
- package/src/data-plane/server/indexing/plugins/labeler.ts +0 -74
- package/src/data-plane/server/indexing/plugins/like.ts +0 -151
- package/src/data-plane/server/indexing/plugins/list-block.ts +0 -84
- package/src/data-plane/server/indexing/plugins/list-item.ts +0 -92
- package/src/data-plane/server/indexing/plugins/list.ts +0 -80
- package/src/data-plane/server/indexing/plugins/notif-declaration.ts +0 -57
- package/src/data-plane/server/indexing/plugins/post-gate.ts +0 -101
- package/src/data-plane/server/indexing/plugins/post.ts +0 -675
- package/src/data-plane/server/indexing/plugins/profile.ts +0 -90
- package/src/data-plane/server/indexing/plugins/repost.ts +0 -176
- package/src/data-plane/server/indexing/plugins/starter-pack.ts +0 -74
- package/src/data-plane/server/indexing/plugins/status.ts +0 -57
- package/src/data-plane/server/indexing/plugins/thread-gate.ts +0 -101
- package/src/data-plane/server/indexing/plugins/verification.ts +0 -113
- package/src/data-plane/server/indexing/processor.ts +0 -292
- package/src/data-plane/server/routes/activity-subscription.ts +0 -84
- package/src/data-plane/server/routes/blocks.ts +0 -127
- package/src/data-plane/server/routes/bookmarks.ts +0 -83
- package/src/data-plane/server/routes/drafts.ts +0 -37
- package/src/data-plane/server/routes/feed-gens.ts +0 -89
- package/src/data-plane/server/routes/feeds.ts +0 -176
- package/src/data-plane/server/routes/follows.ts +0 -141
- package/src/data-plane/server/routes/identity.ts +0 -59
- package/src/data-plane/server/routes/index.ts +0 -67
- package/src/data-plane/server/routes/interactions.ts +0 -120
- package/src/data-plane/server/routes/labels.ts +0 -55
- package/src/data-plane/server/routes/likes.ts +0 -92
- package/src/data-plane/server/routes/lists.ts +0 -88
- package/src/data-plane/server/routes/moderation.ts +0 -102
- package/src/data-plane/server/routes/mutes.ts +0 -195
- package/src/data-plane/server/routes/notifs.ts +0 -239
- package/src/data-plane/server/routes/profile.ts +0 -220
- package/src/data-plane/server/routes/quotes.ts +0 -32
- package/src/data-plane/server/routes/records.ts +0 -119
- package/src/data-plane/server/routes/relationships.ts +0 -174
- package/src/data-plane/server/routes/reposts.ts +0 -76
- package/src/data-plane/server/routes/search.ts +0 -181
- package/src/data-plane/server/routes/site-standard.ts +0 -12
- package/src/data-plane/server/routes/sitemap.ts +0 -43
- package/src/data-plane/server/routes/starter-packs.ts +0 -32
- package/src/data-plane/server/routes/suggestions.ts +0 -175
- package/src/data-plane/server/routes/sync.ts +0 -16
- package/src/data-plane/server/routes/threads.ts +0 -33
- package/src/data-plane/server/subscription.ts +0 -152
- package/src/data-plane/server/util.ts +0 -202
- package/src/error.ts +0 -12
- package/src/etcd.ts +0 -90
- package/src/feature-gates/README.md +0 -83
- package/src/feature-gates/gates.ts +0 -25
- package/src/feature-gates/index.ts +0 -235
- package/src/feature-gates/metrics.test.ts +0 -196
- package/src/feature-gates/metrics.ts +0 -107
- package/src/feature-gates/types.ts +0 -62
- package/src/feature-gates/utils.test.ts +0 -66
- package/src/feature-gates/utils.ts +0 -135
- package/src/hydration/actor.ts +0 -578
- package/src/hydration/external.test.ts +0 -281
- package/src/hydration/external.ts +0 -268
- package/src/hydration/feed.ts +0 -455
- package/src/hydration/graph.ts +0 -354
- package/src/hydration/hydrator.ts +0 -1835
- package/src/hydration/label.ts +0 -207
- package/src/hydration/util.ts +0 -199
- package/src/image/index.ts +0 -2
- package/src/image/invalidator.ts +0 -33
- package/src/image/logger.ts +0 -6
- package/src/image/server.ts +0 -228
- package/src/image/sharp.ts +0 -89
- package/src/image/uri.ts +0 -114
- package/src/image/util.ts +0 -41
- package/src/index.ts +0 -312
- package/src/kws.ts +0 -197
- package/src/logger.ts +0 -40
- package/src/pipeline.ts +0 -60
- package/src/redis.ts +0 -205
- package/src/rolodex.ts +0 -42
- package/src/stash.ts +0 -76
- package/src/util/debug.ts +0 -10
- package/src/util/http.ts +0 -42
- package/src/util/retry.ts +0 -10
- package/src/util/standard-site.test.ts +0 -423
- package/src/util/standard-site.ts +0 -196
- package/src/util/uris.ts +0 -38
- package/src/util.ts +0 -48
- package/src/views/index.ts +0 -2783
- package/src/views/threads-v2.ts +0 -510
- package/src/views/types.ts +0 -193
- package/src/views/util.ts +0 -114
- package/test.env +0 -2
- package/tests/__snapshots__/feed-generation.test.ts.snap +0 -2510
- package/tests/_util.ts +0 -292
- package/tests/admin/admin-auth.test.ts +0 -190
- package/tests/admin/moderation.test.ts +0 -222
- package/tests/auth.test.ts +0 -90
- package/tests/blob-resolver.test.ts +0 -125
- package/tests/data-plane/__snapshots__/indexing.test.ts.snap +0 -887
- package/tests/data-plane/db.test.ts +0 -189
- package/tests/data-plane/duplicate-records.test.ts +0 -156
- package/tests/data-plane/handle-invalidation.test.ts +0 -153
- package/tests/data-plane/indexing.test.ts +0 -827
- package/tests/data-plane/subscription.test.ts +0 -128
- package/tests/data-plane/thread-mutes.test.ts +0 -175
- package/tests/entryway-auth.test.ts +0 -177
- package/tests/etcd.test.ts +0 -302
- package/tests/feed-generation.test.ts +0 -965
- package/tests/hydration/util.test.ts +0 -119
- package/tests/image/server.test.ts +0 -97
- package/tests/image/sharp.test.ts +0 -199
- package/tests/image/uri.test.ts +0 -106
- package/tests/label-hydration.test.ts +0 -245
- package/tests/postgates.test.ts +0 -182
- package/tests/query-labels.test.ts +0 -85
- package/tests/redis-cache.test.ts +0 -230
- package/tests/seed/feed-hidden-replies.ts +0 -63
- package/tests/seed/get-suggested-starter-packs.ts +0 -64
- package/tests/seed/get-trends.ts +0 -71
- package/tests/seed/known-followers.ts +0 -111
- package/tests/seed/postgates.ts +0 -57
- package/tests/server.test.ts +0 -115
- package/tests/sitemap.test.ts +0 -75
- package/tests/stash.test.ts +0 -180
- package/tests/utils.test.ts +0 -36
- package/tests/views/__snapshots__/actor-search.test.ts.snap +0 -155
- package/tests/views/__snapshots__/author-feed.test.ts.snap +0 -6301
- package/tests/views/__snapshots__/block-lists.test.ts.snap +0 -690
- package/tests/views/__snapshots__/blocks.test.ts.snap +0 -462
- package/tests/views/__snapshots__/bookmarks.test.ts.snap +0 -379
- package/tests/views/__snapshots__/follows.test.ts.snap +0 -798
- package/tests/views/__snapshots__/labeler-service.test.ts.snap +0 -233
- package/tests/views/__snapshots__/likes.test.ts.snap +0 -124
- package/tests/views/__snapshots__/list-feed.test.ts.snap +0 -883
- package/tests/views/__snapshots__/lists.test.ts.snap +0 -650
- package/tests/views/__snapshots__/mute-lists.test.ts.snap +0 -705
- package/tests/views/__snapshots__/mutes.test.ts.snap +0 -353
- package/tests/views/__snapshots__/notifications.test.ts.snap +0 -3074
- package/tests/views/__snapshots__/posts.test.ts.snap +0 -1019
- package/tests/views/__snapshots__/profile.test.ts.snap +0 -811
- package/tests/views/__snapshots__/quotes.test.ts.snap +0 -445
- package/tests/views/__snapshots__/reposts.test.ts.snap +0 -112
- package/tests/views/__snapshots__/starter-packs.test.ts.snap +0 -836
- package/tests/views/__snapshots__/thread-v2.test.ts.snap +0 -1298
- package/tests/views/__snapshots__/thread.test.ts.snap +0 -2051
- package/tests/views/__snapshots__/threadgating.test.ts.snap +0 -189
- package/tests/views/__snapshots__/timeline.test.ts.snap +0 -7252
- package/tests/views/account-deactivation.test.ts +0 -81
- package/tests/views/actor-likes.test.ts +0 -138
- package/tests/views/actor-search.test.ts +0 -261
- package/tests/views/age-assurance-v2.test.ts +0 -823
- package/tests/views/age-assurance.test.ts +0 -479
- package/tests/views/author-feed.test.ts +0 -694
- package/tests/views/block-lists.test.ts +0 -661
- package/tests/views/blocks.test.ts +0 -889
- package/tests/views/bookmarks.test.ts +0 -345
- package/tests/views/drafts.test.ts +0 -430
- package/tests/views/feed-hidden-replies.test.ts +0 -243
- package/tests/views/feed-view-post.test.ts +0 -501
- package/tests/views/follows.test.ts +0 -372
- package/tests/views/get-config.test.ts +0 -64
- package/tests/views/get-suggested-onboarding-users.test.ts +0 -193
- package/tests/views/get-suggested-starter-packs.test.ts +0 -137
- package/tests/views/get-trends.test.ts +0 -141
- package/tests/views/internal-actor.test.ts +0 -127
- package/tests/views/known-followers.test.ts +0 -193
- package/tests/views/labeler-service.test.ts +0 -242
- package/tests/views/labels-needs-review.test.ts +0 -316
- package/tests/views/labels-takedown.test.ts +0 -247
- package/tests/views/likes.test.ts +0 -192
- package/tests/views/list-feed.test.ts +0 -195
- package/tests/views/lists.test.ts +0 -412
- package/tests/views/mute-lists.test.ts +0 -509
- package/tests/views/mutes.test.ts +0 -308
- package/tests/views/notifications.test.ts +0 -1581
- package/tests/views/post-search.test.ts +0 -281
- package/tests/views/posts-debug.test.ts +0 -79
- package/tests/views/posts.test.ts +0 -427
- package/tests/views/profile-debug.test.ts +0 -75
- package/tests/views/profile.test.ts +0 -795
- package/tests/views/quotes.test.ts +0 -133
- package/tests/views/reposts.test.ts +0 -112
- package/tests/views/starter-packs.test.ts +0 -395
- package/tests/views/suggestions.test.ts +0 -145
- package/tests/views/thread-v2.test.ts +0 -2005
- package/tests/views/thread.test.ts +0 -637
- package/tests/views/threadgating.test.ts +0 -843
- package/tests/views/timeline.test.ts +0 -328
- package/tests/views/verification.test.ts +0 -296
- package/tsconfig.build.json +0 -10
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -8
- package/vitest.config.ts +0 -10
package/src/hydration/label.ts
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { AtUriString, DidString, UriString, atUri } from '@atproto/lex'
|
|
2
|
-
import { DataPlaneClient } from '../data-plane/client/index.js'
|
|
3
|
-
import { app, com } from '../lexicons/index.js'
|
|
4
|
-
import { ParsedLabelers } from '../util.js'
|
|
5
|
-
import { Label, LabelerRecord } from '../views/types.js'
|
|
6
|
-
import {
|
|
7
|
-
HydrationMap,
|
|
8
|
-
Merges,
|
|
9
|
-
RecordInfo,
|
|
10
|
-
parseJsonBytes,
|
|
11
|
-
parseRecord,
|
|
12
|
-
parseString,
|
|
13
|
-
} from './util.js'
|
|
14
|
-
|
|
15
|
-
export type { Label }
|
|
16
|
-
|
|
17
|
-
export type SubjectLabels = {
|
|
18
|
-
isImpersonation: boolean
|
|
19
|
-
isTakendown: boolean
|
|
20
|
-
needsReview: boolean
|
|
21
|
-
labels: HydrationMap<`${string}::${string}`, Label> // src + val -> label
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class Labels
|
|
25
|
-
extends HydrationMap<UriString, SubjectLabels>
|
|
26
|
-
implements Merges
|
|
27
|
-
{
|
|
28
|
-
static key(label: Label): `${string}::${string}` {
|
|
29
|
-
return `${label.src}::${label.val}`
|
|
30
|
-
}
|
|
31
|
-
merge(map: Labels): this {
|
|
32
|
-
for (const [key, theirs] of map) {
|
|
33
|
-
if (!theirs) continue
|
|
34
|
-
const mine = this.get(key)
|
|
35
|
-
if (mine) {
|
|
36
|
-
mine.isTakendown = mine.isTakendown || theirs.isTakendown
|
|
37
|
-
mine.labels = mine.labels.merge(theirs.labels)
|
|
38
|
-
} else {
|
|
39
|
-
this.set(key, theirs)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return this
|
|
43
|
-
}
|
|
44
|
-
getBySubject(sub: UriString): Label[] {
|
|
45
|
-
const it = this.get(sub)?.labels.values()
|
|
46
|
-
if (!it) return []
|
|
47
|
-
const labels: Label[] = []
|
|
48
|
-
for (const label of it) {
|
|
49
|
-
if (label) labels.push(label)
|
|
50
|
-
}
|
|
51
|
-
return labels
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export type LabelerAgg = {
|
|
56
|
-
likes: number
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export type LabelerAggs = HydrationMap<DidString, LabelerAgg>
|
|
60
|
-
|
|
61
|
-
export type Labeler = RecordInfo<LabelerRecord>
|
|
62
|
-
export type Labelers = HydrationMap<DidString, Labeler>
|
|
63
|
-
|
|
64
|
-
export type LabelerViewerState = {
|
|
65
|
-
like?: AtUriString
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export type LabelerViewerStates = HydrationMap<DidString, LabelerViewerState>
|
|
69
|
-
|
|
70
|
-
export class LabelHydrator {
|
|
71
|
-
constructor(public dataplane: DataPlaneClient) {}
|
|
72
|
-
|
|
73
|
-
async getLabelsForSubjects(
|
|
74
|
-
subjects: UriString[],
|
|
75
|
-
labelers: ParsedLabelers,
|
|
76
|
-
): Promise<Labels> {
|
|
77
|
-
const map = new Labels()
|
|
78
|
-
|
|
79
|
-
if (!subjects.length || !labelers.dids.length) return map
|
|
80
|
-
|
|
81
|
-
const res = await this.dataplane.getLabels({
|
|
82
|
-
subjects,
|
|
83
|
-
issuers: labelers.dids,
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
for (const cur of res.labels) {
|
|
87
|
-
const parsed = parseJsonBytes(com.atproto.label.defs.label, cur)
|
|
88
|
-
if (!parsed || parsed.neg) continue
|
|
89
|
-
const { sig: _, ...label } = parsed
|
|
90
|
-
let entry = map.get(label.uri)
|
|
91
|
-
if (!entry) {
|
|
92
|
-
entry = {
|
|
93
|
-
isImpersonation: false,
|
|
94
|
-
isTakendown: false,
|
|
95
|
-
needsReview: false,
|
|
96
|
-
labels: new HydrationMap(),
|
|
97
|
-
}
|
|
98
|
-
map.set(label.uri, entry)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const isActionableNeedsReview =
|
|
102
|
-
label.val === NEEDS_REVIEW_LABEL &&
|
|
103
|
-
!label.neg &&
|
|
104
|
-
labelers.redact.has(label.src)
|
|
105
|
-
|
|
106
|
-
// we action needs review labels on backend for now so don't send to client until client has proper logic for them
|
|
107
|
-
if (!isActionableNeedsReview) {
|
|
108
|
-
entry.labels.set(Labels.key(label), label)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
TAKEDOWN_LABELS.includes(label.val) &&
|
|
113
|
-
!label.neg &&
|
|
114
|
-
labelers.redact.has(label.src)
|
|
115
|
-
) {
|
|
116
|
-
entry.isTakendown = true
|
|
117
|
-
}
|
|
118
|
-
if (isActionableNeedsReview) {
|
|
119
|
-
entry.needsReview = true
|
|
120
|
-
}
|
|
121
|
-
if (
|
|
122
|
-
label.val === IMPERSONATION_LABEL &&
|
|
123
|
-
!label.neg &&
|
|
124
|
-
labelers.redact.has(label.src)
|
|
125
|
-
) {
|
|
126
|
-
entry.isImpersonation = true
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return map
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async getLabelers(
|
|
134
|
-
dids: DidString[],
|
|
135
|
-
includeTakedowns = false,
|
|
136
|
-
): Promise<Labelers> {
|
|
137
|
-
const map: Labelers = new HydrationMap()
|
|
138
|
-
if (!dids.length) return map
|
|
139
|
-
|
|
140
|
-
const res = await this.dataplane.getLabelerRecords({
|
|
141
|
-
uris: dids.map(labelerDidToUri),
|
|
142
|
-
})
|
|
143
|
-
for (let i = 0; i < dids.length; i++) {
|
|
144
|
-
const did = dids[i]
|
|
145
|
-
const record = parseRecord(
|
|
146
|
-
app.bsky.labeler.service.main,
|
|
147
|
-
res.records[i],
|
|
148
|
-
includeTakedowns,
|
|
149
|
-
)
|
|
150
|
-
map.set(did, record ?? null)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return map
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
async getLabelerViewerStates(
|
|
157
|
-
dids: DidString[],
|
|
158
|
-
viewer: DidString,
|
|
159
|
-
): Promise<LabelerViewerStates> {
|
|
160
|
-
const map: LabelerViewerStates = new HydrationMap()
|
|
161
|
-
if (!dids.length) return map
|
|
162
|
-
|
|
163
|
-
const likes = await this.dataplane.getLikesByActorAndSubjects({
|
|
164
|
-
actorDid: viewer,
|
|
165
|
-
refs: dids.map((did) => ({ uri: labelerDidToUri(did) })),
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
for (let i = 0; i < dids.length; i++) {
|
|
169
|
-
const did = dids[i]
|
|
170
|
-
map.set(did, {
|
|
171
|
-
like: parseString(likes.uris[i]),
|
|
172
|
-
})
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return map
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async getLabelerAggregates(
|
|
179
|
-
dids: DidString[],
|
|
180
|
-
viewer: DidString | null,
|
|
181
|
-
): Promise<LabelerAggs> {
|
|
182
|
-
const map: LabelerAggs = new HydrationMap()
|
|
183
|
-
if (!dids.length) return map
|
|
184
|
-
|
|
185
|
-
const refs = dids.map((did) => ({ uri: labelerDidToUri(did) }))
|
|
186
|
-
const counts = await this.dataplane.getInteractionCounts({
|
|
187
|
-
refs,
|
|
188
|
-
skipCacheForDids: viewer ? [viewer] : undefined,
|
|
189
|
-
})
|
|
190
|
-
for (let i = 0; i < dids.length; i++) {
|
|
191
|
-
const did = dids[i]
|
|
192
|
-
map.set(did, {
|
|
193
|
-
likes: counts.likes[i] ?? 0,
|
|
194
|
-
})
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return map
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function labelerDidToUri<T extends DidString>(did: T) {
|
|
202
|
-
return atUri(did, app.bsky.labeler.service)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const IMPERSONATION_LABEL = 'impersonation'
|
|
206
|
-
const TAKEDOWN_LABELS = ['!takedown', '!suspend']
|
|
207
|
-
const NEEDS_REVIEW_LABEL = 'needs-review'
|
package/src/hydration/util.ts
DELETED
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { Timestamp } from '@bufbuild/protobuf'
|
|
2
|
-
import {
|
|
3
|
-
AtUriString,
|
|
4
|
-
Cid,
|
|
5
|
-
InferInput,
|
|
6
|
-
InferOutput,
|
|
7
|
-
LexParseOptions,
|
|
8
|
-
LexValue,
|
|
9
|
-
RecordSchema,
|
|
10
|
-
Schema,
|
|
11
|
-
TypedLexMap,
|
|
12
|
-
ValidateOptions,
|
|
13
|
-
lexParseJsonBytes,
|
|
14
|
-
parseCidSafe,
|
|
15
|
-
} from '@atproto/lex'
|
|
16
|
-
import { AtUri } from '@atproto/syntax'
|
|
17
|
-
import { Record as RecordEntry } from '../proto/bsky_pb.js'
|
|
18
|
-
|
|
19
|
-
const PARSE_OPTIONS: LexParseOptions & ValidateOptions = {
|
|
20
|
-
strict: false,
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class HydrationMap<K, T> extends Map<K, T | null> implements Merges {
|
|
24
|
-
merge(map: HydrationMap<K, T>): this {
|
|
25
|
-
for (const [key, val] of map) {
|
|
26
|
-
this.set(key, val)
|
|
27
|
-
}
|
|
28
|
-
return this
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface Merges {
|
|
33
|
-
merge<T extends this>(map: T): this
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export type RecordInfo<T extends TypedLexMap> = {
|
|
37
|
-
record: T
|
|
38
|
-
cid: string
|
|
39
|
-
sortedAt: Date
|
|
40
|
-
indexedAt: Date
|
|
41
|
-
takedownRef: string | undefined
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const mergeMaps = <V extends HydrationMap<any, any>>(
|
|
45
|
-
mapA?: V,
|
|
46
|
-
mapB?: V,
|
|
47
|
-
): V | undefined => {
|
|
48
|
-
if (!mapA) return mapB
|
|
49
|
-
if (!mapB) return mapA
|
|
50
|
-
return mapA.merge(mapB)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export const mergeNestedMaps = <K, V extends HydrationMap<any, any>>(
|
|
54
|
-
mapA?: HydrationMap<K, V>,
|
|
55
|
-
mapB?: HydrationMap<K, V>,
|
|
56
|
-
): HydrationMap<K, V> | undefined => {
|
|
57
|
-
if (!mapA) return mapB
|
|
58
|
-
if (!mapB) return mapA
|
|
59
|
-
|
|
60
|
-
for (const [key, map] of mapB) {
|
|
61
|
-
const merged = mergeMaps(mapA.get(key) ?? undefined, map ?? undefined)
|
|
62
|
-
mapA.set(key, merged ?? null)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return mapA
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export const mergeManyMaps = <K, T>(...maps: HydrationMap<K, T>[]) => {
|
|
69
|
-
return maps.reduce(mergeMaps, undefined as HydrationMap<K, T> | undefined)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export type ItemRef = { uri: AtUriString; cid?: string }
|
|
73
|
-
|
|
74
|
-
export function parseRecord<TSchema extends RecordSchema>(
|
|
75
|
-
recordSchema: TSchema,
|
|
76
|
-
recordEntry: RecordEntry,
|
|
77
|
-
includeTakedowns: boolean,
|
|
78
|
-
): RecordInfo<InferInput<TSchema>> | undefined {
|
|
79
|
-
if (!includeTakedowns && recordEntry.takenDown) {
|
|
80
|
-
return undefined
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const cid = recordEntry.cid
|
|
84
|
-
if (!cid) {
|
|
85
|
-
return undefined
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (recordEntry.record.byteLength === 0) {
|
|
89
|
-
return undefined
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const record = lexParseJsonBytes(recordEntry.record, PARSE_OPTIONS)
|
|
93
|
-
if (!record) {
|
|
94
|
-
return undefined
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// @NOTE We cannot use parse mode here. We must return the original to ensure
|
|
98
|
-
// that the caller gets the same data as what is stored in the PDS (in case of
|
|
99
|
-
// records). This is important because the receiver of the data should be able
|
|
100
|
-
// to compute the right record CID.
|
|
101
|
-
|
|
102
|
-
if (!recordSchema.$matches(record, PARSE_OPTIONS)) {
|
|
103
|
-
return undefined
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
record,
|
|
108
|
-
cid,
|
|
109
|
-
sortedAt: parseDate(recordEntry.sortedAt) ?? new Date(0),
|
|
110
|
-
indexedAt: parseDate(recordEntry.indexedAt) ?? new Date(0),
|
|
111
|
-
takedownRef: safeTakedownRef(recordEntry),
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Decodes binary data containing a JSON representation of a Lex value, and
|
|
117
|
-
* validates it against the provided schema, in parse mode (i.e., allowing
|
|
118
|
-
* coercion & defaults).
|
|
119
|
-
*
|
|
120
|
-
* Returns undefined if the input is empty (from dataplane's empty value
|
|
121
|
-
* convention), or if the validation fails.
|
|
122
|
-
*/
|
|
123
|
-
export const parseJsonBytes = <TSchema extends Schema<LexValue>>(
|
|
124
|
-
schema: TSchema,
|
|
125
|
-
bytes: Uint8Array | undefined,
|
|
126
|
-
): InferOutput<TSchema> | undefined => {
|
|
127
|
-
if (!bytes || bytes.byteLength === 0) return undefined
|
|
128
|
-
const value = lexParseJsonBytes(bytes, PARSE_OPTIONS)
|
|
129
|
-
const result = schema.safeParse(value, PARSE_OPTIONS)
|
|
130
|
-
return result.success ? result.value : undefined
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export const parseString = <T extends string | undefined>(
|
|
134
|
-
str: undefined | string,
|
|
135
|
-
): T | undefined => {
|
|
136
|
-
return str ? (str as T) : undefined
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export const parseCid = (cidStr: string | undefined): Cid | null => {
|
|
140
|
-
if (!cidStr) return null
|
|
141
|
-
return parseCidSafe(cidStr)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export const parseDate = (
|
|
145
|
-
timestamp: Timestamp | undefined,
|
|
146
|
-
): Date | undefined => {
|
|
147
|
-
if (!timestamp) return undefined
|
|
148
|
-
const date = timestamp.toDate()
|
|
149
|
-
// Check for year 1 (0001-01-01 00:00:00 UTC) which is -62135596800000ms from epoch.
|
|
150
|
-
// The Go dataplane gives us those values as they come from the Go zero-value for dates.
|
|
151
|
-
if (date.getTime() === -62135596800000) return undefined
|
|
152
|
-
return date
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export const urisByCollection = <T extends string>(
|
|
156
|
-
uris: Iterable<T>,
|
|
157
|
-
): Map<string, T[]> => {
|
|
158
|
-
const result = new Map<string, T[]>()
|
|
159
|
-
for (const uri of uris) {
|
|
160
|
-
const collection = new AtUri(uri).collection
|
|
161
|
-
const items = result.get(collection) ?? []
|
|
162
|
-
items.push(uri)
|
|
163
|
-
result.set(collection, items)
|
|
164
|
-
}
|
|
165
|
-
return result
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export const split = <T>(
|
|
169
|
-
items: T[],
|
|
170
|
-
predicate: (item: T) => boolean,
|
|
171
|
-
): [T[], T[]] => {
|
|
172
|
-
const yes: T[] = []
|
|
173
|
-
const no: T[] = []
|
|
174
|
-
for (const item of items) {
|
|
175
|
-
if (predicate(item)) {
|
|
176
|
-
yes.push(item)
|
|
177
|
-
} else {
|
|
178
|
-
no.push(item)
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return [yes, no]
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
export const safeTakedownRef = (obj?: {
|
|
185
|
-
takenDown: boolean
|
|
186
|
-
takedownRef: string
|
|
187
|
-
}): string | undefined => {
|
|
188
|
-
if (!obj) return
|
|
189
|
-
if (obj.takedownRef) return obj.takedownRef
|
|
190
|
-
if (obj.takenDown) return 'BSKY-TAKEDOWN-UNKNOWN'
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export const isActivitySubscriptionEnabled = ({
|
|
194
|
-
post,
|
|
195
|
-
reply,
|
|
196
|
-
}: {
|
|
197
|
-
post: boolean
|
|
198
|
-
reply: boolean
|
|
199
|
-
}): boolean => post || reply
|
package/src/image/index.ts
DELETED
package/src/image/invalidator.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { BlobCache } from './server.js'
|
|
2
|
-
import { ImageUriBuilder } from './uri.js'
|
|
3
|
-
|
|
4
|
-
// Invalidation is a general interface for propagating an image blob
|
|
5
|
-
// takedown through any caches where a representation of it may be stored.
|
|
6
|
-
// @NOTE this does not remove the blob from storage: just invalidates it from caches.
|
|
7
|
-
// @NOTE keep in sync with same interface in aws/src/cloudfront.ts
|
|
8
|
-
export interface ImageInvalidator {
|
|
9
|
-
invalidate(subject: string, paths: string[]): Promise<void>
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class ImageProcessingServerInvalidator implements ImageInvalidator {
|
|
13
|
-
constructor(private cache: BlobCache) {}
|
|
14
|
-
async invalidate(_subject: string, paths: string[]) {
|
|
15
|
-
const results = await Promise.allSettled(
|
|
16
|
-
paths.map(async (path) => {
|
|
17
|
-
const [, signature] = path.split('/')
|
|
18
|
-
if (!signature) throw new Error('Missing signature')
|
|
19
|
-
const options = ImageUriBuilder.getOptions(path)
|
|
20
|
-
const cacheKey = [
|
|
21
|
-
options.did,
|
|
22
|
-
options.cid.toString(),
|
|
23
|
-
options.preset,
|
|
24
|
-
].join('::')
|
|
25
|
-
await this.cache.clear(cacheKey)
|
|
26
|
-
}),
|
|
27
|
-
)
|
|
28
|
-
const rejection = results.find(
|
|
29
|
-
(result): result is PromiseRejectedResult => result.status === 'rejected',
|
|
30
|
-
)
|
|
31
|
-
if (rejection) throw rejection.reason
|
|
32
|
-
}
|
|
33
|
-
}
|
package/src/image/logger.ts
DELETED
package/src/image/server.ts
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import fsSync from 'node:fs'
|
|
2
|
-
import fs from 'node:fs/promises'
|
|
3
|
-
import os from 'node:os'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
import { Duplex, Readable } from 'node:stream'
|
|
6
|
-
import { pipeline } from 'node:stream/promises'
|
|
7
|
-
import createError, { isHttpError } from 'http-errors'
|
|
8
|
-
import {
|
|
9
|
-
VerifyCidError,
|
|
10
|
-
VerifyCidTransform,
|
|
11
|
-
cloneStream,
|
|
12
|
-
createDecoders,
|
|
13
|
-
isErrnoException,
|
|
14
|
-
} from '@atproto/common'
|
|
15
|
-
import { BlobNotFoundError } from '@atproto/repo'
|
|
16
|
-
import { StreamBlobOptions, streamBlob } from '../api/blob-resolver.js'
|
|
17
|
-
import { AppContext } from '../context.js'
|
|
18
|
-
import { Middleware, responseSignal } from '../util/http.js'
|
|
19
|
-
import log from './logger.js'
|
|
20
|
-
import { createImageProcessor, createImageUpscaler } from './sharp.js'
|
|
21
|
-
import { BadPathError, ImageUriBuilder } from './uri.js'
|
|
22
|
-
import { Options, SharpInfo, formatsToMimes } from './util.js'
|
|
23
|
-
|
|
24
|
-
export function createMiddleware(
|
|
25
|
-
ctx: AppContext,
|
|
26
|
-
{ prefix = '/' }: { prefix?: string } = {},
|
|
27
|
-
): Middleware {
|
|
28
|
-
if (!prefix.startsWith('/') || !prefix.endsWith('/')) {
|
|
29
|
-
throw new TypeError('Prefix must start and end with a slash')
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// If there is a CDN, we don't need to serve images
|
|
33
|
-
if (ctx.cfg.cdnUrl) {
|
|
34
|
-
return (req, res, next) => next()
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const cache = new BlobDiskCache(ctx.cfg.blobCacheLocation)
|
|
38
|
-
|
|
39
|
-
return async (req, res, next) => {
|
|
40
|
-
if (res.destroyed) return
|
|
41
|
-
if (req.method !== 'GET' && req.method !== 'HEAD') return next()
|
|
42
|
-
if (!req.url?.startsWith(prefix)) return next()
|
|
43
|
-
const { 0: path, 1: _search } = req.url.slice(prefix.length - 1).split('?')
|
|
44
|
-
if (!path.startsWith('/') || path === '/') return next()
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
const options = ImageUriBuilder.getOptions(path)
|
|
48
|
-
|
|
49
|
-
const cacheKey = [options.did, options.cid, options.preset].join('::')
|
|
50
|
-
|
|
51
|
-
// Cached flow
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const cachedImage = await cache.get(cacheKey)
|
|
55
|
-
res.statusCode = 200
|
|
56
|
-
res.setHeader('x-cache', 'hit')
|
|
57
|
-
res.setHeader('content-type', getMime(options.format))
|
|
58
|
-
res.setHeader('cache-control', `public, max-age=31536000`) // 1 year
|
|
59
|
-
res.setHeader('content-length', cachedImage.size)
|
|
60
|
-
await pipeline(cachedImage, res)
|
|
61
|
-
return
|
|
62
|
-
} catch (err) {
|
|
63
|
-
if (!(err instanceof BlobNotFoundError)) {
|
|
64
|
-
log.error({ cacheKey, err }, 'failed to serve cached image')
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (res.headersSent || res.destroyed) {
|
|
68
|
-
res.destroy()
|
|
69
|
-
return // nothing we can do...
|
|
70
|
-
} else {
|
|
71
|
-
// Ignore and move on to non-cached flow.
|
|
72
|
-
res.removeHeader('x-cache')
|
|
73
|
-
res.removeHeader('content-type')
|
|
74
|
-
res.removeHeader('cache-control')
|
|
75
|
-
res.removeHeader('content-length')
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Non-cached flow
|
|
80
|
-
|
|
81
|
-
const streamOptions: StreamBlobOptions = {
|
|
82
|
-
did: options.did,
|
|
83
|
-
cid: options.cid,
|
|
84
|
-
signal: responseSignal(res),
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
await streamBlob(ctx, streamOptions, (upstream, { did, cid, url }) => {
|
|
88
|
-
// Definitely not an image ? Let's fail right away.
|
|
89
|
-
if (isImageMime(upstream.headers['content-type']) === false) {
|
|
90
|
-
throw createError(400, 'Not an image')
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Let's transform (decompress, verify CID, upscale), process and respond
|
|
94
|
-
|
|
95
|
-
const transforms: Duplex[] = [
|
|
96
|
-
...createDecoders(upstream.headers['content-encoding']),
|
|
97
|
-
new VerifyCidTransform(cid),
|
|
98
|
-
createImageUpscaler(options),
|
|
99
|
-
]
|
|
100
|
-
const processor = createImageProcessor(options)
|
|
101
|
-
|
|
102
|
-
// Cache in the background
|
|
103
|
-
cache
|
|
104
|
-
.put(cacheKey, cloneStream(processor))
|
|
105
|
-
.catch((err) => log.error({ err }, 'failed to cache image'))
|
|
106
|
-
|
|
107
|
-
res.statusCode = 200
|
|
108
|
-
res.setHeader('cache-control', `public, max-age=31536000`) // 1 year
|
|
109
|
-
res.setHeader('x-cache', 'miss')
|
|
110
|
-
processor.once('info', ({ size, format }: SharpInfo) => {
|
|
111
|
-
const type = formatsToMimes.get(format) || 'application/octet-stream'
|
|
112
|
-
|
|
113
|
-
// @NOTE sharp does emit this in time to be set as a header
|
|
114
|
-
res.setHeader('content-length', size)
|
|
115
|
-
res.setHeader('content-type', type)
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
const streams = [...transforms, processor, res]
|
|
119
|
-
void pipeline(streams).catch((err: unknown) => {
|
|
120
|
-
log.warn(
|
|
121
|
-
{ err, did, cid: cid.toString(), pds: url.origin },
|
|
122
|
-
'blob resolution failed during transmission',
|
|
123
|
-
)
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
return streams[0]!
|
|
127
|
-
})
|
|
128
|
-
} catch (err) {
|
|
129
|
-
if (res.headersSent || res.destroyed) {
|
|
130
|
-
res.destroy()
|
|
131
|
-
} else {
|
|
132
|
-
res.removeHeader('content-type')
|
|
133
|
-
res.removeHeader('content-length')
|
|
134
|
-
res.removeHeader('cache-control')
|
|
135
|
-
res.removeHeader('x-cache')
|
|
136
|
-
|
|
137
|
-
if (err instanceof BadPathError) {
|
|
138
|
-
next(createError(400, err))
|
|
139
|
-
} else if (err instanceof VerifyCidError) {
|
|
140
|
-
next(createError(404, 'Blob not found', err))
|
|
141
|
-
} else if (isHttpError(err)) {
|
|
142
|
-
next(err)
|
|
143
|
-
} else {
|
|
144
|
-
next(createError(502, 'Upstream Error', { cause: err }))
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function isImageMime(
|
|
152
|
-
contentType: string | string[] | undefined,
|
|
153
|
-
): undefined | boolean {
|
|
154
|
-
if (contentType == null || contentType === 'application/octet-stream') {
|
|
155
|
-
return undefined // maybe
|
|
156
|
-
}
|
|
157
|
-
if (Array.isArray(contentType)) {
|
|
158
|
-
if (contentType.length === 0) return undefined // should never happen
|
|
159
|
-
if (contentType.length === 1) return isImageMime(contentType[0])
|
|
160
|
-
return contentType.every(isImageMime) // Should we throw a 502 here?
|
|
161
|
-
}
|
|
162
|
-
return contentType.startsWith('image/')
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function getMime(format: Options['format']) {
|
|
166
|
-
const mime = formatsToMimes.get(format)
|
|
167
|
-
if (!mime) throw new Error('Unknown format')
|
|
168
|
-
return mime
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export interface BlobCache {
|
|
172
|
-
get(fileId: string): Promise<Readable & { size: number }>
|
|
173
|
-
put(fileId: string, stream: Readable): Promise<void>
|
|
174
|
-
clear(fileId: string): Promise<void>
|
|
175
|
-
clearAll(): Promise<void>
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export class BlobDiskCache implements BlobCache {
|
|
179
|
-
tempDir: string
|
|
180
|
-
constructor(basePath?: string) {
|
|
181
|
-
this.tempDir = basePath || path.join(os.tmpdir(), 'bsky--processed-images')
|
|
182
|
-
if (!path.isAbsolute(this.tempDir)) {
|
|
183
|
-
throw new Error('Must provide an absolute path')
|
|
184
|
-
}
|
|
185
|
-
try {
|
|
186
|
-
fsSync.mkdirSync(this.tempDir, { recursive: true })
|
|
187
|
-
} catch (err) {
|
|
188
|
-
// All good if cache dir already exists
|
|
189
|
-
if (isErrnoException(err) && err.code === 'EEXIST') return
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async get(fileId: string) {
|
|
194
|
-
try {
|
|
195
|
-
const handle = await fs.open(path.join(this.tempDir, fileId), 'r')
|
|
196
|
-
const { size } = await handle.stat()
|
|
197
|
-
if (size === 0) {
|
|
198
|
-
throw new BlobNotFoundError()
|
|
199
|
-
}
|
|
200
|
-
return Object.assign(handle.createReadStream(), { size })
|
|
201
|
-
} catch (err) {
|
|
202
|
-
if (isErrnoException(err) && err.code === 'ENOENT') {
|
|
203
|
-
throw new BlobNotFoundError()
|
|
204
|
-
}
|
|
205
|
-
throw err
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
async put(fileId: string, stream: Readable) {
|
|
210
|
-
const filename = path.join(this.tempDir, fileId)
|
|
211
|
-
try {
|
|
212
|
-
await fs.writeFile(filename, stream, { flag: 'wx' })
|
|
213
|
-
} catch (err) {
|
|
214
|
-
// Do not overwrite existing file, just ignore the error
|
|
215
|
-
if (isErrnoException(err) && err.code === 'EEXIST') return
|
|
216
|
-
throw err
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
async clear(fileId: string) {
|
|
221
|
-
const filename = path.join(this.tempDir, fileId)
|
|
222
|
-
await fs.rm(filename, { force: true })
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
async clearAll() {
|
|
226
|
-
await fs.rm(this.tempDir, { recursive: true, force: true })
|
|
227
|
-
}
|
|
228
|
-
}
|