diff options
-rw-r--r-- | package.json | 4 | ||||
-rw-r--r-- | src/App.native.tsx | 6 | ||||
-rw-r--r-- | src/App.web.tsx | 6 | ||||
-rw-r--r-- | src/components/interstitials/Trending.tsx | 2 | ||||
-rw-r--r-- | src/components/live/GoLiveDialog.tsx | 21 | ||||
-rw-r--r-- | src/components/live/temp.ts | 41 | ||||
-rw-r--r-- | src/lib/actor-status.ts | 33 | ||||
-rw-r--r-- | src/screens/Search/modules/ExploreRecommendations.tsx | 2 | ||||
-rw-r--r-- | src/screens/Search/modules/ExploreTrendingTopics.tsx | 2 | ||||
-rw-r--r-- | src/screens/Settings/ContentAndMediaSettings.tsx | 2 | ||||
-rw-r--r-- | src/state/queries/service-config.ts | 6 | ||||
-rw-r--r-- | src/state/service-config.tsx (renamed from src/state/trending-config.tsx) | 45 | ||||
-rw-r--r-- | src/view/com/posts/PostFeed.tsx | 12 | ||||
-rw-r--r-- | src/view/com/profile/ProfileMenu.tsx | 5 | ||||
-rw-r--r-- | src/view/shell/desktop/SidebarTrendingTopics.tsx | 2 | ||||
-rw-r--r-- | yarn.lock | 54 |
16 files changed, 140 insertions, 103 deletions
diff --git a/package.json b/package.json index f50651a87..62762bfc7 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "icons:optimize": "svgo -f ./assets/icons" }, "dependencies": { - "@atproto/api": "^0.15.6", + "@atproto/api": "^0.15.7", "@bitdrift/react-native": "^0.6.8", "@braintree/sanitize-url": "^6.0.2", "@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet", @@ -219,7 +219,7 @@ "zod": "^3.20.2" }, "devDependencies": { - "@atproto/dev-env": "^0.3.129", + "@atproto/dev-env": "^0.3.131", "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "@babel/runtime": "^7.26.0", diff --git a/src/App.native.tsx b/src/App.native.tsx index ea50fdfb9..baab8c838 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -43,6 +43,7 @@ import {Provider as PrefsStateProvider} from '#/state/preferences' import {Provider as LabelDefsProvider} from '#/state/preferences/label-defs' import {Provider as ModerationOptsProvider} from '#/state/preferences/moderation-opts' import {Provider as UnreadNotifsProvider} from '#/state/queries/notifications/unread' +import {Provider as ServiceAccountManager} from '#/state/service-config' import { Provider as SessionProvider, type SessionAccount, @@ -57,7 +58,6 @@ import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide' import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed' import {Provider as StarterPackProvider} from '#/state/shell/starter-pack' import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies' -import {Provider as TrendingConfigProvider} from '#/state/trending-config' import {TestCtrls} from '#/view/com/testing/TestCtrls' import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext' import * as Toast from '#/view/com/util/Toast' @@ -149,7 +149,7 @@ function InnerApp() { <BackgroundNotificationPreferencesProvider> <MutedThreadsProvider> <ProgressGuideProvider> - <TrendingConfigProvider> + <ServiceAccountManager> <GestureHandlerRootView style={s.h100pct}> <IntentDialogProvider> @@ -158,7 +158,7 @@ function InnerApp() { <NuxDialogs /> </IntentDialogProvider> </GestureHandlerRootView> - </TrendingConfigProvider> + </ServiceAccountManager> </ProgressGuideProvider> </MutedThreadsProvider> </BackgroundNotificationPreferencesProvider> diff --git a/src/App.web.tsx b/src/App.web.tsx index bbe23e5a5..c5ec0473c 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -33,6 +33,7 @@ import {Provider as PrefsStateProvider} from '#/state/preferences' import {Provider as LabelDefsProvider} from '#/state/preferences/label-defs' import {Provider as ModerationOptsProvider} from '#/state/preferences/moderation-opts' import {Provider as UnreadNotifsProvider} from '#/state/queries/notifications/unread' +import {Provider as ServiceConfigProvider} from '#/state/service-config' import { Provider as SessionProvider, type SessionAccount, @@ -47,7 +48,6 @@ import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide' import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed' import {Provider as StarterPackProvider} from '#/state/shell/starter-pack' import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies' -import {Provider as TrendingConfigProvider} from '#/state/trending-config' import {Provider as ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoWebContext' import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext' import * as Toast from '#/view/com/util/Toast' @@ -130,12 +130,12 @@ function InnerApp() { <MutedThreadsProvider> <SafeAreaProvider> <ProgressGuideProvider> - <TrendingConfigProvider> + <ServiceConfigProvider> <IntentDialogProvider> <Shell /> <NuxDialogs /> </IntentDialogProvider> - </TrendingConfigProvider> + </ServiceConfigProvider> </ProgressGuideProvider> </SafeAreaProvider> </MutedThreadsProvider> diff --git a/src/components/interstitials/Trending.tsx b/src/components/interstitials/Trending.tsx index 56c756c50..5561be18e 100644 --- a/src/components/interstitials/Trending.tsx +++ b/src/components/interstitials/Trending.tsx @@ -9,7 +9,7 @@ import { useTrendingSettingsApi, } from '#/state/preferences/trending' import {useTrendingTopics} from '#/state/queries/trending/useTrendingTopics' -import {useTrendingConfig} from '#/state/trending-config' +import {useTrendingConfig} from '#/state/service-config' import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture' import {atoms as a, useGutters, useTheme} from '#/alf' diff --git a/src/components/live/GoLiveDialog.tsx b/src/components/live/GoLiveDialog.tsx index 2fad009fd..027447272 100644 --- a/src/components/live/GoLiveDialog.tsx +++ b/src/components/live/GoLiveDialog.tsx @@ -10,7 +10,8 @@ import {cleanError} from '#/lib/strings/errors' import {toNiceDomain} from '#/lib/strings/url-helpers' import {definitelyUrl} from '#/lib/strings/url-helpers' import {useModerationOpts} from '#/state/preferences/moderation-opts' -import {useAgent} from '#/state/session' +import {useLiveNowConfig} from '#/state/service-config' +import {useAgent, useSession} from '#/state/session' import {useTickEveryMinute} from '#/state/shell' import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {atoms as a, ios, native, platform, useTheme, web} from '#/alf' @@ -58,6 +59,10 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) { const [duration, setDuration] = useState(60) const moderationOpts = useModerationOpts() const tick = useTickEveryMinute() + const liveNowConfig = useLiveNowConfig() + const {currentAccount} = useSession() + + const config = liveNowConfig.find(cfg => cfg.did === currentAccount?.did) const time = useCallback( (offset: number) => { @@ -79,7 +84,6 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) { const liveLinkUrl = definitelyUrl(liveLink) const debouncedUrl = useDebouncedValue(liveLinkUrl, 500) - const hasLink = !!debouncedUrl const { data: linkMeta, @@ -91,6 +95,13 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) { queryKey: ['link-meta', debouncedUrl], queryFn: async () => { if (!debouncedUrl) return null + if (!config) throw new Error(_(msg`You are not allowed to go live`)) + + const urlp = new URL(debouncedUrl) + if (!config.domains.includes(urlp.hostname)) { + throw new Error(_(msg`${urlp.hostname} is not a valid URL`)) + } + return getLinkMeta(agent, debouncedUrl) }, }) @@ -101,6 +112,10 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) { error: goLiveError, } = useUpsertLiveStatusMutation(duration, linkMeta) + const isSourceInvalid = !!liveLinkError || !!linkMetaError + + const hasLink = !!debouncedUrl && !isSourceInvalid + return ( <Dialog.ScrollableInner label={_(msg`Go Live`)} @@ -136,7 +151,7 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) { <TextField.LabelText> <Trans>Live link</Trans> </TextField.LabelText> - <TextField.Root isInvalid={!!liveLinkError || !!linkMetaError}> + <TextField.Root isInvalid={isSourceInvalid}> <TextField.Input label={_(msg`Live link`)} placeholder={_(msg`www.mylivestream.tv`)} diff --git a/src/components/live/temp.ts b/src/components/live/temp.ts deleted file mode 100644 index fb26b8c06..000000000 --- a/src/components/live/temp.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {type AppBskyActorDefs, AppBskyEmbedExternal} from '@atproto/api' - -import {DISCOVER_DEBUG_DIDS} from '#/lib/constants' -import type * as bsky from '#/types/bsky' - -export const LIVE_DIDS: Record<string, true> = { - 'did:plc:7sfnardo5xxznxc6esxc5ooe': true, // nba.com - 'did:plc:gx6fyi3jcfxd7ammq2t7mzp2': true, // rtgame.bsky.social -} - -export const LIVE_SOURCES: Record<string, true> = { - 'nba.com': true, - 'twitch.tv': true, -} - -// TEMP: dumb gating -export function temp__canBeLive(profile: bsky.profile.AnyProfileView) { - if (__DEV__) - return !!DISCOVER_DEBUG_DIDS[profile.did] || !!LIVE_DIDS[profile.did] - return !!LIVE_DIDS[profile.did] -} - -export function temp__canGoLive(profile: bsky.profile.AnyProfileView) { - if (__DEV__) return true - return !!LIVE_DIDS[profile.did] -} - -// status must have a embed, and the embed must be an approved host for the status to be valid -export function temp__isStatusValid(status: AppBskyActorDefs.StatusView) { - if (status.status !== 'app.bsky.actor.status#live') return false - try { - if (AppBskyEmbedExternal.isView(status.embed)) { - const url = new URL(status.embed.external.uri) - return !!LIVE_SOURCES[url.hostname] - } else { - return false - } - } catch { - return false - } -} diff --git a/src/lib/actor-status.ts b/src/lib/actor-status.ts index 30921a88a..7e023be44 100644 --- a/src/lib/actor-status.ts +++ b/src/lib/actor-status.ts @@ -2,27 +2,28 @@ import {useMemo} from 'react' import { type $Typed, type AppBskyActorDefs, - type AppBskyEmbedExternal, + AppBskyEmbedExternal, } from '@atproto/api' import {isAfter, parseISO} from 'date-fns' import {useMaybeProfileShadow} from '#/state/cache/profile-shadow' +import {useLiveNowConfig} from '#/state/service-config' import {useTickEveryMinute} from '#/state/shell' -import {temp__canBeLive, temp__isStatusValid} from '#/components/live/temp' import type * as bsky from '#/types/bsky' export function useActorStatus(actor?: bsky.profile.AnyProfileView) { const shadowed = useMaybeProfileShadow(actor) const tick = useTickEveryMinute() + const config = useLiveNowConfig() + return useMemo(() => { tick! // revalidate every minute if ( shadowed && - temp__canBeLive(shadowed) && 'status' in shadowed && shadowed.status && - temp__isStatusValid(shadowed.status) && + validateStatus(shadowed.did, shadowed.status, config) && isStatusStillActive(shadowed.status.expiresAt) ) { return { @@ -39,7 +40,7 @@ export function useActorStatus(actor?: bsky.profile.AnyProfileView) { record: {}, } satisfies AppBskyActorDefs.StatusView } - }, [shadowed, tick]) + }, [shadowed, config, tick]) } export function isStatusStillActive(timeStr: string | undefined) { @@ -49,3 +50,25 @@ export function isStatusStillActive(timeStr: string | undefined) { return isAfter(expiry, now) } + +export function validateStatus( + did: string, + status: AppBskyActorDefs.StatusView, + config: {did: string; domains: string[]}[], +) { + if (status.status !== 'app.bsky.actor.status#live') return false + const sources = config.find(cfg => cfg.did === did) + if (!sources) { + return false + } + try { + if (AppBskyEmbedExternal.isView(status.embed)) { + const url = new URL(status.embed.external.uri) + return sources.domains.includes(url.hostname) + } else { + return false + } + } catch { + return false + } +} diff --git a/src/screens/Search/modules/ExploreRecommendations.tsx b/src/screens/Search/modules/ExploreRecommendations.tsx index 4cf84269a..de70240b1 100644 --- a/src/screens/Search/modules/ExploreRecommendations.tsx +++ b/src/screens/Search/modules/ExploreRecommendations.tsx @@ -8,7 +8,7 @@ import { DEFAULT_LIMIT as RECOMMENDATIONS_COUNT, useTrendingTopics, } from '#/state/queries/trending/useTrendingTopics' -import {useTrendingConfig} from '#/state/trending-config' +import {useTrendingConfig} from '#/state/service-config' import {atoms as a, useGutters, useTheme} from '#/alf' import {Hashtag_Stroke2_Corner0_Rounded} from '#/components/icons/Hashtag' import { diff --git a/src/screens/Search/modules/ExploreTrendingTopics.tsx b/src/screens/Search/modules/ExploreTrendingTopics.tsx index 1d3bc2d86..ee541e385 100644 --- a/src/screens/Search/modules/ExploreTrendingTopics.tsx +++ b/src/screens/Search/modules/ExploreTrendingTopics.tsx @@ -8,7 +8,7 @@ import {logger} from '#/logger' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useTrendingSettings} from '#/state/preferences/trending' import {useGetTrendsQuery} from '#/state/queries/trending/useGetTrendsQuery' -import {useTrendingConfig} from '#/state/trending-config' +import {useTrendingConfig} from '#/state/service-config' import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder' import {formatCount} from '#/view/com/util/numeric/format' import {atoms as a, useGutters, useTheme, type ViewStyleProp, web} from '#/alf' diff --git a/src/screens/Settings/ContentAndMediaSettings.tsx b/src/screens/Settings/ContentAndMediaSettings.tsx index 6fa90b1e2..10d5b140b 100644 --- a/src/screens/Settings/ContentAndMediaSettings.tsx +++ b/src/screens/Settings/ContentAndMediaSettings.tsx @@ -14,7 +14,7 @@ import { useTrendingSettings, useTrendingSettingsApi, } from '#/state/preferences/trending' -import {useTrendingConfig} from '#/state/trending-config' +import {useTrendingConfig} from '#/state/service-config' import * as SettingsList from '#/screens/Settings/components/SettingsList' import * as Toggle from '#/components/forms/Toggle' import {Bubbles_Stroke2_Corner2_Rounded as BubblesIcon} from '#/components/icons/Bubble' diff --git a/src/state/queries/service-config.ts b/src/state/queries/service-config.ts index 12d2cc6be..890a49a5c 100644 --- a/src/state/queries/service-config.ts +++ b/src/state/queries/service-config.ts @@ -6,6 +6,10 @@ import {useAgent} from '#/state/session' type ServiceConfig = { checkEmailConfirmed: boolean topicsEnabled: boolean + liveNow: { + did: string + domains: string[] + }[] } export function useServiceConfigQuery() { @@ -21,11 +25,13 @@ export function useServiceConfigQuery() { checkEmailConfirmed: Boolean(data.checkEmailConfirmed), // @ts-expect-error not included in types atm topicsEnabled: Boolean(data.topicsEnabled), + liveNow: data.liveNow ?? [], } } catch (e) { return { checkEmailConfirmed: false, topicsEnabled: false, + liveNow: [], } } }, diff --git a/src/state/trending-config.tsx b/src/state/service-config.tsx index 1e5db9dc9..37d5685bd 100644 --- a/src/state/trending-config.tsx +++ b/src/state/service-config.tsx @@ -1,21 +1,28 @@ -import React from 'react' +import {createContext, useContext, useMemo} from 'react' import {useLanguagePrefs} from '#/state/preferences/languages' import {useServiceConfigQuery} from '#/state/queries/service-config' import {device} from '#/storage' -type Context = { +type TrendingContext = { enabled: boolean } -const Context = React.createContext<Context>({ +type LiveNowContext = { + did: string + domains: string[] +}[] + +const TrendingContext = createContext<TrendingContext>({ enabled: false, }) -export function Provider({children}: React.PropsWithChildren<{}>) { +const LiveNowContext = createContext<LiveNowContext | null>(null) + +export function Provider({children}: {children: React.ReactNode}) { const langPrefs = useLanguagePrefs() const {data: config, isLoading: isInitialLoad} = useServiceConfigQuery() - const ctx = React.useMemo<Context>(() => { + const trending = useMemo<TrendingContext>(() => { if (__DEV__) { return {enabled: true} } @@ -49,9 +56,33 @@ export function Provider({children}: React.PropsWithChildren<{}>) { return {enabled} }, [isInitialLoad, config, langPrefs.contentLanguages]) - return <Context.Provider value={ctx}>{children}</Context.Provider> + + const liveNow = useMemo<LiveNowContext>(() => config?.liveNow ?? [], [config]) + + return ( + <TrendingContext.Provider value={trending}> + <LiveNowContext.Provider value={liveNow}> + {children} + </LiveNowContext.Provider> + </TrendingContext.Provider> + ) } export function useTrendingConfig() { - return React.useContext(Context) + return useContext(TrendingContext) +} + +export function useLiveNowConfig() { + const ctx = useContext(LiveNowContext) + if (!ctx) { + throw new Error( + 'useLiveNowConfig must be used within a LiveNowConfigProvider', + ) + } + return ctx +} + +export function useCanGoLive(did?: string) { + const config = useLiveNowConfig() + return !!config.find(cfg => cfg.did === did) } diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx index b4c2b2710..732d0fcab 100644 --- a/src/view/com/posts/PostFeed.tsx +++ b/src/view/com/posts/PostFeed.tsx @@ -19,7 +19,7 @@ import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useQueryClient} from '@tanstack/react-query' -import {isStatusStillActive} from '#/lib/actor-status' +import {isStatusStillActive, validateStatus} from '#/lib/actor-status' import {DISCOVER_FEED_URI, KNOWN_SHUTDOWN_FEEDS} from '#/lib/constants' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {logEvent} from '#/lib/statsig/statsig' @@ -39,6 +39,7 @@ import { RQKEY, usePostFeedQuery, } from '#/state/queries/post-feed' +import {useLiveNowConfig} from '#/state/service-config' import {useSession} from '#/state/session' import {useProgressGuide} from '#/state/shell/progress-guide' import {List, type ListRef} from '#/view/com/util/List' @@ -53,7 +54,6 @@ import { } from '#/components/feeds/PostFeedVideoGridRow' import {TrendingInterstitial} from '#/components/interstitials/Trending' import {TrendingVideos as TrendingVideosInterstitial} from '#/components/interstitials/TrendingVideos' -import {temp__canBeLive, temp__isStatusValid} from '#/components/live/temp' import {DiscoverFallbackHeader} from './DiscoverFallbackHeader' import {FeedShutdownMsg} from './FeedShutdownMsg' import {PostFeedErrorMessage} from './PostFeedErrorMessage' @@ -777,16 +777,18 @@ let PostFeed = ({ ) }, [isFetchingNextPage, shouldRenderEndOfFeed, renderEndOfFeed, headerOffset]) + const liveNowConfig = useLiveNowConfig() + const seenActorWithStatusRef = useRef<Set<string>>(new Set()) const onItemSeen = useCallback( (item: FeedRow) => { feedFeedback.onItemSeen(item) if (item.type === 'sliceItem') { const actor = item.slice.items[item.indexInSlice].post.author + if ( actor.status && - temp__canBeLive(actor) && - temp__isStatusValid(actor.status) && + validateStatus(actor.did, actor.status, liveNowConfig) && isStatusStillActive(actor.status.expiresAt) ) { if (!seenActorWithStatusRef.current.has(actor.did)) { @@ -799,7 +801,7 @@ let PostFeed = ({ } } }, - [feedFeedback, feed], + [feedFeedback, feed, liveNowConfig], ) return ( diff --git a/src/view/com/profile/ProfileMenu.tsx b/src/view/com/profile/ProfileMenu.tsx index 1c2a7d62d..f1fd237ec 100644 --- a/src/view/com/profile/ProfileMenu.tsx +++ b/src/view/com/profile/ProfileMenu.tsx @@ -20,6 +20,7 @@ import { useProfileFollowMutationQueue, useProfileMuteMutationQueue, } from '#/state/queries/profile' +import {useCanGoLive} from '#/state/service-config' import {useSession} from '#/state/session' import {EventStopper} from '#/view/com/util/EventStopper' import * as Toast from '#/view/com/util/Toast' @@ -43,7 +44,6 @@ import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker' import {EditLiveDialog} from '#/components/live/EditLiveDialog' import {GoLiveDialog} from '#/components/live/GoLiveDialog' -import {temp__canGoLive} from '#/components/live/temp' import * as Menu from '#/components/Menu' import { ReportDialog, @@ -73,6 +73,7 @@ let ProfileMenu = ({ const isLabelerAndNotBlocked = !!profile.associated?.labeler && !isBlocked const [devModeEnabled] = useDevMode() const verification = useFullVerificationState({profile}) + const canGoLive = useCanGoLive(currentAccount?.did) const [queueMute, queueUnmute] = useProfileMuteMutationQueue(profile) const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile) @@ -299,7 +300,7 @@ let ProfileMenu = ({ </Menu.ItemText> <Menu.ItemIcon icon={List} /> </Menu.Item> - {isSelf && temp__canGoLive(profile) && ( + {isSelf && canGoLive && ( <Menu.Item testID="profileHeaderDropdownListAddRemoveBtn" label={ diff --git a/src/view/shell/desktop/SidebarTrendingTopics.tsx b/src/view/shell/desktop/SidebarTrendingTopics.tsx index db9492349..6b49f5834 100644 --- a/src/view/shell/desktop/SidebarTrendingTopics.tsx +++ b/src/view/shell/desktop/SidebarTrendingTopics.tsx @@ -9,7 +9,7 @@ import { useTrendingSettingsApi, } from '#/state/preferences/trending' import {useTrendingTopics} from '#/state/queries/trending/useTrendingTopics' -import {useTrendingConfig} from '#/state/trending-config' +import {useTrendingConfig} from '#/state/service-config' import {atoms as a, useTheme} from '#/alf' import {Button, ButtonIcon} from '#/components/Button' import {Divider} from '#/components/Divider' diff --git a/yarn.lock b/yarn.lock index 261622c33..54b18d8cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -64,10 +64,10 @@ "@atproto/xrpc" "^0.7.0" "@atproto/xrpc-server" "^0.7.18" -"@atproto/api@^0.15.6": - version "0.15.6" - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.15.6.tgz#3832f16641d89c687794cea14b4aba05ba5993c8" - integrity sha512-hKwrBf60LcI4BqArWyrhWJWIpjwAWUJpW3PVvNzUB1q2W/ByC0JAuwq/F8tZpCEiiVBzHjHVRx4QNA2TA1cG3g== +"@atproto/api@^0.15.7": + version "0.15.7" + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.15.7.tgz#8436162d9fa5dac627bdd5c0f5c9598309ec1383" + integrity sha512-YRETLcOwDCYfGs7Sl9ObqPwhOlVWrPkw4f1AYGIrXLQS58WHe/vz1lZbqOqMsC6gvCnyZnOuKlhsRHZ14rBLzg== dependencies: "@atproto/common-web" "^0.4.2" "@atproto/lexicon" "^0.4.11" @@ -95,14 +95,14 @@ multiformats "^9.9.0" uint8arrays "3.0.0" -"@atproto/bsky@^0.0.148": - version "0.0.148" - resolved "https://registry.yarnpkg.com/@atproto/bsky/-/bsky-0.0.148.tgz#f864631e5a9726d3a40c15b0311f730bc16d6bd9" - integrity sha512-09Lzjz9kCK7kPOlJcVj6KbATtoPQwNeeU5s0J2apZYCQmA7wN2xRb5KMf9wr+wa1KO7FwbXKSunwer96dB6zrQ== +"@atproto/bsky@^0.0.149": + version "0.0.149" + resolved "https://registry.yarnpkg.com/@atproto/bsky/-/bsky-0.0.149.tgz#3e9cfb999b9958e9a61776eddb72d424905ec3be" + integrity sha512-7j2KgWHm1nOTQDmtEcNwtldTArS9WwZS3M+aw7OmGH8wCa8vEljNxP6HETjtktDMNTrSipHmmyqh25+Rc5+Ziw== dependencies: "@atproto-labs/fetch-node" "0.1.8" "@atproto-labs/xrpc-utils" "0.0.14" - "@atproto/api" "^0.15.6" + "@atproto/api" "^0.15.7" "@atproto/common" "^0.4.11" "@atproto/crypto" "^0.4.4" "@atproto/did" "^0.1.5" @@ -219,20 +219,20 @@ "@noble/hashes" "^1.6.1" uint8arrays "3.0.0" -"@atproto/dev-env@^0.3.129": - version "0.3.130" - resolved "https://registry.yarnpkg.com/@atproto/dev-env/-/dev-env-0.3.130.tgz#444ad315c00bdcf8bdae036d1e6a56a1808b98c6" - integrity sha512-xRQb+b09lpdG1vGdvMk8Yf/AnO4SDQTjKLyPO+LYYeHuOrKKjJWiBorFC8Lp/rnraoM3AcwMKmW48wdd7cOL9g== +"@atproto/dev-env@^0.3.131": + version "0.3.131" + resolved "https://registry.yarnpkg.com/@atproto/dev-env/-/dev-env-0.3.131.tgz#b3b4cee5f367766d542515b1713523423ecb5a71" + integrity sha512-Tijqc/vq7qKGTpgoKm1BwyvP2QfoOQRjNm9Ro5CDAMXsKqHfXxPiytxYqxj6QR/PptC27aDUqgmexluZN6XbWg== dependencies: - "@atproto/api" "^0.15.6" - "@atproto/bsky" "^0.0.148" + "@atproto/api" "^0.15.7" + "@atproto/bsky" "^0.0.149" "@atproto/bsync" "^0.0.19" "@atproto/common-web" "^0.4.2" "@atproto/crypto" "^0.4.4" "@atproto/identity" "^0.4.8" "@atproto/lexicon" "^0.4.11" - "@atproto/ozone" "^0.1.109" - "@atproto/pds" "^0.4.136" + "@atproto/ozone" "^0.1.110" + "@atproto/pds" "^0.4.137" "@atproto/sync" "^0.1.23" "@atproto/syntax" "^0.4.0" "@atproto/xrpc-server" "^0.7.18" @@ -348,12 +348,12 @@ "@atproto/jwk" "0.1.5" zod "^3.23.8" -"@atproto/ozone@^0.1.109": - version "0.1.109" - resolved "https://registry.yarnpkg.com/@atproto/ozone/-/ozone-0.1.109.tgz#538de28cb21c10afa3fbce0140cd695ef7948e09" - integrity sha512-KokZtu5mhYJdNmYqkI2JZ2hiehxXpi8bbULyWE3f0RKbQRBUBGDVBSF8WkuJUuLzaquyYJVtg3MZFp9ELBcg0g== +"@atproto/ozone@^0.1.110": + version "0.1.110" + resolved "https://registry.yarnpkg.com/@atproto/ozone/-/ozone-0.1.110.tgz#78ad57961b4699c8aa3e6f7d5b6f215d7760a723" + integrity sha512-X7VU7QAkwJrwpgmAuAHqvVDX9CEW0Ts5R4ovATgEt2lbxyxtJtYIm1dG346fAlOfC9f3RGN+HI8vBMWrrrLKAQ== dependencies: - "@atproto/api" "^0.15.6" + "@atproto/api" "^0.15.7" "@atproto/common" "^0.4.11" "@atproto/crypto" "^0.4.4" "@atproto/identity" "^0.4.8" @@ -378,14 +378,14 @@ undici "^6.14.1" ws "^8.12.0" -"@atproto/pds@^0.4.136": - version "0.4.136" - resolved "https://registry.yarnpkg.com/@atproto/pds/-/pds-0.4.136.tgz#53989ff7784c4d1e68d745d69721e71ba82a111d" - integrity sha512-sao4iq/CRWwdM0gljw7XGg/ef4OTWFc6RU2g0nNgJLvxfPO3uMG8Ze1S6tfhr9wvhIKZWVCzzPruTglrlWMEYw== +"@atproto/pds@^0.4.137": + version "0.4.137" + resolved "https://registry.yarnpkg.com/@atproto/pds/-/pds-0.4.137.tgz#87468703b02bf42681ddd50049ee906331655731" + integrity sha512-DRUck9CgOdK0cP6B6/1Cku2gb5t31Vhh9su2TcqF9eymZP1dNSI6nfTIEp+cuwpW/VpDeu7AfHCSgYfnJeZ5yg== dependencies: "@atproto-labs/fetch-node" "0.1.8" "@atproto-labs/xrpc-utils" "0.0.14" - "@atproto/api" "^0.15.6" + "@atproto/api" "^0.15.7" "@atproto/aws" "^0.2.21" "@atproto/common" "^0.4.11" "@atproto/crypto" "^0.4.4" |