diff --git a/Makefile b/Makefile index e8c331f5b..d5a53cfca 100644 --- a/Makefile +++ b/Makefile @@ -411,20 +411,19 @@ go-lexicons: .PHONY: js-lexicons js-lexicons: - node_modules/.bin/lex gen-api ./js/streamplace/src/lexicons $$(find ./lexicons -type f -name '*.json') --yes \ - && rm -rf ./js/streamplace/src/lexicons/types/com ./js/streamplace/src/lexicons/types/app \ - && sed -i.bak "s/^..port.*app\/bsky.*//g" $$(find ./js/streamplace/src/lexicons -type f) \ - && sed -i.bak "s/^..port.*com\/atproto.*//g" $$(find ./js/streamplace/src/lexicons -type f) \ - && sed -i.bak "s/\(..port .*\)\.js\(.*\)/\1\2/g" $$(find ./js/streamplace/src/lexicons -type f) \ - && sed -i.bak 's/AppBskyGraphBlock\.Main/AppBskyGraphBlock\.Record/' $$(find ./js/streamplace/src/lexicons/types/place/stream -type f) \ - && sed -i.bak 's/PlaceStreamMultistreamTarget\.Main/PlaceStreamMultistreamTarget\.Record/' $$(find ./js/streamplace/src/lexicons/types/place/stream -type f) \ - && sed -i.bak 's/PlaceStreamChatProfile\.Main/PlaceStreamChatProfile\.Record/' $$(find ./js/streamplace/src/lexicons/types/place/stream -type f) \ - && sed -i.bak 's/PlaceStreamLivestream\.Main/PlaceStreamLivestream\.Record/' $$(find ./js/streamplace/src/lexicons/types/place/stream/live -type f) \ - && for x in $$(find ./js/streamplace/src/lexicons -type f -name '*.ts'); do \ - echo 'import { ComAtprotoSyncGetRepo, AppBskyRichtextFacet, AppBskyGraphBlock, ComAtprotoRepoStrongRef, AppBskyActorDefs, ComAtprotoSyncListRepos, AppBskyActorGetProfile, AppBskyFeedGetFeedSkeleton, ComAtprotoIdentityResolveHandle, ComAtprotoModerationCreateReport, ComAtprotoRepoCreateRecord, ComAtprotoRepoDeleteRecord, ComAtprotoRepoDescribeRepo, ComAtprotoRepoGetRecord, ComAtprotoRepoListRecords, ComAtprotoRepoPutRecord, ComAtprotoRepoUploadBlob, ComAtprotoServerDescribeServer, ComAtprotoSyncGetRecord, ComAtprotoIdentityRefreshIdentity } from "@atproto/api"' >> $$x; \ - done \ - && npx prettier --ignore-unknown --write $$(find ./js/streamplace/src/lexicons -type f -name '*.ts') \ - && find . | grep bak$$ | xargs rm + rm -rf .build/lexmerge \ + && mkdir -p .build/lexmerge \ + && node js/streamplace/scripts/dump-external-lexicons.mjs .build/lexmerge \ + && cp -r lexicons/. .build/lexmerge/ \ + && node_modules/.bin/lex build \ + --lexicons .build/lexmerge \ + --out js/streamplace/src/lexicons \ + --clear --index-file --no-pretty \ + --exclude place.stream.live.subscribeSegments \ + && node js/streamplace/scripts/gen-raw-lexicons.mjs \ + && rm -rf .build/lexmerge \ + && npx prettier --ignore-unknown --write \ + ./js/streamplace/src/lexicons ./js/streamplace/src/raw-lexicons.ts .PHONY: md-lexicons md-lexicons: diff --git a/js/app/components/activity-picker.tsx b/js/app/components/activity-picker.tsx index d310c2005..b4b586f69 100644 --- a/js/app/components/activity-picker.tsx +++ b/js/app/components/activity-picker.tsx @@ -15,11 +15,7 @@ import { Image } from "expo-image"; import { X } from "lucide-react-native"; import { useEffect, useLayoutEffect, useRef, useState } from "react"; import { Platform, Pressable, View } from "react-native"; -import { - GamesGamesgamesgamesgamesDefs, - PlaceStreamDefs, - PlaceStreamLivestream, -} from "streamplace"; +import { games as gamesLex, place } from "streamplace"; import { getDidFromAtUri, getGameCoverUrl } from "../utils/game"; const { p, px, r, layout, borders, gap, flex } = zero; @@ -32,9 +28,9 @@ interface GameResult { } interface ActivityPickerProps { - value: PlaceStreamLivestream.Record["activity"] | undefined; + value: place.stream.livestream.Main["activity"] | undefined; onChange: ( - activity: PlaceStreamLivestream.Record["activity"] | undefined, + activity: place.stream.livestream.Main["activity"] | undefined, ) => void; } @@ -72,22 +68,22 @@ export default function ActivityPicker({ const selectedGame = value?.$type === "place.stream.defs#activityGame" - ? (value as PlaceStreamDefs.ActivityGame) + ? (value as place.stream.defs.ActivityGame) : null; const selectedLabel = value?.$type === "place.stream.defs#activityLabel" - ? (value as PlaceStreamDefs.ActivityLabel) + ? (value as place.stream.defs.ActivityLabel) : null; const showResults = searching || results.length > 0; useEffect(() => { if (!selectedGame || !agent || selectedCoverUrl !== undefined) return; - agent.place.stream.game - .getGame({ uri: selectedGame.uri }) + agent.client + .call(place.stream.game.getGame, { uri: selectedGame.uri }) .then((res) => { - setSelectedCoverUrl(res.data.coverUrl); - setSelectedGenres(res.data.genres ?? []); + setSelectedCoverUrl(res.coverUrl); + setSelectedGenres(res.genres ?? []); }) .catch(() => {}); }, [selectedGame?.uri]); @@ -116,13 +112,15 @@ export default function ActivityPicker({ debounceRef.current = setTimeout(async () => { setSearching(true); try { - const res = await agent.place.stream.game.search({ + const res = await agent.client.call(place.stream.game.search, { q: query, limit: 8, }); const games: GameResult[] = []; - for (const result of res.data.results) { - if (!GamesGamesgamesgamesgamesDefs.isGameSummaryView(result)) + for (const result of res.results) { + if ( + !gamesLex.gamesgamesgamesgames.defs.gameSummaryView.isTypeOf(result) + ) continue; const did = getDidFromAtUri(result.uri); const cover = getGameCoverUrl(result.media, did); @@ -150,7 +148,7 @@ export default function ActivityPicker({ const selectGame = (game: GameResult) => { onChange({ $type: "place.stream.defs#activityGame", - uri: game.uri, + uri: game.uri as any, name: game.name, }); setSelectedCoverUrl(game.coverUrl); diff --git a/js/app/components/create-livestream.tsx b/js/app/components/create-livestream.tsx index 84bd7daa8..435b4511a 100644 --- a/js/app/components/create-livestream.tsx +++ b/js/app/components/create-livestream.tsx @@ -14,7 +14,7 @@ import { } from "react-native"; import { useStore } from "store"; import { useNewLivestream, useUserProfile } from "store/hooks"; -import type { PlaceStreamLivestream } from "streamplace"; +import type { place } from "streamplace"; const isWeb = Platform.OS === "web"; @@ -27,7 +27,7 @@ export default function CreateLivestream() { const userIsLive = useLiveUser(); const [title, setTitle] = useState(""); const [activity, setActivity] = useState< - PlaceStreamLivestream.Record["activity"] | undefined + place.stream.livestream.Main["activity"] | undefined >(undefined); const [tags, setTags] = useState([]); const [tagInput, setTagInput] = useState(""); diff --git a/js/app/components/edit-livestream.tsx b/js/app/components/edit-livestream.tsx index 6b4aaec95..c2ab1078d 100644 --- a/js/app/components/edit-livestream.tsx +++ b/js/app/components/edit-livestream.tsx @@ -10,7 +10,7 @@ import { useEffect, useState } from "react"; import { Pressable, ScrollView, TextInput, View } from "react-native"; import { useStore } from "store"; import { useNewLivestream, useUserProfile } from "store/hooks"; -import type { PlaceStreamLivestream } from "streamplace"; +import type { place } from "streamplace"; export default function UpdateLivestream() { const updateLivestreamRecord = useStore( @@ -21,7 +21,7 @@ export default function UpdateLivestream() { const userIsLive = useLiveUser(); const [title, setTitle] = useState(""); const [activity, setActivity] = useState< - PlaceStreamLivestream.Record["activity"] | undefined + place.stream.livestream.Main["activity"] | undefined >(undefined); const [tags, setTags] = useState([]); const [tagInput, setTagInput] = useState(""); @@ -32,8 +32,8 @@ export default function UpdateLivestream() { useEffect(() => { if (livestream?.record) { - const rec = livestream.record as PlaceStreamLivestream.Record; - setActivity(rec.activity as PlaceStreamLivestream.Record["activity"]); + const rec = livestream.record as place.stream.livestream.Main; + setActivity(rec.activity as place.stream.livestream.Main["activity"]); setTags(rec.tags ?? []); } }, [livestream?.uri]); diff --git a/js/app/components/live-dashboard/livestream-panel.tsx b/js/app/components/live-dashboard/livestream-panel.tsx index ef34417e5..bc331cc1e 100644 --- a/js/app/components/live-dashboard/livestream-panel.tsx +++ b/js/app/components/live-dashboard/livestream-panel.tsx @@ -37,7 +37,7 @@ import { } from "react-native"; import { useStore } from "store"; import { useUserProfile } from "store/hooks"; -import type { PlaceStreamLivestream } from "streamplace"; +import type { place } from "streamplace"; import { SCOPE_BSKY_POST_CREATE, scopeGrants, @@ -322,7 +322,7 @@ function LivestreamPanel({ scrollable = true }: { scrollable?: boolean }) { ); const [activity, setActivity] = useState< - PlaceStreamLivestream.Record["activity"] | undefined + place.stream.livestream.Main["activity"] | undefined >(undefined); const [tags, setTags] = useState([]); const [tagInput, setTagInput] = useState(""); @@ -352,7 +352,7 @@ function LivestreamPanel({ scrollable = true }: { scrollable?: boolean }) { if (livestream.record.activity) { setActivity( livestream.record - .activity as PlaceStreamLivestream.Record["activity"], + .activity as place.stream.livestream.Main["activity"], ); } @@ -509,7 +509,7 @@ function LivestreamPanel({ scrollable = true }: { scrollable?: boolean }) { try { const did = livestream.uri.split("/")[2]; - const cid = (livestream.record.thumb.ref as any).$link; + const cid = (livestream.record.thumb as any).ref.$link; const didDoc = await resolveDIDDocument(did); const blob = await getBlob(did, cid, didDoc); diff --git a/js/app/components/live-dashboard/multistream-status.tsx b/js/app/components/live-dashboard/multistream-status.tsx index a7b7405d3..03584611d 100644 --- a/js/app/components/live-dashboard/multistream-status.tsx +++ b/js/app/components/live-dashboard/multistream-status.tsx @@ -9,12 +9,12 @@ import Animated, { withRepeat, withTiming, } from "react-native-reanimated"; -import { PlaceStreamMultistreamDefs } from "streamplace"; +import { place } from "streamplace"; const { flex, p, gap, layout, bg, borders, text, r } = zero; interface MultistreamTargetViewHydrated - extends PlaceStreamMultistreamDefs.TargetView { + extends place.stream.multistream.defs.TargetView { record: any; } @@ -51,10 +51,11 @@ export default function MultistreamStatus() { try { setLoading(true); - const targetViews = await agent.place.stream.multistream.listTargets({ - limit: 50, - }); - setTargets(targetViews.data.targets as MultistreamTargetViewHydrated[]); + const targetViews = await agent.client.call( + place.stream.multistream.listTargets, + { limit: 50 }, + ); + setTargets(targetViews.targets as MultistreamTargetViewHydrated[]); } catch (error) { console.error("Failed to load multistream targets:", error); setTargets([]); @@ -68,7 +69,7 @@ export default function MultistreamStatus() { if (!agent) return; try { setTogglingTargets((prev) => new Set(prev).add(target.uri)); - await agent.place.stream.multistream.putTarget({ + await agent.client.call(place.stream.multistream.putTarget, { multistreamTarget: { ...target.record, active: newActiveState, diff --git a/js/app/components/live-dashboard/stream-key.tsx b/js/app/components/live-dashboard/stream-key.tsx index 0a35447b6..98f5f99e6 100644 --- a/js/app/components/live-dashboard/stream-key.tsx +++ b/js/app/components/live-dashboard/stream-key.tsx @@ -18,7 +18,7 @@ import { useEffect, useState } from "react"; import { ScrollView, TextInput } from "react-native"; import { useStore } from "store"; import { useIsReady, useUserProfile } from "store/hooks"; -import { PlaceStreamIngestDefs } from "streamplace"; +import { place } from "streamplace"; const FormRow = ({ children }: { children: React.ReactNode }) => { return ( @@ -45,7 +45,7 @@ const Content = ({ children }: { children: React.ReactNode }) => { }; export function StreamKeyScreen() { - const [ingest, setIngest] = useState( + const [ingest, setIngest] = useState( null, ); const isReady = useIsReady(); diff --git a/js/app/components/mobile/badge-picker.tsx b/js/app/components/mobile/badge-picker.tsx index 2fa2a86f7..594afc871 100644 --- a/js/app/components/mobile/badge-picker.tsx +++ b/js/app/components/mobile/badge-picker.tsx @@ -28,7 +28,7 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { ActivityIndicator, TouchableOpacity, View } from "react-native"; import { useStore } from "store"; import { useUserProfile } from "store/hooks"; -import type { PlaceStreamBadgeDefs } from "streamplace"; +import { place } from "streamplace"; const { gap, layout } = zero; @@ -69,10 +69,10 @@ export function BadgePicker() { if (!agent?.did || badgeSlots !== null) return; try { setLoading(true); - const res = await agent.place.stream.badge.getIssuedBadges({ - streamer: streamerDid, + const res = await agent.client.call(place.stream.badge.getIssuedBadges, { + streamer: streamerDid as any, }); - setBadgeSlots({ streamer: res.data.streamer, user: res.data.user }); + setBadgeSlots({ streamer: res.streamer, user: res.user }); } catch (e: any) { toast.show("Failed to load badges", e?.message, { variant: "error" }); } finally { @@ -86,7 +86,7 @@ export function BadgePicker() { const handleToggle = useCallback( async ( - badge: PlaceStreamBadgeDefs.BadgeIssuanceView, + badge: place.stream.badge.defs.BadgeIssuanceView, slot: "streamer" | "global", ) => { if (!agent?.did || togglingRef.current) return; @@ -100,13 +100,9 @@ export function BadgePicker() { let swapCid: string | undefined; try { - const getRes = await agent.com.atproto.repo.getRecord({ - repo: agent.did, - collection: "place.stream.chat.profile", - rkey: "self", - }); - currentRecord = getRes.data.value as Record; - swapCid = getRes.data.cid; + const getRes = await agent.client.get(place.stream.chat.profile); + currentRecord = getRes.value as Record; + swapCid = getRes.cid; } catch { // no profile yet, will create } @@ -138,13 +134,12 @@ export function BadgePicker() { }; } - await agent.com.atproto.repo.putRecord({ - repo: agent.did, - collection: "place.stream.chat.profile", - rkey: "self", - record: { ...currentRecord, badges: newBadges }, - swapRecord: swapCid, - }); + const { $type: _omitType, ...currentRecordRest } = currentRecord; + await agent.client.put( + place.stream.chat.profile, + { ...currentRecordRest, badges: newBadges } as any, + { swapRecord: swapCid }, + ); if (slot === "streamer") { setBadgeSlots({ @@ -346,7 +341,7 @@ function BadgeTriggerButton({ badge, loading, }: { - badge: PlaceStreamBadgeDefs.BadgeIssuanceView | null; + badge: place.stream.badge.defs.BadgeIssuanceView | null; loading: boolean; }) { const { theme } = zero.useTheme(); @@ -397,7 +392,7 @@ function BadgeCheckboxItem({ toggling, onToggle, }: { - badge: PlaceStreamBadgeDefs.BadgeIssuanceView; + badge: place.stream.badge.defs.BadgeIssuanceView; toggling: boolean; onToggle: () => void; }) { diff --git a/js/app/components/mobile/bottom-metadata.tsx b/js/app/components/mobile/bottom-metadata.tsx index 6f1d4e76e..7bde017f3 100644 --- a/js/app/components/mobile/bottom-metadata.tsx +++ b/js/app/components/mobile/bottom-metadata.tsx @@ -24,10 +24,7 @@ import FollowButton from "components/follow-button"; import { Image } from "expo-image"; import { ChevronLeft, ChevronRight } from "lucide-react-native"; import { Linking, Pressable, ScrollView, View } from "react-native"; -import type { - ActivityGame, - ActivityLabel, -} from "streamplace/src/lexicons/types/place/stream/defs"; +import type { place } from "streamplace"; import { LANG_TAG_PREFIX, LANGUAGES } from "../live-dashboard/livestream-panel"; import { KebabMenu } from "./desktop-ui/kebab"; @@ -97,7 +94,9 @@ export function BottomMetadata({ const { theme } = useTheme(); const avatarUri = useAvatar(); const activity = ls?.record.activity as - | ((ActivityGame | ActivityLabel) & { $type?: string }) + | ((place.stream.defs.ActivityGame | place.stream.defs.ActivityLabel) & { + $type?: string; + }) | undefined; const activityLabel = activity ? formatActivity(activity) : null; const rawTags = ls?.record.tags as string[] | undefined; diff --git a/js/app/components/name-color-picker/name-color-picker.tsx b/js/app/components/name-color-picker/name-color-picker.tsx index 966b7cf74..20e4f55b6 100644 --- a/js/app/components/name-color-picker/name-color-picker.tsx +++ b/js/app/components/name-color-picker/name-color-picker.tsx @@ -22,7 +22,7 @@ import ColorPicker, { } from "reanimated-color-picker"; import { useStore } from "store"; import { useChatProfile, useUserProfile } from "store/hooks"; -import { PlaceStreamChatProfile } from "streamplace"; +import { place } from "streamplace"; /** * Returns black or white depending on which contrasts better against the given color @@ -55,7 +55,7 @@ function getContrastColor(color: string): string { /** * Parses an RGB color string and returns an object with red, green, and blue values */ -function parseRgbString(rgbString: string): PlaceStreamChatProfile.Color { +function parseRgbString(rgbString: string): place.stream.chat.profile.Color { console.log(rgbString); if ( !rgbString || @@ -78,7 +78,7 @@ function parseRgbString(rgbString: string): PlaceStreamChatProfile.Color { }; } -function rgbToHex(rgb: PlaceStreamChatProfile.Color) { +function rgbToHex(rgb: place.stream.chat.profile.Color) { const hex = ( (1 << 24) + (rgb.red << 16) + diff --git a/js/app/components/settings/backup-settings.tsx b/js/app/components/settings/backup-settings.tsx index f1318640c..d53000702 100644 --- a/js/app/components/settings/backup-settings.tsx +++ b/js/app/components/settings/backup-settings.tsx @@ -16,6 +16,7 @@ import { usePDSAgent } from "@streamplace/components/src/streamplace-store/xrpc" import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { ScrollView } from "react-native"; +import { place } from "streamplace"; import { SettingToggle } from "./components/setting-toggle"; import { SettingsRowItem } from "./components/settings-navigation-item"; @@ -118,11 +119,11 @@ export function BackupSettings() { try { setLoading(true); - const response = await agent.place.stream.server.getStorage(); - if (response.data.storage) { - setOriginalUrl(response.data.storage.url); - setEnabled(response.data.storage.isActive); - const parsed = parseS3Url(response.data.storage.url); + const response = await agent.client.call(place.stream.server.getStorage); + if (response.storage) { + setOriginalUrl(response.storage.url); + setEnabled(response.storage.isActive); + const parsed = parseS3Url(response.storage.url); if (parsed) { setConfig(parsed); setFullUrl(buildS3Url(parsed, showPassword)); @@ -147,7 +148,9 @@ export function BackupSettings() { const previous = enabled; setEnabled(value); try { - await agent.place.stream.server.upsertStorage({ isActive: value }); + await agent.client.call(place.stream.server.upsertStorage, { + isActive: value, + }); } catch (err: any) { console.error("Failed to toggle backup:", err); setEnabled(previous); @@ -222,7 +225,7 @@ export function BackupSettings() { } } - await agent.place.stream.server.upsertStorage(payload); + await agent.client.call(place.stream.server.upsertStorage, payload); await loadStorage(); } catch (error: any) { console.error("Failed to save storage settings:", error); diff --git a/js/app/components/settings/badge-issuer-panel.tsx b/js/app/components/settings/badge-issuer-panel.tsx index c3992c42d..9b82960a5 100644 --- a/js/app/components/settings/badge-issuer-panel.tsx +++ b/js/app/components/settings/badge-issuer-panel.tsx @@ -1,4 +1,3 @@ -import { BlobRef } from "@atproto/lexicon"; import { Button, hexToRgba, @@ -26,7 +25,7 @@ import { ScrollView, TouchableOpacity, } from "react-native"; -import type { PlaceStreamBadgeDef } from "streamplace"; +import { place } from "streamplace"; const { gap, p, px, py, layout } = zero; @@ -44,7 +43,7 @@ type PanelView = "main" | "create" | "issue"; interface BadgeDefItem { uri: string; cid: string; - value: PlaceStreamBadgeDef.Record; + value: place.stream.badge.def.Main; } function getDidFromAtUri(uri: string) { @@ -75,7 +74,7 @@ function BadgeDefRow({ "https://cdn.bsky.app/img/feed_fullsize/plain/" + getDidFromAtUri(def.uri) + "/" + - def.value.image.ref.toString(), + (def.value.image as any).ref.toString(), }} style={{ width: 28, height: 28, borderRadius: radiusTokens.sm }} /> @@ -195,8 +194,8 @@ export function BadgeIssuerPanel() { if (!agent?.did) return; setLoadingDefs(true); try { - const res = await agent.place.stream.badge.def.list({ - repo: agent.did, + const res = await agent.client.list(place.stream.badge.def, { + repo: agent.did as any, limit: 100, }); setDefs( @@ -204,7 +203,7 @@ export function BadgeIssuerPanel() { res.records as { uri: string; cid: string; - value: PlaceStreamBadgeDef.Record; + value: place.stream.badge.def.Main; }[] ).map(({ uri, cid, value }) => ({ uri, cid: cid ?? "", value })), ); @@ -256,24 +255,21 @@ export function BadgeIssuerPanel() { if (!agent?.did || !createName.trim() || working) return; setWorking(true); try { - let imageBlob: BlobRef | undefined; + let imageBlob: place.stream.badge.def.Main["image"]; if (createImageBlob) { - const uploaded = await agent.uploadBlob(createImageBlob, { - encoding: createImageBlob.type, + const uploaded = await agent.client.uploadBlob(createImageBlob, { + encoding: createImageBlob.type as any, }); - imageBlob = uploaded.data.blob; + imageBlob = uploaded.body.blob; } - await agent.place.stream.badge.def.create( - { repo: agent.did }, - { - name: createName.trim(), - description: createDescription.trim() || undefined, - badgeType: createBadgeType, - image: imageBlob, - createdAt: new Date().toISOString(), - }, - ); + await agent.client.create(place.stream.badge.def, { + name: createName.trim(), + description: createDescription.trim() || undefined, + badgeType: createBadgeType, + image: imageBlob, + createdAt: new Date().toISOString(), + } as any); setLastResult({ label: t("issue-badges-definition-created"), @@ -301,14 +297,11 @@ export function BadgeIssuerPanel() { if (!agent?.did || !selectedDef || !recipientDid.trim() || working) return; setWorking(true); try { - await agent.place.stream.badge.issuance.create( - { repo: agent.did }, - { - did: recipientDid.trim(), - badge: { uri: selectedDef.uri, cid: selectedDef.cid }, - createdAt: new Date().toISOString(), - }, - ); + await agent.client.create(place.stream.badge.issuance, { + did: recipientDid.trim(), + badge: { uri: selectedDef.uri, cid: selectedDef.cid }, + createdAt: new Date().toISOString(), + } as any); setLastResult({ label: t("issue-badges-issued-to", { did: recipientDid.trim() }), uri: recipientDid.trim(), diff --git a/js/app/components/settings/badge-selection-manager.tsx b/js/app/components/settings/badge-selection-manager.tsx index c3f75b135..c674d328b 100644 --- a/js/app/components/settings/badge-selection-manager.tsx +++ b/js/app/components/settings/badge-selection-manager.tsx @@ -24,7 +24,7 @@ import { TouchableOpacity, View, } from "react-native"; -import type { PlaceStreamBadgeDefs } from "streamplace"; +import { place } from "streamplace"; const { gap, p, px, py, layout, w } = zero; @@ -33,8 +33,8 @@ function BadgeIssuanceRow({ onToggle, toggling, }: { - badge: PlaceStreamBadgeDefs.BadgeIssuanceView; - onToggle: (badge: PlaceStreamBadgeDefs.BadgeIssuanceView) => void; + badge: place.stream.badge.defs.BadgeIssuanceView; + onToggle: (badge: place.stream.badge.defs.BadgeIssuanceView) => void; toggling: boolean; }) { const { theme } = zero.useTheme(); @@ -130,9 +130,9 @@ export function BadgeSelectionManager() { const [loading, setLoading] = useState(true); const [streamerSlot, setStreamerSlot] = - useState(null); + useState(null); const [userSlot, setUserSlot] = - useState(null); + useState(null); const [toggling, setToggling] = useState(null); const togglingRef = useRef(null); @@ -140,9 +140,12 @@ export function BadgeSelectionManager() { if (!agent) return; try { setLoading(true); - const res = await agent.place.stream.badge.getIssuedBadges({}); - setStreamerSlot(res.data.streamer); - setUserSlot(res.data.user); + const res = await agent.client.call( + place.stream.badge.getIssuedBadges, + {}, + ); + setStreamerSlot(res.streamer); + setUserSlot(res.user); } catch (e: any) { toast.show(t("badges-failed-load"), e?.message, { variant: "error" }); } finally { @@ -156,7 +159,7 @@ export function BadgeSelectionManager() { const handleToggle = useCallback( async ( - badge: PlaceStreamBadgeDefs.BadgeIssuanceView, + badge: place.stream.badge.defs.BadgeIssuanceView, slot: "streamer" | "global", ) => { if (!agent?.did || togglingRef.current) return; @@ -170,13 +173,9 @@ export function BadgeSelectionManager() { let swapCid: string | undefined; try { - const getRes = await agent.com.atproto.repo.getRecord({ - repo: agent.did, - collection: "place.stream.chat.profile", - rkey: "self", - }); - currentRecord = getRes.data.value as Record; - swapCid = getRes.data.cid; + const getRes = await agent.client.get(place.stream.chat.profile); + currentRecord = getRes.value as Record; + swapCid = getRes.cid; } catch { // no profile yet, will create } @@ -208,13 +207,12 @@ export function BadgeSelectionManager() { }; } - await agent.com.atproto.repo.putRecord({ - repo: agent.did, - collection: "place.stream.chat.profile", - rkey: "self", - record: { ...currentRecord, badges: newBadges }, - swapRecord: swapCid, - }); + const { $type: _omitType, ...currentRecordRest } = currentRecord; + await agent.client.put( + place.stream.chat.profile, + { ...currentRecordRest, badges: newBadges } as any, + { swapRecord: swapCid }, + ); // Optimistically update local state if (slot === "streamer") { diff --git a/js/app/components/settings/branding-admin.tsx b/js/app/components/settings/branding-admin.tsx index 44dc21feb..3d00d3a68 100644 --- a/js/app/components/settings/branding-admin.tsx +++ b/js/app/components/settings/branding-admin.tsx @@ -23,6 +23,7 @@ import { usePDSAgent } from "@streamplace/components/src/streamplace-store/xrpc" import { Image } from "expo-image"; import { useEffect, useState } from "react"; import { ActivityIndicator, Platform, ScrollView } from "react-native"; +import { place } from "streamplace"; import { SettingsRowItem } from "./components/settings-navigation-item"; export function BrandingAdmin() { @@ -93,9 +94,9 @@ export function BrandingAdmin() { const textBytes = new TextEncoder().encode(value.trim()); const base64Data = btoa(String.fromCharCode(...textBytes)); - await agent.place.stream.branding.updateBlob({ + await agent.client.call(place.stream.branding.updateBlob, { key, - broadcaster: broadcasterDID || undefined, + broadcaster: (broadcasterDID || undefined) as any, data: base64Data, mimeType: "text/plain", }); @@ -183,9 +184,9 @@ export function BrandingAdmin() { }); } - await agent.place.stream.branding.updateBlob({ + await agent.client.call(place.stream.branding.updateBlob, { key, - broadcaster: broadcasterDID || undefined, + broadcaster: (broadcasterDID || undefined) as any, data: base64Data, mimeType: file.type, width, @@ -251,9 +252,9 @@ export function BrandingAdmin() { setUploading(true); try { - await agent.place.stream.branding.deleteBlob({ + await agent.client.call(place.stream.branding.deleteBlob, { key, - broadcaster: broadcasterDID || undefined, + broadcaster: (broadcasterDID || undefined) as any, }); toast.show( diff --git a/js/app/components/settings/key-manager.tsx b/js/app/components/settings/key-manager.tsx index dfb96b2f5..e5fe49fc3 100644 --- a/js/app/components/settings/key-manager.tsx +++ b/js/app/components/settings/key-manager.tsx @@ -10,7 +10,7 @@ import { } from "react-native"; import { useStore } from "store"; import { useKeyRecords } from "store/hooks"; -import { PlaceStreamKey } from "streamplace"; +import { place } from "streamplace"; import { timeAgo } from "utils/timeAgo"; import { Text, zero } from "@streamplace/components"; @@ -24,7 +24,7 @@ function KeyRow({ deleteKeyRecord, isDeleting, }: { - keyRecord: PlaceStreamKey.Record; + keyRecord: place.stream.key.Main; rkey: string; deleteKeyRecord: (rkey: string) => void; isDeleting: boolean; diff --git a/js/app/components/settings/multistream-manager.tsx b/js/app/components/settings/multistream-manager.tsx index 864490cef..efeda0606 100644 --- a/js/app/components/settings/multistream-manager.tsx +++ b/js/app/components/settings/multistream-manager.tsx @@ -14,16 +14,13 @@ import Loading from "components/loading/loading"; import { Plus, RefreshCw } from "lucide-react-native"; import { useEffect, useState } from "react"; import { Alert, ScrollView, Switch, View } from "react-native"; -import { - PlaceStreamMultistreamDefs, - PlaceStreamMultistreamTarget, -} from "streamplace"; +import { place } from "streamplace"; import { timeAgo } from "utils/timeAgo"; import { SettingsListItem } from "./settings-list-item"; interface MultistreamTargetViewHydrated - extends PlaceStreamMultistreamDefs.TargetView { - record: PlaceStreamMultistreamTarget.Record; + extends place.stream.multistream.defs.TargetView { + record: place.stream.multistream.target.Main; } const redactMultistreamTargetURL = (url: string) => { @@ -87,10 +84,11 @@ export default function MultistreamManager() { try { setLoading(true); - const targetViews = await agent.place.stream.multistream.listTargets({ - limit: 50, - }); - setTargets(targetViews.data.targets as MultistreamTargetViewHydrated[]); + const targetViews = await agent.client.call( + place.stream.multistream.listTargets, + { limit: 50 }, + ); + setTargets(targetViews.targets as MultistreamTargetViewHydrated[]); } catch (error) { console.error("Failed to load multistream targets:", error); Alert.alert("Error", t("failed-load-multistream-targets")); @@ -100,16 +98,16 @@ export default function MultistreamManager() { }; const createMultistreamTarget = async ( - record: PlaceStreamMultistreamTarget.Record, + record: place.stream.multistream.target.Main, ) => { if (!agent) return; try { setFormError(""); setFormLoading(true); - await agent.place.stream.multistream.createTarget({ + await agent.client.call(place.stream.multistream.createTarget, { multistreamTarget: { ...record, - createdAt: new Date().toISOString(), + createdAt: new Date().toISOString() as any, }, }); setShowForm(false); @@ -124,13 +122,13 @@ export default function MultistreamManager() { const editMultistreamTarget = async ( uri: string, - record: PlaceStreamMultistreamTarget.Record, + record: place.stream.multistream.target.Main, ) => { if (!agent) return; try { setFormError(""); setFormLoading(true); - await agent.place.stream.multistream.putTarget({ + await agent.client.call(place.stream.multistream.putTarget, { multistreamTarget: record, rkey: uri.split("/").pop() || "", }); @@ -152,7 +150,7 @@ export default function MultistreamManager() { if (!agent) return; try { setTogglingTargets((prev) => new Set(prev).add(target.uri)); - await agent.place.stream.multistream.putTarget({ + await agent.client.call(place.stream.multistream.putTarget, { multistreamTarget: { ...target.record, active: newActiveState, @@ -177,7 +175,7 @@ export default function MultistreamManager() { try { setFormError(""); setDeletingTargets((prev) => new Set(prev).add(uri)); - await agent.place.stream.multistream.deleteTarget({ + await agent.client.call(place.stream.multistream.deleteTarget, { rkey: uri.split("/").pop() || "", }); setShowForm(false); @@ -304,7 +302,7 @@ export default function MultistreamManager() { onClose={() => { setShowForm(false); }} - onSubmit={(record: PlaceStreamMultistreamTarget.Record) => { + onSubmit={(record: place.stream.multistream.target.Main) => { if (editingTarget) { editMultistreamTarget(editingTarget.uri, record); } else { @@ -398,20 +396,19 @@ function MultistreamTargetForm({ target?: MultistreamTargetViewHydrated; isVisible: boolean; onClose: () => void; - onSubmit: (record: PlaceStreamMultistreamTarget.Record) => void; + onSubmit: (record: place.stream.multistream.target.Main) => void; isLoading: boolean; formError: string; }) { const { t } = useTranslation("settings"); - const [formData, setFormData] = useState( - { + const [formData, setFormData] = + useState({ $type: "place.stream.multistream.target", name: target?.record.name || "", url: target?.record.url || "", active: target?.record.active ?? true, createdAt: target?.record.createdAt || "", - }, - ); + } as any); const [errors, setErrors] = useState>({}); const [changedTargetUrl, setChangedTargetUrl] = useState(false); @@ -436,7 +433,7 @@ function MultistreamTargetForm({ url: "", active: true, createdAt: "", - }); + } as any); } }, [target, isVisible]); @@ -459,7 +456,7 @@ function MultistreamTargetForm({ } }; - let displayUrl = formData.url; + let displayUrl: string = formData.url; if (target && !changedTargetUrl) { displayUrl = ""; } @@ -502,10 +499,13 @@ function MultistreamTargetForm({ value={displayUrl} onChangeText={(text) => { setChangedTargetUrl(true); - setFormData((prev) => ({ - ...prev, - url: text.trim().replaceAll(/\n/g, ""), - })); + setFormData( + (prev) => + ({ + ...prev, + url: text.trim().replaceAll(/\n/g, ""), + }) as any, + ); }} placeholder={ target diff --git a/js/app/components/settings/recommendations-manager.tsx b/js/app/components/settings/recommendations-manager.tsx index 053760462..c129c7d21 100644 --- a/js/app/components/settings/recommendations-manager.tsx +++ b/js/app/components/settings/recommendations-manager.tsx @@ -27,6 +27,7 @@ import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Pressable, ScrollView, View } from "react-native"; import Sortable from "react-native-sortables"; +import { place } from "streamplace"; import { SettingsRowItem } from "./components/settings-navigation-item"; const { text, mt, mb, px, py, w, layout, gap, r, p } = zero; @@ -75,29 +76,20 @@ export default function RecommendationsManager() { } // Get the record directly from the PDS for editing - const response = await agent.com.atproto.repo.getRecord({ - repo: userDID, - collection: "place.stream.live.recommendations", - rkey: "self", - }); - // todo: type this right - let record = response.data.value as any; - - if (!response.success) { + let record: any; + try { + const { value } = await agent.client.get( + place.stream.live.recommendations, + ); + record = value; + } catch { // Create a new empty record if not found - const res = await agent.com.atproto.repo.createRecord({ - repo: userDID, - collection: "place.stream.live.recommendations", - record: { - streamers: [], - createdAt: new Date().toISOString(), - }, - }); - if (!res.success) { - throw new Error("Failed to create recommendations record"); - } - record = res.data; + await agent.client.create(place.stream.live.recommendations, { + streamers: [], + createdAt: new Date().toISOString(), + } as any); + record = { streamers: [] }; } setStreamers(record.streamers || []); } catch (error: any) { @@ -120,16 +112,11 @@ export default function RecommendationsManager() { } setSaving(true); - // Use putRecord to create or update the record - await agent.com.atproto.repo.putRecord({ - repo: agent.did, - collection: "place.stream.live.recommendations", - rkey: "self", - record: { - streamers: newStreamers, - createdAt: new Date().toISOString(), - }, - }); + // Use put to create or update the record + await agent.client.put(place.stream.live.recommendations, { + streamers: newStreamers, + createdAt: new Date().toISOString(), + } as any); setStreamers(newStreamers); } catch (error: any) { @@ -154,13 +141,16 @@ export default function RecommendationsManager() { try { setSearching(true); - const response = await agent.place.stream.live.searchActorsTypeahead({ - q: query, - limit: 10, - }); + const response = await agent.client.call( + place.stream.live.searchActorsTypeahead, + { + q: query, + limit: 10, + }, + ); setSearchResults( - response.data.actors.map((actor: any) => ({ + response.actors.map((actor: any) => ({ did: actor.did, handle: actor.handle, })), diff --git a/js/app/components/settings/webhook-manager.tsx b/js/app/components/settings/webhook-manager.tsx index c7a7ab551..8e3cfcaf9 100644 --- a/js/app/components/settings/webhook-manager.tsx +++ b/js/app/components/settings/webhook-manager.tsx @@ -17,6 +17,7 @@ import { Edit2, Plus, RefreshCw, Trash2, X } from "lucide-react-native"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Alert, Pressable, ScrollView, Switch, View } from "react-native"; +import { place } from "streamplace"; import { SettingsRowItem } from "./components/settings-navigation-item"; const { @@ -653,19 +654,22 @@ export default function WebhookManager() { try { setLoading(true); - const response = await agent.place.stream.server.listWebhooks({ - limit: 50, - }); + const response = await agent.client.call( + place.stream.server.listWebhooks, + { + limit: 50, + }, + ); // Filter out unknown event types returned by the server. // todo: find a better way to check this - if (response.data.webhooks) { - for (const webhook of response.data.webhooks) { + if (response.webhooks) { + for (const webhook of response.webhooks) { webhook.events = (webhook.events as string[]).filter((event) => VALID_WEBHOOK_EVENTS.includes(event as WebhookEvent), ) as WebhookEvent[]; } } - setWebhooks((response.data.webhooks as any) || []); + setWebhooks((response.webhooks as any) || []); } catch (error) { console.error("Failed to load webhooks:", error); Alert.alert("Error", "Failed to load webhooks. Please try again."); @@ -685,9 +689,9 @@ export default function WebhookManager() { (r) => r.from.trim() && r.to.trim(), ); - await agent.place.stream.server.createWebhook({ + await agent.client.call(place.stream.server.createWebhook, { name: data.name || undefined, - url: data.url, + url: data.url as any, events: data.events as WebhookEvent[], active: data.active, prefix: data.prefix || undefined, @@ -721,10 +725,10 @@ export default function WebhookManager() { (r) => r.from.trim() && r.to.trim(), ); - await agent.place.stream.server.updateWebhook({ + await agent.client.call(place.stream.server.updateWebhook, { id: editingWebhook.id, name: data.name || undefined, - url: data.url, + url: data.url as any, events: data.events as WebhookEvent[], active: data.active, prefix: data.prefix || undefined, @@ -761,7 +765,7 @@ export default function WebhookManager() { try { setDeletingWebhooks((prev) => new Set(prev).add(id)); - await agent.place.stream.server.deleteWebhook({ id }); + await agent.client.call(place.stream.server.deleteWebhook, { id }); await loadWebhooks(); setDeleteDialog({ isVisible: false, webhook: null }); } catch (error: any) { diff --git a/js/app/components/video/video-card.tsx b/js/app/components/video/video-card.tsx index e6dd70584..ca6baad07 100644 --- a/js/app/components/video/video-card.tsx +++ b/js/app/components/video/video-card.tsx @@ -2,7 +2,7 @@ import { Text, useTheme, zero } from "@streamplace/components"; import { Image } from "expo-image"; import { VideoView } from "hooks/useVideoList"; import { View } from "react-native"; -import { PlaceStreamVideo } from "streamplace"; +import { place } from "streamplace"; import { formatDuration, getTidFromAtUri, @@ -28,7 +28,7 @@ export default function VideoCard({ avatarUrl?: string; }) { const { theme } = useTheme(); - const record = video.record as PlaceStreamVideo.Record; + const record = video.record as place.stream.video.Main; const author = video.author; const user = author.handle || author.did; const tid = getTidFromAtUri(video.uri); diff --git a/js/app/hooks/useLiveUser.tsx b/js/app/hooks/useLiveUser.tsx index cdc287390..fe8c06b12 100644 --- a/js/app/hooks/useLiveUser.tsx +++ b/js/app/hooks/useLiveUser.tsx @@ -1,5 +1,5 @@ import { useStore } from "store"; -import { PlaceStreamSegment } from "streamplace"; +import { place } from "streamplace"; // composite selector that tells us when the current user is live export const useLiveUser = (): boolean => { @@ -7,10 +7,10 @@ export const useLiveUser = (): boolean => { if (mySegments.length === 0) { return false; } - if (!PlaceStreamSegment.isRecord(mySegments[0].record)) { + if (!place.stream.segment.$isTypeOf(mySegments[0].record)) { return false; } - const record = mySegments[0].record as PlaceStreamSegment.Record; + const record = mySegments[0].record as place.stream.segment.Main; if (Date.now() - new Date(record.startTime).getTime() < 1000 * 10) { return true; } diff --git a/js/app/hooks/useVideoList.ts b/js/app/hooks/useVideoList.ts index aff376699..8f72b3952 100644 --- a/js/app/hooks/useVideoList.ts +++ b/js/app/hooks/useVideoList.ts @@ -1,8 +1,8 @@ import { usePossiblyUnauthedPDSAgent } from "@streamplace/components"; import { useCallback, useEffect, useRef, useState } from "react"; -import { PlaceStreamMediaGetVideo } from "streamplace"; +import { place } from "streamplace"; -export type VideoView = PlaceStreamMediaGetVideo.VideoView; +export type VideoView = place.stream.media.getVideo.VideoView; const PAGE_SIZE = 24; @@ -35,15 +35,15 @@ export function useVideoList(repo?: string) { } setError(null); try { - const res = await agent.place.stream.media.getVideoList({ + const res = await agent.client.call(place.stream.media.getVideoList, { ...(repo ? { repo } : {}), limit: PAGE_SIZE, ...(nextCursor ? { cursor: nextCursor } : {}), }); - const page = (res.data.videos ?? []) as VideoView[]; + const page = (res.videos ?? []) as VideoView[]; setVideos((prev) => (replace ? page : [...prev, ...page])); - setCursor(res.data.cursor); - setHasMore(Boolean(res.data.cursor) && page.length > 0); + setCursor(res.cursor); + setHasMore(Boolean(res.cursor) && page.length > 0); } catch (e: any) { console.error("error fetching video list", e); setError(e?.message ?? "failed to load videos"); diff --git a/js/app/metro.config.js b/js/app/metro.config.js index 897c993ce..ef35682ca 100644 --- a/js/app/metro.config.js +++ b/js/app/metro.config.js @@ -50,7 +50,18 @@ config.resolver.resolveRequest = (context, moduleName, platform) => { } } // otherwise chain to the standard Metro resolver. - return context.resolveRequest(context, moduleName, platform); + try { + return context.resolveRequest(context, moduleName, platform); + } catch (error) { + // The `streamplace` workspace package is ESM and (per Node's ESM rules) + // uses explicit ".js" extensions on its relative imports even though the + // sources are ".ts"/".tsx" (e.g. the generated lexicon files). Metro does + // not perform that ".js" -> ".ts" remap, so retry extension-less. + if (moduleName.startsWith(".") && moduleName.endsWith(".js")) { + return context.resolveRequest(context, moduleName.slice(0, -3), platform); + } + throw error; + } }; config.resolver.sourceExts.push("mjs"); diff --git a/js/app/src/screens/home.tsx b/js/app/src/screens/home.tsx index c8f82cfc5..695d845ca 100644 --- a/js/app/src/screens/home.tsx +++ b/js/app/src/screens/home.tsx @@ -17,18 +17,18 @@ import useAvatars from "hooks/useAvatars"; import { useEffect, useState } from "react"; import { Platform, View, useWindowDimensions } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import { PlaceStreamDefs, PlaceStreamLivestream } from "streamplace"; +import { place } from "streamplace"; function getStreamActivity( - record: PlaceStreamLivestream.Record, + record: place.stream.livestream.Main, ): string | undefined { if (!record.activity) return undefined; if (record.activity.$type === "place.stream.defs#activityGame") { - const game = record.activity as PlaceStreamDefs.ActivityGame; + const game = record.activity as place.stream.defs.ActivityGame; return game.name ?? undefined; } if (record.activity.$type === "place.stream.defs#activityLabel") { - const label = record.activity as PlaceStreamDefs.ActivityLabel; + const label = record.activity as place.stream.defs.ActivityLabel; return ACTIVITY_LABEL_DISPLAY[label.label] ?? label.label; } return undefined; @@ -53,9 +53,9 @@ type StreamRecord = { // Function to generate mock data for testing purposes function generateMockSegments(count: number): { - streams: PlaceStreamLivestream.LivestreamView[]; + streams: place.stream.livestream.LivestreamView[]; } { - const mockSegments: PlaceStreamLivestream.LivestreamView[] = []; + const mockSegments: place.stream.livestream.LivestreamView[] = []; const baseDid = "did:plc:mockmockmockmockmockmockmockmockmock"; for (let i = 0; i < count; i++) { @@ -68,14 +68,14 @@ function generateMockSegments(count: number): { $type: "place.stream.livestream", createdAt: new Date().toISOString(), title: `Mock Stream ${i + 1}`, - } as PlaceStreamLivestream.Record, + } as place.stream.livestream.Main, author: { did: did, handle: handle, }, indexedAt: new Date().toISOString(), viewerCount: { count: Math.floor(Math.random() * 1000) }, - }); + } as any); } return { streams: mockSegments }; } @@ -109,7 +109,7 @@ function HomeScreenItem({ horizontal = false, showAvatar = true, }: { - item: PlaceStreamLivestream.LivestreamView; + item: place.stream.livestream.LivestreamView; size: StreamCardSize; avatarUrl?: string; horizontal?: boolean; @@ -131,7 +131,7 @@ function HomeScreenItem({ @@ -227,7 +227,7 @@ export default function HomeScreen({ const firstRowItems = segments.slice(0, firstRowCols); let cutSegs = segments.slice(firstRowCols); - let rows: (PlaceStreamLivestream.LivestreamView | null)[][] = []; + let rows: (place.stream.livestream.LivestreamView | null)[][] = []; if (!useHorizontalAll) { for (let i = 0; i < cutSegs.length; i += cols) { diff --git a/js/app/src/screens/upload.tsx b/js/app/src/screens/upload.tsx index 23341cb4b..1138e1366 100644 --- a/js/app/src/screens/upload.tsx +++ b/js/app/src/screens/upload.tsx @@ -44,11 +44,7 @@ import { } from "react-native"; import { useStore } from "store"; import { useIsReady, useUserProfile } from "store/hooks"; -import type { - PlaceStreamLivestream, - PlaceStreamVodDraftDefs, - PlaceStreamVodDraftVideo, -} from "streamplace"; +import { place } from "streamplace"; import * as tus from "tus-js-client"; // ── types ──────────────────────────────────────────────────────────────────── @@ -79,7 +75,7 @@ type UploadPhase = type MetadataState = { title: string; description: string; - activity: PlaceStreamLivestream.Record["activity"]; + activity: place.stream.livestream.Main["activity"]; tags: string[]; tagInput: string; thumbnail: Blob | undefined; @@ -731,19 +727,20 @@ export default function UploadScreen() { // upload. The user lands on the draft editor immediately and can edit // metadata while the upload runs; a failed upload leaves the draft // intact so they can re-upload against the same draft. - const draftRes = await agent.place.stream.vod.createDraft({}); - if (!draftRes.success) throw new Error("createDraft failed"); - const draftUri = draftRes.data.uri; + const draftRes = await agent.client.call( + place.stream.vod.createDraft, + {}, + ); + const draftUri = draftRes.uri; const tid = tidFromUri(draftUri); - const res = await agent.place.stream.media.createUpload({ + const res = await agent.client.call(place.stream.media.createUpload, { size: file.size, mimeType, filename: file.name, draftUri, }); - if (!res.success) throw new Error("createUpload failed"); - const { uploadUrl, uploadToken } = res.data; + const { uploadUrl, uploadToken } = res; // Navigate to the draft editor now — the upload continues in the // background and fills this draft when it finishes processing. @@ -1146,7 +1143,7 @@ export function UploadVideoScreen({ route }: { route: any }) { const [draftUri_, setDraftUri] = useState(undefined); const [videoUri_, setVideoUri] = useState(undefined); const [draftStatus, setDraftStatus] = useState< - PlaceStreamVodDraftVideo.Main["status"] | undefined + place.stream.vod.draftVideo.Main["status"] | undefined >(undefined); const [meta, setMetaState] = useState(DEFAULT_METADATA); const [updating, setUpdating] = useState(false); @@ -1179,9 +1176,11 @@ export function UploadVideoScreen({ route }: { route: any }) { const did = agent.did!; const duri = draftUri(did, tid); try { - const res = await agent.place.stream.vod.getDraft({ uri: duri }); + const res = await agent.client.call(place.stream.vod.getDraft, { + uri: duri, + }); if (cancelled) return; - const rec = res.data.draft.record as PlaceStreamVodDraftVideo.Main; + const rec = res.draft.record as place.stream.vod.draftVideo.Main; setMode("draft"); setDraftUri(duri); setVideoUri(undefined); @@ -1194,7 +1193,7 @@ export function UploadVideoScreen({ route }: { route: any }) { tagInput: "", thumbnail: undefined, thumbnailUrl: rec.thumb - ? `https://cdn.stream.place/thumb/${(rec.thumb.ref as any)?.$link || (rec.thumb as any).cid || ""}` + ? `https://cdn.stream.place/thumb/${(rec.thumb as any).ref?.$link || (rec.thumb as any).cid || ""}` : undefined, warnings: new Set(rec.contentWarnings?.warnings || []), license: @@ -1208,13 +1207,11 @@ export function UploadVideoScreen({ route }: { route: any }) { // NotFound → fall back to published video const vuri = videoUri(did, tid); try { - const existing = await agent.com.atproto.repo.getRecord({ - repo: did, - collection: "place.stream.video", - rkey: tid, + const existing = await agent.client.get(place.stream.video, { + rkey: tid as any, }); if (cancelled) return; - const rec = existing.data.value as any; + const rec = existing.value as any; setMode("video"); setVideoUri(vuri); setDraftUri(undefined); @@ -1227,7 +1224,7 @@ export function UploadVideoScreen({ route }: { route: any }) { tagInput: "", thumbnail: undefined, thumbnailUrl: rec.thumb - ? `https://cdn.stream.place/thumb/${(rec.thumb.ref as any)?.$link || (rec.thumb as any).cid || ""}` + ? `https://cdn.stream.place/thumb/${(rec.thumb as any).ref?.$link || (rec.thumb as any).cid || ""}` : undefined, warnings: new Set(rec.contentWarnings?.warnings || []), license: @@ -1265,11 +1262,11 @@ export function UploadVideoScreen({ route }: { route: any }) { let cancelled = false; const poll = async () => { try { - const res = await agent.place.stream.vod.getDraft({ + const res = await agent.client.call(place.stream.vod.getDraft, { uri: draftUri(agent.did!, tid), }); if (cancelled) return; - const rec = res.data.draft.record as PlaceStreamVodDraftVideo.Main; + const rec = res.draft.record as place.stream.vod.draftVideo.Main; setDraftStatus(rec.status); } catch { // Transient fetch failures are fine; the next tick retries. @@ -1314,23 +1311,21 @@ export function UploadVideoScreen({ route }: { route: any }) { } if (meta.thumbnail) { try { - const blobRes = await agent.uploadBlob(meta.thumbnail, { - encoding: meta.thumbnail.type || "image/jpeg", + const blobRes = await agent.client.uploadBlob(meta.thumbnail, { + encoding: (meta.thumbnail.type || "image/jpeg") as any, }); - if (blobRes.success) params.thumb = blobRes.data.blob; + params.thumb = blobRes.body.blob; } catch { // thumbnail failure is non-fatal } } - await agent.place.stream.vod.updateDraft(params as any); + await agent.client.call(place.stream.vod.updateDraft, params as any); } else if (mode === "video") { // Published video: putRecord. Preserve source/duration/createdAt. - const existing = await agent.com.atproto.repo.getRecord({ - repo: agent.did, - collection: "place.stream.video", - rkey: tid, + const existing = await agent.client.get(place.stream.video, { + rkey: tid as any, }); - const existingRec = existing.data.value as any; + const existingRec = existing.value as any; const record: Record = { $type: "place.stream.video", title: meta.title.trim(), @@ -1360,19 +1355,17 @@ export function UploadVideoScreen({ route }: { route: any }) { } if (meta.thumbnail) { try { - const blobRes = await agent.uploadBlob(meta.thumbnail, { - encoding: meta.thumbnail.type || "image/jpeg", + const blobRes = await agent.client.uploadBlob(meta.thumbnail, { + encoding: (meta.thumbnail.type || "image/jpeg") as any, }); - if (blobRes.success) record.thumb = blobRes.data.blob; + record.thumb = blobRes.body.blob; } catch { // thumbnail is non-fatal } } - await agent.com.atproto.repo.putRecord({ - repo: agent.did, - collection: "place.stream.video", - rkey: tid, - record: record as any, + const { $type: _videoType, ...videoRecordInput } = record; + await agent.client.put(place.stream.video, videoRecordInput as any, { + rkey: tid as any, }); } } catch (err) { @@ -1388,11 +1381,11 @@ export function UploadVideoScreen({ route }: { route: any }) { setPublishing(true); setError(undefined); try { - const res = await agent.place.stream.vod.publishDraft({ + const res = await agent.client.call(place.stream.vod.publishDraft, { uri: draftUri_, }); // Navigate to the player route for the published video. - const publishedTid = tidFromUri(res.data.videoUri); + const publishedTid = tidFromUri(res.videoUri); navigation.navigate("Video" as any, { user: agent.did ?? "", tid: publishedTid, @@ -1411,13 +1404,11 @@ export function UploadVideoScreen({ route }: { route: any }) { setError(undefined); try { if (mode === "draft" && draftUri_) { - await agent.place.stream.vod.deleteDraft({ uri: draftUri_ }); - } else if (mode === "video") { - await agent.com.atproto.repo.deleteRecord({ - repo: agent.did, - collection: "place.stream.video", - rkey: tid, + await agent.client.call(place.stream.vod.deleteDraft, { + uri: draftUri_, }); + } else if (mode === "video") { + await agent.client.delete(place.stream.video, { rkey: tid as any }); } navigation.navigate("UploadVideos" as any); } catch (err) { @@ -1572,7 +1563,9 @@ export function UploadDraftsScreen() { const { theme } = useTheme(); const navigation = useNavigation(); - const [drafts, setDrafts] = useState([]); + const [drafts, setDrafts] = useState( + [], + ); const [draftsLoading, setDraftsLoading] = useState(false); const [draftAction, setDraftAction] = useState< Record @@ -1590,8 +1583,10 @@ export function UploadDraftsScreen() { if (!agent) return; setDraftsLoading(true); try { - const res = await agent.place.stream.vod.listDrafts({ limit: 100 }); - setDrafts(res.data.drafts || []); + const res = await agent.client.call(place.stream.vod.listDrafts, { + limit: 100, + }); + setDrafts(res.drafts || []); } catch (err) { console.error("Failed to fetch drafts", err); } finally { @@ -1608,8 +1603,10 @@ export function UploadDraftsScreen() { if (!agent) return; setDraftAction((m) => ({ ...m, [uri]: "publishing" })); try { - const res = await agent.place.stream.vod.publishDraft({ uri }); - setPublishedFromDrafts((m) => ({ ...m, [uri]: res.data.videoUri })); + const res = await agent.client.call(place.stream.vod.publishDraft, { + uri, + }); + setPublishedFromDrafts((m) => ({ ...m, [uri]: res.videoUri })); setDrafts((prev) => prev.filter((d) => d.uri !== uri)); } catch (err) { console.error("Failed to publish draft", err); @@ -1624,7 +1621,7 @@ export function UploadDraftsScreen() { if (!agent) return; setDraftAction((m) => ({ ...m, [uri]: "deleting" })); try { - await agent.place.stream.vod.deleteDraft({ uri }); + await agent.client.call(place.stream.vod.deleteDraft, { uri }); setDrafts((prev) => prev.filter((d) => d.uri !== uri)); } catch (err) { console.error("Failed to delete draft", err); @@ -1664,7 +1661,7 @@ export function UploadDraftsScreen() { )} {drafts.map((draft) => { - const rec = draft.record as PlaceStreamVodDraftVideo.Main; + const rec = draft.record as place.stream.vod.draftVideo.Main; const status = rec.status; const action = draftAction[draft.uri]; const publishedUri = publishedFromDrafts[draft.uri]; @@ -1826,10 +1823,10 @@ export function UploadLivestreamsScreen() { const fetchVideos = useCallback(async () => { if (!agent || !agent.did) return; try { - const res = await agent.place.stream.media.getVideoList({ + const res = await agent.client.call(place.stream.media.getVideoList, { repo: agent.did, }); - setUserVideos(res.data.videos || []); + setUserVideos(res.videos || []); } catch (err) { console.error("Failed to fetch videos", err); } @@ -1839,12 +1836,11 @@ export function UploadLivestreamsScreen() { if (!agent || !agent.did) return; setLivestreamsLoading(true); try { - const res = await agent.com.atproto.repo.listRecords({ - repo: agent.did, - collection: "place.stream.livestream", + const res = await agent.client.list(place.stream.livestream, { + repo: agent.did as any, limit: 100, }); - const records = (res.data.records ?? []).slice().sort((a, b) => { + const records = (res.records ?? []).slice().sort((a, b) => { const at = ((a.value as any)?.createdAt as string) ?? ""; const bt = ((b.value as any)?.createdAt as string) ?? ""; return bt.localeCompare(at); // newest first @@ -1896,16 +1892,18 @@ export function UploadLivestreamsScreen() { if (!agent || !agent.did) return; setFinalizing((m) => ({ ...m, [ls.uri]: "finalizing" })); try { - const res = await agent.place.stream.media.finalizeLivestream({ - livestream: ls.uri, - }); - if (!res.success) throw new Error("finalizeLivestream failed"); + const res = await agent.client.call( + place.stream.media.finalizeLivestream, + { + livestream: ls.uri as any, + }, + ); // Record the draft URI so the row can offer a deep-link to the // draft editor. - if (res.data.draftUri) { + if (res.draftUri) { setFinalizedDraftUris((m) => ({ ...m, - [ls.uri]: res.data.draftUri as string, + [ls.uri]: res.draftUri as string, })); } setFinalizing((m) => ({ ...m, [ls.uri]: "done" })); @@ -2082,10 +2080,10 @@ export function UploadVideosScreen() { const fetchVideos = useCallback(async () => { if (!agent || !agent.did) return; try { - const res = await agent.place.stream.media.getVideoList({ + const res = await agent.client.call(place.stream.media.getVideoList, { repo: agent.did, }); - setUserVideos(res.data.videos || []); + setUserVideos(res.videos || []); } catch (err) { console.error("Failed to fetch videos", err); } diff --git a/js/app/store/slices/blueskySlice.ts b/js/app/store/slices/blueskySlice.ts index a42092b0a..40ee76a7e 100644 --- a/js/app/store/slices/blueskySlice.ts +++ b/js/app/store/slices/blueskySlice.ts @@ -12,13 +12,7 @@ import { OAuthSession } from "@atproto/oauth-client"; import { storage } from "@streamplace/components"; import { Platform } from "react-native"; import { AppStore } from "store"; -import { - PlaceStreamChatProfile, - PlaceStreamKey, - PlaceStreamLivestream, - PlaceStreamServerSettings, - StreamplaceAgent, -} from "streamplace"; +import { place, StreamplaceAgent } from "streamplace"; import clearQueryParams from "utils/clear-query-params"; import { privateKeyToAccount } from "viem/accounts"; import { StateCreator } from "zustand"; @@ -30,7 +24,7 @@ import { DID_KEY, STORED_KEY_KEY, StreamKey } from "./baseSlice"; type NewLivestream = { loading: boolean; error: string | null; - record: PlaceStreamLivestream.Record | null; + record: place.stream.livestream.Main | null; }; export interface BlueskySlice { @@ -66,9 +60,9 @@ export interface BlueskySlice { chatProfile: { loading: boolean; error: null | string; - profile: null | PlaceStreamChatProfile.Record; + profile: null | place.stream.chat.profile.Main; }; - serverSettings: null | PlaceStreamServerSettings.Record; + serverSettings: null | place.stream.server.settings.Main; returnRoute: null | { name: string; params?: any }; notification: { message: string; @@ -109,13 +103,13 @@ export interface BlueskySlice { createLivestreamRecord: ( title: string, customThumbnail?: Blob, - activity?: PlaceStreamLivestream.Record["activity"], + activity?: place.stream.livestream.Main["activity"], tags?: string[], ) => Promise; updateLivestreamRecord: ( title: string, livestream: any, - activity?: PlaceStreamLivestream.Record["activity"], + activity?: place.stream.livestream.Main["activity"], tags?: string[], ) => Promise; getChatProfileRecordFromPDS: () => Promise; @@ -128,7 +122,7 @@ export interface BlueskySlice { unfollowUser: (subjectDID: string, followUri?: string) => Promise; getServerSettingsFromPDS: () => Promise; createServerSettingsRecord: ( - patch: Partial>, + patch: Partial>, ) => Promise; } @@ -665,17 +659,14 @@ export const createBlueskySlice: StateCreator< platform = "Windows"; } - const record: PlaceStreamKey.Record = { + const record: place.stream.key.Main = { $type: "place.stream.key", signingKey: keypair.did(), createdAt: new Date().toISOString(), createdBy: "Streamplace on " + platform, - }; - await state.pdsAgent.com.atproto.repo.createRecord({ - repo: did, - collection: "place.stream.key", - record, - }); + } as any; + const { $type: _keyType, ...keyRecordInput } = record; + await state.pdsAgent.client.create(place.stream.key, keyRecordInput); if (store) { await storage.setItem(STORED_KEY_KEY, JSON.stringify(newKey)); } @@ -713,9 +704,8 @@ export const createBlueskySlice: StateCreator< if (!profile) { throw new Error("No profile"); } - const result = await state.pdsAgent.com.atproto.repo.listRecords({ - repo: did, - collection: "place.stream.key", + const result = await state.pdsAgent.client.list(place.stream.key, { + repo: did as any, limit: 100, }); console.log(result); @@ -723,7 +713,7 @@ export const createBlueskySlice: StateCreator< streamKeysResponse: { loading: false, error: null, - records: result.data, + records: result as any, }, }); } catch (error) { @@ -753,10 +743,8 @@ export const createBlueskySlice: StateCreator< if (!profile) { throw new Error("No profile"); } - await state.pdsAgent.com.atproto.repo.deleteRecord({ - repo: did, - collection: "place.stream.key", - rkey, + await state.pdsAgent.client.delete(place.stream.key, { + rkey: rkey as any, }); let records = state.streamKeysResponse.records ? state.streamKeysResponse.records.records.filter( @@ -810,7 +798,7 @@ export const createBlueskySlice: StateCreator< createLivestreamRecord: async ( title: string, customThumbnail?: Blob, - activity?: PlaceStreamLivestream.Record["activity"], + activity?: place.stream.livestream.Main["activity"], tags?: string[], ) => { set({ @@ -901,7 +889,7 @@ export const createBlueskySlice: StateCreator< ); } - const record: PlaceStreamLivestream.Record = { + const record: place.stream.livestream.Main = { $type: "place.stream.livestream", title: title, url: streamplaceUrl, @@ -913,13 +901,13 @@ export const createBlueskySlice: StateCreator< thumb: thumbnail, activity, tags: tags && tags.length > 0 ? tags : undefined, - }; + } as any; - await state.pdsAgent.com.atproto.repo.createRecord({ - repo: did, - collection: "place.stream.livestream", - record, - }); + const { $type: _lsType, ...livestreamRecordInput } = record; + await state.pdsAgent.client.create( + place.stream.livestream, + livestreamRecordInput, + ); set({ newLivestream: { loading: false, @@ -942,7 +930,7 @@ export const createBlueskySlice: StateCreator< updateLivestreamRecord: async ( title: string, livestream: any, - activity?: PlaceStreamLivestream.Record["activity"], + activity?: place.stream.livestream.Main["activity"], tags?: string[], ) => { set({ @@ -974,7 +962,7 @@ export const createBlueskySlice: StateCreator< } let rkey = oldRecord.uri.split("/").pop(); - let oldRecordValue: PlaceStreamLivestream.Record = oldRecord.record; + let oldRecordValue: place.stream.livestream.Main = oldRecord.record; if (!rkey) { throw new Error("No rkey?"); @@ -983,7 +971,7 @@ export const createBlueskySlice: StateCreator< console.log("Updating rkey", rkey); const streamplaceUrl = get().url; - const record: PlaceStreamLivestream.Record = { + const record: place.stream.livestream.Main = { $type: "place.stream.livestream", title: title, url: streamplaceUrl, @@ -991,14 +979,14 @@ export const createBlueskySlice: StateCreator< post: oldRecordValue.post, activity, tags: tags && tags.length > 0 ? tags : undefined, - }; - - await state.pdsAgent.com.atproto.repo.putRecord({ - repo: did, - collection: "place.stream.livestream", - rkey, - record, - }); + } as any; + + const { $type: _lsType, ...livestreamRecordInput } = record; + await state.pdsAgent.client.put( + place.stream.livestream, + livestreamRecordInput, + { rkey: rkey as any }, + ); set({ newLivestream: { loading: false, @@ -1039,25 +1027,18 @@ export const createBlueskySlice: StateCreator< if (!state.pdsAgent) { throw new Error("No agent"); } - const res = await state.pdsAgent.com.atproto.repo.getRecord({ - repo: did, - collection: "place.stream.chat.profile", - rkey: "self", - }); - if (!res.success) { - throw new Error("Failed to get chat profile record"); - } + const res = await state.pdsAgent.client.get(place.stream.chat.profile); - if (PlaceStreamChatProfile.isRecord(res.data.value)) { + if (place.stream.chat.profile.$isTypeOf(res.value)) { set({ chatProfile: { loading: false, error: null, - profile: res.data.value, + profile: res.value, }, }); } else { - console.log("not a record", res.data.value); + console.log("not a record", res.value); } } catch (error) { console.error("getChatProfileRecordFromPDS error", error); @@ -1087,7 +1068,7 @@ export const createBlueskySlice: StateCreator< } const existingProfile = (get() as BlueskySlice).chatProfile?.profile; - const chatProfile: PlaceStreamChatProfile.Record = { + const chatProfile: place.stream.chat.profile.Main = { ...existingProfile, $type: "place.stream.chat.profile", color: { @@ -1095,17 +1076,13 @@ export const createBlueskySlice: StateCreator< green: green, blue: blue, }, - }; + } as any; - const res = await state.pdsAgent.com.atproto.repo.putRecord({ - repo: did, - collection: "place.stream.chat.profile", - record: chatProfile, - rkey: "self", - }); - if (!res.success) { - throw new Error("Failed to create chat profile record"); - } + const { $type: _cpType, ...chatProfileInput } = chatProfile; + await state.pdsAgent.client.put( + place.stream.chat.profile, + chatProfileInput, + ); set({ chatProfile: { loading: false, @@ -1196,21 +1173,19 @@ export const createBlueskySlice: StateCreator< } const streamplaceUrl = get().url; const u = new URL(streamplaceUrl); - const res = await state.pdsAgent.com.atproto.repo.getRecord({ - repo: did, - collection: "place.stream.server.settings", - rkey: u.host, - }); - if (!res.success) { - throw new Error("Failed to get chat profile record"); - } + const res = await state.pdsAgent.client.get( + place.stream.server.settings, + { + rkey: u.host, + }, + ); - if (PlaceStreamServerSettings.isRecord(res.data.value)) { + if (place.stream.server.settings.$isTypeOf(res.value)) { set({ - serverSettings: res.data.value as PlaceStreamServerSettings.Record, + serverSettings: res.value as place.stream.server.settings.Main, }); } else { - console.log("not a record", res.data.value); + console.log("not a record", res.value); } } catch (error) { console.error("getServerSettingsFromPDS rejected", error); @@ -1218,7 +1193,7 @@ export const createBlueskySlice: StateCreator< }, createServerSettingsRecord: async ( - patch: Partial>, + patch: Partial>, ) => { try { const state = get() as BlueskySlice; @@ -1237,21 +1212,18 @@ export const createBlueskySlice: StateCreator< const u = new URL(streamplaceUrl); // Merge the patch onto the current record so toggling one flag doesn't // clobber the others (the record holds several independent settings). - const serverSettings: PlaceStreamServerSettings.Record = { + const serverSettings: place.stream.server.settings.Main = { ...(state.serverSettings ?? {}), $type: "place.stream.server.settings", ...patch, - }; - - const res = await state.pdsAgent.com.atproto.repo.putRecord({ - repo: did, - collection: "place.stream.server.settings", - record: serverSettings, - rkey: u.host, - }); - if (!res.success) { - throw new Error("Failed to create server settings record"); - } + } as any; + + const { $type: _ssType, ...serverSettingsInput } = serverSettings; + await state.pdsAgent.client.put( + place.stream.server.settings, + serverSettingsInput, + { rkey: u.host }, + ); set({ serverSettings: serverSettings, }); diff --git a/js/app/store/slices/streamplaceSlice.ts b/js/app/store/slices/streamplaceSlice.ts index 434b8c0ac..c3110dfba 100644 --- a/js/app/store/slices/streamplaceSlice.ts +++ b/js/app/store/slices/streamplaceSlice.ts @@ -1,6 +1,6 @@ import { storage } from "@streamplace/components"; import { Platform } from "react-native"; -import type { PlaceStreamSegment } from "streamplace"; +import { place } from "streamplace"; import { StateCreator } from "zustand"; let DEFAULT_URL = process.env.EXPO_PUBLIC_STREAMPLACE_URL as string; @@ -30,7 +30,7 @@ export interface StreamplaceSlice { initialized: boolean; userMuted: boolean | null; chatWarned: boolean; - mySegments: PlaceStreamSegment.SegmentView[]; + mySegments: place.stream.segment.SegmentView[]; // actions initialize: () => Promise; setURL: (url: string) => void; @@ -115,10 +115,13 @@ export const createStreamplaceSlice: StateCreator = ( if (!state.oauthSession) { throw new Error("no oauthSession"); } - const result = await state.pdsAgent.place.stream.live.getSegments({ - userDID: state.oauthSession?.did ?? "", - }); - set({ mySegments: result.data.segments ?? [] }); + const result = await state.pdsAgent.client.call( + place.stream.live.getSegments, + { + userDID: state.oauthSession?.did ?? "", + }, + ); + set({ mySegments: result.segments ?? [] }); } catch (err) { // silently fail } @@ -128,9 +131,12 @@ export const createStreamplaceSlice: StateCreator = ( if (!state.pdsAgent) { throw new Error("no pdsAgent"); } - const result = await state.pdsAgent.place.stream.live.getRecommendations({ - userDID, - }); - return result.data; + const result = await state.pdsAgent.client.call( + place.stream.live.getRecommendations, + { + userDID, + }, + ); + return result; }, }); diff --git a/js/app/tsconfig.base.json b/js/app/tsconfig.base.json index 9d8b8dab8..d17582de7 100644 --- a/js/app/tsconfig.base.json +++ b/js/app/tsconfig.base.json @@ -2,6 +2,18 @@ "compilerOptions": { "baseUrl": ".", "rootDir": ".", + // The workspaces that consume the (now ESM) `streamplace` package and its + // ESM-only `@atproto/lex` dep set `moduleResolution: bundler` in their own + // tsconfig (js/app, js/components). Under bundler, tsc enforces each + // package's `exports` map, so a few workspace/vendor packages that only + // export `.` need a paths alias for the historical deep imports that + // Metro/web bundlers still resolve at runtime. See the lex codegen migration. + "paths": { + "@streamplace/components/src/*": ["../components/src/*"], + "@atproto/oauth-client/dist/*": [ + "../../node_modules/@atproto/oauth-client/dist/*" + ] + }, "importHelpers": true, "allowJs": false, "allowSyntheticDefaultImports": true, diff --git a/js/app/tsconfig.json b/js/app/tsconfig.json index 69689c83b..9d8294d9a 100644 --- a/js/app/tsconfig.json +++ b/js/app/tsconfig.json @@ -2,7 +2,11 @@ "extends": "./tsconfig.base", "compilerOptions": { "strict": true, - "rootDir": ".." + "rootDir": "..", + // js/app compiles the ESM `streamplace` package (and its ESM-only + // `@atproto/lex` dep) from source; bundler resolution reads their exports + // maps and .js->.ts import extensions without CJS/ESM interop errors. + "moduleResolution": "bundler" }, "include": ["**/*.ts", "**/*.tsx"] } diff --git a/js/app/utils/game.ts b/js/app/utils/game.ts index 58934d63b..b4629b8cf 100644 --- a/js/app/utils/game.ts +++ b/js/app/utils/game.ts @@ -1,16 +1,16 @@ -import { GamesGamesgamesgamesgamesDefs } from "streamplace"; +import { games } from "streamplace"; const COVER_MEDIA_TYPES: Set< - GamesGamesgamesgamesgamesDefs.MediaItem["mediaType"] + games.gamesgamesgamesgames.defs.MediaItem["mediaType"] > = new Set(["cover", "coverSquare"]); export function getGameCoverUrl( - media: GamesGamesgamesgamesgamesDefs.MediaItem[] | undefined, + media: games.gamesgamesgamesgames.defs.MediaItem[] | undefined, did: string, ): string | undefined { const coverItem = media?.find((m) => COVER_MEDIA_TYPES.has(m.mediaType)) ?? media?.[0]; - const cid = coverItem?.blob?.ref?.toString(); + const cid = (coverItem?.blob as any)?.ref?.toString(); if (!cid) return undefined; return `https://cdn.bsky.app/img/feed_thumbnail/plain/${did}/${cid}@jpeg`; } diff --git a/js/app/utils/video.ts b/js/app/utils/video.ts index b260e28ce..bb6c9e580 100644 --- a/js/app/utils/video.ts +++ b/js/app/utils/video.ts @@ -1,4 +1,4 @@ -import { PlaceStreamVideo } from "streamplace"; +import { place } from "streamplace"; // The DID lives in the second path segment of an at:// URI: // at:///place.stream.video/ @@ -16,11 +16,11 @@ export function getTidFromAtUri(uri: string): string { // use. Returns undefined when the video has no thumbnail so callers can fall // back to a placeholder. export function getVideoThumbnailUrl( - record: PlaceStreamVideo.Record | undefined, + record: place.stream.video.Main | undefined, did: string, ): string | undefined { - const ref = record?.thumb?.ref as { $link?: string } | undefined; - const cid = ref?.$link ?? record?.thumb?.ref?.toString(); + const ref = (record?.thumb as any)?.ref as { $link?: string } | undefined; + const cid = ref?.$link ?? (record?.thumb as any)?.ref?.toString(); if (!cid || !did) return undefined; return `https://cdn.bsky.app/img/feed_thumbnail/plain/${did}/${cid}@jpeg`; } diff --git a/js/components/src/components/chat/system-message.tsx b/js/components/src/components/chat/system-message.tsx index d23c19d01..32e05f221 100644 --- a/js/components/src/components/chat/system-message.tsx +++ b/js/components/src/components/chat/system-message.tsx @@ -1,5 +1,5 @@ import { Platform, View } from "react-native"; -import { Main } from "streamplace/src/lexicons/types/place/stream/richtext/facet"; +import { place } from "streamplace"; import { SystemMessageType } from "../../lib/system-messages"; import { bg, colors, flex, gap, layout, ml, pb, pl, px, r, w } from "../../ui"; import { Code, Text } from "../ui/text"; @@ -9,7 +9,7 @@ interface SystemMessageProps { variant: SystemMessageType; title: string; timestamp: Date; - facets?: Main[]; + facets?: place.stream.richtext.facet.Main[]; } export function SystemMessage({ diff --git a/js/components/src/components/chat/teleport-modal.tsx b/js/components/src/components/chat/teleport-modal.tsx index f14fe73cd..05eb713eb 100644 --- a/js/components/src/components/chat/teleport-modal.tsx +++ b/js/components/src/components/chat/teleport-modal.tsx @@ -2,7 +2,7 @@ import { Image } from "expo-image"; import { Check, X } from "lucide-react-native"; import React, { useEffect, useMemo, useState } from "react"; import { Pressable, ScrollView, View } from "react-native"; -import { PlaceStreamLivestream } from "streamplace"; +import { place } from "streamplace"; import { useAvatars, zero } from "../.."; import { useStreamplaceStore } from "../../streamplace-store"; import { Button, Input, ResponsiveDialog, Text, useTheme } from "../ui"; @@ -20,7 +20,7 @@ export const TeleportModal: React.FC = ({ }) => { const [searchQuery, setSearchQuery] = useState(""); const [selectedStream, setSelectedStream] = - useState(null); + useState(null); const [countdownSeconds, setCountdownSeconds] = useState("10"); const { theme } = useTheme(); diff --git a/js/components/src/components/content-metadata/content-metadata-form.tsx b/js/components/src/components/content-metadata/content-metadata-form.tsx index 304d1a270..a21ec8156 100644 --- a/js/components/src/components/content-metadata/content-metadata-form.tsx +++ b/js/components/src/components/content-metadata/content-metadata-form.tsx @@ -6,11 +6,7 @@ import { } from "../../lib/metadata-constants"; import { ExternalLink } from "lucide-react-native"; -import { - PlaceStreamMetadataConfiguration, - PlaceStreamMetadataContentRights, - PlaceStreamMetadataDistributionPolicy, -} from "streamplace"; +import { place } from "streamplace"; import { useGetBroadcasterDID, useGetContentMetadata, @@ -37,9 +33,9 @@ const { p, r, bg, borders, w, text, layout, gap, flex } = zero; export interface ContentMetadataFormProps { showUpdateButton?: boolean; onMetadataChange?: ( - metadata: PlaceStreamMetadataConfiguration.Record, + metadata: place.stream.metadata.configuration.Main, ) => void; - initialMetadata?: PlaceStreamMetadataConfiguration.Record; + initialMetadata?: place.stream.metadata.configuration.Main; style?: any; } @@ -98,9 +94,9 @@ export const ContentMetadataForm = forwardRef( // Local state for metadata const [contentWarnings, setContentWarnings] = useState([]); const [distributionPolicy, setDistributionPolicy] = - useState({}); + useState({}); const [contentRights, setContentRights] = - useState({}); + useState({}); const [selectedLicense, setSelectedLicense] = useState(""); const [customLicenseText, setCustomLicenseText] = useState(""); const [loading, setLoading] = useState(false); @@ -206,7 +202,7 @@ export const ContentMetadataForm = forwardRef( deleteAfter?: string; allowedBroadcasters?: string; }) => { - let newDistributionPolicy: PlaceStreamMetadataDistributionPolicy.Main = + let newDistributionPolicy: place.stream.metadata.distributionPolicy.Main = { ...distributionPolicy, }; @@ -268,7 +264,7 @@ export const ContentMetadataForm = forwardRef( setLoading(true); try { // Build the metadata object, only including non-empty fields - const metadata: PlaceStreamMetadataConfiguration.Record = { + const metadata: place.stream.metadata.configuration.Main = { $type: "place.stream.metadata.configuration", }; diff --git a/js/components/src/components/mobile-player/use-webrtc.tsx b/js/components/src/components/mobile-player/use-webrtc.tsx index 18261a4a9..af2fc8890 100644 --- a/js/components/src/components/mobile-player/use-webrtc.tsx +++ b/js/components/src/components/mobile-player/use-webrtc.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from "react"; import * as sdpTransform from "sdp-transform"; -import { StreamplaceAgent } from "streamplace"; +import { place, StreamplaceAgent } from "streamplace"; import { PlayerStatus, useDID, @@ -174,18 +174,14 @@ export async function negotiateConnectionWithClientOffer( isOwnStream, playbackWorkerUrl, ); - let text = new TextDecoder().decode(response.data); - if (response.success) { - if ((peerConnection.connectionState as string) === "closed") { - return; - } - await peerConnection.setRemoteDescription( - new RTCSessionDescription({ type: "answer", sdp: text }), - ); - return "https://stream.place/example"; - } else { - console.error(text); + let text = new TextDecoder().decode(response); + if ((peerConnection.connectionState as string) === "closed") { + return; } + await peerConnection.setRemoteDescription( + new RTCSessionDescription({ type: "answer", sdp: text }), + ); + return "https://stream.place/example"; } catch (e) { console.error(`posting sdp offer failed: ${e}`); } @@ -267,11 +263,12 @@ async function getPlaybackServerAgent( try { const lookupAgent = new StreamplaceAgent(playbackWorkerUrl); - const res = await lookupAgent.place.stream.playback.getPlaybackServer({ - stream: streamer, - }); - if (res.data.servers.length > 0) { - const serverUrl = res.data.servers[0]; + const res = await lookupAgent.client.call( + place.stream.playback.getPlaybackServer, + { stream: streamer }, + ); + if (res.servers.length > 0) { + const serverUrl = res.servers[0]; console.log(`Using playback server: ${serverUrl}`); return new StreamplaceAgent(serverUrl); } @@ -298,12 +295,16 @@ async function postSDPOffer( const playbackAgent = isOwnStream ? agent : await getPlaybackServerAgent(agent, streamer, playbackWorkerUrl); - return await playbackAgent.place.stream.playback.whep(data, { - qp: { - rendition: "source", - streamer: streamer, + return await playbackAgent.client.call( + place.stream.playback.whep, + data as any, + { + params: { + rendition: "source", + streamer: streamer, + }, }, - }); + ); } async function postSDPIngestOffer( diff --git a/js/components/src/components/stream-notification/pin-notification.tsx b/js/components/src/components/stream-notification/pin-notification.tsx index a56766b3b..a510f3198 100644 --- a/js/components/src/components/stream-notification/pin-notification.tsx +++ b/js/components/src/components/stream-notification/pin-notification.tsx @@ -1,7 +1,7 @@ import { EyeOff, Pin, X } from "lucide-react-native"; import { useEffect, useState } from "react"; import { Linking, Pressable, View } from "react-native"; -import { PinnedRecordViewHydrated, PlaceStreamChatProfile } from "streamplace"; +import { PinnedRecordViewHydrated, place } from "streamplace"; import { Text, useCanModerate, @@ -12,7 +12,7 @@ import { import { RichtextSegment, segmentize } from "../../lib/facet"; import { formatHandleWithAt } from "../../utils/format-handle"; -const getRgbColor = (color?: PlaceStreamChatProfile.Color) => +const getRgbColor = (color?: place.stream.chat.profile.Color) => color ? `rgb(${color.red}, ${color.green}, ${color.blue})` : undefined; function renderSegment(segment: RichtextSegment, index: number) { diff --git a/js/components/src/components/vod/like-button.tsx b/js/components/src/components/vod/like-button.tsx index b669e5f6c..93356c9fc 100644 --- a/js/components/src/components/vod/like-button.tsx +++ b/js/components/src/components/vod/like-button.tsx @@ -65,7 +65,7 @@ export function LikeButton({ subjectUri }: { subjectUri: string }) { } else { const result = await createLike(subjectUri); setUserLiked(true); - setUserLikeUri(result.data.uri); + setUserLikeUri(result.uri); setLikeCount((c) => c + 1); } } catch (e) { diff --git a/js/components/src/components/vod/vod-comments.tsx b/js/components/src/components/vod/vod-comments.tsx index 1d7b99de9..4ecd49908 100644 --- a/js/components/src/components/vod/vod-comments.tsx +++ b/js/components/src/components/vod/vod-comments.tsx @@ -5,7 +5,7 @@ import { TouchableOpacity, View, } from "react-native"; -import type { PlaceStreamVodDefs } from "streamplace"; +import type { place } from "streamplace"; import { borders, flex, @@ -27,7 +27,7 @@ import { Text } from "../ui/text"; import { Textarea } from "../ui/textarea"; export function VodComments({ videoUri }: { videoUri: string }) { - const [comments, setComments] = useState( + const [comments, setComments] = useState( [], ); const [loading, setLoading] = useState(true); @@ -75,7 +75,7 @@ export function VodComments({ videoUri }: { videoUri: string }) { }, [draft, videoUri, createComment, loadComments]); const renderComment = useCallback( - ({ item }: { item: PlaceStreamVodDefs.CommentView }) => { + ({ item }: { item: place.stream.vod.defs.CommentView }) => { const record = item.record as any; return ( = [ { value: "just_chatting", display: "Just Chatting" }, @@ -36,15 +36,18 @@ export const ACTIVITY_LABEL_DISPLAY: Record = ); export function formatActivity( - activity: (PlaceStreamDefs.ActivityGame | PlaceStreamDefs.ActivityLabel) & { + activity: ( + | place.stream.defs.ActivityGame + | place.stream.defs.ActivityLabel + ) & { $type?: string; }, ): string | null { if (activity.$type === "place.stream.defs#activityGame") { - return (activity as PlaceStreamDefs.ActivityGame).name ?? null; + return (activity as place.stream.defs.ActivityGame).name ?? null; } if (activity.$type === "place.stream.defs#activityLabel") { - const label = (activity as PlaceStreamDefs.ActivityLabel).label; + const label = (activity as place.stream.defs.ActivityLabel).label; return ACTIVITY_LABEL_DISPLAY[label] ?? label; } return null; diff --git a/js/components/src/lib/slash-commands/teleport.ts b/js/components/src/lib/slash-commands/teleport.ts index 603f359e6..5add08f88 100644 --- a/js/components/src/lib/slash-commands/teleport.ts +++ b/js/components/src/lib/slash-commands/teleport.ts @@ -1,4 +1,4 @@ -import { PlaceStreamLiveTeleport, StreamplaceAgent } from "streamplace"; +import { place, StreamplaceAgent } from "streamplace"; import { registerSlashCommand, SlashCommandHandler, @@ -14,9 +14,8 @@ export async function deleteTeleport( if (!rkey) { throw new Error("No rkey found in teleport URI"); } - return await pdsAgent.com.atproto.repo.deleteRecord({ - repo: userDID, - collection: "place.stream.live.teleport", + return await pdsAgent.client.delete(place.stream.live.teleport, { + repo: userDID as any, rkey: rkey, }); } @@ -57,22 +56,18 @@ export async function createTeleport( const startsAt = new Date(Date.now() + countdownSeconds * 1000).toISOString(); - const record: PlaceStreamLiveTeleport.Record = { - $type: "place.stream.live.teleport", - streamer: targetDID, - startsAt, - countdownSeconds, - }; - try { - const result = await pdsAgent.com.atproto.repo.createRecord({ - repo: userDID, - collection: "place.stream.live.teleport", - record, - }); + const result = await pdsAgent.client.create( + place.stream.live.teleport, + { + streamer: targetDID as any, + startsAt: startsAt as any, + }, + { repo: userDID as any }, + ); if (setActiveTeleportUri) { - setActiveTeleportUri(result.data.uri); + setActiveTeleportUri(result.uri); } return { success: true }; @@ -160,23 +155,19 @@ export function registerTeleportCommand( Date.now() + countdownSeconds * 1000, ).toISOString(); - const record: PlaceStreamLiveTeleport.Record = { - $type: "place.stream.live.teleport", - streamer: targetDID, - startsAt, - countdownSeconds, - }; - try { - const result = await pdsAgent.com.atproto.repo.createRecord({ - repo: userDID, - collection: "place.stream.live.teleport", - record, - }); + const result = await pdsAgent.client.create( + place.stream.live.teleport, + { + streamer: targetDID as any, + startsAt: startsAt as any, + }, + { repo: userDID as any }, + ); // store the URI in the livestream store if (setActiveTeleportUri) { - setActiveTeleportUri(result.data.uri); + setActiveTeleportUri(result.uri); } return { handled: true }; diff --git a/js/components/src/lib/system-messages.ts b/js/components/src/lib/system-messages.ts index 2c48ce44d..5744d8df3 100644 --- a/js/components/src/lib/system-messages.ts +++ b/js/components/src/lib/system-messages.ts @@ -39,15 +39,15 @@ export const createSystemMessage = ( cid: `system-${uniqueId}`, author: { did: "did:sys:system", - handle: type, // Use handle to specify the type of system message + handle: type as any, // Use handle to specify the type of system message }, record: { text, - createdAt: now.toISOString(), - streamer: "system", + createdAt: now.toISOString() as any, + streamer: "system" as any, $type: "place.stream.chat.message", }, - indexedAt: now.toISOString(), + indexedAt: now.toISOString() as any, chatProfile: { color: { red: 128, green: 128, blue: 128 }, // Gray color for system messages $type: "place.stream.chat.profile", @@ -110,7 +110,7 @@ export const SystemMessages = { features: [ { $type: "app.bsky.richtext.facet#mention", - did: streamerDid, + did: streamerDid as any, }, ], }, diff --git a/js/components/src/livestream-store/chat.tsx b/js/components/src/livestream-store/chat.tsx index f85d1ebc9..c80e53964 100644 --- a/js/components/src/livestream-store/chat.tsx +++ b/js/components/src/livestream-store/chat.tsx @@ -1,10 +1,6 @@ import { ComAtprotoModerationCreateReport, RichText } from "@atproto/api"; import { useCallback } from "react"; -import { - ChatMessageViewHydrated, - PlaceStreamChatMessage, - PlaceStreamDefs, -} from "streamplace"; +import { ChatMessageViewHydrated, place } from "streamplace"; import { useChatProfile, useDID, useHandle } from "../streamplace-store"; import { usePDSAgent } from "../streamplace-store/xrpc"; import { LivestreamState } from "./livestream-state"; @@ -114,12 +110,12 @@ export const useCreateChatMessage = () => { ); }); - const record: PlaceStreamChatMessage.Record = { + const record = { $type: "place.stream.chat.message", text: msg.text, createdAt: new Date().toISOString(), streamer: streamerProfile.did, - facets: rt.facets as PlaceStreamChatMessage.Record["facets"], + facets: rt.facets, ...(msg.reply ? { reply: { @@ -134,17 +130,17 @@ export const useCreateChatMessage = () => { }, } : {}), - }; + } as unknown as place.stream.chat.message.Main; const localChat: ChatMessageViewHydrated = { - uri: `local-${Date.now()}`, + uri: `local-${Date.now()}` as any, cid: "", author: { - did: userDID, - handle: userHandle || userDID, + did: userDID as any, + handle: (userHandle || userDID) as any, }, record: record, - indexedAt: new Date().toISOString(), + indexedAt: new Date().toISOString() as any, chatProfile: chatProfile || undefined, }; @@ -258,7 +254,7 @@ const profileIsDifferent = ( export const reduceChatIncremental = ( state: LivestreamState, newMessages: ChatMessageViewHydrated[], - blocks: PlaceStreamDefs.BlockView[], + blocks: place.stream.defs.BlockView[], hideUris: string[] = [], ): LivestreamState => { if ( @@ -519,11 +515,11 @@ export const usePinChatMessage = () => { } // Otherwise, use delegated moderation endpoint - const result = await agent.place.stream.moderation.createPin({ + const result = await agent.client.call(place.stream.moderation.createPin, { streamer: streamerDID, messageUri, ...(expiresAt ? { expiresAt } : {}), - }); + } as any); return result; }; }; @@ -555,10 +551,10 @@ export const useUnpinChatMessage = () => { } // Otherwise, use delegated moderation endpoint - await agent.place.stream.moderation.deletePin({ + await agent.client.call(place.stream.moderation.deletePin, { streamer: streamerDID, pinUri, - }); + } as any); // Optimistically clear the pinned comment store.setState({ pinnedComment: null }); }; diff --git a/js/components/src/livestream-store/livestream-state.tsx b/js/components/src/livestream-store/livestream-state.tsx index 06741fa61..bd1157b60 100644 --- a/js/components/src/livestream-store/livestream-state.tsx +++ b/js/components/src/livestream-store/livestream-state.tsx @@ -3,11 +3,7 @@ import { ChatMessageViewHydrated, LivestreamViewHydrated, PinnedRecordViewHydrated, - PlaceStreamBadgeDefs, - PlaceStreamDefs, - PlaceStreamLiveTeleport, - PlaceStreamModerationPermission, - PlaceStreamSegment, + place, } from "streamplace"; export interface LivestreamState { @@ -18,27 +14,27 @@ export interface LivestreamState { livestream: LivestreamViewHydrated | null; viewers: number | null; pendingHides: string[]; - segment: PlaceStreamSegment.Record | null; - recentSegments: PlaceStreamSegment.Record[]; + segment: place.stream.segment.Main | null; + recentSegments: place.stream.segment.Main[]; problems: LivestreamProblem[]; - renditions: PlaceStreamDefs.Rendition[]; + renditions: place.stream.defs.Rendition[]; replyToMessage: ChatMessageViewHydrated | null; chatDraft: string; badgeSlots: { - streamer: PlaceStreamBadgeDefs.BadgeSlot | null; - user: PlaceStreamBadgeDefs.BadgeSlot | null; + streamer: place.stream.badge.defs.BadgeSlot | null; + user: place.stream.badge.defs.BadgeSlot | null; } | null; streamKey: string | null; setStreamKey: (key: string | null) => void; - activeTeleport: PlaceStreamLiveTeleport.Record | null; + activeTeleport: place.stream.live.teleport.Main | null; activeTeleportUri: string | null; setActiveTeleportUri: (uri: string | null) => void; websocketConnected: boolean; hasReceivedSegment: boolean; pinnedComment: PinnedRecordViewHydrated | null; - moderationPermissions: PlaceStreamModerationPermission.Record[]; + moderationPermissions: place.stream.moderation.permission.Main[]; setModerationPermissions: ( - permissions: PlaceStreamModerationPermission.Record[], + permissions: place.stream.moderation.permission.Main[], ) => void; localLivestreamURI: string | null; setLocalLivestreamURI: (uri: string | null) => void; diff --git a/js/components/src/livestream-store/problems.tsx b/js/components/src/livestream-store/problems.tsx index bcce311ec..52a40a7ad 100644 --- a/js/components/src/livestream-store/problems.tsx +++ b/js/components/src/livestream-store/problems.tsx @@ -1,11 +1,11 @@ -import { PlaceStreamSegment } from "streamplace"; +import { place } from "streamplace"; import { LivestreamProblem } from "./livestream-state"; const VARIANCE_THRESHOLD = 0.5; const DURATION_THRESHOLD = 5000000000; // 5s in ns const detectVariableSegmentLength = ( - segments: PlaceStreamSegment.Record[], + segments: place.stream.segment.Main[], ): { variable: boolean; duration: boolean } => { if (segments.length < 3) { // Need at least 3 segments to detect variability @@ -47,7 +47,7 @@ const detectVariableSegmentLength = ( }; export const findProblems = ( - segments: PlaceStreamSegment.Record[], + segments: place.stream.segment.Main[], ): LivestreamProblem[] => { const problems: LivestreamProblem[] = []; let hasBFrames = false; diff --git a/js/components/src/livestream-store/stream-key.tsx b/js/components/src/livestream-store/stream-key.tsx index 90aa96a99..19bcb5397 100644 --- a/js/components/src/livestream-store/stream-key.tsx +++ b/js/components/src/livestream-store/stream-key.tsx @@ -4,7 +4,7 @@ import "../crypto-polyfill"; import { bytesToMultibase, Secp256k1Keypair } from "@atproto/crypto"; import { useEffect, useState } from "react"; import { Platform } from "react-native"; -import { PlaceStreamKey } from "streamplace"; +import { place } from "streamplace"; import { privateKeyToAccount } from "viem/accounts"; import { getBrowserName } from "../lib/browser"; import { usePDSAgent } from "../streamplace-store/xrpc"; @@ -77,16 +77,13 @@ export const useStreamKey = (): { platform = "Windows"; } - const record: PlaceStreamKey.Record = { - $type: "place.stream.key", + const record = { signingKey: keypair.did(), - createdAt: new Date().toISOString(), + createdAt: new Date().toISOString() as any, createdBy: "Streamplace on " + platform, }; - await pdsAgent.com.atproto.repo.createRecord({ - repo: did, - collection: "place.stream.key", - record, + await pdsAgent.client.create(place.stream.key, record, { + repo: did as any, }); setStreamKey(JSON.stringify(newKey)); diff --git a/js/components/src/livestream-store/websocket-consumer.tsx b/js/components/src/livestream-store/websocket-consumer.tsx index e6cd02be7..5a44bdae6 100644 --- a/js/components/src/livestream-store/websocket-consumer.tsx +++ b/js/components/src/livestream-store/websocket-consumer.tsx @@ -3,13 +3,7 @@ import { ChatMessageViewHydrated, LivestreamViewHydrated, PinnedRecordViewHydrated, - PlaceStreamChatDefs, - PlaceStreamChatGate, - PlaceStreamChatMessage, - PlaceStreamDefs, - PlaceStreamLivestream, - PlaceStreamLiveTeleport, - PlaceStreamSegment, + place, } from "streamplace"; import { SystemMessages } from "../lib/system-messages"; import { formatHandleWithAt } from "../utils/format-handle"; @@ -47,7 +41,7 @@ export const handleWebSocketMessages = ( }; } - if (PlaceStreamLivestream.isLivestreamView(message)) { + if (place.stream.livestream.livestreamView.isTypeOf(message)) { const newLivestream = message as LivestreamViewHydrated; const oldLivestream = state.livestream; @@ -66,20 +60,20 @@ export const handleWebSocketMessages = ( ...state, livestream: newLivestream, }; - } else if (PlaceStreamLivestream.isViewerCount(message)) { - message = message as PlaceStreamLivestream.ViewerCount; + } else if (place.stream.livestream.viewerCount.isTypeOf(message)) { + message = message as place.stream.livestream.ViewerCount; state = { ...state, viewers: message.count, }; - } else if (PlaceStreamChatDefs.isMessageView(message)) { - message = message as PlaceStreamChatDefs.MessageView; + } else if (place.stream.chat.defs.messageView.isTypeOf(message)) { + message = message as place.stream.chat.defs.MessageView; // Explicitly map MessageView to MessageViewHydrated const hydrated: ChatMessageViewHydrated = { uri: message.uri, cid: message.cid, author: message.author, - record: message.record as PlaceStreamChatMessage.Record, + record: message.record as place.stream.chat.message.Main, indexedAt: message.indexedAt, chatProfile: (message as any).chatProfile, replyTo: (message as any).replyTo, @@ -87,7 +81,7 @@ export const handleWebSocketMessages = ( badges: message.badges, }; state = reduceChat(state, [hydrated], [], []); - } else if (PlaceStreamSegment.isRecord(message)) { + } else if (place.stream.segment.$isTypeOf(message)) { const newRecentSegments = [...state.recentSegments]; newRecentSegments.unshift(message); if (newRecentSegments.length > MAX_RECENT_SEGMENTS) { @@ -95,16 +89,16 @@ export const handleWebSocketMessages = ( } state = { ...state, - segment: message as PlaceStreamSegment.Record, + segment: message as place.stream.segment.Main, recentSegments: newRecentSegments, problems: findProblems(newRecentSegments), hasReceivedSegment: true, }; - } else if (PlaceStreamDefs.isBlockView(message)) { - const block = message as PlaceStreamDefs.BlockView; + } else if (place.stream.defs.blockView.isTypeOf(message)) { + const block = message as place.stream.defs.BlockView; state = reduceChat(state, [], [block], []); - } else if (PlaceStreamDefs.isRenditions(message)) { - message = message as PlaceStreamDefs.Renditions; + } else if (place.stream.defs.renditions.isTypeOf(message)) { + message = message as place.stream.defs.Renditions; state = { ...state, renditions: message.renditions, @@ -114,8 +108,8 @@ export const handleWebSocketMessages = ( ...state, profile: message, }; - } else if (PlaceStreamChatGate.isRecord(message)) { - const hideRecord = message as PlaceStreamChatGate.Record; + } else if (place.stream.chat.gate.$isTypeOf(message)) { + const hideRecord = message as place.stream.chat.gate.Main; const hiddenMessageUri = hideRecord.hiddenMessage; const newPendingHides = [...state.pendingHides]; if (!newPendingHides.includes(hiddenMessageUri)) { @@ -127,7 +121,7 @@ export const handleWebSocketMessages = ( pendingHides: newPendingHides, }; state = reduceChat(state, [], [], [hiddenMessageUri]); - } else if (PlaceStreamChatDefs.isPinnedRecordView(message)) { + } else if (place.stream.chat.defs.pinnedRecordView.isTypeOf(message)) { const pinnedView = message as PinnedRecordViewHydrated; state = { ...state, @@ -141,15 +135,15 @@ export const handleWebSocketMessages = ( ...state, pinnedComment: null, }; - } else if (PlaceStreamLiveTeleport.isRecord(message)) { - const teleportRecord = message as PlaceStreamLiveTeleport.Record; + } else if (place.stream.live.teleport.$isTypeOf(message)) { + const teleportRecord = message as place.stream.live.teleport.Main; state = { ...state, activeTeleport: teleportRecord, }; - } else if (PlaceStreamLivestream.isTeleportArrival(message)) { + } else if (place.stream.livestream.teleportArrival.isTypeOf(message)) { // teleport has succeeded, we are now at the target stream - const arrival = message as PlaceStreamLivestream.TeleportArrival; + const arrival = message as place.stream.livestream.TeleportArrival; // add the teleporter's chat profile to the authors cache FIRST so mention rendering works if (arrival.chatProfile && arrival.source.did) { @@ -173,7 +167,7 @@ export const handleWebSocketMessages = ( systemMessage.record.createdAt = arrival.startsAt; state = reduceChat(state, [systemMessage], []); - } else if (PlaceStreamLivestream.isTeleportCanceled(message)) { + } else if (place.stream.livestream.teleportCanceled.isTypeOf(message)) { // teleport was canceled (deleted or denied) state = { ...state, diff --git a/js/components/src/streamplace-provider/poller.tsx b/js/components/src/streamplace-provider/poller.tsx index 7e4721597..c4e726a2c 100644 --- a/js/components/src/streamplace-provider/poller.tsx +++ b/js/components/src/streamplace-provider/poller.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import { StreamplaceAgent } from "streamplace"; +import { place, StreamplaceAgent } from "streamplace"; import { useDID, useGetBskyProfile, @@ -36,9 +36,9 @@ export default function Poller({ children }: { children: React.ReactNode }) { liveUsersLoading: true, }); try { - const res = await agent.place.stream.live.getLiveUsers(); + const res = await agent.client.call(place.stream.live.getLiveUsers); setLiveUsers({ - liveUsers: res.data.streams || [], + liveUsers: res.streams || [], liveUsersLoading: false, liveUsersError: null, }); diff --git a/js/components/src/streamplace-store/block.tsx b/js/components/src/streamplace-store/block.tsx index 53abb98fe..99db61182 100644 --- a/js/components/src/streamplace-store/block.tsx +++ b/js/components/src/streamplace-store/block.tsx @@ -1,5 +1,6 @@ import { AppBskyGraphBlock } from "@atproto/api"; import { useState } from "react"; +import { place } from "streamplace"; import { usePDSAgent } from "./xrpc"; /** @@ -43,10 +44,13 @@ export function useCreateBlockRecord() { } // Otherwise, use delegated moderation endpoint - const result = await agent.place.stream.moderation.createBlock({ - streamer: streamerDID, - subject: subjectDID, - }); + const result = await agent.client.call( + place.stream.moderation.createBlock, + { + streamer: streamerDID as any, + subject: subjectDID as any, + }, + ); return result; } finally { setIsLoading(false); @@ -86,24 +90,24 @@ export function useCreateHideChatRecord() { try { // If no streamerDID provided or caller is the streamer, use direct ATProto write if (!streamerDID || agent.did === streamerDID) { - const record = { - $type: "place.stream.chat.gate", - hiddenMessage: chatMessageUri, - }; - - const result = await agent.com.atproto.repo.createRecord({ - repo: agent.did, - collection: "place.stream.chat.gate", - record, - }); + const result = await agent.client.create( + place.stream.chat.gate, + { + hiddenMessage: chatMessageUri as any, + }, + { repo: agent.did as any }, + ); return result; } // Otherwise, use delegated moderation endpoint - const result = await agent.place.stream.moderation.createGate({ - streamer: streamerDID, - messageUri: chatMessageUri, - }); + const result = await agent.client.call( + place.stream.moderation.createGate, + { + streamer: streamerDID as any, + messageUri: chatMessageUri as any, + }, + ); return result; } finally { setIsLoading(false); @@ -151,13 +155,12 @@ export function useUpdateLivestreamRecord() { } // Get existing record to copy fields - const getResult = await agent.com.atproto.repo.getRecord({ - repo: agent.did, - collection: "place.stream.livestream", + const getResult = await agent.client.get(place.stream.livestream, { + repo: agent.did as any, rkey, }); - const oldRecord = getResult.data.value as any; + const oldRecord = getResult.value as any; // Create new record (don't edit - old records are "chapter markers") // Spread entire record to preserve all fields (agent, canonicalUrl, notificationSettings, etc.) @@ -167,20 +170,23 @@ export function useUpdateLivestreamRecord() { createdAt: new Date().toISOString(), // Override timestamp for new chapter marker }; - const result = await agent.com.atproto.repo.createRecord({ - repo: agent.did, - collection: "place.stream.livestream", + const result = await agent.client.create( + place.stream.livestream, record, - }); + { repo: agent.did as any }, + ); return result; } // Otherwise, use delegated moderation endpoint - const result = await agent.place.stream.moderation.updateLivestream({ - streamer: streamerDID, - livestreamUri: livestreamUri, - title: title, - }); + const result = await agent.client.call( + place.stream.moderation.updateLivestream, + { + streamer: streamerDID as any, + livestreamUri: livestreamUri as any, + title: title, + }, + ); return result; } finally { setIsLoading(false); diff --git a/js/components/src/streamplace-store/branding.tsx b/js/components/src/streamplace-store/branding.tsx index ddc01b70a..93b00dfe4 100644 --- a/js/components/src/streamplace-store/branding.tsx +++ b/js/components/src/streamplace-store/branding.tsx @@ -1,4 +1,5 @@ import { useCallback, useEffect } from "react"; +import { place } from "streamplace"; import storage from "../storage"; import { getStreamplaceStoreFromContext, @@ -90,14 +91,15 @@ export function useFetchBroadcasterDID() { if (!streamplaceAgent) { throw new Error("Streamplace agent not available"); } - const result = - await streamplaceAgent.place.stream.broadcast.getBroadcaster(); - store.setState({ broadcasterDID: result.data.broadcaster }); - if (result.data.server) { - store.setState({ serverDID: result.data.server }); + const result = await streamplaceAgent.client.call( + place.stream.broadcast.getBroadcaster, + ); + store.setState({ broadcasterDID: result.broadcaster }); + if (result.server) { + store.setState({ serverDID: result.server }); } - if (result.data.admins) { - store.setState({ adminDIDs: result.data.admins }); + if (result.admins) { + store.setState({ adminDIDs: result.admins }); } } catch (err) { console.error("Failed to fetch broadcaster DID:", err); @@ -115,11 +117,13 @@ export function useFetchEnvConfig() { if (!streamplaceAgent) { throw new Error("Streamplace agent not available"); } - const result = await streamplaceAgent.place.stream.config.getEnv(); - if (result.data.playbackWorkerUrl) { - store.setState({ playbackWorkerUrl: result.data.playbackWorkerUrl }); + const result = await streamplaceAgent.client.call( + place.stream.config.getEnv, + ); + if (result.playbackWorkerUrl) { + store.setState({ playbackWorkerUrl: result.playbackWorkerUrl }); } - store.setState({ gamesEnabled: result.data.gamesEnabled ?? false }); + store.setState({ gamesEnabled: result.gamesEnabled ?? false }); } catch (err) { console.error("Failed to fetch env config:", err); } @@ -165,10 +169,13 @@ export function useFetchBranding() { if (!streamplaceAgent) { throw new Error("Streamplace agent not available"); } - const res = await streamplaceAgent.place.stream.branding.getBranding({ - broadcaster: broadcasterDID, - }); - const assets = res.data.assets; + const res = await streamplaceAgent.client.call( + place.stream.branding.getBranding, + { + broadcaster: broadcasterDID as any, + }, + ); + const assets = res.assets; // convert assets array to keyed object and fetch blob data const brandingMap: Record = {}; diff --git a/js/components/src/streamplace-store/content-metadata-actions.tsx b/js/components/src/streamplace-store/content-metadata-actions.tsx index 84a9b9e21..8012d3d03 100644 --- a/js/components/src/streamplace-store/content-metadata-actions.tsx +++ b/js/components/src/streamplace-store/content-metadata-actions.tsx @@ -1,4 +1,4 @@ -import { PlaceStreamMetadataConfiguration } from "streamplace"; +import { place } from "streamplace"; import { ContentMetadataResult, useDID, @@ -19,13 +19,12 @@ export const useGetBroadcasterDID = () => { throw new Error("No PDS agent or DID available"); } - const result = await pdsAgent.place.stream.broadcast.getBroadcaster(); - if (!result.success) { - throw new Error("Failed to get broadcaster DID"); - } - setBroadcasterDID(result.data.broadcaster); - if (result.data.server) { - setServerDID(result.data.server); + const result = await pdsAgent.client.call( + place.stream.broadcast.getBroadcaster, + ); + setBroadcasterDID(result.broadcaster); + if (result.server) { + setServerDID(result.server); } else { setServerDID(null); } @@ -37,24 +36,23 @@ export const useSaveContentMetadata = () => { const did = useDID(); const setContentMetadata = useSetContentMetadata(); - return async (metadataRecord: PlaceStreamMetadataConfiguration.Record) => { + return async (metadataRecord: place.stream.metadata.configuration.Main) => { if (!pdsAgent || !did) { throw new Error("No PDS agent or DID available"); } try { // Try to update existing record first - const result = await (pdsAgent as any).com.atproto.repo.putRecord({ - repo: did, - collection: "place.stream.metadata.configuration", - rkey: "self", - record: metadataRecord, - }); + const result = await pdsAgent.client.put( + place.stream.metadata.configuration, + metadataRecord, + { repo: did as any, rkey: "self" }, + ); const contentMetadata: ContentMetadataResult = { record: metadataRecord as any, - uri: result.data.uri, - cid: result.data.cid || "", + uri: result.uri, + cid: result.cid || "", rkey: "self", }; @@ -69,19 +67,16 @@ export const useSaveContentMetadata = () => { error.message?.includes("mst: not found") || (error as any)?.status === 404) ) { - const createResult = await ( - pdsAgent as any - ).com.atproto.repo.createRecord({ - repo: did, - collection: "place.stream.metadata.configuration", - rkey: "self", - record: metadataRecord, - }); + const createResult = await pdsAgent.client.create( + place.stream.metadata.configuration, + metadataRecord, + { repo: did as any, rkey: "self" }, + ); const contentMetadata: ContentMetadataResult = { record: metadataRecord as any, - uri: createResult.data.uri, - cid: createResult.data.cid || "", + uri: createResult.uri, + cid: createResult.cid || "", rkey: "self", }; @@ -110,20 +105,18 @@ export const useGetContentMetadata = () => { } try { - const result = await (pdsAgent as any).com.atproto.repo.getRecord({ - repo: targetDid, - collection: "place.stream.metadata.configuration", - rkey: params?.rkey || "self", - }); - - if (!result.success) { - throw new Error("Failed to get content metadata record"); - } + const result = await pdsAgent.client.get( + place.stream.metadata.configuration, + { + repo: targetDid as any, + rkey: (params?.rkey || "self") as "self", + }, + ); const contentMetadata: ContentMetadataResult = { - record: result.data.value, - uri: result.data.uri, - cid: result.data.cid || "", + record: result.value, + uri: result.uri, + cid: result.cid || "", }; setContentMetadata(contentMetadata); diff --git a/js/components/src/streamplace-store/ingest.tsx b/js/components/src/streamplace-store/ingest.tsx index 23f89b91c..ce65ec800 100644 --- a/js/components/src/streamplace-store/ingest.tsx +++ b/js/components/src/streamplace-store/ingest.tsx @@ -1,4 +1,4 @@ -import { PlaceStreamIngestDefs } from "streamplace"; +import { place } from "streamplace"; import { useDID, useStreamplaceStore } from "./streamplace-store"; import { usePDSAgent } from "./xrpc"; @@ -12,14 +12,13 @@ export default function useGetIngests() { throw new Error("No PDS agent or DID available"); } - const result = await pdsAgent.place.stream.ingest.getIngestUrls(); - if (!result.success) { - throw new Error("Failed to get ingests"); - } + const result = await pdsAgent.client.call( + place.stream.ingest.getIngestUrls, + ); - const ingests = result.data.ingests + const ingests = result.ingests .map((ingest) => { - if (PlaceStreamIngestDefs.isIngest(ingest)) { + if (place.stream.ingest.defs.ingest.isTypeOf(ingest)) { return ingest; } console.error("Invalid ingest", ingest); diff --git a/js/components/src/streamplace-store/moderation.tsx b/js/components/src/streamplace-store/moderation.tsx index 34f071f9a..a6ffab596 100644 --- a/js/components/src/streamplace-store/moderation.tsx +++ b/js/components/src/streamplace-store/moderation.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { PlaceStreamModerationPermission } from "streamplace"; +import { place } from "streamplace"; import { useLivestreamStore } from "../livestream-store/livestream-store"; import { usePDSAgent } from "./xrpc"; @@ -69,14 +69,16 @@ export function useCanModerate( try { // Use authenticated agent to list permission records from the streamer's repo - const result = await agent.com.atproto.repo.listRecords({ - repo: streamerDID, - collection: "place.stream.moderation.permission", - limit: 100, - }); - - const records = result.data.records || []; - const permissionRecords: PlaceStreamModerationPermission.Record[] = + const result = await agent.client.list( + place.stream.moderation.permission, + { + repo: streamerDID as any, + limit: 100, + }, + ); + + const records = result.records || []; + const permissionRecords: place.stream.moderation.permission.Main[] = records .map((r: { value: any }) => r.value) .filter( @@ -113,14 +115,16 @@ export function useCanModerate( ) { const fetchDelegation = async () => { try { - const result = await agent.com.atproto.repo.listRecords({ - repo: streamerDID, - collection: "place.stream.moderation.permission", - limit: 100, - }); - - const records = result.data.records || []; - const permissionRecords: PlaceStreamModerationPermission.Record[] = + const result = await agent.client.list( + place.stream.moderation.permission, + { + repo: streamerDID as any, + limit: 100, + }, + ); + + const records = result.records || []; + const permissionRecords: place.stream.moderation.permission.Main[] = records .map((r: { value: any }) => r.value) .filter( diff --git a/js/components/src/streamplace-store/moderator-management.tsx b/js/components/src/streamplace-store/moderator-management.tsx index 15ce5e968..52b715979 100644 --- a/js/components/src/streamplace-store/moderator-management.tsx +++ b/js/components/src/streamplace-store/moderator-management.tsx @@ -1,11 +1,11 @@ import { useCallback, useEffect, useState } from "react"; -import { PlaceStreamModerationPermission } from "streamplace"; +import { place } from "streamplace"; import { usePDSAgent } from "./xrpc"; interface ModeratorRecord { uri: string; cid: string; - value: PlaceStreamModerationPermission.Record; + value: place.stream.moderation.permission.Main; rkey: string; } @@ -43,9 +43,12 @@ export function useListModerators(): ListModeratorsResult { setError(null); try { - const result = await agent.place.stream.moderation.permission.list({ - repo: agent.did!, - }); + const result = await agent.client.list( + place.stream.moderation.permission, + { + repo: agent.did! as any, + }, + ); const records = result.records.map((record: any) => { const rkey = record.uri.split("/").pop(); @@ -122,20 +125,20 @@ export function useAddModerator() { } } - const record: PlaceStreamModerationPermission.Record = { - $type: "place.stream.moderation.permission", - moderator: moderatorDID, + const record = { + moderator: moderatorDID as any, permissions: params.permissions, - createdAt: new Date().toISOString(), + createdAt: new Date().toISOString() as any, }; if (params.expirationTime) { (record as any).expirationTime = params.expirationTime; } - const result = await agent.place.stream.moderation.permission.create( - { repo: agent.did }, + const result = await agent.client.create( + place.stream.moderation.permission, record, + { repo: agent.did as any }, ); return result; @@ -162,8 +165,8 @@ export function useRemoveModerator() { setIsLoading(true); try { - await agent.place.stream.moderation.permission.delete({ - repo: agent.did, + await agent.client.delete(place.stream.moderation.permission, { + repo: agent.did as any, rkey, }); } finally { diff --git a/js/components/src/streamplace-store/stream.tsx b/js/components/src/streamplace-store/stream.tsx index 6de8cf08b..ef476f352 100644 --- a/js/components/src/streamplace-store/stream.tsx +++ b/js/components/src/streamplace-store/stream.tsx @@ -1,8 +1,6 @@ import { AppBskyFeedPost, BlobRef, RichText } from "@atproto/api"; import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; -import { StreamplaceAgent } from "streamplace/src/agent"; -import { PlaceStreamLivestream } from "streamplace/src/lexicons"; -import { LivestreamViewHydrated } from "streamplace/src/useful-types"; +import { LivestreamViewHydrated, place, StreamplaceAgent } from "streamplace"; import { useUrl } from "./streamplace-store"; import { usePDSAgent } from "./xrpc"; @@ -141,9 +139,9 @@ export function useCreateStreamRecord() { customThumbnail?: Blob; submitPost?: boolean; canonicalUrl?: string; - notificationSettings?: PlaceStreamLivestream.NotificationSettings; + notificationSettings?: place.stream.livestream.NotificationSettings; idleTimeoutSeconds?: number; - activity?: PlaceStreamLivestream.Record["activity"]; + activity?: place.stream.livestream.Main["activity"]; tags?: string[]; }) => { if (!agent) { @@ -168,14 +166,14 @@ export function useCreateStreamRecord() { const thisUrl = `${url}/${agent.did}`; - const record: PlaceStreamLivestream.Record = { + const record: place.stream.livestream.Main = { $type: "place.stream.livestream", title: title, - url: thisUrl, - createdAt: new Date().toISOString(), - lastSeenAt: new Date().toISOString(), + url: thisUrl as any, + createdAt: new Date().toISOString() as any, + lastSeenAt: new Date().toISOString() as any, // would match up with e.g. https://stream.place/iame.li - canonicalUrl: canonicalUrl, + canonicalUrl: canonicalUrl as any, // user agent style string // e.g. `@streamplace/components/0.1.0 (ios, 32.0)` agent: `@streamplace/components/${PackageJson.version} (${platform}, ${platVersion})`, @@ -191,23 +189,19 @@ export function useCreateStreamRecord() { if (customThumbnail) { try { const thumbnail = await uploadThumbnail(agent, customThumbnail); - record.thumb = thumbnail; + record.thumb = thumbnail as any; } catch (e) { throw new Error(`Custom thumbnail upload failed ${e}`); } } - const output = await agent.place.stream.live.startLivestream({ + const output = await agent.client.call(place.stream.live.startLivestream, { livestream: record, - streamer: agent.did, + streamer: agent.did as any, createBlueskyPost: submitPost, }); - if (!output.success) { - throw new Error("Failed to start livestream"); - } - - return output.data; + return output; }; } @@ -220,7 +214,7 @@ export function useUpdateStreamRecord(customUrl: string | null = null) { title: string, livestream: LivestreamViewHydrated | null, customThumbnail?: Blob, - activity?: PlaceStreamLivestream.Record["activity"], + activity?: place.stream.livestream.Main["activity"], tags?: string[], ) => { if (!agent) { @@ -239,13 +233,13 @@ export function useUpdateStreamRecord(customUrl: string | null = null) { const finalUrl = customUrl || url; let rkey = livestream.uri.split("/").pop(); - let oldRecordValue: PlaceStreamLivestream.Record = livestream.record; + let oldRecordValue: place.stream.livestream.Main = livestream.record; if (!rkey) { throw new Error("No rkey?"); } - let thumbnail: BlobRef | undefined = oldRecordValue.thumb; + let thumbnail: BlobRef | undefined = oldRecordValue.thumb as any; // update thumbnail if a new one is provided if (customThumbnail) { @@ -256,22 +250,20 @@ export function useUpdateStreamRecord(customUrl: string | null = null) { } } - const record: PlaceStreamLivestream.Record = { + const record: place.stream.livestream.Main = { $type: "place.stream.livestream", title: title, - url: finalUrl, - createdAt: new Date().toISOString(), + url: finalUrl as any, + createdAt: new Date().toISOString() as any, post: oldRecordValue.post, - thumb: thumbnail, + thumb: thumbnail as any, activity: activity, tags: tags?.length ? tags : undefined, }; - await agent.com.atproto.repo.putRecord({ - repo: agent.did, - collection: "place.stream.livestream", + await agent.client.put(place.stream.livestream, record, { + repo: agent.did as any, rkey, - record, }); return record; @@ -289,6 +281,6 @@ export function useEndLivestream() { throw new Error("No user DID found, assuming not logged in"); } - return await agent.place.stream.live.stopLivestream({}); + return await agent.client.call(place.stream.live.stopLivestream, {}); }; } diff --git a/js/components/src/streamplace-store/streamplace-store.tsx b/js/components/src/streamplace-store/streamplace-store.tsx index 8a3c4c1d4..df192aa20 100644 --- a/js/components/src/streamplace-store/streamplace-store.tsx +++ b/js/components/src/streamplace-store/streamplace-store.tsx @@ -1,10 +1,6 @@ import { SessionManager } from "@atproto/api/dist/session-manager"; import { useContext } from "react"; -import { - PlaceStreamChatProfile, - PlaceStreamIngestDefs, - PlaceStreamLivestream, -} from "streamplace"; +import { place } from "streamplace"; import { createStore, StoreApi, useStore } from "zustand"; import storage from "../storage"; import { StreamplaceContext } from "../streamplace-provider/context"; @@ -31,9 +27,9 @@ export interface ContentMetadataResult { export interface StreamplaceState { url: string; - liveUsers: PlaceStreamLivestream.LivestreamView[] | null; + liveUsers: place.stream.livestream.LivestreamView[] | null; setLiveUsers: (opts: { - liveUsers?: PlaceStreamLivestream.LivestreamView[]; + liveUsers?: place.stream.livestream.LivestreamView[]; liveUsersLoading?: boolean; liveUsersError?: string | null; liveUsersRefresh?: number; @@ -43,10 +39,10 @@ export interface StreamplaceState { liveUsersError: string | null; oauthSession: SessionManager | null | undefined; handle: string | null; - chatProfile: PlaceStreamChatProfile.Record | null; + chatProfile: place.stream.chat.profile.Main | null; - ingests: PlaceStreamIngestDefs.Ingest[] | null; - setIngests: (ingests: PlaceStreamIngestDefs.Ingest[] | null) => void; + ingests: place.stream.ingest.defs.Ingest[] | null; + setIngests: (ingests: place.stream.ingest.defs.Ingest[] | null) => void; // Content metadata state contentMetadata: ContentMetadataResult | null; @@ -111,7 +107,7 @@ export const makeStreamplaceStore = ({ url, liveUsers: null, setLiveUsers: (opts: { - liveUsers?: PlaceStreamLivestream.LivestreamView[]; + liveUsers?: place.stream.livestream.LivestreamView[]; liveUsersLoading?: boolean; liveUsersError?: string | null; liveUsersRefresh?: number; @@ -127,7 +123,7 @@ export const makeStreamplaceStore = ({ handle: null, chatProfile: null, ingests: null, - setIngests: (ingests: PlaceStreamIngestDefs.Ingest[] | null) => + setIngests: (ingests: place.stream.ingest.defs.Ingest[] | null) => set({ ingests: ingests }), broadcasterDID: null, setBroadcasterDID: (broadcasterDID: string | null) => diff --git a/js/components/src/streamplace-store/user.tsx b/js/components/src/streamplace-store/user.tsx index 8f5252f52..0d174e142 100644 --- a/js/components/src/streamplace-store/user.tsx +++ b/js/components/src/streamplace-store/user.tsx @@ -1,5 +1,4 @@ -import { Response } from "@atproto/api/dist/client/types/com/atproto/repo/getRecord"; -import { PlaceStreamChatProfile } from "streamplace"; +import { place } from "streamplace"; import { getStreamplaceStoreFromContext, useDID, @@ -17,12 +16,11 @@ export function useGetChatProfile() { if (!did || !pdsAgent) { throw new Error("No DID or PDS agent"); } - let res: Response | undefined; + let res: any; let notFound = false; try { - res = await pdsAgent.com.atproto.repo.getRecord({ - repo: did, - collection: "place.stream.chat.profile", + res = await pdsAgent.client.get(place.stream.chat.profile, { + repo: did as any, rkey: "self", }); } catch (e) { @@ -36,12 +34,11 @@ export function useGetChatProfile() { console.error("Failed to get chat profile record", e); } } - if (notFound || (res && !res.success)) { + if (notFound || !res) { try { await createEmptyChatProfile(); - res = await pdsAgent.com.atproto.repo.getRecord({ - repo: did, - collection: "place.stream.chat.profile", + res = await pdsAgent.client.get(place.stream.chat.profile, { + repo: did as any, rkey: "self", }); } catch (e) { @@ -55,10 +52,10 @@ export function useGetChatProfile() { return; } - if (PlaceStreamChatProfile.isRecord(res.data.value)) { - store.setState({ chatProfile: res.data.value }); + if (place.stream.chat.profile.$isTypeOf(res.value)) { + store.setState({ chatProfile: res.value }); } else { - console.log("not a record", res.data.value); + console.log("not a record", res.value); } }; } @@ -71,19 +68,14 @@ export function useCreateEmptyChatProfile() { if (!did || !pdsAgent) { throw new Error("No DID or PDS agent"); } - const res = await pdsAgent.com.atproto.repo.putRecord({ - repo: did, - collection: "place.stream.chat.profile", - rkey: "self", - record: { - $type: "place.stream.chat.profile", + await pdsAgent.client.put( + place.stream.chat.profile, + { color: DEFAULT_COLORS[Math.floor(Math.random() * DEFAULT_COLORS.length)], }, - }); - if (!res.success) { - throw new Error("Failed to create empty chat profile record"); - } + { repo: did as any, rkey: "self" }, + ); }; } @@ -111,7 +103,7 @@ export function useChatProfile() { return useStreamplaceStore((x) => x.chatProfile); } -const DEFAULT_COLORS: PlaceStreamChatProfile.Color[] = [ +const DEFAULT_COLORS: place.stream.chat.profile.Color[] = [ { red: 244, green: 67, blue: 54 }, { red: 233, green: 30, blue: 99 }, { red: 156, green: 39, blue: 176 }, diff --git a/js/components/src/time-sync/useTimeSync.tsx b/js/components/src/time-sync/useTimeSync.tsx index ddabfd8fa..dc0c3c231 100644 --- a/js/components/src/time-sync/useTimeSync.tsx +++ b/js/components/src/time-sync/useTimeSync.tsx @@ -1,7 +1,7 @@ import { TriangleAlert } from "lucide-react-native"; import { useEffect, useRef } from "react"; import { Platform } from "react-native"; -import { StreamplaceAgent } from "streamplace"; +import { place, StreamplaceAgent } from "streamplace"; import { useToast } from "../components/ui/toast"; import { useUrl } from "../streamplace-store/streamplace-store"; import { checkClockDrift, syncTimeWithServer } from "./time-sync"; @@ -19,9 +19,11 @@ export function useTimeSync() { try { const agent = new StreamplaceAgent(url); const start = new Date().getTime(); - const response = await agent.place.stream.server.getServerTime(); + const response = await agent.client.call( + place.stream.server.getServerTime, + ); const roundTripLatency = new Date().getTime() - start; - const serverTime = response.data.serverTime; + const serverTime = response.serverTime; // always sync with server time syncTimeWithServer(serverTime, roundTripLatency / 2); diff --git a/js/components/src/video-provider/fetch.tsx b/js/components/src/video-provider/fetch.tsx index bef5eea04..820c76a1f 100644 --- a/js/components/src/video-provider/fetch.tsx +++ b/js/components/src/video-provider/fetch.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef } from "react"; -import { VideoViewHydrated } from "streamplace"; +import { place, VideoViewHydrated } from "streamplace"; import { usePossiblyUnauthedPDSAgent } from "../streamplace-store"; import { getVideoStoreFromContext } from "../video-store"; @@ -28,11 +28,15 @@ export function useVideoFetch(aturi: string) { setLoading(true); setError(null); - agent.place.stream.media - .getVideo({ uri: aturi }, { signal: abort.signal }) + agent.client + .call( + place.stream.media.getVideo, + { uri: aturi as any }, + { signal: abort.signal }, + ) .then((res) => { if (abort.signal.aborted) return; - setVideo(res.data as unknown as VideoViewHydrated); + setVideo(res as unknown as VideoViewHydrated); setLoading(false); }) .catch((e) => { diff --git a/js/components/src/vod-store/vod-interactions.tsx b/js/components/src/vod-store/vod-interactions.tsx index 6a79e414d..47e35bc3c 100644 --- a/js/components/src/vod-store/vod-interactions.tsx +++ b/js/components/src/vod-store/vod-interactions.tsx @@ -1,11 +1,5 @@ import { useCallback } from "react"; -import { - PlaceStreamGetLikes, - PlaceStreamLike, - PlaceStreamVodComment, - PlaceStreamVodDefs, - PlaceStreamVodGetComments, -} from "streamplace"; +import { place } from "streamplace"; import { useDID } from "../streamplace-store/streamplace-store"; import { usePDSAgent } from "../streamplace-store/xrpc"; @@ -19,17 +13,14 @@ export const useCreateVodComment = () => { throw new Error("No PDS agent or user DID found"); } - const record: PlaceStreamVodComment.Record = { - $type: "place.stream.vod.comment", + const record = { text: params.text, - createdAt: new Date().toISOString(), - video: params.video, + createdAt: new Date().toISOString() as any, + video: params.video as any, }; - return await pdsAgent.com.atproto.repo.createRecord({ - repo: userDID, - collection: "place.stream.vod.comment", - record, + return await pdsAgent.client.create(place.stream.vod.comment, record, { + repo: userDID as any, }); }, [pdsAgent, userDID], @@ -47,9 +38,8 @@ export const useDeleteVodComment = () => { } const rkey = uri.split("/").pop(); if (!rkey) throw new Error("No rkey found"); - return await pdsAgent.com.atproto.repo.deleteRecord({ - repo: userDID, - collection: "place.stream.vod.comment", + return await pdsAgent.client.delete(place.stream.vod.comment, { + repo: userDID as any, rkey, }); }, @@ -67,16 +57,13 @@ export const useCreateLike = () => { throw new Error("No PDS agent or user DID found"); } - const record: PlaceStreamLike.Record = { - $type: "place.stream.like", - subject, - createdAt: new Date().toISOString(), + const record = { + subject: subject as any, + createdAt: new Date().toISOString() as any, }; - return await pdsAgent.com.atproto.repo.createRecord({ - repo: userDID, - collection: "place.stream.like", - record, + return await pdsAgent.client.create(place.stream.like, record, { + repo: userDID as any, }); }, [pdsAgent, userDID], @@ -94,9 +81,8 @@ export const useDeleteLike = () => { } const rkey = uri.split("/").pop(); if (!rkey) throw new Error("No rkey found"); - return await pdsAgent.com.atproto.repo.deleteRecord({ - repo: userDID, - collection: "place.stream.like", + return await pdsAgent.client.delete(place.stream.like, { + repo: userDID as any, rkey, }); }, @@ -112,16 +98,16 @@ export const useGetVodComments = () => { video: string, limit?: number, cursor?: string, - ): Promise => { + ): Promise => { if (!pdsAgent) { throw new Error("No PDS agent found"); } - const res = await pdsAgent.place.stream.vod.getComments({ - video, + const res = await pdsAgent.client.call(place.stream.vod.getComments, { + video: video as any, limit, cursor, }); - return res.data; + return res; }, [pdsAgent], ); @@ -135,16 +121,16 @@ export const useGetLikes = () => { subject: string, limit?: number, cursor?: string, - ): Promise => { + ): Promise => { if (!pdsAgent) { throw new Error("No PDS agent found"); } - const res = await pdsAgent.place.stream.getLikes({ - subject, + const res = await pdsAgent.client.call(place.stream.getLikes, { + subject: subject as any, limit, cursor, }); - return res.data; + return res; }, [pdsAgent], ); @@ -158,16 +144,16 @@ export const useLikeCount = () => { if (!pdsAgent) { throw new Error("No PDS agent found"); } - const res = await pdsAgent.place.stream.getLikes({ - subject, + const res = await pdsAgent.client.call(place.stream.getLikes, { + subject: subject as any, limit: 1, }); - return res.data.count; + return res.count; }, [pdsAgent], ); }; -export type VodCommentHydrated = PlaceStreamVodDefs.CommentView & { - record: PlaceStreamVodComment.Record; +export type VodCommentHydrated = place.stream.vod.defs.CommentView & { + record: place.stream.vod.comment.Main; }; diff --git a/js/components/tsconfig.json b/js/components/tsconfig.json index 0757c1721..260611c52 100644 --- a/js/components/tsconfig.json +++ b/js/components/tsconfig.json @@ -4,10 +4,18 @@ "rootDir": "./src", "outDir": "./dist", "jsx": "react-jsx", - "module": "commonjs", + // Consumes the ESM `streamplace` package + ESM-only `@atproto/lex`; bundler + // resolution (which requires an es2015+ module target) avoids CJS/ESM + // interop errors. This package is always consumed via a bundler (Metro/web), + // never node-required directly. + "module": "esnext", + "moduleResolution": "bundler", "noEmit": false, - "declaration": true, - "declarationMap": true, + // Consumers type-check against this package's source (the `types` / + // `@streamplace/dev` export conditions point at ./src), so the emitted + // .d.ts are unused. Skipping declaration emit avoids TS2742 portability + // errors from hook return types that reference @atproto/lex-client internals. + "declaration": false, "sourceMap": true, "resolveJsonModule": true }, diff --git a/js/streamplace/.gitignore b/js/streamplace/.gitignore index 00b89734c..ea81e14f3 100644 --- a/js/streamplace/.gitignore +++ b/js/streamplace/.gitignore @@ -1 +1,2 @@ lexicons +raw-lexicons.ts diff --git a/js/streamplace/package.json b/js/streamplace/package.json index 56bfd269a..c4191c992 100644 --- a/js/streamplace/package.json +++ b/js/streamplace/package.json @@ -2,6 +2,7 @@ "name": "streamplace", "version": "0.11.0", "description": "Live video on the AT Protocol", + "type": "module", "main": "dist/index.js", "types": "src/index.ts", "exports": { @@ -14,7 +15,7 @@ "scripts": { "build": "tsc", "prepare": "cd ../.. && make js-lexicons && cd - && pnpm run build && pnpm run test-import", - "test-import": "node -e 'require(\".\")' || (echo 'require(\".\") failed, npm package is not working' && exit 1)", + "test-import": "node -e 'import(\"./dist/index.js\").catch((e) => { console.error(e); process.exit(1); })'", "start": "tsc --watch --preserveWatchOutput" }, "keywords": [ @@ -27,12 +28,13 @@ "packageManager": "pnpm@10.11.0", "dependencies": { "@atproto/api": "^0.19.3", + "@atproto/lex": "^0.2.0", "@atproto/lexicon": "^0.6.2", "@atproto/xrpc": "^0.7.7", "multiformats": "^9.9.0", "tslib": "^2.8.1" }, "devDependencies": { - "typescript": "~5.3.3" + "typescript": "^5.8.3" } } diff --git a/js/streamplace/scripts/dump-external-lexicons.mjs b/js/streamplace/scripts/dump-external-lexicons.mjs new file mode 100644 index 000000000..9f0f58f26 --- /dev/null +++ b/js/streamplace/scripts/dump-external-lexicons.mjs @@ -0,0 +1,46 @@ +// Writes the external (Bluesky / com.atproto / chat / ozone) lexicon documents +// bundled in @atproto/api into a target directory as a JSON file tree, so that +// `lex build` can resolve the refs that Streamplace's own lexicons make into +// them. We source these from the pinned @atproto/api dependency rather than a +// checked-out upstream copy so the JS codegen is self-contained in CI. +// +// Usage: node dump-external-lexicons.mjs +import { mkdirSync, writeFileSync } from "node:fs"; +import { createRequire } from "node:module"; +import { dirname, join } from "node:path"; + +const require = createRequire(import.meta.url); +// @atproto/api is CommonJS; load its bundled lexicon docs via require. +const { schemas } = require("@atproto/api/dist/client/lexicons"); + +// @atproto/api's compiled docs express refs as `lex:`-prefixed URIs +// (e.g. "lex:app.bsky.actor.defs#profileView"), but `lex build` resolves plain +// NSID refs. Strip the scheme so the docs match the on-disk raw lexicon format. +function stripLexUris(value) { + if (typeof value === "string") { + return value.startsWith("lex:") ? value.slice(4) : value; + } + if (Array.isArray(value)) return value.map(stripLexUris); + if (value && typeof value === "object") { + const out = {}; + for (const [k, v] of Object.entries(value)) out[k] = stripLexUris(v); + return out; + } + return value; +} + +const outDir = process.argv[2]; +if (!outDir) { + console.error("usage: dump-external-lexicons.mjs "); + process.exit(1); +} + +let count = 0; +for (const doc of schemas) { + if (typeof doc?.id !== "string") continue; + const file = join(outDir, doc.id.replace(/\./g, "/") + ".json"); + mkdirSync(dirname(file), { recursive: true }); + writeFileSync(file, JSON.stringify(stripLexUris(doc), null, 2)); + count++; +} +console.log(`wrote ${count} external lexicon docs to ${outDir}`); diff --git a/js/streamplace/scripts/gen-raw-lexicons.mjs b/js/streamplace/scripts/gen-raw-lexicons.mjs new file mode 100644 index 000000000..bea3cc7ff --- /dev/null +++ b/js/streamplace/scripts/gen-raw-lexicons.mjs @@ -0,0 +1,48 @@ +// Generates js/streamplace/src/raw-lexicons.ts — a bundle of the raw Streamplace +// lexicon documents, exported as `schemas`. The old @atproto/lex-cli codegen emitted +// this array; @atproto/lex does not, but a few consumers (e.g. metadata-constants.ts) +// still introspect the raw lexicon docs at runtime, so we keep exporting it. +// +// Run via `make js-lexicons`. Do not edit the generated output by hand. +import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const here = dirname(fileURLToPath(import.meta.url)); +const repoRoot = join(here, "..", "..", ".."); +// Only our own lexicons — bsky/com.atproto docs come from @atproto/api at runtime. +const roots = [ + join(repoRoot, "lexicons", "place"), + join(repoRoot, "lexicons", "games"), +]; + +function walk(dir) { + const out = []; + for (const name of readdirSync(dir)) { + const full = join(dir, name); + if (statSync(full).isDirectory()) out.push(...walk(full)); + else if (name.endsWith(".json")) out.push(full); + } + return out; +} + +const docs = roots + .flatMap(walk) + .map((f) => JSON.parse(readFileSync(f, "utf8"))) + .filter((doc) => typeof doc?.id === "string") + .sort((a, b) => a.id.localeCompare(b.id)); + +const body = `/** + * GENERATED CODE - DO NOT MODIFY + * + * Raw Streamplace lexicon documents, produced by scripts/gen-raw-lexicons.mjs. + * Run \`make js-lexicons\` to regenerate. + */ +import type { LexiconDoc } from "@atproto/lexicon"; + +export const schemas = ${JSON.stringify(docs, null, 2)} as unknown as LexiconDoc[]; +`; + +const outFile = join(here, "..", "src", "raw-lexicons.ts"); +writeFileSync(outFile, body); +console.log(`wrote ${docs.length} lexicon docs to ${outFile}`); diff --git a/js/streamplace/src/agent.ts b/js/streamplace/src/agent.ts index 1ca4ca0d4..e7803987e 100644 --- a/js/streamplace/src/agent.ts +++ b/js/streamplace/src/agent.ts @@ -1,30 +1,18 @@ import { Agent } from "@atproto/api"; -import { schemas as parentSchemas } from "@atproto/api/dist/client/lexicons"; -import { SessionManager } from "@atproto/api/dist/session-manager"; -import { Lexicons } from "@atproto/lexicon"; -import { PlaceNS } from "./lexicons"; -import { schemas as appSchemas } from "./lexicons/lexicons"; +import { Client } from "@atproto/lex"; export class StreamplaceAgent extends Agent { - place = new PlaceNS(this); - lex: Lexicons; + // The @atproto/lex client for Streamplace (place.stream.* / games.*) XRPC and + // records. It rides on the same session/fetchHandler as the @atproto/api Agent, + // which we keep for OAuth and Bluesky (app.bsky.*) reads. + client: Client; - constructor(options: string | URL | SessionManager) { + constructor(options: ConstructorParameters[0]) { super(options); - const streamplaceSchemas = appSchemas.filter((x) => - x.id.startsWith("place.stream"), - ); - - // for game search - const pentaractSchemas = appSchemas.filter((x) => - x.id.startsWith("games.gamesgamesgamesgames"), - ); - - this.lex = new Lexicons([ - ...parentSchemas, - ...streamplaceSchemas, - ...pentaractSchemas, - ]); + this.client = new Client({ + did: this.did as `did:${string}:${string}` | undefined, + fetchHandler: this.fetchHandler, + }); } } diff --git a/js/streamplace/src/index.ts b/js/streamplace/src/index.ts index 06b0a5bc0..dd1ebeabf 100644 --- a/js/streamplace/src/index.ts +++ b/js/streamplace/src/index.ts @@ -1,4 +1,4 @@ -export * from "./agent"; -export * from "./lexicons"; -export { schemas } from "./lexicons/lexicons"; -export * from "./useful-types"; +export * from "./agent.js"; +export * from "./lexicons/index.js"; +export { schemas } from "./raw-lexicons.js"; +export * from "./useful-types.js"; diff --git a/js/streamplace/src/useful-types.ts b/js/streamplace/src/useful-types.ts index 263d4f8ea..0b125fec0 100644 --- a/js/streamplace/src/useful-types.ts +++ b/js/streamplace/src/useful-types.ts @@ -1,32 +1,26 @@ -import { - PlaceStreamChatDefs, - PlaceStreamChatMessage, - PlaceStreamChatPinnedRecord, - PlaceStreamLivestream, - PlaceStreamMediaGetVideo, - PlaceStreamMediaTrack, -} from "./lexicons"; +import { place } from "./lexicons/index.js"; export interface LivestreamViewHydrated - extends PlaceStreamLivestream.LivestreamView { - record: PlaceStreamLivestream.Record; + extends place.stream.livestream.LivestreamView { + record: place.stream.livestream.Main; } export interface ChatMessageViewHydrated - extends PlaceStreamChatDefs.MessageView { - record: PlaceStreamChatMessage.Record; + extends place.stream.chat.defs.MessageView { + record: place.stream.chat.message.Main; } export interface PinnedRecordViewHydrated - extends PlaceStreamChatDefs.PinnedRecordView { - record: PlaceStreamChatPinnedRecord.Record; + extends place.stream.chat.defs.PinnedRecordView { + record: place.stream.chat.pinnedRecord.Main; } -export interface VideoViewHydrated extends PlaceStreamMediaGetVideo.VideoView { - record: PlaceStreamLivestream.Record; +export interface VideoViewHydrated + extends place.stream.media.getVideo.VideoView { + record: place.stream.livestream.Main; tracks: TrackViewHydrated[]; } -export interface TrackViewHydrated extends PlaceStreamMediaTrack.TrackView { - record: PlaceStreamMediaTrack.Record; +export interface TrackViewHydrated extends place.stream.media.track.TrackView { + record: place.stream.media.track.Main; } diff --git a/js/streamplace/tsconfig.json b/js/streamplace/tsconfig.json index 437c0a25e..9c2da93d3 100644 --- a/js/streamplace/tsconfig.json +++ b/js/streamplace/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "rootDir": "./src", "outDir": "./dist", - "module": "commonjs" + "module": "node16", + "moduleResolution": "node16" }, "include": ["./src"] } diff --git a/package.json b/package.json index 719566fd8..d2a5e0dbd 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "author": "Streamplace", "license": "MIT", "devDependencies": { - "@atproto/lex-cli": "^0.9.9", + "@atproto/lex": "^0.2.0", "@types/node": "^22.15.17", "husky": "^9.1.6", "knip": "^5.59.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa4ac084b..ecc268197 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,9 +26,9 @@ importers: specifier: ^23.2.6 version: 23.2.6(@swc/core@1.15.18(@swc/helpers@0.5.17))(encoding@0.1.13) devDependencies: - '@atproto/lex-cli': - specifier: ^0.9.9 - version: 0.9.9 + '@atproto/lex': + specifier: ^0.2.0 + version: 0.2.0 '@types/node': specifier: ^22.15.17 version: 22.19.15 @@ -736,6 +736,9 @@ importers: '@atproto/api': specifier: ^0.19.3 version: 0.19.3 + '@atproto/lex': + specifier: ^0.2.0 + version: 0.2.0 '@atproto/lexicon': specifier: ^0.6.2 version: 0.6.2 @@ -750,8 +753,8 @@ importers: version: 2.8.1 devDependencies: typescript: - specifier: ~5.3.3 - version: 5.3.3 + specifier: ^5.8.3 + version: 5.9.3 packages: @@ -814,6 +817,10 @@ packages: '@atproto-labs/did-resolver@0.2.6': resolution: {integrity: sha512-2K1bC04nI2fmgNcvof+yA28IhGlpWn2JKYlPa7To9JTKI45FINCGkQSGiL2nyXlyzDJJ34fZ1aq6/IRFIOIiqg==} + '@atproto-labs/did-resolver@0.3.4': + resolution: {integrity: sha512-nBECoVG59NfbYthayxfR0s1dLFVdN+RKMaL0p0uaNX/U1SAETksN6Yydmr4oWOKSkyyU3GO9XZeo1yzwHnRcZw==} + engines: {node: '>=22'} + '@atproto-labs/fetch-node@0.2.0': resolution: {integrity: sha512-Krq09nH/aeoiU2s9xdHA0FjTEFWG9B5FFenipv1iRixCcPc7V3DhTNDawxG9gI8Ny0k4dBVS9WTRN/IDzBx86Q==} engines: {node: '>=18.7.0'} @@ -821,6 +828,10 @@ packages: '@atproto-labs/fetch@0.2.3': resolution: {integrity: sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw==} + '@atproto-labs/fetch@0.3.3': + resolution: {integrity: sha512-2gABLf0VEI86Sxo6YhGKQYK1tGhTZ4y+3TrCWputnupEZxuS/hw6+pFw9ceXZ3v9nnMNthzsAEcaAQ3V/tXsqg==} + engines: {node: '>=22'} + '@atproto-labs/handle-resolver@0.3.6': resolution: {integrity: sha512-qnSTXvOBNj1EHhp2qTWSX8MS5q3AwYU5LKlt5fBvSbCjgmTr2j0URHCv+ydrwO55KvsojIkTMgeMOh4YuY4fCA==} @@ -830,9 +841,17 @@ packages: '@atproto-labs/pipe@0.1.1': resolution: {integrity: sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg==} + '@atproto-labs/pipe@0.2.3': + resolution: {integrity: sha512-hsjkaKGdhEGKhXuOOfIeYyZSQZfw007AXQJak/QTd9pLY1wHUJG85a9+u13xoMeav95gHkBRzswti7+kqsxgdw==} + engines: {node: '>=22'} + '@atproto-labs/simple-store-memory@0.1.4': resolution: {integrity: sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw==} + '@atproto-labs/simple-store-memory@0.2.3': + resolution: {integrity: sha512-RtL48op8Db/QFNbW+9Y+Os6DOMqysOkoG5QelTZ0qcZt/j0pUBY8v2zSr5/faVJ5Y30h1cONxMydV48tVWf8pg==} + engines: {node: '>=22'} + '@atproto-labs/simple-store-redis@0.0.1': resolution: {integrity: sha512-hGkfDNVtTqwcRx27k6u25pgwNIHq3xDCRuojkfHf6c1B9R5rKphdZJ91Mn3lCvsyDB/lUqqLuzKuXQWFml/u5g==} peerDependencies: @@ -841,6 +860,10 @@ packages: '@atproto-labs/simple-store@0.3.0': resolution: {integrity: sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ==} + '@atproto-labs/simple-store@0.4.3': + resolution: {integrity: sha512-ML1HAEtQIixkcU4FCGbZtAkoHTfmXDi63tNVuSSPG/cVUKyrUURg6Cr1bcfvbbkMTwRj9abEwa3JZR7UUYi4Ew==} + engines: {node: '>=22'} + '@atproto-labs/xrpc-utils@0.0.24': resolution: {integrity: sha512-wWXd2Ht47UsL/UbDCr3twMFSZrh0xSI56u4O3kz0DTU4G+530mCG71mMVE6eeYcR+j6FEjp7o2Ld6c7wFklYGw==} @@ -854,6 +877,10 @@ packages: '@atproto/common-web@0.4.18': resolution: {integrity: sha512-ilImzP+9N/mtse440kN60pGrEzG7wi4xsV13nGeLrS+Zocybc/ISOpKlbZM13o+twPJ+Q7veGLw9CtGg0GAFoQ==} + '@atproto/common-web@0.5.4': + resolution: {integrity: sha512-06DqJc5k8H9p2njfYVEfxMT0RWlHzVElX60wiwQSgsaVWwP5IIF6MgXXX/44VwK0bir5hKpQ/J75MpCD5v8RFQ==} + engines: {node: '>=22'} + '@atproto/common@0.1.0': resolution: {integrity: sha512-OB5tWE2R19jwiMIs2IjQieH5KTUuMb98XGCn9h3xuu6NanwjlmbCYMv08fMYwIp3UQ6jcq//84cDT3Bu6fJD+A==} @@ -864,6 +891,10 @@ packages: resolution: {integrity: sha512-FnhTppvJw8I1AuvEkL9JREFwmM6ciYfSlQ0Zo6neiJIhTf1wf5/ONeFSYKu1/dxC63JEratGIAfVjSBJJZi7sg==} engines: {node: '>=18.7.0'} + '@atproto/common@0.7.0': + resolution: {integrity: sha512-R4E2oZrJIhUGWGr6tIxqjknlhjEHn0BjO4yVOnaUPbWFDFfPN4zi9rTQJsH2Ruu0LlBTTNgvIPkH7FkyC+M35w==} + engines: {node: '>=22'} + '@atproto/crypto@0.1.0': resolution: {integrity: sha512-9xgFEPtsCiJEPt9o3HtJT30IdFTGw5cQRSJVIy5CFhqBA4vDLcdXiRDLCjkzHEVbtNCsHUW6CrlfOgbeLPcmcg==} @@ -871,9 +902,17 @@ packages: resolution: {integrity: sha512-n40aKkMoCatP0u9Yvhrdk6fXyOHFDDbkdm4h4HCyWW+KlKl8iXfD5iV+ECq+w5BM+QH25aIpt3/j6EUNerhLxw==} engines: {node: '>=18.7.0'} + '@atproto/crypto@0.5.3': + resolution: {integrity: sha512-Ea5VwU+ofVXrm4BWpTa7gI9kz7A6NwPpCY6r0f5k8UJpNq0GDHSb1F5MdsFDVNnSK5bEI7DIMNZwspkNsAKOgA==} + engines: {node: '>=22'} + '@atproto/did@0.3.0': resolution: {integrity: sha512-raUPzUGegtW/6OxwCmM8bhZvuIMzxG5t9oWsth6Tp91Kb5fTnHV2h/KKNF1C82doeA4BdXCErTyg7ISwLbQkzA==} + '@atproto/did@0.5.3': + resolution: {integrity: sha512-nKcdu5qB9iNJFaBeQOQUJ+6mA1npFcZ3DmD0xB+etkMRd/3UvOQql/CvcMZaYzl+eGoVs1JDdEsvoZz+idfhaw==} + engines: {node: '>=22'} + '@atproto/identity@0.4.12': resolution: {integrity: sha512-P+Jn0HvKhIh1tps5n3xGrCxt+XiFWzp4kdgloyFhFmVLwjDU547DQkWx4r5Vhuiah7fRZGVSlk39R4U6SPrACg==} engines: {node: '>=18.7.0'} @@ -887,32 +926,68 @@ packages: '@atproto/jwk@0.6.0': resolution: {integrity: sha512-bDoJPvt7TrQVi/rBfBrSSpGykhtIriKxeYCYQTiPRKFfyRhbgpElF0wPXADjIswnbzZdOwbY63az4E/CFVT3Tw==} + '@atproto/lex-builder@0.1.6': + resolution: {integrity: sha512-HtnjVSoJwgHLlDVY2Dg3WrKFUPg1htaZoydIPlHu0xgdTFdj5n6eyERnypzwLZgQEAOwiPvVYzIE4j/S7ArLZw==} + engines: {node: '>=22'} + '@atproto/lex-cbor@0.0.14': resolution: {integrity: sha512-zeqxaKAifR8qlFKg4A6t1RCT8TcjeDnIXLtp3QnDu0QoxslxsmcsrqNrrgmka8w+bYW2+h/rT9MPWglkT7vHyw==} - '@atproto/lex-cli@0.9.9': - resolution: {integrity: sha512-n/ClCBNnrjbqflLFt+j8Vx8q+lQd6OPQiDToi80akUoW2wNyPmsWtK5tKje0HS+7aHZD3gO3yX4B/x6ZqqrEeA==} - engines: {node: '>=18.7.0'} - hasBin: true + '@atproto/lex-cbor@0.1.3': + resolution: {integrity: sha512-L9g6X8DAus+CrgrxwKehn5EUjsYuuySmUttkaHngXAT9+QOIsGqe7Bdcfd2FBZxomFjCamp5hFrN5M4hrGpdNA==} + engines: {node: '>=22'} '@atproto/lex-client@0.0.15': resolution: {integrity: sha512-j/eZGCdkhABU8Z868Y/gn909hS77rOCdMqtOaQdflEaKUKiAo2/gqeTpoAjHBnL5Rzz255wj9qZMqZTR/Ygwxw==} + '@atproto/lex-client@0.2.2': + resolution: {integrity: sha512-AuRNoNNcgnbwLpI1MC4jFU7sEORLN32qw7e1D6ExbSj8TALAiRBJAhiy6Mrqca9KTjWV3OPKwMJ+kLdTjWheZg==} + engines: {node: '>=22'} + '@atproto/lex-data@0.0.13': resolution: {integrity: sha512-7Z7RwZ1Y/JzBF/Tcn/I4UJ/vIGfh5zn1zjv0KX+flke2JtgFkSE8uh2hOtqgBQMNqE3zdJFM+dcSWln86hR3MQ==} + '@atproto/lex-data@0.1.4': + resolution: {integrity: sha512-f9U95sk0zUtxHktvK59peU+Shd0cIUYV68p//GS7sfdkAxUIeaspvX6CY+Quv9Oa4aozmsXI52SjaNn1wuU8RQ==} + engines: {node: '>=22'} + '@atproto/lex-document@0.0.15': resolution: {integrity: sha512-QT2MbICG4cTFrrA19SIHpZJ33WRLdzjhDsEhSknQ4dE5CjqPf4BP9LaC4pOeW8NE5Kn92hgIm3JWNjoak8blXw==} + '@atproto/lex-document@0.1.4': + resolution: {integrity: sha512-zr6RI6hyQmzs8PawTXMjU5ovCKldlvXXcGYVuL4kPzWkJFVcgAQFitEXl1uR4diT7fgtpuOiCxQljauHXLCTcA==} + engines: {node: '>=22'} + + '@atproto/lex-installer@0.1.5': + resolution: {integrity: sha512-aKHU9p7F/R4DFHAHXUFcqUyDvdkBoHwYOI0uCHZYaPMAMxPuadRcLgE5dmmsE0jOx0ImSvmZkbTLpZ+pQfaXEQ==} + engines: {node: '>=22'} + '@atproto/lex-json@0.0.13': resolution: {integrity: sha512-hwLhkKaIHulGJpt0EfXAEWdrxqM2L1tV/tvilzhMp3QxPqYgXchFnrfVmLsyFDx6P6qkH1GsX/XC2V36U0UlPQ==} + '@atproto/lex-json@0.1.3': + resolution: {integrity: sha512-Ch2w9bCLFOwWINFFxpZo6DBW7+ZxkehciU3P8N94gSyENLe3/5WWXHdHvkiH7EXZrpI7fKY8nVWn4GIL0ttqxw==} + engines: {node: '>=22'} + '@atproto/lex-resolver@0.0.17': resolution: {integrity: sha512-6nI5bYZUYh50ZI8r4erLRP9EbNcW226VShpVN3vHyOSgTje4VP1RTcvBhROBAPj4rL3vc+Oa8OiL6IQXkYrQBg==} + '@atproto/lex-resolver@0.1.5': + resolution: {integrity: sha512-tpVG5yktdsidpkUZrHXw0S//XyWSAbsXWkHLnDJYcTvnfeZBUsKCRJbWecdIU9KkbwL2Lqn7ZhJZnaGMlBhFDA==} + engines: {node: '>=22'} + '@atproto/lex-schema@0.0.14': resolution: {integrity: sha512-xUxFuXdgVVI1IBDXcQlanH7HuEo9Pk65DYifnhqFDzNRH9SZQxPvPO+rOxMG/bRHygPaI+A+UbXr+S7qpPYOLg==} + '@atproto/lex-schema@0.2.0': + resolution: {integrity: sha512-gKfiuEsaLGaJqydCKxnphjBnUIHqckStnY3KGQgu4CJoulGBMXVwkIhQMA94sXHh7xKnIeq1Y/xfARVOp8qNsg==} + engines: {node: '>=22'} + + '@atproto/lex@0.2.0': + resolution: {integrity: sha512-M52pE+p1bkxEA9xhKptRB+nEvapcOw6LZawBUKoCqkFccWwnoR2/DcrBzyS3P4l7NvOOul/4EOJtVPWB40vMtg==} + engines: {node: '>=22'} + hasBin: true + '@atproto/lexicon@0.6.2': resolution: {integrity: sha512-p3Ly6hinVZW0ETuAXZMeUGwuMm3g8HvQMQ41yyEE6AL0hAkfeKFaZKos6BdBrr6CjkpbrDZqE8M+5+QOceysMw==} @@ -956,6 +1031,10 @@ packages: resolution: {integrity: sha512-bTWeWg3H0TlELfE2eI2ySQuC6ojsCBSSmPtCXBh3Td9TFNpIoZQ/tYLUJMtMXaiVUi6HxzXQL1/iuYfC4Y+ZYQ==} engines: {node: '>=18.7.0'} + '@atproto/repo@0.10.4': + resolution: {integrity: sha512-LmjhdIktudlk9SWv74ctc70HGOhWRCcHquwgN00gmQOeX+SIwLpXgFxJasx/DfOuQHPJKVYaPE5O8QSMZef4Kw==} + engines: {node: '>=22'} + '@atproto/repo@0.8.12': resolution: {integrity: sha512-QpVTVulgfz5PUiCTELlDBiRvnsnwrFWi+6CfY88VwXzrRHd9NE8GItK7sfxQ6U65vD/idH8ddCgFrlrsn1REPQ==} engines: {node: '>=18.7.0'} @@ -970,6 +1049,10 @@ packages: resolution: {integrity: sha512-kA4dQDoMPpWCH8N0Q4KoSq024u5MkVfDVa8DdhyLjGA72z/khbOf1jXKPv7NIL2oEc9aj7geKELdvqyf4ogopA==} engines: {node: '>=22'} + '@atproto/syntax@0.7.0': + resolution: {integrity: sha512-bKilYbACYg87TgKa60NNoM1cysQxN9ikgIrgKQH4OpeXVN2TTb0qxqLDbxiU/rp+ugdrf4JYl6UyrQSM+IagHg==} + engines: {node: '>=22'} + '@atproto/ws-client@0.0.4': resolution: {integrity: sha512-dox1XIymuC7/ZRhUqKezIGgooZS45C6vHCfu0PnWjfvsLCK2kAlnvX4IBkA/WpcoijDhQ9ejChnFbo/sLmgvAg==} engines: {node: '>=18.7.0'} @@ -4277,6 +4360,9 @@ packages: cpu: [x64] os: [win32] + '@pinojs/redact@0.4.0': + resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -5837,6 +5923,9 @@ packages: '@speed-highlight/core@1.2.15': resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@swc/core-darwin-arm64@1.15.18': resolution: {integrity: sha512-+mIv7uBuSaywN3C9LNuWaX1jJJ3SKfiJuE6Lr3bd+/1Iv8oMU7oLBjYMluX1UrEPzwN2qCdY6Io0yVicABoCwQ==} engines: {node: '>=10'} @@ -5934,8 +6023,8 @@ packages: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} - '@ts-morph/common@0.25.0': - resolution: {integrity: sha512-kMnZz+vGGHi4GoHnLmMhGNjm44kGtKUXGnOvrKmMwAuvNjM/PgKVGfUnL7IDvK7Jb2QQ82jq3Zmp04Gy+r3Dkg==} + '@ts-morph/common@0.28.1': + resolution: {integrity: sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==} '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -7183,6 +7272,10 @@ packages: resolution: {integrity: sha512-b3tFPA9pUr2zCUiCfRd2+wok2/LBSNUMKOuRRok+WlvvAgEt/PlbgPTsZUcwCOs53IJvLgTp0eotwtosE6njug==} hasBin: true + cborg@4.5.8: + resolution: {integrity: sha512-6/viltD51JklRhq4L7jC3zgy6gryuG5xfZ3kzpE+PravtyeQLeQmCYLREhQH7pWENg5pY4Yu/XCd6a7dKScVlw==} + hasBin: true + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -7357,6 +7450,10 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + cliui@9.0.1: + resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} + engines: {node: '>=20'} + clone-deep@4.0.1: resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} engines: {node: '>=6'} @@ -12202,12 +12299,22 @@ packages: pino-abstract-transport@1.2.0: resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + pino-abstract-transport@3.0.0: + resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} + pino-http@8.6.1: resolution: {integrity: sha512-J0hiJgUExtBXP2BjrK4VB305tHXS31sCmWJ9XJo2wPkLHa1NFPuW4V9wjG27PAc2fmBCigiNhQKpvrx+kntBPA==} pino-std-serializers@6.2.2: resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + + pino@10.3.1: + resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} + hasBin: true + pino@8.21.0: resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} hasBin: true @@ -12389,6 +12496,9 @@ packages: process-warning@3.0.0: resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -12889,6 +12999,9 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} + real-require@1.0.0: + resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} + reanimated-color-picker@4.2.0: resolution: {integrity: sha512-nDG/OiQu7hcvVJw3IP0rIdhw+x0rSNxhmfnrXC2je6kqlLSs4txbSVCqDA0kTnVjbD0o2CBvw9/LjY8aq2Lelw==} peerDependencies: @@ -13500,6 +13613,9 @@ packages: sonic-boom@3.8.1: resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + sonic-boom@4.2.1: + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} + sonner@0.3.5: resolution: {integrity: sha512-yIwaQ4dftMvFApuruto2t7wGyyaPRpj5qYBWYJIz4Z7uGcVn0IfqI/hWN0JyJN4izNbZFuCYZISf3fOGnvSlNQ==} peerDependencies: @@ -14002,6 +14118,10 @@ packages: thread-stream@2.7.0: resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + thread-stream@4.2.0: + resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} + engines: {node: '>=20'} + throat@5.0.0: resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} @@ -14132,8 +14252,8 @@ packages: typescript: '*' webpack: ^5.0.0 - ts-morph@24.0.0: - resolution: {integrity: sha512-2OAOg/Ob5yx9Et7ZX4CvTCc0UFoZHwLEJ+dpDPSUi5TgwwlTlX47w+iFRrEwzUZwYACjq83cgjS/Da50Ga37uw==} + ts-morph@27.0.2: + resolution: {integrity: sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==} ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} @@ -15161,6 +15281,10 @@ packages: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yargs@15.4.1: resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} engines: {node: '>=8'} @@ -15173,12 +15297,13 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yargs@18.0.0: + resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - yesno@0.4.0: - resolution: {integrity: sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA==} - yn@3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} @@ -15409,6 +15534,15 @@ snapshots: '@atproto/did': 0.3.0 zod: 3.25.76 + '@atproto-labs/did-resolver@0.3.4': + dependencies: + '@atproto-labs/fetch': 0.3.3 + '@atproto-labs/pipe': 0.2.3 + '@atproto-labs/simple-store': 0.4.3 + '@atproto-labs/simple-store-memory': 0.2.3 + '@atproto/did': 0.5.3 + zod: 3.25.76 + '@atproto-labs/fetch-node@0.2.0': dependencies: '@atproto-labs/fetch': 0.2.3 @@ -15420,6 +15554,10 @@ snapshots: dependencies: '@atproto-labs/pipe': 0.1.1 + '@atproto-labs/fetch@0.3.3': + dependencies: + '@atproto-labs/pipe': 0.2.3 + '@atproto-labs/handle-resolver@0.3.6': dependencies: '@atproto-labs/simple-store': 0.3.0 @@ -15434,11 +15572,18 @@ snapshots: '@atproto-labs/pipe@0.1.1': {} + '@atproto-labs/pipe@0.2.3': {} + '@atproto-labs/simple-store-memory@0.1.4': dependencies: '@atproto-labs/simple-store': 0.3.0 lru-cache: 10.4.3 + '@atproto-labs/simple-store-memory@0.2.3': + dependencies: + '@atproto-labs/simple-store': 0.4.3 + lru-cache: 10.4.3 + '@atproto-labs/simple-store-redis@0.0.1(ioredis@5.10.0)': dependencies: '@atproto-labs/simple-store': 0.3.0 @@ -15446,6 +15591,8 @@ snapshots: '@atproto-labs/simple-store@0.3.0': {} + '@atproto-labs/simple-store@0.4.3': {} + '@atproto-labs/xrpc-utils@0.0.24(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@atproto/xrpc': 0.7.7 @@ -15490,6 +15637,13 @@ snapshots: '@atproto/syntax': 0.5.0 zod: 3.25.76 + '@atproto/common-web@0.5.4': + dependencies: + '@atproto/lex-data': 0.1.4 + '@atproto/lex-json': 0.1.3 + '@atproto/syntax': 0.7.0 + zod: 3.25.76 + '@atproto/common@0.1.0': dependencies: '@ipld/dag-cbor': 7.0.3 @@ -15512,6 +15666,14 @@ snapshots: multiformats: 9.9.0 pino: 8.21.0 + '@atproto/common@0.7.0': + dependencies: + '@atproto/common-web': 0.5.4 + '@atproto/lex-cbor': 0.1.3 + '@atproto/lex-data': 0.1.4 + multiformats: 13.4.2 + pino: 10.3.1 + '@atproto/crypto@0.1.0': dependencies: '@noble/secp256k1': 1.7.2 @@ -15526,10 +15688,20 @@ snapshots: '@noble/hashes': 1.8.0 uint8arrays: 3.0.0 + '@atproto/crypto@0.5.3': + dependencies: + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + uint8arrays: 5.1.0 + '@atproto/did@0.3.0': dependencies: zod: 3.25.76 + '@atproto/did@0.5.3': + dependencies: + zod: 3.25.76 + '@atproto/identity@0.4.12': dependencies: '@atproto/common-web': 0.4.18 @@ -15551,21 +15723,24 @@ snapshots: multiformats: 9.9.0 zod: 3.25.76 + '@atproto/lex-builder@0.1.6': + dependencies: + '@atproto/lex-document': 0.1.4 + '@atproto/lex-schema': 0.2.0 + prettier: 3.8.4 + ts-morph: 27.0.2 + tslib: 2.8.1 + '@atproto/lex-cbor@0.0.14': dependencies: '@atproto/lex-data': 0.0.13 tslib: 2.8.1 - '@atproto/lex-cli@0.9.9': + '@atproto/lex-cbor@0.1.3': dependencies: - '@atproto/lexicon': 0.6.2 - '@atproto/syntax': 0.5.0 - chalk: 4.1.2 - commander: 9.5.0 - prettier: 3.8.4 - ts-morph: 24.0.0 - yesno: 0.4.0 - zod: 3.25.76 + '@atproto/lex-data': 0.1.4 + cborg: 4.5.8 + tslib: 2.8.1 '@atproto/lex-client@0.0.15': dependencies: @@ -15574,6 +15749,13 @@ snapshots: '@atproto/lex-schema': 0.0.14 tslib: 2.8.1 + '@atproto/lex-client@0.2.2': + dependencies: + '@atproto/lex-data': 0.1.4 + '@atproto/lex-json': 0.1.3 + '@atproto/lex-schema': 0.2.0 + tslib: 2.8.1 + '@atproto/lex-data@0.0.13': dependencies: multiformats: 9.9.0 @@ -15581,17 +15763,46 @@ snapshots: uint8arrays: 3.0.0 unicode-segmenter: 0.14.5 + '@atproto/lex-data@0.1.4': + dependencies: + multiformats: 13.4.2 + tslib: 2.8.1 + uint8arrays: 5.1.0 + unicode-segmenter: 0.14.5 + '@atproto/lex-document@0.0.15': dependencies: '@atproto/lex-schema': 0.0.14 core-js: 3.48.0(patch_hash=21c25ca1c792d52c0af7b9b268c163b4bda8a72bb061fcc6a07c81a8a42ecfe7) tslib: 2.8.1 + '@atproto/lex-document@0.1.4': + dependencies: + '@atproto/lex-schema': 0.2.0 + core-js: 3.48.0(patch_hash=21c25ca1c792d52c0af7b9b268c163b4bda8a72bb061fcc6a07c81a8a42ecfe7) + tslib: 2.8.1 + + '@atproto/lex-installer@0.1.5': + dependencies: + '@atproto/lex-builder': 0.1.6 + '@atproto/lex-cbor': 0.1.3 + '@atproto/lex-data': 0.1.4 + '@atproto/lex-document': 0.1.4 + '@atproto/lex-resolver': 0.1.5 + '@atproto/lex-schema': 0.2.0 + '@atproto/syntax': 0.7.0 + tslib: 2.8.1 + '@atproto/lex-json@0.0.13': dependencies: '@atproto/lex-data': 0.0.13 tslib: 2.8.1 + '@atproto/lex-json@0.1.3': + dependencies: + '@atproto/lex-data': 0.1.4 + tslib: 2.8.1 + '@atproto/lex-resolver@0.0.17': dependencies: '@atproto-labs/did-resolver': 0.2.6 @@ -15604,12 +15815,42 @@ snapshots: '@atproto/syntax': 0.5.0 tslib: 2.8.1 + '@atproto/lex-resolver@0.1.5': + dependencies: + '@atproto-labs/did-resolver': 0.3.4 + '@atproto/crypto': 0.5.3 + '@atproto/lex-client': 0.2.2 + '@atproto/lex-data': 0.1.4 + '@atproto/lex-document': 0.1.4 + '@atproto/lex-schema': 0.2.0 + '@atproto/repo': 0.10.4 + '@atproto/syntax': 0.7.0 + tslib: 2.8.1 + '@atproto/lex-schema@0.0.14': dependencies: '@atproto/lex-data': 0.0.13 '@atproto/syntax': 0.5.0 tslib: 2.8.1 + '@atproto/lex-schema@0.2.0': + dependencies: + '@atproto/lex-data': 0.1.4 + '@atproto/syntax': 0.7.0 + '@standard-schema/spec': 1.1.0 + tslib: 2.8.1 + + '@atproto/lex@0.2.0': + dependencies: + '@atproto/lex-builder': 0.1.6 + '@atproto/lex-client': 0.2.2 + '@atproto/lex-data': 0.1.4 + '@atproto/lex-installer': 0.1.5 + '@atproto/lex-json': 0.1.3 + '@atproto/lex-schema': 0.2.0 + tslib: 2.8.1 + yargs: 18.0.0 + '@atproto/lexicon@0.6.2': dependencies: '@atproto/common-web': 0.4.18 @@ -15778,6 +16019,17 @@ snapshots: - supports-color - utf-8-validate + '@atproto/repo@0.10.4': + dependencies: + '@atproto/common': 0.7.0 + '@atproto/common-web': 0.5.4 + '@atproto/crypto': 0.5.3 + '@atproto/lex-cbor': 0.1.3 + '@atproto/lex-data': 0.1.4 + '@atproto/syntax': 0.7.0 + varint: 6.0.0 + zod: 3.25.76 + '@atproto/repo@0.8.12': dependencies: '@atproto/common': 0.5.14 @@ -15801,6 +16053,11 @@ snapshots: iso-datestring-validator: 2.2.2 tslib: 2.8.1 + '@atproto/syntax@0.7.0': + dependencies: + iso-datestring-validator: 2.2.2 + tslib: 2.8.1 + '@atproto/ws-client@0.0.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@atproto/common': 0.5.14 @@ -20451,6 +20708,8 @@ snapshots: '@pagefind/windows-x64@1.3.0': optional: true + '@pinojs/redact@0.4.0': {} + '@pkgjs/parseargs@0.11.0': optional: true @@ -22267,6 +22526,8 @@ snapshots: '@speed-highlight/core@1.2.15': {} + '@standard-schema/spec@1.1.0': {} + '@swc/core-darwin-arm64@1.15.18': optional: true @@ -22340,9 +22601,9 @@ snapshots: '@tootallnate/once@2.0.0': {} - '@ts-morph/common@0.25.0': + '@ts-morph/common@0.28.1': dependencies: - minimatch: 9.0.9 + minimatch: 10.2.4 path-browserify: 1.0.1 tinyglobby: 0.2.15 @@ -23976,6 +24237,8 @@ snapshots: cborg@1.10.2: {} + cborg@4.5.8: {} + ccount@2.0.1: {} chalk-template@0.4.0: @@ -24164,6 +24427,12 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + cliui@9.0.1: + dependencies: + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + clone-deep@4.0.1: dependencies: is-plain-object: 2.0.4 @@ -30646,6 +30915,10 @@ snapshots: readable-stream: 4.7.0 split2: 4.2.0 + pino-abstract-transport@3.0.0: + dependencies: + split2: 4.2.0 + pino-http@8.6.1: dependencies: get-caller-file: 2.0.5 @@ -30655,6 +30928,22 @@ snapshots: pino-std-serializers@6.2.2: {} + pino-std-serializers@7.1.0: {} + + pino@10.3.1: + dependencies: + '@pinojs/redact': 0.4.0 + atomic-sleep: 1.0.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 3.0.0 + pino-std-serializers: 7.1.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.1 + thread-stream: 4.2.0 + pino@8.21.0: dependencies: atomic-sleep: 1.0.0 @@ -30819,6 +31108,8 @@ snapshots: process-warning@3.0.0: {} + process-warning@5.0.0: {} + process@0.11.10: {} proggy@2.0.0: {} @@ -31505,6 +31796,8 @@ snapshots: real-require@0.2.0: {} + real-require@1.0.0: {} + reanimated-color-picker@4.2.0(expo@55.0.6)(react-native-gesture-handler@2.30.0(react-native@0.83.2(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.0.8)(react@19.2.0)(utf-8-validate@5.0.10))(react@19.2.0))(react-native-reanimated@4.2.2(react-native-worklets@0.7.4(@babel/core@7.29.0)(react-native@0.83.2(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.0.8)(react@19.2.0)(utf-8-validate@5.0.10))(react@19.2.0))(react-native@0.83.2(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.0.8)(react@19.2.0)(utf-8-validate@5.0.10))(react@19.2.0))(react-native@0.83.2(@babel/core@7.29.0)(@types/react@19.2.14)(bufferutil@4.0.8)(react@19.2.0)(utf-8-validate@5.0.10))(react@19.2.0): dependencies: react: 19.2.0 @@ -32379,6 +32672,10 @@ snapshots: dependencies: atomic-sleep: 1.0.0 + sonic-boom@4.2.1: + dependencies: + atomic-sleep: 1.0.0 + sonner@0.3.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: react: 19.2.0 @@ -32937,6 +33234,10 @@ snapshots: dependencies: real-require: 0.2.0 + thread-stream@4.2.0: + dependencies: + real-require: 1.0.0 + throat@5.0.0: {} through2@2.0.5: @@ -33048,9 +33349,9 @@ snapshots: typescript: 5.6.3 webpack: 5.94.0(@swc/core@1.15.18(@swc/helpers@0.5.17)) - ts-morph@24.0.0: + ts-morph@27.0.2: dependencies: - '@ts-morph/common': 0.25.0 + '@ts-morph/common': 0.28.1 code-block-writer: 13.0.3 ts-node@10.9.2(@swc/core@1.15.18(@swc/helpers@0.5.17))(@types/node@16.18.126)(typescript@4.9.4): @@ -34140,6 +34441,8 @@ snapshots: yargs-parser@21.1.1: {} + yargs-parser@22.0.0: {} + yargs@15.4.1: dependencies: cliui: 6.0.0 @@ -34174,13 +34477,20 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yargs@18.0.0: + dependencies: + cliui: 9.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + string-width: 7.2.0 + y18n: 5.0.8 + yargs-parser: 22.0.0 + yauzl@2.10.0: dependencies: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 - yesno@0.4.0: {} - yn@3.1.1: {} yocto-queue@0.1.0: {}