···189189 */
190190 ignore: ['^#\/locale\/locales\/.+\/messages'],
191191 }],
192192+ 'import-x/no-extraneous-dependencies': ['error', {
193193+ 'whitelist': [
194194+ // test files only
195195+ '@jest/globals',
196196+ // we only use a really simple util from this, and we know it will be present
197197+ 'expo-modules-core',
198198+ // this is a dep for @atproto/api, but we absolutely need them in sync, so just
199199+ // rely on the transient version
200200+ '@atproto/common-web',
201201+ ]
202202+ }],
203203+ 'import-x/no-nodejs-modules': 'error',
192204193205 /**
194206 * TypeScript-specific rules
···11import {useState} from 'react'
22import {View} from 'react-native'
33-import {XRPCError} from '@atproto/xrpc'
33+import {XRPCError} from '@atproto/api'
44import {msg} from '@lingui/core/macro'
55import {useLingui} from '@lingui/react'
66import {Trans} from '@lingui/react/macro'
+1-1
src/components/dialogs/EmailDialog/events.ts
···11import {useEffect} from 'react'
22-import EventEmitter from 'eventemitter3'
22+import {EventEmitter} from 'eventemitter3'
3344const events = new EventEmitter<{
55 emailVerified: void
+1-1
src/components/moderation/LabelsOnMeDialog.tsx
···11import {useCallback, useMemo, useState} from 'react'
22import {View} from 'react-native'
33import {type ComAtprotoLabelDefs, ToolsOzoneReportDefs} from '@atproto/api'
44-import {XRPCError} from '@atproto/xrpc'
44+import {XRPCError} from '@atproto/api'
55import {msg} from '@lingui/core/macro'
66import {useLingui} from '@lingui/react'
77import {Trans} from '@lingui/react/macro'
+1-1
src/geolocation/service.ts
···11import {useEffect, useState} from 'react'
22-import EventEmitter from 'eventemitter3'
22+import {EventEmitter} from 'eventemitter3'
3344import {networkRetry} from '#/lib/async/retry'
55import {
+1-1
src/lib/hooks/useNavigationDeduped.ts
···11import {useMemo} from 'react'
22-import {useNavigation} from '@react-navigation/core'
22+import {useNavigation} from '@react-navigation/native'
3344import {useDedupe} from '#/lib/hooks/useDedupe'
55import {type NavigationProp} from '#/lib/routes/types'
+1-1
src/lib/hooks/useWebScrollRestoration.ts
···11import {useEffect, useMemo, useState} from 'react'
22-import {type EventArg, useNavigation} from '@react-navigation/core'
22+import {type EventArg, useNavigation} from '@react-navigation/native'
3344if ('scrollRestoration' in history) {
55 // Tell the brower not to mess with the scroll.
+6-2
src/lib/media/manip.ts
···1515import {manipulateAsync, SaveFormat} from 'expo-image-manipulator'
1616import * as MediaLibrary from 'expo-media-library'
1717import * as Sharing from 'expo-sharing'
1818-import {Buffer} from 'buffer'
19182019import {POST_IMG_MAX} from '#/lib/constants'
2120import {logger} from '#/logger'
···322321 bytes: Uint8Array,
323322 type: string,
324323) {
325325- const encoded = Buffer.from(bytes).toString('base64')
324324+ // ideally we'd use `bytes.toBase64()`, but that's only baseline newly available
325325+ let binary = ''
326326+ for (const byte of bytes) {
327327+ binary += String.fromCharCode(byte)
328328+ }
329329+ const encoded = btoa(binary)
326330 return await saveToDevice(filename, encoded, type)
327331}
328332
+1-1
src/lib/strings/errors.ts
···11-import {XRPCError} from '@atproto/xrpc'
11+import {XRPCError} from '@atproto/api'
22import {t} from '@lingui/core/macro'
3344export function cleanError(str: any): string {
+1-1
src/state/cache/post-shadow.ts
···55 type AppBskyFeedDefs,
66} from '@atproto/api'
77import {type QueryClient} from '@tanstack/react-query'
88-import EventEmitter from 'eventemitter3'
88+import {EventEmitter} from 'eventemitter3'
991010import {batchedUpdates} from '#/lib/batchedUpdates'
1111import {findAllPostsInQueryData as findAllPostsInBookmarksQueryData} from '#/state/queries/bookmarks/useBookmarksQuery'
+1-1
src/state/cache/profile-shadow.ts
···11import {useEffect, useMemo, useState} from 'react'
22import {type AppBskyActorDefs, type AppBskyNotificationDefs} from '@atproto/api'
33import {type QueryClient} from '@tanstack/react-query'
44-import EventEmitter from 'eventemitter3'
44+import {EventEmitter} from 'eventemitter3'
5566import {batchedUpdates} from '#/lib/batchedUpdates'
77import {findAllProfilesInQueryData as findAllProfilesInActivitySubscriptionsQueryData} from '#/state/queries/activity-subscriptions'
+1-1
src/state/events.ts
···11-import EventEmitter from 'eventemitter3'
11+import {EventEmitter} from 'eventemitter3'
2233type UnlistenFn = () => void
44
+1-1
src/state/global-gesture-events/index.tsx
···77 type GestureUpdateEvent,
88 type PanGestureHandlerEventPayload,
99} from 'react-native-gesture-handler'
1010-import EventEmitter from 'eventemitter3'
1010+import {EventEmitter} from 'eventemitter3'
11111212export type GlobalGestureEvents = {
1313 begin: GestureStateChangeEvent<PanGestureHandlerEventPayload>
+2-2
src/state/messages/convo/agent.ts
···55 type ChatBskyConvoGetLog,
66 type ChatBskyConvoSendMessage,
77} from '@atproto/api'
88-import {XRPCError} from '@atproto/xrpc'
99-import EventEmitter from 'eventemitter3'
88+import {XRPCError} from '@atproto/api'
99+import {EventEmitter} from 'eventemitter3'
1010import {nanoid} from 'nanoid/non-secure'
11111212import {networkRetry} from '#/lib/async/retry'
+1-1
src/state/messages/events/agent.ts
···11import {type BskyAgent, type ChatBskyConvoGetLog} from '@atproto/api'
22-import EventEmitter from 'eventemitter3'
22+import {EventEmitter} from 'eventemitter3'
33import {nanoid} from 'nanoid/non-secure'
4455import {networkRetry} from '#/lib/async/retry'
+1-1
src/state/persisted/index.web.ts
···11-import EventEmitter from 'eventemitter3'
11+import {EventEmitter} from 'eventemitter3'
2233import BroadcastChannel from '#/lib/broadcast'
44import {logger} from '#/logger'
+1-1
src/state/queries/notifications/unread.tsx
···1212} from 'react'
1313import {AppState} from 'react-native'
1414import {useQueryClient} from '@tanstack/react-query'
1515-import EventEmitter from 'eventemitter3'
1515+import {EventEmitter} from 'eventemitter3'
16161717import BroadcastChannel from '#/lib/broadcast'
1818import {resetBadgeCount} from '#/lib/notifications/notifications'
···11-import EventEmitter from 'eventemitter3'
11+import {EventEmitter} from 'eventemitter3'
2233export const textInputWebEmitter = new EventEmitter()
+1-1
src/view/com/util/MainScrollProvider.tsx
···77 withSpring,
88} from 'react-native-reanimated'
99import {useSafeAreaInsets} from 'react-native-safe-area-context'
1010-import EventEmitter from 'eventemitter3'
1010+import {EventEmitter} from 'eventemitter3'
11111212import {ScrollProvider} from '#/lib/ScrollContext'
1313import {useMinimalShellMode} from '#/state/shell'
+1-1
src/view/shell/desktop/RightNav.tsx
···33import {msg} from '@lingui/core/macro'
44import {useLingui} from '@lingui/react'
55import {Trans} from '@lingui/react/macro'
66-import {useNavigation} from '@react-navigation/core'
66+import {useNavigation} from '@react-navigation/native'
7788import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
99import {useKawaiiMode} from '#/state/preferences/kawaii'
+1-1
src/view/shell/index.web.tsx
···3636import {RedirectOverlay} from '#/ageAssurance/components/RedirectOverlay'
3737import {PassiveAnalytics} from '#/analytics/PassiveAnalytics'
3838import {FlatNavigator, RoutesContainer} from '#/Navigation'
3939-import {Composer} from './Composer.web'
3939+import {Composer} from './Composer'
4040import {DrawerContent} from './Drawer'
41414242function ShellInner() {