finish phase 3: type-only codemod and zero non-bridge @atproto/api imports

Task 8: codemod repointed 150 namespace-type files to #/lexicons
(AppBskyFeedDefs.PostView -> app.bsky.feed.defs.PostView, .Record -> .Main)
plus ~105 hand-fixed consumers (composer/video state, DebugMod, Profile,
onboarding, dialogs). DM_SERVICE_HEADERS deleted; last chat call sites on
the chat client. RichText.tsx flipped to SDK-only. Remaining @atproto/api
importers: the three session bridge files, the deliberate dual-world
matcher in lib/xrpc-error.ts, and test fixtures. toLex boundary casts are
TODO(phase4)-tagged for bridge removal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-07-17 02:08:48 +03:00
parent 300a50b69a
commit e7d349bc30
283 changed files with 2029 additions and 1806 deletions
@@ -1,5 +1,4 @@
import {View} from 'react-native'
import {type AppBskyNotificationDeclaration} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
@@ -18,6 +17,7 @@ import * as Toggle from '#/components/forms/Toggle'
import {BellRinging_Stroke2_Corner0_Rounded as BellRingingIcon} from '#/components/icons/BellRinging'
import * as Layout from '#/components/Layout'
import {Loader} from '#/components/Loader'
import {type app} from '#/lexicons'
import * as SettingsList from './components/SettingsList'
import {ItemTextWithSubtitle} from './NotificationSettings/components/ItemTextWithSubtitle'
@@ -85,7 +85,7 @@ export function Inner({
notificationDeclaration: {
uri?: string
cid?: string
value: AppBskyNotificationDeclaration.Record
value: app.bsky.notification.declaration.Main
}
}) {
const t = useTheme()
+2 -2
View File
@@ -6,7 +6,6 @@ import Animated, {
LayoutAnimationConfig,
LinearTransition,
} from 'react-native-reanimated'
import {type ComAtprotoServerListAppPasswords} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
@@ -33,6 +32,7 @@ import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
import {type com} from '#/lexicons'
import {AddAppPasswordDialog} from './components/AddAppPasswordDialog'
import * as SettingsList from './components/SettingsList'
@@ -135,7 +135,7 @@ export function AppPasswordsScreen({}: Props) {
function AppPasswordCard({
appPassword,
}: {
appPassword: ComAtprotoServerListAppPasswords.AppPassword
appPassword: com.atproto.server.listAppPasswords.AppPassword
}) {
const t = useTheme()
const {i18n, _} = useLingui()
@@ -1,5 +1,5 @@
import {View} from 'react-native'
import {type $Typed, type ComAtprotoLabelDefs} from '@atproto/api'
import {type $Typed} from '@atproto/lex'
import {Trans, useLingui} from '@lingui/react/macro'
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
import {useQueryClient} from '@tanstack/react-query'
@@ -54,15 +54,13 @@ export function AutomationLabelSettingsScreen({}: Props) {
{
profile,
updates: existing => {
const labels: $Typed<ComAtprotoLabelDefs.SelfLabels> = bsky.matches(
com.atproto.label.defs.selfLabels,
existing.labels,
)
? existing.labels
: {
$type: 'com.atproto.label.defs#selfLabels',
values: [],
}
const labels: $Typed<com.atproto.label.defs.SelfLabels> =
bsky.matches(com.atproto.label.defs.selfLabels, existing.labels)
? existing.labels
: {
$type: 'com.atproto.label.defs#selfLabels',
values: [],
}
const hasLabel = labels.values.some(l => l.val === 'bot')
if (hasLabel) {
@@ -82,7 +80,7 @@ export function AutomationLabelSettingsScreen({}: Props) {
return existing
},
checkCommitted: res => {
const exists = !!res.data.labels?.some(l => l.val === 'bot')
const exists = !!res.labels?.some(l => l.val === 'bot')
return exists === wasAdded
},
},
+18 -12
View File
@@ -1,10 +1,6 @@
import {useCallback, useEffect, useState} from 'react'
import {type ListRenderItemInfo, View} from 'react-native'
import * as Contacts from 'expo-contacts'
import {
type AppBskyContactDefs,
type AppBskyContactGetSyncStatus,
} from '@atproto/api'
import {type ModerationOpts} from '@bsky.app/sdk/moderation'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
@@ -32,7 +28,12 @@ import {
useContactsMatchesQuery,
useContactsSyncStatusQuery,
} from '#/state/queries/find-contacts'
import {useAgent, useSession} from '#/state/session'
import {
useAgent,
useAppviewClient,
usePdsClient,
useSession,
} from '#/state/session'
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
import {List} from '#/view/com/util/List'
import {atoms as a, tokens, useGutters, useTheme} from '#/alf'
@@ -52,6 +53,7 @@ import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_NATIVE} from '#/env'
import {InviteFriendsDialog} from '#/features/inviteFriends'
import {app} from '#/lexicons'
import type * as bsky from '#/types/bsky'
import {bulkWriteFollows} from '../Onboarding/util'
@@ -190,7 +192,7 @@ function SyncStatus({
info,
refetchStatus,
}: {
info: AppBskyContactDefs.SyncStatus
info: app.bsky.contact.defs.SyncStatus
refetchStatus: () => Promise<any>
}) {
const ax = useAnalytics()
@@ -370,7 +372,8 @@ function StatusHeader({
}) {
const {_} = useLingui()
const ax = useAnalytics()
const agent = useAgent()
const pdsClient = usePdsClient()
const appviewClient = useAppviewClient()
const queryClient = useQueryClient()
const {currentAccount} = useSession()
@@ -384,12 +387,12 @@ function StatusHeader({
let cursor: string | undefined
do {
const page = await agent.app.bsky.contact.getMatches({
const page = await appviewClient.call(app.bsky.contact.getMatches, {
limit: 100,
cursor,
})
cursor = page.data.cursor
for (const profile of page.data.matches) {
cursor = page.cursor
for (const profile of page.matches) {
if (
profile.did !== currentAccount?.did &&
!isBlockedOrBlocking(profile) &&
@@ -405,7 +408,10 @@ function StatusHeader({
followCount: didsToFollow.length,
})
const uris = await wait(500, bulkWriteFollows(agent, didsToFollow))
const uris = await wait(
500,
bulkWriteFollows(pdsClient, appviewClient, didsToFollow),
)
for (const did of didsToFollow) {
const uri = uris.get(did)
@@ -497,7 +503,7 @@ function StatusFooter({syncedAt}: {syncedAt: string}) {
onMutate: () => ax.metric('contacts:settings:removeData', {}),
onSuccess: () => {
Toast.show(_(msg`Contacts removed`))
queryClient.setQueryData<AppBskyContactGetSyncStatus.OutputSchema>(
queryClient.setQueryData<app.bsky.contact.getSyncStatus.$OutputBody>(
findContactsStatusQueryKey,
{syncStatus: undefined},
)
@@ -1,4 +1,3 @@
import {type AppBskyNotificationDeclaration} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
@@ -17,6 +16,7 @@ import {Key_Stroke2_Corner2_Rounded as KeyIcon} from '#/components/icons/Key'
import {ShieldCheck_Stroke2_Corner0_Rounded as ShieldIcon} from '#/components/icons/Shield'
import * as Layout from '#/components/Layout'
import {InlineLinkText} from '#/components/Link'
import {type app} from '#/lexicons'
import {Email2FAToggle} from './components/Email2FAToggle'
import {PwiOptOut} from './components/PwiOptOut'
import {ItemTextWithSubtitle} from './NotificationSettings/components/ItemTextWithSubtitle'
@@ -146,7 +146,7 @@ function NotificationDeclaration({
isError,
}: {
data?: {
value: AppBskyNotificationDeclaration.Record
value: app.bsky.notification.declaration.Main
}
isError?: boolean
}) {
+3 -3
View File
@@ -1,7 +1,6 @@
import {useState} from 'react'
import {Alert, LayoutAnimation, Linking, Pressable, View} from 'react-native'
import {useReducedMotion} from 'react-native-reanimated'
import {type AppBskyActorDefs} from '@atproto/api'
import {moderateProfile} from '@bsky.app/sdk/moderation'
import {Trans, useLingui} from '@lingui/react/macro'
import {useNavigation} from '@react-navigation/native'
@@ -68,6 +67,7 @@ import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_INTERNAL, IS_IOS, IS_NATIVE} from '#/env'
import {useActorStatus} from '#/features/liveNow'
import {type app} from '#/lexicons'
import {device, useStorage} from '#/storage'
import {useActivitySubscriptionsNudged} from '#/storage/hooks/activity-subscriptions-nudged'
import {toLex} from '#/types/bsky'
@@ -321,7 +321,7 @@ export function SettingsScreen({}: Props) {
function ProfilePreview({
profile,
}: {
profile: AppBskyActorDefs.ProfileViewDetailed
profile: app.bsky.actor.defs.ProfileViewDetailed
}) {
const t = useTheme()
const {gtMobile} = useBreakpoints()
@@ -609,7 +609,7 @@ function AccountRow({
pendingDid,
onPressSwitchAccount,
}: {
profile?: AppBskyActorDefs.ProfileViewDetailed
profile?: app.bsky.actor.defs.ProfileViewDetailed
account: SessionAccount
pendingDid: string | null
onPressSwitchAccount: (
@@ -8,7 +8,6 @@ import Animated, {
SlideInRight,
SlideOutLeft,
} from 'react-native-reanimated'
import {type ComAtprotoServerCreateAppPassword} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
@@ -25,6 +24,7 @@ import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components
import {SquareBehindSquare4_Stroke2_Corner0_Rounded as CopyIcon} from '#/components/icons/SquareBehindSquare4'
import {Text} from '#/components/Typography'
import {IS_WEB} from '#/env'
import {type com} from '#/lexicons'
import {CopyButton} from './CopyButton'
export function AddAppPasswordDialog({
@@ -70,7 +70,7 @@ function CreateDialogInner({passwords}: {passwords: string[]}) {
error: validationError,
isPending,
} = useMutation<
ComAtprotoServerCreateAppPassword.AppPassword,
com.atproto.server.createAppPassword.AppPassword,
Error | DisplayableError
>({
mutationFn: async () => {
@@ -10,7 +10,6 @@ import Animated, {
SlideOutLeft,
SlideOutRight,
} from 'react-native-reanimated'
import {type ComAtprotoServerDescribeServer} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
@@ -46,6 +45,7 @@ import {InlineLinkText} from '#/components/Link'
import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography'
import {useSimpleVerificationState} from '#/components/verification'
import {type com} from '#/lexicons'
import {CopyButton} from './CopyButton'
export function ChangeHandleDialog({
@@ -147,7 +147,7 @@ function ProvidedHandlePage({
serviceInfo,
goToOwnHandle,
}: {
serviceInfo: ComAtprotoServerDescribeServer.OutputSchema
serviceInfo: com.atproto.server.describeServer.$OutputBody
goToOwnHandle: () => void
}) {
const {_} = useLingui()
@@ -4,11 +4,15 @@ import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {useCleanError} from '#/lib/hooks/useCleanError'
import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger'
import {useAgent, useSession, useSessionApi} from '#/state/session'
import {
useAgent,
useChatClient,
useSession,
useSessionApi,
} from '#/state/session'
import {atoms as a, useTheme} from '#/alf'
import {Admonition} from '#/components/Admonition'
import {type DialogOuterProps} from '#/components/Dialog'
@@ -24,6 +28,7 @@ import {Loader} from '#/components/Loader'
import * as Prompt from '#/components/Prompt'
import * as toast from '#/components/Toast'
import {Span, Text} from '#/components/Typography'
import {chat} from '#/lexicons'
import {resetToTab} from '#/Navigation'
const WHITESPACE_RE = /\s/gu
@@ -73,6 +78,7 @@ function DeleteAccountDialogInner({
const {_} = useLingui()
const cleanError = useCleanError()
const agent = useAgent()
const chatClient = useChatClient()
const {currentAccount} = useSession()
const {removeAccount} = useSessionApi()
@@ -112,13 +118,9 @@ function DeleteAccountDialogInner({
throw new Error('Invalid did')
}
const token = confirmCode.replace(WHITESPACE_RE, '')
// Inform chat service of intent to delete account.
const {success} = await agent.chat.bsky.actor.deleteAccount(undefined, {
headers: DM_SERVICE_HEADERS,
})
if (!success) {
throw new Error('Failed to inform chat service of account deletion')
}
// Inform chat service of intent to delete account. The chat client is
// proxied to the chat service; a failure throws.
await chatClient.call(chat.bsky.actor.deleteAccount)
await agent.com.atproto.server.deleteAccount({
did: currentAccount.did,
password,
@@ -2,7 +2,6 @@ import {useCallback, useState} from 'react'
import {View} from 'react-native'
import {Trans, useLingui} from '@lingui/react/macro'
import {DM_SERVICE_HEADERS} from '#/lib/constants'
import {saveBytesToDisk} from '#/lib/media/manip'
import {logger} from '#/logger'
import {useAgent} from '#/state/session'
@@ -14,6 +13,7 @@ import {InlineLinkText} from '#/components/Link'
import {Loader} from '#/components/Loader'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
import {CHAT_PROXY_DID} from '#/env'
export function ExportCarDialog({
control,
@@ -58,9 +58,11 @@ export function ExportCarDialog({
setLoading('chat')
// Using raw fetch because the XRPC client incorrectly tries to JSON-parse
// application/jsonl responses (substring match on application/json).
// The chat-service proxy header is inlined here (the endpoint is proxied
// to `did:web:api.bsky.chat`); this raw path bypasses the lex client.
const res = await agent.sessionManager.fetchHandler(
'/xrpc/chat.bsky.actor.exportAccountData',
{headers: DM_SERVICE_HEADERS},
{headers: {'atproto-proxy': `${CHAT_PROXY_DID}#bsky_chat`}},
)
if (!res.ok) {
throw new Error(`HTTP ${res.status}`)
@@ -1,6 +1,6 @@
import {useCallback} from 'react'
import {View} from 'react-native'
import {type $Typed, type ComAtprotoLabelDefs} from '@atproto/api'
import {type $Typed} from '@atproto/lex'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
@@ -36,7 +36,7 @@ export function PwiOptOut() {
profile,
updates: existing => {
// create labels attr if needed
const labels: $Typed<ComAtprotoLabelDefs.SelfLabels> = bsky.matches(
const labels: $Typed<com.atproto.label.defs.SelfLabels> = bsky.matches(
com.atproto.label.defs.selfLabels,
existing.labels,
)
@@ -70,9 +70,7 @@ export function PwiOptOut() {
return existing
},
checkCommitted: res => {
const exists = !!res.data.labels?.some(
l => l.val === '!no-unauthenticated',
)
const exists = !!res.labels?.some(l => l.val === '!no-unauthenticated')
return exists === wasAdded
},
})