JavaScript-optional public web frontend for Bluesky anartia.kelinci.net
sveltekit atcute bluesky typescript svelte

chore: upgrade to latest atcute

mary.my.id 99fb5a6d 37eea32e

verified
Changed files
+677 -757
src
lib
params
routes
+4 -2
package.json
··· 27 27 "wrangler": "^4.22.0" 28 28 }, 29 29 "dependencies": { 30 - "@atcute/bluesky": "^2.1.1", 30 + "@atcute/atproto": "^3.1.0", 31 + "@atcute/bluesky": "^3.1.4", 31 32 "@atcute/bluesky-richtext-parser": "^1.0.7", 32 33 "@atcute/bluesky-richtext-segmenter": "^2.0.3", 33 - "@atcute/client": "^3.1.0", 34 + "@atcute/client": "^4.0.3", 35 + "@atcute/lexicons": "^1.1.0", 34 36 "@badrap/valita": "^0.4.5", 35 37 "@mary/array-fns": "jsr:^0.1.4", 36 38 "@mary/date-fns": "jsr:^0.1.3",
+24 -16
pnpm-lock.yaml
··· 8 8 9 9 .: 10 10 dependencies: 11 + '@atcute/atproto': 12 + specifier: ^3.1.0 13 + version: 3.1.0 11 14 '@atcute/bluesky': 12 - specifier: ^2.1.1 13 - version: 2.1.1(@atcute/client@3.1.0) 15 + specifier: ^3.1.4 16 + version: 3.1.4 14 17 '@atcute/bluesky-richtext-parser': 15 18 specifier: ^1.0.7 16 19 version: 1.0.7 ··· 18 21 specifier: ^2.0.3 19 22 version: 2.0.3 20 23 '@atcute/client': 21 - specifier: ^3.1.0 22 - version: 3.1.0 24 + specifier: ^4.0.3 25 + version: 4.0.3 26 + '@atcute/lexicons': 27 + specifier: ^1.1.0 28 + version: 1.1.0 23 29 '@badrap/valita': 24 30 specifier: ^0.4.5 25 31 version: 0.4.5 ··· 85 91 '@atcute/bluesky-richtext-segmenter@2.0.3': 86 92 resolution: {integrity: sha512-8Jy2EHdqx0mKI0k8l4h2cnaBN1pGgSzSazO0Gp85ttIQmgynZsQX6l4OWgaQx0aNZFmqZRXJ7N0rj/b2dlO1eQ==} 87 93 88 - '@atcute/bluesky@2.1.1': 89 - resolution: {integrity: sha512-wEZfFW58J6yC1SqHcVJOn4qbHENTTzjeCEWthRT5HvKovADLqk54HSMSAuXDMBUbintSTBr0khQNZQ3ZdgzDdQ==} 90 - peerDependencies: 91 - '@atcute/client': ^3.0.0 92 - 93 94 '@atcute/bluesky@3.1.4': 94 95 resolution: {integrity: sha512-iSdZGk/UktgKpT/lI0/YxRjM3E5dkd6/vIa2mgH82lgRjI0jH5LJAfLXPyr2mPeZ/qku1gf2/KrkqJ9dfiNxVw==} 95 96 96 - '@atcute/client@3.1.0': 97 - resolution: {integrity: sha512-+rQPsHXSf0DUm8XoHoaH7Y2E8tIpbsW84djyPj7dqAyrFIjvGuJ1X1DvMufwbTIcmLerdy+dzl34iZcz/h3Vhg==} 97 + '@atcute/client@4.0.3': 98 + resolution: {integrity: sha512-RIOZWFVLca/HiPAAUDqQPOdOreCxTbL5cb+WUf5yqQOKIu5yEAP3eksinmlLmgIrlr5qVOE7brazUUzaskFCfw==} 99 + 100 + '@atcute/identity@1.0.3': 101 + resolution: {integrity: sha512-mNMxbKHFGys03A8JXKk0KfMBzdd0vrYMzZZWjpw1nYTs0+ea6bo5S1hwqVUZxHdo1gFHSe/t63jxQIF4yL9aKw==} 98 102 99 103 '@atcute/lexicons@1.1.0': 100 104 resolution: {integrity: sha512-LFqwnria78xLYb62Ri/+WwQpUTgZp2DuyolNGIIOV1dpiKhFFFh//nscHMA6IExFLQRqWDs3tTjy7zv0h3sf1Q==} ··· 1164 1168 '@atcute/bluesky': 3.1.4 1165 1169 '@atcute/lexicons': 1.1.0 1166 1170 1167 - '@atcute/bluesky@2.1.1(@atcute/client@3.1.0)': 1168 - dependencies: 1169 - '@atcute/client': 3.1.0 1170 - 1171 1171 '@atcute/bluesky@3.1.4': 1172 1172 dependencies: 1173 1173 '@atcute/atproto': 3.1.0 1174 1174 '@atcute/lexicons': 1.1.0 1175 1175 1176 - '@atcute/client@3.1.0': {} 1176 + '@atcute/client@4.0.3': 1177 + dependencies: 1178 + '@atcute/identity': 1.0.3 1179 + '@atcute/lexicons': 1.1.0 1180 + 1181 + '@atcute/identity@1.0.3': 1182 + dependencies: 1183 + '@atcute/lexicons': 1.1.0 1184 + '@badrap/valita': 0.4.5 1177 1185 1178 1186 '@atcute/lexicons@1.1.0': 1179 1187 dependencies:
+4 -4
src/hooks.server.ts
··· 1 - import { XRPCError } from '@atcute/client'; 1 + import { ClientResponseError } from '@atcute/client'; 2 2 import type { HandleServerError } from '@sveltejs/kit'; 3 3 4 4 export const handleError: HandleServerError = async ({ error, event, status, message }) => { 5 5 console.error(error); 6 6 7 - if (error instanceof XRPCError) { 7 + if (error instanceof ClientResponseError) { 8 8 if (error.status === 403) { 9 9 return { 10 10 message: `Upstream server is forbidding access to this resource`, 11 11 }; 12 12 } 13 13 14 - if (error.kind === 'AuthRequired' || error.kind === 'auth required') { 14 + if (error.error === 'AuthRequired' || error.error === 'auth required') { 15 15 return { 16 16 message: `Upstream server is requiring authentication to access this resource`, 17 17 }; 18 18 } 19 19 20 - if (error.kind === 'InternalServerError' || error.description === 'Internal Server Error') { 20 + if (error.error === 'InternalServerError' || error.description === 'Internal Server Error') { 21 21 return { 22 22 message: `Upstream server returned an internal error`, 23 23 };
+1 -1
src/lib/components/avatar.svelte
··· 15 15 </script> 16 16 17 17 <script lang="ts"> 18 - import type { AppBskyActorDefs } from '@atcute/client/lexicons'; 18 + import type { AppBskyActorDefs } from '@atcute/bluesky'; 19 19 20 20 interface Props { 21 21 profile?:
+16 -19
src/lib/components/embeds/embeds.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs } from '@atcute/client/lexicons'; 2 + import { unwrapEmbed, type AppBskyFeedDefs, type MediaEmbed, type RecordEmbed } from '@atcute/bluesky'; 3 3 4 4 import { findLabel, FlagsBlurMedia } from '$lib/moderation'; 5 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 - import { unwrapEmbedView, type MediaEmbed, type RecordEmbed } from '$lib/utils/bluesky/embeds'; 5 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 6 import { collectionToLabel } from '$lib/utils/bluesky/records'; 8 7 9 8 import ContentHider from '../content-hider.svelte'; ··· 27 26 28 27 const { embed, large = false, post }: Props = $props(); 29 28 30 - const { media, record } = $derived(unwrapEmbedView(embed)); 29 + const { media, record } = $derived(unwrapEmbed(embed)); 31 30 </script> 32 31 33 32 <div class="embeds"> ··· 56 55 {/snippet} 57 56 58 57 {#snippet Record(embed: RecordEmbed)} 59 - {@const record = embed.record} 60 - 61 - {#if record.$type === 'app.bsky.embed.record#viewRecord'} 62 - <QuoteEmbed embed={record} {large} /> 63 - {:else if record.$type === 'app.bsky.feed.defs#generatorView'} 64 - <FeedEmbed embed={record} /> 65 - {:else if record.$type === 'app.bsky.graph.defs#listView'} 66 - <ListEmbed embed={record} /> 67 - {:else if record.$type === 'app.bsky.graph.defs#starterPackViewBasic'} 68 - <StarterpackEmbed embed={record} {large} /> 58 + {#if embed.$type === 'app.bsky.embed.record#viewRecord'} 59 + <QuoteEmbed {embed} {large} /> 60 + {:else if embed.$type === 'app.bsky.feed.defs#generatorView'} 61 + <FeedEmbed {embed} /> 62 + {:else if embed.$type === 'app.bsky.graph.defs#listView'} 63 + <ListEmbed {embed} /> 64 + {:else if embed.$type === 'app.bsky.graph.defs#starterPackViewBasic'} 65 + <StarterpackEmbed {embed} {large} /> 69 66 {:else} 70 - {@const uri = parseAddressedAtUri(record.uri)} 67 + {@const uri = assertCanonicalResourceUri(embed.uri)} 71 68 72 - {#if uri.collection === 'app.bsky.feed.post' && (record.$type === 'app.bsky.embed.record#viewBlocked' || record.$type === 'app.bsky.embed.record#viewDetached')} 73 - <QuoteBlockedEmbed embed={record} {uri} /> 69 + {#if uri.collection === 'app.bsky.feed.post' && (embed.$type === 'app.bsky.embed.record#viewBlocked' || embed.$type === 'app.bsky.embed.record#viewDetached')} 70 + <QuoteBlockedEmbed {embed} {uri} /> 74 71 {:else} 75 72 {@const resource = collectionToLabel(uri.collection)} 76 73 {@const isUnavailable = 77 74 resource && 78 - (record.$type === 'app.bsky.embed.record#viewNotFound' || 79 - record.$type === 'app.bsky.embed.record#viewBlocked')} 75 + (embed.$type === 'app.bsky.embed.record#viewNotFound' || 76 + embed.$type === 'app.bsky.embed.record#viewBlocked')} 80 77 81 78 {@render Message(isUnavailable ? `This ${resource} is unavailable` : `Unsupported record embed`)} 82 79 {/if}
+1 -1
src/lib/components/embeds/external-embed.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyEmbedExternal } from '@atcute/client/lexicons'; 2 + import type { AppBskyEmbedExternal } from '@atcute/bluesky'; 3 3 4 4 import { redirectBskyUrl } from '$lib/redirector'; 5 5 import { truncateRight } from '$lib/utils/strings';
+3 -3
src/lib/components/embeds/feed-embed.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedDefs } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 6 import { findLabel, FlagsBlurMedia } from '$lib/moderation'; 7 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 7 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 8 8 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 9 9 import { trimRichText } from '$lib/utils/bluesky/richtext'; 10 10 import { truncateRight } from '$lib/utils/strings'; ··· 22 22 const blurAvi = $derived(!!findLabel(feed.labels, creator.did, FlagsBlurMedia)); 23 23 </script> 24 24 25 - <a href="{base}/{creator.did}/feeds/{parseAddressedAtUri(feed.uri).rkey}" class="feed-embed"> 25 + <a href="{base}/{creator.did}/feeds/{assertCanonicalResourceUri(feed.uri).rkey}" class="feed-embed"> 26 26 <div class="main"> 27 27 <Avatar type="generator" src={feed.avatar} blur={blurAvi} /> 28 28
+1 -1
src/lib/components/embeds/image-embed.svelte
··· 3 3 </script> 4 4 5 5 <script lang="ts"> 6 - import type { AppBskyEmbedImages } from '@atcute/client/lexicons'; 6 + import type { AppBskyEmbedImages } from '@atcute/bluesky'; 7 7 8 8 import { trimRichText } from '$lib/utils/bluesky/richtext'; 9 9
+3 -3
src/lib/components/embeds/list-embed.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyGraphDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyGraphDefs } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 6 import { findLabel, FlagsBlurMedia } from '$lib/moderation'; 7 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 7 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 8 8 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 9 9 import { purposeToLabel } from '$lib/utils/bluesky/lists'; 10 10 import { trimRichText } from '$lib/utils/bluesky/richtext'; ··· 23 23 const blurAvi = $derived(!!findLabel(list.labels, creator.did, FlagsBlurMedia)); 24 24 </script> 25 25 26 - <a href="{base}/{creator.did}/lists/{parseAddressedAtUri(list.uri).rkey}" class="list-embed"> 26 + <a href="{base}/{creator.did}/lists/{assertCanonicalResourceUri(list.uri).rkey}" class="list-embed"> 27 27 <div class="main"> 28 28 <Avatar type="list" src={list.avatar} blur={blurAvi} /> 29 29
+4 -5
src/lib/components/embeds/quote-blocked-embed.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyEmbedRecord, Brand } from '@atcute/client/lexicons'; 2 + import type { AppBskyEmbedRecord } from '@atcute/bluesky'; 3 + import type { $type, ParsedCanonicalResourceUri } from '@atcute/lexicons'; 3 4 4 5 import { base } from '$app/paths'; 5 - 6 - import type { AddressedAtUri } from '$lib/types/at-uri'; 7 6 8 7 import CircleBanSignOutlined from '$lib/components/central-icons/circle-ban-sign-outlined.svelte'; 9 8 10 9 interface Props { 11 - embed: Brand.Union<AppBskyEmbedRecord.ViewBlocked | AppBskyEmbedRecord.ViewDetached>; 12 - uri: AddressedAtUri; 10 + embed: $type.enforce<AppBskyEmbedRecord.ViewBlocked | AppBskyEmbedRecord.ViewDetached>; 11 + uri: ParsedCanonicalResourceUri; 13 12 } 14 13 15 14 const { embed, uri }: Props = $props();
+5 -6
src/lib/components/embeds/quote-embed.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyEmbedRecord, AppBskyFeedPost } from '@atcute/client/lexicons'; 2 + import { unwrapMediaEmbed, type AppBskyEmbedRecord, type AppBskyFeedPost } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 6 import { findLabel, FlagsBlurContent, FlagsBlurMedia } from '$lib/moderation'; 7 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 7 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 8 8 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 9 - import { unwrapMediaEmbedView } from '$lib/utils/bluesky/embeds'; 10 9 import { trimRichText } from '$lib/utils/bluesky/richtext'; 11 10 12 11 import Avatar from '$lib/components/avatar.svelte'; ··· 24 23 25 24 const { embed: quote, large = false }: Props = $props(); 26 25 27 - const record = $derived(quote.value as AppBskyFeedPost.Record); 26 + const record = $derived(quote.value as AppBskyFeedPost.Main); 28 27 const text = $derived(trimRichText(record.text)); 29 28 30 29 const author = $derived(quote.author); 31 30 const authorName = $derived(normalizeDisplayName(author.displayName ?? '')); 32 31 33 32 const embed = $derived(quote.embeds?.[0]); 34 - const media = $derived(unwrapMediaEmbedView(embed)); 33 + const media = $derived(unwrapMediaEmbed(embed)); 35 34 36 35 const blurAvi = $derived(!!findLabel(author.labels, author.did, FlagsBlurMedia)); 37 36 const blurContent = $derived(findLabel(quote.labels, author.did, FlagsBlurContent)); ··· 39 38 </script> 40 39 41 40 <ContentHider blur={blurContent}> 42 - <a href="{base}/{author.did}/{parseAddressedAtUri(quote.uri).rkey}#main" class="quote-embed"> 41 + <a href="{base}/{author.did}/{assertCanonicalResourceUri(quote.uri).rkey}#main" class="quote-embed"> 43 42 <div class="meta"> 44 43 <Avatar profile={author} src={author.avatar} size="xs" blur={blurAvi} /> 45 44
+4 -4
src/lib/components/embeds/starterpack-embed.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyGraphDefs, AppBskyGraphStarterpack } from '@atcute/client/lexicons'; 2 + import type { AppBskyGraphDefs, AppBskyGraphStarterpack } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 8 8 import { trimRichText } from '$lib/utils/bluesky/richtext'; 9 9 import { truncateRight } from '$lib/utils/strings'; ··· 17 17 18 18 const { embed: pack, large = false }: Props = $props(); 19 19 20 - const record = pack.record as AppBskyGraphStarterpack.Record; 20 + const record = pack.record as AppBskyGraphStarterpack.Main; 21 21 22 22 const creator = $derived(pack.creator); 23 23 24 - const rkey = $derived(parseAddressedAtUri(pack.uri).rkey); 24 + const rkey = $derived(assertCanonicalResourceUri(pack.uri).rkey); 25 25 </script> 26 26 27 27 <a href="{base}/{creator.did}/packs/{rkey}" class="starterpack-embed">
+1 -1
src/lib/components/embeds/video-standalone-embed.svelte
··· 3 3 </script> 4 4 5 5 <script lang="ts"> 6 - import type { AppBskyEmbedVideo } from '@atcute/client/lexicons'; 6 + import type { AppBskyEmbedVideo } from '@atcute/bluesky'; 7 7 8 8 import { dev } from '$app/environment'; 9 9 import { base } from '$app/paths';
+1 -1
src/lib/components/embeds/video-thumbnail-embed.svelte
··· 1 1 <script lang="ts"> 2 2 // This is meant to be used inside quote embeds, so it's non-standalone. 3 3 4 - import type { AppBskyEmbedVideo } from '@atcute/client/lexicons'; 4 + import type { AppBskyEmbedVideo } from '@atcute/bluesky'; 5 5 6 6 import { replaceVideoCdnUrl } from '$lib/utils/bluesky/videos'; 7 7
+3 -3
src/lib/components/feeds/feed-item.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedDefs } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 8 8 import { trimRichText } from '$lib/utils/bluesky/richtext'; 9 9 import { formatLongNumber } from '$lib/utils/intl/number'; ··· 19 19 20 20 const creator = $derived(feed.creator); 21 21 22 - const href = $derived(`${base}/${creator.did}/feeds/${parseAddressedAtUri(feed.uri).rkey}`); 22 + const href = $derived(`${base}/${creator.did}/feeds/${assertCanonicalResourceUri(feed.uri).rkey}`); 23 23 </script> 24 24 25 25 <div class="feed-item">
+3 -3
src/lib/components/lists/list-item.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyGraphDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyGraphDefs } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 8 8 import { purposeToLabel } from '$lib/utils/bluesky/lists'; 9 9 import { trimRichText } from '$lib/utils/bluesky/richtext'; ··· 19 19 20 20 const creator = $derived(list.creator); 21 21 22 - const href = $derived(`${base}/${creator.did}/lists/${parseAddressedAtUri(list.uri).rkey}`); 22 + const href = $derived(`${base}/${creator.did}/lists/${assertCanonicalResourceUri(list.uri).rkey}`); 23 23 </script> 24 24 25 25 <div class="list-item">
+1 -1
src/lib/components/profiles/profile-item.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyActorDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyActorDefs } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5
+4 -4
src/lib/components/starterpacks/starterpack-item.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyGraphDefs, AppBskyGraphStarterpack } from '@atcute/client/lexicons'; 2 + import type { AppBskyGraphDefs, AppBskyGraphStarterpack } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 8 8 import { trimRichText } from '$lib/utils/bluesky/richtext'; 9 9 import { truncateMiddle } from '$lib/utils/strings'; ··· 18 18 19 19 const creator = $derived(pack.creator); 20 20 21 - const record = $derived(pack.record as AppBskyGraphStarterpack.Record); 22 - const href = $derived(`${base}/${creator.did}/packs/${parseAddressedAtUri(pack.uri).rkey}`); 21 + const record = $derived(pack.record as AppBskyGraphStarterpack.Main); 22 + const href = $derived(`${base}/${creator.did}/packs/${assertCanonicalResourceUri(pack.uri).rkey}`); 23 23 </script> 24 24 25 25 <div class="starterpack-item">
+4 -4
src/lib/components/timeline/post-feed-item.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedPost } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedPost } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 6 import type { UiTimelineItem } from '$lib/models/timeline'; 7 7 import { findLabel, FlagsBlurContent, FlagsBlurMedia } from '$lib/moderation'; 8 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 8 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 9 9 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 10 10 11 11 import ArrowsRepeatRightLeftOutlined from '$lib/components/central-icons/arrows-repeat-right-left-outlined.svelte'; ··· 29 29 const author = $derived(post.author); 30 30 const authorUrl = $derived(`${base}/${author.did}`); 31 31 32 - const record = $derived(post.record as AppBskyFeedPost.Record); 33 - const postUrl = $derived(`${base}/${author.did}/${parseAddressedAtUri(post.uri).rkey}#main`); 32 + const record = $derived(post.record as AppBskyFeedPost.Main); 33 + const postUrl = $derived(`${base}/${author.did}/${assertCanonicalResourceUri(post.uri).rkey}#main`); 34 34 35 35 const isAviBlurred = $derived(!!findLabel(author.labels, author.did, FlagsBlurMedia)); 36 36 const blur = $derived(findLabel(post.labels, author.did, FlagsBlurContent));
+2 -2
src/lib/components/timeline/post-meta.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/bluesky'; 3 3 4 4 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 5 5 ··· 17 17 const author = $derived(post.author); 18 18 const authorName = $derived(normalizeDisplayName(author.displayName ?? '')); 19 19 20 - const createdAt = $derived((post.record as AppBskyFeedPost.Record).createdAt); 20 + const createdAt = $derived((post.record as AppBskyFeedPost.Main).createdAt); 21 21 </script> 22 22 23 23 <div class={['post-meta', gutterBottom && 'has-bottom-gutter']}>
+1 -1
src/lib/components/timeline/post-metrics.svelte
··· 1 1 <script lang="ts"> 2 2 import type { Component } from 'svelte'; 3 3 4 - import type { AppBskyFeedDefs } from '@atcute/client/lexicons'; 4 + import type { AppBskyFeedDefs } from '@atcute/bluesky'; 5 5 6 6 import { formatCompactNumber, formatLongNumber } from '$lib/utils/intl/number'; 7 7
+2 -2
src/lib/constants.ts
··· 1 - import type { At } from '@atcute/client/lexicons'; 1 + import type { CanonicalResourceUri } from '@atcute/lexicons'; 2 2 3 3 // Popular feeds that requires authentication to view 4 - export const AUTHENTICATED_FEEDS: At.CanonicalResourceUri[] = [ 4 + export const AUTHENTICATED_FEEDS: CanonicalResourceUri[] = [ 5 5 // "Popular With Friends" by @bsky.app 6 6 `at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/with-friends`, 7 7 // "Mutuals" by @skyfeed.xyz
+1 -1
src/lib/models/timeline.ts
··· 1 - import type { AppBskyFeedDefs } from '@atcute/client/lexicons'; 1 + import type { AppBskyFeedDefs } from '@atcute/bluesky'; 2 2 import { mapDefined } from '@mary/array-fns'; 3 3 4 4 export type TimelineItem = AppBskyFeedDefs.FeedViewPost;
+3 -2
src/lib/moderation.ts
··· 1 - import type { At, ComAtprotoLabelDefs } from '@atcute/client/lexicons'; 1 + import type { ComAtprotoLabelDefs } from '@atcute/atproto'; 2 + import type { Did } from '@atcute/lexicons'; 2 3 3 4 export const FlagsNone = 0; 4 5 ··· 123 124 124 125 export const findLabel = ( 125 126 labels: Label[] | undefined, 126 - authorDid: At.Did, 127 + authorDid: Did, 127 128 mask: number, 128 129 ): LabelDefinition | undefined => { 129 130 if (labels?.length) {
+2 -2
src/lib/queries/constellation.ts
··· 1 1 import { error } from '@sveltejs/kit'; 2 2 3 - import type { Records } from '@atcute/client/lexicons'; 3 + import type { Did } from '@atcute/lexicons'; 4 + import type { Records } from '@atcute/lexicons/ambient'; 4 5 5 6 import * as v from '@badrap/valita'; 6 7 7 8 import { PUBLIC_APP_USER_AGENT, PUBLIC_CONSTELLATION_URL } from '$env/static/public'; 8 9 9 - import type { Did } from '$lib/types/identity'; 10 10 import { didString, integer, nsidString, recordKeyString } from '$lib/types/valita'; 11 11 12 12 const linkResponse = v.object({
+9 -10
src/lib/queries/handle.ts
··· 1 - import type { XRPC } from '@atcute/client'; 2 - import type { At } from '@atcute/client/lexicons'; 1 + import { ok, type Client } from '@atcute/client'; 2 + import type { Did, Handle } from '@atcute/lexicons'; 3 3 4 - import type { Did } from '$lib/types/identity'; 4 + export const resolveHandle = async ({ client, handle }: { client: Client; handle: Handle }): Promise<Did> => { 5 + const data = await ok( 6 + client.get('com.atproto.identity.resolveHandle', { 7 + params: { handle }, 8 + }), 9 + ); 5 10 6 - export const resolveHandle = async ({ rpc, handle }: { rpc: XRPC; handle: At.Handle }): Promise<Did> => { 7 - const { data } = await rpc.get('com.atproto.identity.resolveHandle', { 8 - params: { handle }, 9 - }); 10 - 11 - // because my types are stricter than atcute's 12 - return data.did as Did; 11 + return data.did; 13 12 };
+23 -11
src/lib/queries/post.ts
··· 1 - import { XRPC, XRPCError } from '@atcute/client'; 2 - import type { AppBskyFeedDefs, At } from '@atcute/client/lexicons'; 1 + import type { AppBskyFeedDefs } from '@atcute/bluesky'; 2 + import { type Client, ClientResponseError, ok } from '@atcute/client'; 3 + import type { ResourceUri } from '@atcute/lexicons'; 3 4 4 5 export interface GetPostReturn { 5 6 post: AppBskyFeedDefs.PostView; 6 7 threadgate?: AppBskyFeedDefs.ThreadgateView; 7 8 } 8 9 9 - export const getPost = async ({ rpc, uri }: { rpc: XRPC; uri: At.ResourceUri }): Promise<GetPostReturn> => { 10 - const { data } = await rpc.get('app.bsky.feed.getPostThread', { 11 - params: { 12 - uri: uri, 13 - depth: 0, 14 - parentHeight: 0, 15 - }, 16 - }); 10 + export const getPost = async ({ 11 + client, 12 + uri, 13 + }: { 14 + client: Client; 15 + uri: ResourceUri; 16 + }): Promise<GetPostReturn> => { 17 + const data = await ok( 18 + client.get('app.bsky.feed.getPostThread', { 19 + params: { 20 + uri: uri, 21 + depth: 0, 22 + parentHeight: 0, 23 + }, 24 + }), 25 + ); 17 26 18 27 const { thread, threadgate } = data; 19 28 switch (thread.$type) { 20 29 case 'app.bsky.feed.defs#notFoundPost': 21 30 case 'app.bsky.feed.defs#blockedPost': { 22 - throw new XRPCError(400, { kind: 'NotFound', description: 'Post not found' }); 31 + throw new ClientResponseError({ 32 + status: 400, 33 + data: { error: 'NotFound', message: `Post not found` }, 34 + }); 23 35 } 24 36 } 25 37
+42 -36
src/lib/queries/timeline.ts
··· 1 - import type { XRPC } from '@atcute/client'; 2 - import type { AppBskyActorDefs, AppBskyFeedDefs, AppBskyFeedGetTimeline, At } from '@atcute/client/lexicons'; 1 + import type { AppBskyActorDefs, AppBskyFeedDefs, AppBskyFeedGetTimeline } from '@atcute/bluesky'; 2 + import { type Client, ok } from '@atcute/client'; 3 3 4 4 import { 5 5 buildTimelineSlices, ··· 10 10 type TimelineSlice, 11 11 type UiTimelineItem, 12 12 } from '$lib/models/timeline'; 13 - import type { Did } from '$lib/types/identity'; 14 13 import { assertNever } from '$lib/utils/invariant'; 14 + import type { Did, ResourceUri } from '@atcute/lexicons'; 15 15 16 16 export const enum TimelineType { 17 17 PROFILE, ··· 35 35 36 36 export interface CustomFeedTimelineParams { 37 37 type: TimelineType.CUSTOM_FEED; 38 - feed: At.ResourceUri; 38 + feed: ResourceUri; 39 39 cursor?: string; 40 40 } 41 41 42 42 export interface UserListTimelineParams { 43 43 type: TimelineType.USER_LIST; 44 - list: At.ResourceUri; 44 + list: ResourceUri; 45 45 cursor?: string; 46 46 } 47 47 ··· 55 55 const PAGE_LIMIT = 50; 56 56 57 57 export const fetchTimeline = async ({ 58 - rpc, 58 + client, 59 59 params, 60 60 }: { 61 - rpc: XRPC; 61 + client: Client; 62 62 params: TimelineParams; 63 63 }): Promise<TimelinePage> => { 64 64 let sliceFilter: SliceFilter | undefined; 65 65 let postFilter: PostFilter | undefined; 66 66 67 - let timeline: AppBskyFeedGetTimeline.Output; 67 + let timeline: AppBskyFeedGetTimeline.$output; 68 68 69 69 switch (params.type) { 70 70 case TimelineType.PROFILE: { 71 - const { data } = await rpc.get('app.bsky.feed.getAuthorFeed', { 72 - params: { 73 - actor: params.actor, 74 - cursor: params.cursor, 75 - limit: PAGE_LIMIT, 76 - includePins: params.pinned ?? params.filter !== ProfileFilter.MEDIA, 77 - filter: 78 - params.filter === ProfileFilter.MEDIA 79 - ? 'posts_with_media' 80 - : params.filter === ProfileFilter.POSTS_WITH_REPLIES 81 - ? 'posts_with_replies' 82 - : 'posts_and_author_threads', 83 - }, 84 - }); 71 + const data = await ok( 72 + client.get('app.bsky.feed.getAuthorFeed', { 73 + params: { 74 + actor: params.actor, 75 + cursor: params.cursor, 76 + limit: PAGE_LIMIT, 77 + includePins: params.pinned ?? params.filter !== ProfileFilter.MEDIA, 78 + filter: 79 + params.filter === ProfileFilter.MEDIA 80 + ? 'posts_with_media' 81 + : params.filter === ProfileFilter.POSTS_WITH_REPLIES 82 + ? 'posts_with_replies' 83 + : 'posts_and_author_threads', 84 + }, 85 + }), 86 + ); 85 87 86 88 timeline = data; 87 89 ··· 92 94 break; 93 95 } 94 96 case TimelineType.CUSTOM_FEED: { 95 - const { data } = await rpc.get('app.bsky.feed.getFeed', { 96 - params: { 97 - feed: params.feed, 98 - cursor: params.cursor, 99 - limit: PAGE_LIMIT, 100 - }, 101 - }); 97 + const data = await ok( 98 + client.get('app.bsky.feed.getFeed', { 99 + params: { 100 + feed: params.feed, 101 + cursor: params.cursor, 102 + limit: PAGE_LIMIT, 103 + }, 104 + }), 105 + ); 102 106 103 107 timeline = { 104 108 // Discover feed, wooo. ··· 110 114 break; 111 115 } 112 116 case TimelineType.USER_LIST: { 113 - const { data } = await rpc.get('app.bsky.feed.getListFeed', { 114 - params: { 115 - list: params.list, 116 - cursor: params.cursor, 117 - limit: PAGE_LIMIT, 118 - }, 119 - }); 117 + const data = await ok( 118 + client.get('app.bsky.feed.getListFeed', { 119 + params: { 120 + list: params.list, 121 + cursor: params.cursor, 122 + limit: PAGE_LIMIT, 123 + }, 124 + }), 125 + ); 120 126 121 127 timeline = data; 122 128 break;
+6 -7
src/lib/redirector.ts
··· 1 1 import { base } from '$app/paths'; 2 - import { type PartialAtUri, parsePartialAtUri } from './types/at-uri'; 2 + 3 + import { isDid, isHandle, isRecordKey, isTid, parseResourceUri } from '@atcute/lexicons/syntax'; 3 4 4 - import { isDid, isHandle } from './types/identity'; 5 - import { isRecordKey, isTid } from './types/rkey'; 6 5 import { 7 6 BSKY_FEED_LINK_RE, 8 7 BSKY_GO_SHORTLINK_RE, ··· 206 205 }; 207 206 208 207 export const redirectAtUri = (raw: string): RedirectResult => { 209 - let uri: PartialAtUri; 210 - try { 211 - uri = parsePartialAtUri(raw); 212 - } catch (e) { 208 + const result = parseResourceUri(raw); 209 + if (!result.ok) { 213 210 return; 214 211 } 212 + 213 + const uri = result.value; 215 214 216 215 if (uri.rkey) { 217 216 switch (uri.collection) {
+16 -9
src/lib/rss.ts
··· 1 + import { 2 + unwrapEmbed, 3 + type AppBskyFeedDefs, 4 + type AppBskyFeedPost, 5 + type AppBskyRichtextFacet, 6 + } from '@atcute/bluesky'; 1 7 import { segmentize } from '@atcute/bluesky-richtext-segmenter'; 2 - import type { AppBskyFeedDefs, AppBskyFeedPost, AppBskyRichtextFacet } from '@atcute/client/lexicons'; 3 8 4 9 import { PUBLIC_APP_URL } from '$env/static/public'; 5 10 6 11 import { findLabel, FlagsBlurMedia } from './moderation'; 7 - import { parseAddressedAtUri } from './types/at-uri'; 8 - import { getQuoteEmbedView, unwrapEmbedView } from './utils/bluesky/embeds'; 12 + import { assertCanonicalResourceUri } from './types/at-uri'; 13 + import { getQuoteEmbed } from './utils/bluesky/embeds'; 9 14 import { assertNever } from './utils/invariant'; 10 15 import type { UnwrapArray } from './utils/types'; 11 16 ··· 181 186 const post = item.post; 182 187 const author = post.author; 183 188 184 - const record = post.record as AppBskyFeedPost.Record; 189 + const record = post.record as AppBskyFeedPost.Main; 185 190 186 - const { media, record: recordEmbed } = unwrapEmbedView(post.embed); 187 - const quote = getQuoteEmbedView(recordEmbed); 191 + const { media, record: recordEmbed } = unwrapEmbed(post.embed); 192 + const quote = getQuoteEmbed(recordEmbed); 188 193 189 194 const shouldBlurMedia = !!findLabel(post.labels, author.did, FlagsBlurMedia); 190 195 ··· 195 200 switch (quote.$type) { 196 201 case 'app.bsky.embed.record#viewRecord': { 197 202 const author = quote.author; 198 - const record = quote.value as AppBskyFeedPost.Record; 203 + const record = quote.value as AppBskyFeedPost.Main; 199 204 200 - const postUrl = `${PUBLIC_APP_URL}/${author.did}/${parseAddressedAtUri(quote.uri).rkey}`; 205 + const uri = assertCanonicalResourceUri(quote.uri); 206 + 207 + const postUrl = `${PUBLIC_APP_URL}/${author.did}/${uri.rkey}`; 201 208 202 209 html += `<blockquote>`; 203 210 html += `<b><a href="${escapeAttribute(postUrl)}">`; ··· 225 232 226 233 return { 227 234 id: `${post.uri}|${post.cid}`, 228 - url: `${PUBLIC_APP_URL}/${author.did}/${parseAddressedAtUri(post.uri).rkey}`, 235 + url: `${PUBLIC_APP_URL}/${author.did}/${assertCanonicalResourceUri(post.uri).rkey}`, 229 236 date: new Date(post.indexedAt), 230 237 description: { html }, 231 238 images:
+19 -55
src/lib/types/at-uri.ts
··· 1 - import type { At, Records } from '@atcute/client/lexicons'; 1 + import { 2 + parseCanonicalResourceUri, 3 + type Did, 4 + type Handle, 5 + type Nsid, 6 + type ParsedCanonicalResourceUri, 7 + type ResourceUri, 8 + } from '@atcute/lexicons'; 9 + import type { Records } from '@atcute/lexicons/ambient'; 2 10 3 11 import { assert } from '$lib/utils/invariant'; 4 12 5 - import { isDid, isHandle, type Did, type Handle } from './identity'; 6 - import { isNsid, type Nsid } from './nsid'; 7 - import { isRecordKey, type RecordKey } from './rkey'; 8 - 9 - const ATURI_RE = 10 - /^at:\/\/([a-zA-Z0-9._:%-]+)(?:\/([a-zA-Z0-9-.]+)(?:\/([a-zA-Z0-9._~:@!$&%')(*+,;=-]+))?)?(?:#(\/[a-zA-Z0-9._~:@!$&%')(*+,;=\-[\]/\\]*))?$/; 11 - 12 - export type AddressedAtUri = { 13 - repo: Did; 14 - collection: Nsid; 15 - rkey: RecordKey; 16 - fragment: string | undefined; 17 - }; 18 - 19 - export const parseAddressedAtUri = (str: string): AddressedAtUri => { 20 - const match = ATURI_RE.exec(str); 21 - assert(match !== null, `invalid addressed-at-uri: ${str}`); 22 - 23 - const [, r, c, k, f] = match; 24 - assert(isDid(r), `invalid repo in addressed-at-uri: ${r}`); 25 - assert(isNsid(c), `invalid collection in addressed-at-uri: ${c}`); 26 - assert(isRecordKey(k), `invalid rkey in addressed-at-uri: ${k}`); 27 - 28 - return { 29 - repo: r, 30 - collection: c, 31 - rkey: k, 32 - fragment: f, 33 - }; 34 - }; 35 - 36 - export type PartialAtUri = 37 - | { repo: Did | Handle; collection: undefined; rkey: undefined; fragment: string | undefined } 38 - | { repo: Did | Handle; collection: Nsid; rkey: undefined; fragment: string | undefined } 39 - | { repo: Did | Handle; collection: Nsid; rkey: RecordKey; fragment: string | undefined }; 40 - 41 - export const parsePartialAtUri = (str: string): PartialAtUri => { 42 - const match = ATURI_RE.exec(str); 43 - assert(match !== null, `invalid partial-at-uri: ${str}`); 44 - 45 - const [, r, c, k, f] = match; 46 - assert(isDid(r) || isHandle(r), `invalid repo in partial-at-uri: ${r}`); 47 - assert(c === undefined || isNsid(c), `invalid collection in partial-at-uri: ${c}`); 48 - assert(k === undefined || isRecordKey(k), `invalid rkey in partial-at-uri: ${k}`); 49 - 50 - return { 51 - repo: r, 52 - collection: c, 53 - rkey: k, 54 - fragment: f, 55 - }; 56 - }; 57 - 58 13 export const makeAtUri = ( 59 14 repo: Did | Handle, 60 15 collection: keyof Records | (Nsid & {}), 61 16 rkey: string, 62 - ): At.ResourceUri => { 17 + ): ResourceUri => { 63 18 return `at://${repo}/${collection as Nsid}/${rkey}`; 64 19 }; 20 + 21 + export const assertCanonicalResourceUri = (input: string): ParsedCanonicalResourceUri => { 22 + const result = parseCanonicalResourceUri(input); 23 + if (!result.ok) { 24 + assert(false, result.error); 25 + } 26 + 27 + return result.value; 28 + };
-34
src/lib/types/identity.ts
··· 1 - export type Handle = `${string}.${string}`; 2 - 3 - const HANDLE_RE = 4 - /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+([a-zA-Z][a-zA-Z0-9-]{0,61}[a-zA-Z])$/; 5 - 6 - export const isHandle = (input: unknown): input is Handle => { 7 - return typeof input === 'string' && input.length >= 3 && input.length <= 253 && HANDLE_RE.test(input); 8 - }; 9 - 10 - export type Did<TMethod extends string = string> = `did:${TMethod}:${string}`; 11 - export type AtprotoDid = Did<'plc' | 'web'>; 12 - 13 - const DID_RE = /^did:([a-z]+):([a-zA-Z0-9._:%\-]*[a-zA-Z0-9._\-])$/; 14 - 15 - export const isDid = (input: unknown): input is Did => { 16 - return typeof input === 'string' && input.length >= 7 && input.length <= 2048 && DID_RE.test(input); 17 - }; 18 - 19 - const ATPROTO_WEB_DID_RE = 20 - /^did:web:([a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(?:\.[a-zA-Z]{2,})|localhost(?:%3[aA]\d+)?)$/; 21 - 22 - export const isAtprotoWebDid = (input: unknown): input is Did<'web'> => { 23 - return typeof input === 'string' && input.length >= 12 && ATPROTO_WEB_DID_RE.test(input); 24 - }; 25 - 26 - const PLC_DID_RE = /^did:plc:([a-z2-7]{24})$/; 27 - 28 - export const isPlcDid = (input: unknown): input is Did<'plc'> => { 29 - return typeof input === 'string' && input.length === 32 && PLC_DID_RE.test(input); 30 - }; 31 - 32 - export const isAtprotoDid = (input: unknown): input is AtprotoDid => { 33 - return isPlcDid(input) || isAtprotoWebDid(input); 34 - };
-8
src/lib/types/nsid.ts
··· 1 - export type Nsid = `${string}.${string}.${string}`; 2 - 3 - const NSID_RE = 4 - /^[a-zA-Z](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?:\.[a-zA-Z](?:[a-zA-Z0-9]{0,62})?)$/; 5 - 6 - export const isNsid = (input: unknown): input is Nsid => { 7 - return typeof input === 'string' && input.length >= 5 && input.length <= 317 && NSID_RE.test(input); 8 - };
-13
src/lib/types/rkey.ts
··· 1 - const RECORD_KEY_RE = /^(?!\.{1,2}$)[a-zA-Z0-9_~.:-]{1,512}$/; 2 - 3 - export type RecordKey = string; 4 - 5 - export const isRecordKey = (input: unknown): input is RecordKey => { 6 - return typeof input === 'string' && input.length >= 1 && input.length <= 512 && RECORD_KEY_RE.test(input); 7 - }; 8 - 9 - const TID_RE = /^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$/; 10 - 11 - export const isTid = (input: string) => { 12 - return input.length === 13 && TID_RE.test(input); 13 - };
+2 -4
src/lib/types/valita.ts
··· 1 - import * as v from '@badrap/valita'; 1 + import { isDid, isNsid, isRecordKey } from '@atcute/lexicons/syntax'; 2 2 3 - import { isDid } from './identity'; 4 - import { isNsid } from './nsid'; 5 - import { isRecordKey } from './rkey'; 3 + import * as v from '@badrap/valita'; 6 4 7 5 export const didString = v.string().assert(isDid); 8 6
+11 -45
src/lib/utils/bluesky/embeds.ts
··· 1 - import type { AppBskyEmbedRecordWithMedia, AppBskyFeedDefs } from '@atcute/client/lexicons'; 2 - 3 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 4 - 5 - export interface Embed { 6 - media?: AppBskyEmbedRecordWithMedia.View['media']; 7 - record?: AppBskyEmbedRecordWithMedia.View['record']; 8 - } 9 - 10 - export type MediaEmbed = NonNullable<Embed['media']>; 11 - export type RecordEmbed = NonNullable<Embed['record']>; 12 - 13 - export const unwrapMediaEmbedView = (embed: AppBskyFeedDefs.PostView['embed']): Embed['media'] => { 14 - switch (embed?.$type) { 15 - case 'app.bsky.embed.recordWithMedia#view': 16 - return embed.media; 17 - case 'app.bsky.embed.record#view': 18 - return; 19 - } 20 - 21 - return embed; 22 - }; 1 + import type { RecordEmbed } from '@atcute/bluesky'; 2 + import { parseCanonicalResourceUri } from '@atcute/lexicons'; 23 3 24 - export const unwrapRecordEmbedView = (embed: AppBskyFeedDefs.PostView['embed']): Embed['record'] => { 4 + export const getQuoteEmbed = (embed: RecordEmbed | undefined) => { 25 5 switch (embed?.$type) { 26 - case 'app.bsky.embed.recordWithMedia#view': 27 - return embed.record; 28 - 29 - case 'app.bsky.embed.record#view': 30 - return embed; 31 - } 32 - }; 33 - 34 - export const unwrapEmbedView = (embed: AppBskyFeedDefs.PostView['embed']): Embed => { 35 - return { 36 - media: unwrapMediaEmbedView(embed), 37 - record: unwrapRecordEmbedView(embed), 38 - }; 39 - }; 40 - 41 - export const getQuoteEmbedView = (embed: RecordEmbed | undefined) => { 42 - const record = embed?.record; 43 - 44 - switch (record?.$type) { 45 6 case 'app.bsky.embed.record#viewRecord': { 46 - return record; 7 + return embed; 47 8 } 48 9 49 10 case 'app.bsky.embed.record#viewNotFound': 50 11 case 'app.bsky.embed.record#viewDetached': 51 12 case 'app.bsky.embed.record#viewBlocked': { 52 - const uri = parseAddressedAtUri(record.uri); 13 + const result = parseCanonicalResourceUri(embed.uri); 14 + if (!result.ok) { 15 + return; 16 + } 17 + 18 + const uri = result.value; 53 19 if (uri.collection === 'app.bsky.feed.post') { 54 - return record; 20 + return embed; 55 21 } 56 22 } 57 23 }
+1 -1
src/lib/utils/bluesky/lists.ts
··· 1 - import type { AppBskyGraphDefs } from '@atcute/client/lexicons'; 1 + import type { AppBskyGraphDefs } from '@atcute/bluesky'; 2 2 3 3 export const purposeToLabel = (purpose: AppBskyGraphDefs.ListView['purpose']): string => { 4 4 switch (purpose) {
+1 -1
src/params/did.ts
··· 1 1 import type { ParamMatcher } from '@sveltejs/kit'; 2 2 3 - import { isDid } from '$lib/types/identity'; 3 + import { isDid } from '@atcute/lexicons/syntax'; 4 4 5 5 export const match = isDid satisfies ParamMatcher;
+2 -4
src/params/didOrHandle.ts
··· 1 1 import type { ParamMatcher } from '@sveltejs/kit'; 2 2 3 - import { isDid, isHandle, type Did, type Handle } from '$lib/types/identity'; 3 + import { isActorIdentifier } from '@atcute/lexicons/syntax'; 4 4 5 - export const match = ((param: string): param is Did | Handle => { 6 - return isDid(param) || isHandle(param); 7 - }) satisfies ParamMatcher; 5 + export const match = isActorIdentifier satisfies ParamMatcher;
+1 -1
src/params/handle.ts
··· 1 1 import type { ParamMatcher } from '@sveltejs/kit'; 2 2 3 - import { isHandle } from '$lib/types/identity'; 3 + import { isHandle } from '@atcute/lexicons/syntax'; 4 4 5 5 export const match = isHandle satisfies ParamMatcher;
+1 -1
src/params/rkey.ts
··· 1 1 import type { ParamMatcher } from '@sveltejs/kit'; 2 2 3 - import { isRecordKey } from '$lib/types/rkey'; 3 + import { isRecordKey } from '@atcute/lexicons/syntax'; 4 4 5 5 export const match = isRecordKey satisfies ParamMatcher;
+1 -1
src/params/tid.ts
··· 1 1 import type { ParamMatcher } from '@sveltejs/kit'; 2 2 3 - import { isTid } from '$lib/types/rkey'; 3 + import { isTid } from '@atcute/lexicons/syntax'; 4 4 5 5 export const match = isTid satisfies ParamMatcher;
+4 -4
src/routes/(app)/(profile)/[actor=didOrHandle]/(timeline)/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, simpleFetchHandler } from '@atcute/client'; 2 + import { isDid, type Did } from '@atcute/lexicons/syntax'; 2 3 3 4 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 5 import type { PageLoad } from './$types'; 5 6 6 7 import { fetchTimeline, ProfileFilter, TimelineType } from '$lib/queries/timeline'; 7 - import { isDid, type Did } from '$lib/types/identity'; 8 8 9 9 export const load: PageLoad = async ({ url, params, fetch, parent }) => { 10 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 11 11 12 12 let did: Did; 13 13 if (isDid(params.actor)) { ··· 18 18 } 19 19 20 20 const timeline = await fetchTimeline({ 21 - rpc, 21 + client, 22 22 params: { 23 23 type: TimelineType.PROFILE, 24 24 actor: did,
+6 -6
src/routes/(app)/(profile)/[actor=didOrHandle]/(timeline)/media/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, simpleFetchHandler } from '@atcute/client'; 2 + import { isDid, type Did } from '@atcute/lexicons/syntax'; 2 3 3 4 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 - import { fetchTimeline, ProfileFilter, TimelineType } from '$lib/queries/timeline'; 5 - import { isDid, type Did } from '$lib/types/identity'; 6 - 7 5 import type { PageLoad } from './$types'; 8 6 7 + import { fetchTimeline, ProfileFilter, TimelineType } from '$lib/queries/timeline'; 8 + 9 9 export const load: PageLoad = async ({ url, params, fetch, parent }) => { 10 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 11 11 12 12 let did: Did; 13 13 if (isDid(params.actor)) { ··· 18 18 } 19 19 20 20 const timeline = await fetchTimeline({ 21 - rpc, 21 + client, 22 22 params: { 23 23 type: TimelineType.PROFILE, 24 24 actor: did,
+20 -16
src/routes/(app)/(profile)/[actor=didOrHandle]/(timeline)/rss/+server.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 2 - import type { AppBskyActorDefs } from '@atcute/client/lexicons'; 1 + import type { AppBskyActorDefs } from '@atcute/bluesky'; 2 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 3 3 4 4 import { PUBLIC_APP_URL, PUBLIC_APPVIEW_URL } from '$env/static/public'; 5 5 import type { RequestHandler } from './$types'; ··· 9 9 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 10 10 11 11 export const GET: RequestHandler = async ({ params, fetch }) => { 12 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 12 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 13 13 14 14 const [profile, timeline] = await Promise.all([ 15 15 (async () => { 16 - const { data } = await rpc.get('app.bsky.actor.getProfile', { 17 - params: { 18 - actor: params.actor, 19 - }, 20 - }); 16 + const data = await ok( 17 + client.get('app.bsky.actor.getProfile', { 18 + params: { 19 + actor: params.actor, 20 + }, 21 + }), 22 + ); 21 23 22 24 return data; 23 25 })(), 24 26 25 27 (async () => { 26 - const { data } = await rpc.get('app.bsky.feed.getAuthorFeed', { 27 - params: { 28 - actor: params.actor, 29 - limit: 100, 30 - filter: 'posts_and_author_threads', 31 - includePins: false, 32 - }, 33 - }); 28 + const data = await ok( 29 + client.get('app.bsky.feed.getAuthorFeed', { 30 + params: { 31 + actor: params.actor, 32 + limit: 100, 33 + filter: 'posts_and_author_threads', 34 + includePins: false, 35 + }, 36 + }), 37 + ); 34 38 35 39 // Build into slices so we can filter out non-self threads 36 40 const slices = buildTimelineSlices(
+6 -6
src/routes/(app)/(profile)/[actor=didOrHandle]/(timeline)/with_replies/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, simpleFetchHandler } from '@atcute/client'; 2 + import { isDid, type Did } from '@atcute/lexicons/syntax'; 2 3 3 4 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 - import { fetchTimeline, ProfileFilter, TimelineType } from '$lib/queries/timeline'; 5 - import { isDid, type Did } from '$lib/types/identity'; 6 - 7 5 import type { PageLoad } from './$types'; 8 6 7 + import { fetchTimeline, ProfileFilter, TimelineType } from '$lib/queries/timeline'; 8 + 9 9 export const load: PageLoad = async ({ url, params, fetch, parent }) => { 10 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 11 11 12 12 let did: Did; 13 13 if (isDid(params.actor)) { ··· 18 18 } 19 19 20 20 const timeline = await fetchTimeline({ 21 - rpc, 21 + client, 22 22 params: { 23 23 type: TimelineType.PROFILE, 24 24 actor: did,
+21 -24
src/routes/(app)/(profile)/[actor=didOrHandle]/+layout.ts
··· 1 - import { XRPC, XRPCError, simpleFetchHandler } from '@atcute/client'; 1 + import { Client, ClientResponseError, simpleFetchHandler } from '@atcute/client'; 2 2 import { error } from '@sveltejs/kit'; 3 3 4 4 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; ··· 6 6 import type { LayoutLoad } from './$types'; 7 7 8 8 export const load: LayoutLoad = async ({ params, fetch }) => { 9 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 9 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 10 11 - try { 12 - const { data } = await rpc.get('app.bsky.actor.getProfile', { 13 - params: { 14 - actor: params.actor, 15 - }, 16 - }); 11 + const response = await client.get('app.bsky.actor.getProfile', { 12 + params: { 13 + actor: params.actor, 14 + }, 15 + }); 17 16 18 - return { 19 - profile: data, 20 - }; 21 - } catch (err) { 22 - if (err instanceof XRPCError) { 23 - switch (err.kind) { 24 - case 'InvalidRequest': { 25 - error(404, `Account doesn't exist`); 26 - } 27 - case 'AccountTakedown': { 28 - error(404, `Account is taken down`); 29 - } 30 - case 'AccountDeactivated': { 31 - error(404, `Account is deactivated`); 32 - } 17 + if (!response.ok) { 18 + const err = response.data; 19 + switch (err.error) { 20 + case 'InvalidRequest': { 21 + error(404, `Account doesn't exist`); 22 + } 23 + case 'AccountTakedown': { 24 + error(404, `Account is taken down`); 25 + } 26 + case 'AccountDeactivated': { 27 + error(404, `Account is deactivated`); 33 28 } 34 29 } 35 30 36 - throw err; 31 + throw new ClientResponseError(response); 37 32 } 33 + 34 + return { profile: response.data }; 38 35 };
+1 -1
src/routes/(app)/(profile)/[actor=didOrHandle]/components/profile-aside.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyActorDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyActorDefs } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5
+1 -1
src/routes/(app)/(profile)/[actor=didOrHandle]/components/profile-meta-tags.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyActorDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyActorDefs } from '@atcute/bluesky'; 3 3 4 4 import { PUBLIC_APP_NAME, PUBLIC_APP_URL } from '$env/static/public'; 5 5
+11 -9
src/routes/(app)/(profile)/[actor=didOrHandle]/feeds/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { PageLoad } from './$types'; 5 5 6 6 export const load: PageLoad = async ({ url, params, fetch }) => { 7 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 7 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 8 8 9 - const { data } = await rpc.get('app.bsky.feed.getActorFeeds', { 10 - params: { 11 - actor: params.actor, 12 - limit: 50, 13 - cursor: url.searchParams.get('cursor') || undefined, 14 - }, 15 - }); 9 + const data = await ok( 10 + client.get('app.bsky.feed.getActorFeeds', { 11 + params: { 12 + actor: params.actor, 13 + limit: 50, 14 + cursor: url.searchParams.get('cursor') || undefined, 15 + }, 16 + }), 17 + ); 16 18 17 19 return { feeds: { cursor: data.cursor, items: data.feeds } }; 18 20 };
+11 -9
src/routes/(app)/(profile)/[actor=didOrHandle]/followers/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { PageLoad } from './$types'; 5 5 6 6 export const load: PageLoad = async ({ url, params, fetch }) => { 7 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 7 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 8 8 9 - const { data } = await rpc.get('app.bsky.graph.getFollowers', { 10 - params: { 11 - actor: params.actor, 12 - limit: 50, 13 - cursor: url.searchParams.get('cursor') || undefined, 14 - }, 15 - }); 9 + const data = await ok( 10 + client.get('app.bsky.graph.getFollowers', { 11 + params: { 12 + actor: params.actor, 13 + limit: 50, 14 + cursor: url.searchParams.get('cursor') || undefined, 15 + }, 16 + }), 17 + ); 16 18 17 19 return { followers: { cursor: data.cursor, items: data.followers } }; 18 20 };
+11 -9
src/routes/(app)/(profile)/[actor=didOrHandle]/following/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { PageLoad } from './$types'; 5 5 6 6 export const load: PageLoad = async ({ url, params, fetch }) => { 7 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 7 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 8 8 9 - const { data } = await rpc.get('app.bsky.graph.getFollows', { 10 - params: { 11 - actor: params.actor, 12 - limit: 50, 13 - cursor: url.searchParams.get('cursor') || undefined, 14 - }, 15 - }); 9 + const data = await ok( 10 + client.get('app.bsky.graph.getFollows', { 11 + params: { 12 + actor: params.actor, 13 + limit: 50, 14 + cursor: url.searchParams.get('cursor') || undefined, 15 + }, 16 + }), 17 + ); 16 18 17 19 return { following: { cursor: data.cursor, items: data.follows } }; 18 20 };
+11 -9
src/routes/(app)/(profile)/[actor=didOrHandle]/lists/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { PageLoad } from './$types'; 5 5 6 6 export const load: PageLoad = async ({ url, params, fetch }) => { 7 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 7 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 8 8 9 - const { data } = await rpc.get('app.bsky.graph.getLists', { 10 - params: { 11 - actor: params.actor, 12 - limit: 50, 13 - cursor: url.searchParams.get('cursor') || undefined, 14 - }, 15 - }); 9 + const data = await ok( 10 + client.get('app.bsky.graph.getLists', { 11 + params: { 12 + actor: params.actor, 13 + limit: 50, 14 + cursor: url.searchParams.get('cursor') || undefined, 15 + }, 16 + }), 17 + ); 16 18 17 19 return { lists: { cursor: data.cursor, items: data.lists } }; 18 20 };
+11 -9
src/routes/(app)/(profile)/[actor=didOrHandle]/packs/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { PageLoad } from './$types'; 5 5 6 6 export const load: PageLoad = async ({ url, params, fetch }) => { 7 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 7 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 8 8 9 - const { data } = await rpc.get('app.bsky.graph.getActorStarterPacks', { 10 - params: { 11 - actor: params.actor, 12 - limit: 50, 13 - cursor: url.searchParams.get('cursor') || undefined, 14 - }, 15 - }); 9 + const data = await ok( 10 + client.get('app.bsky.graph.getActorStarterPacks', { 11 + params: { 12 + actor: params.actor, 13 + limit: 50, 14 + cursor: url.searchParams.get('cursor') || undefined, 15 + }, 16 + }), 17 + ); 16 18 17 19 return { packs: { cursor: data.cursor, items: data.starterPacks } }; 18 20 };
+7 -7
src/routes/(app)/[actor=didOrHandle]/[rkey=tid]/+page.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedPost } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedPost } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 import { PUBLIC_APP_NAME } from '$env/static/public'; 6 6 import type { PageProps } from './$types'; 7 7 8 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 8 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 9 9 import { truncateMiddle, truncateRight } from '$lib/utils/strings'; 10 10 11 11 import BlockedAscendantItem from './components/blocked-ascendant-item.svelte'; ··· 22 22 const { data }: PageProps = $props(); 23 23 24 24 const main = $derived(data.thread.post); 25 - const uri = $derived(parseAddressedAtUri(main.uri)); 25 + const uri = $derived(assertCanonicalResourceUri(main.uri)); 26 26 27 27 const title = $derived.by(() => { 28 28 const author = `@${truncateMiddle(main.author.handle, 29)}`; 29 - const content = truncateRight((main.record as AppBskyFeedPost.Record).text.trim(), 70); 29 + const content = truncateRight((main.record as AppBskyFeedPost.Main).text.trim(), 70); 30 30 31 31 return `${author}: "${content}" — ${PUBLIC_APP_NAME}`; 32 32 }); ··· 52 52 {#if item.type === 'post'} 53 53 <PostAscendantItem {item} /> 54 54 {:else if item.type === 'overflow'} 55 - {@const uri = parseAddressedAtUri(item.uri)} 55 + {@const uri = assertCanonicalResourceUri(item.uri)} 56 56 57 57 <OverflowAscendantItem postUrl="{base}/{uri.repo}/{uri.rkey}#main" /> 58 58 {:else if item.type === 'blocked'} 59 - {@const uri = parseAddressedAtUri(item.uri)} 59 + {@const uri = assertCanonicalResourceUri(item.uri)} 60 60 61 61 <BlockedAscendantItem postUrl="{base}/{uri.repo}/{uri.rkey}#main" /> 62 62 {:else if item.type === 'nonexistent'} 63 - {@const uri = parseAddressedAtUri(item.uri)} 63 + {@const uri = assertCanonicalResourceUri(item.uri)} 64 64 65 65 <NonexistentAscendantPost postUrl="{base}/{uri.repo}/{uri.rkey}#main" /> 66 66 {/if}
+20 -19
src/routes/(app)/[actor=didOrHandle]/[rkey=tid]/+page.ts
··· 1 - import { simpleFetchHandler, XRPC, XRPCError } from '@atcute/client'; 2 - import type { AppBskyFeedGetPostThread } from '@atcute/client/lexicons'; 1 + import type { AppBskyFeedGetPostThread } from '@atcute/bluesky'; 2 + import { Client, ClientResponseError, ok, simpleFetchHandler } from '@atcute/client'; 3 + import { isDid, type Did } from '@atcute/lexicons/syntax'; 4 + 3 5 import { error } from '@sveltejs/kit'; 4 6 5 7 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; ··· 7 9 8 10 import { resolveHandle } from '$lib/queries/handle'; 9 11 import { makeAtUri } from '$lib/types/at-uri'; 10 - import { isDid, type Did } from '$lib/types/identity'; 11 12 12 13 export const load: PageLoad = async ({ params }) => { 13 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 14 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 14 15 15 16 let did: Did; 16 17 if (!isDid(params.actor)) { 17 18 try { 18 - did = await resolveHandle({ rpc, handle: params.actor }); 19 + did = await resolveHandle({ client: client, handle: params.actor }); 19 20 } catch (err) { 20 - if (err instanceof XRPCError) { 21 - switch (err.kind) { 21 + if (err instanceof ClientResponseError) { 22 + switch (err.error) { 22 23 case 'InvalidRequest': { 23 24 error(404, `Account doesn't exist`); 24 25 } ··· 33 34 34 35 const uri = makeAtUri(did, 'app.bsky.feed.post', params.rkey); 35 36 36 - let data: AppBskyFeedGetPostThread.Output; 37 + let data: AppBskyFeedGetPostThread.$output; 37 38 38 39 try { 39 - const response = await rpc.get('app.bsky.feed.getPostThread', { 40 - params: { 41 - uri: uri, 42 - depth: 4, 43 - parentHeight: 10, 44 - }, 45 - }); 46 - 47 - data = response.data; 40 + data = await ok( 41 + client.get('app.bsky.feed.getPostThread', { 42 + params: { 43 + uri: uri, 44 + depth: 4, 45 + parentHeight: 10, 46 + }, 47 + }), 48 + ); 48 49 } catch (err) { 49 - if (err instanceof XRPCError) { 50 - switch (err.kind) { 50 + if (err instanceof ClientResponseError) { 51 + switch (err.error) { 51 52 case 'NotFound': { 52 53 error(404, `Post not found`); 53 54 }
+6 -4
src/routes/(app)/[actor=didOrHandle]/[rkey=tid]/components/descendants.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs, AppBskyFeedThreadgate } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedDefs, AppBskyFeedThreadgate } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 8 8 import { createReplyCollator } from '../utils'; 9 9 ··· 17 17 18 18 const { thread: root, threadgate }: Props = $props(); 19 19 20 - const gate = $derived(threadgate?.record as AppBskyFeedThreadgate.Record | undefined); 20 + const gate = $derived(threadgate?.record as AppBskyFeedThreadgate.Main | undefined); 21 21 const sort = $derived(createReplyCollator(threadgate)); 22 22 </script> 23 23 ··· 59 59 </div> 60 60 {/if} 61 61 62 - <OverflowDescendantItem postUrl="{base}/{post.author.did}/{parseAddressedAtUri(post.uri).rkey}#main" /> 62 + <OverflowDescendantItem 63 + postUrl="{base}/{post.author.did}/{assertCanonicalResourceUri(post.uri).rkey}#main" 64 + /> 63 65 {/if} 64 66 {/each} 65 67 {/snippet}
+5 -5
src/routes/(app)/[actor=didOrHandle]/[rkey=tid]/components/interaction-state.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs, AppBskyFeedThreadgate } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedDefs, AppBskyFeedThreadgate } from '@atcute/bluesky'; 3 3 4 4 import type { UnwrapArray } from '$lib/utils/types'; 5 5 ··· 7 7 import Group_2Outlined from '$lib/components/central-icons/group-2-outlined.svelte'; 8 8 import CircleBanSignOutlined from '$lib/components/central-icons/circle-ban-sign-outlined.svelte'; 9 9 import { base } from '$app/paths'; 10 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 10 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 11 11 12 12 interface Props { 13 13 threadgate: AppBskyFeedDefs.ThreadgateView | undefined; 14 14 } 15 15 16 - type GateRecord = AppBskyFeedThreadgate.Record; 16 + type GateRecord = AppBskyFeedThreadgate.Main; 17 17 type Allow = UnwrapArray<GateRecord['allow']>; 18 18 19 19 const { threadgate }: Props = $props(); 20 20 21 21 const id = $props.id(); 22 22 23 - const record = $derived(threadgate?.record as AppBskyFeedThreadgate.Record | undefined); 23 + const record = $derived(threadgate?.record as AppBskyFeedThreadgate.Main | undefined); 24 24 25 25 const allow = $derived.by(() => { 26 26 const order: Record<Allow['$type'], number> = { ··· 99 99 {@const hydrated = threadgate!.lists?.find((list) => list.uri === rule.list)} 100 100 101 101 {#if hydrated} 102 - {@const uri = parseAddressedAtUri(rule.list)} 102 + {@const uri = assertCanonicalResourceUri(rule.list)} 103 103 104 104 <li> 105 105 Users in <a class="link" href="{base}/{uri.repo}/lists/{uri.rkey}">{hydrated.name}</a> list
+3 -3
src/routes/(app)/[actor=didOrHandle]/[rkey=tid]/components/main-post-metrics.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedDefs } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { formatCompactNumber } from '$lib/utils/intl/number'; 8 8 9 9 interface Props { ··· 12 12 13 13 const { post }: Props = $props(); 14 14 15 - const baseUrl = $derived(`${base}/${post.author.did}/${parseAddressedAtUri(post.uri).rkey}`); 15 + const baseUrl = $derived(`${base}/${post.author.did}/${assertCanonicalResourceUri(post.uri).rkey}`); 16 16 </script> 17 17 18 18 {#snippet Stat(count: number | undefined, one: string, many: string, href: string)}
+4 -4
src/routes/(app)/[actor=didOrHandle]/[rkey=tid]/components/main-post.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 6 import { findLabel, FlagsBlurContent, FlagsBlurMedia } from '$lib/moderation'; 7 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 7 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 8 8 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 9 9 10 10 import Avatar from '$lib/components/avatar.svelte'; ··· 27 27 28 28 const { post, threadgate, prev = false }: Props = $props(); 29 29 30 - const uri = $derived(parseAddressedAtUri(post.uri)); 30 + const uri = $derived(assertCanonicalResourceUri(post.uri)); 31 31 32 32 const author = $derived(post.author); 33 33 const authorName = $derived(normalizeDisplayName(author.displayName ?? '')); 34 34 35 - const record = $derived(post.record as AppBskyFeedPost.Record); 35 + const record = $derived(post.record as AppBskyFeedPost.Main); 36 36 37 37 const authorUrl = $derived(`${base}/${author.did}`); 38 38 const postUrl = $derived(`${base}/${author.did}/${uri.rkey}#main`);
+5 -3
src/routes/(app)/[actor=didOrHandle]/[rkey=tid]/components/missing-descendant-item.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedDefs } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 8 8 interface Props { 9 9 count: number; ··· 15 15 16 16 <div class="missing-descendant-item"> 17 17 <span class="label">{count === 1 ? `${count} missing reply` : `${count} missing replies`}</span> 18 - <a href="{base}/{post.author.did}/{parseAddressedAtUri(post.uri).rkey}/all-replies" class="link">View</a> 18 + <a href="{base}/{post.author.did}/{assertCanonicalResourceUri(post.uri).rkey}/all-replies" class="link" 19 + >View</a 20 + > 19 21 </div> 20 22 21 23 <style>
+4 -4
src/routes/(app)/[actor=didOrHandle]/[rkey=tid]/components/post-ascendant-item.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedPost } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedPost } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 6 import { findLabel, FlagsBlurContent, FlagsBlurMedia } from '$lib/moderation'; 7 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 7 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 8 8 9 9 import Avatar from '$lib/components/avatar.svelte'; 10 10 import RichtextRenderer from '$lib/components/richtext-renderer.svelte'; ··· 27 27 const author = $derived(post.author); 28 28 const authorUrl = $derived(`${base}/${author.did}`); 29 29 30 - const record = $derived(post.record as AppBskyFeedPost.Record); 31 - const postUrl = $derived(`${base}/${author.did}/${parseAddressedAtUri(post.uri).rkey}#main`); 30 + const record = $derived(post.record as AppBskyFeedPost.Main); 31 + const postUrl = $derived(`${base}/${author.did}/${assertCanonicalResourceUri(post.uri).rkey}#main`); 32 32 33 33 const isAviBlurred = $derived(!!findLabel(author.labels, author.did, FlagsBlurMedia)); 34 34 const blur = $derived(findLabel(post.labels, author.did, FlagsBlurContent));
+4 -4
src/routes/(app)/[actor=didOrHandle]/[rkey=tid]/components/post-descendant-item.svelte
··· 1 1 <script lang="ts"> 2 2 import type { Snippet } from 'svelte'; 3 3 4 - import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/client/lexicons'; 4 + import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/bluesky'; 5 5 6 6 import { base } from '$app/paths'; 7 7 8 8 import { findLabel, FlagsBlurContent, FlagsBlurMedia } from '$lib/moderation'; 9 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 9 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 10 10 11 11 import Avatar from '$lib/components/avatar.svelte'; 12 12 import ContentHider from '$lib/components/content-hider.svelte'; ··· 28 28 const author = $derived(post.author); 29 29 const authorUrl = $derived(`${base}/${author.did}`); 30 30 31 - const record = $derived(post.record as AppBskyFeedPost.Record); 32 - const postUrl = $derived(`${base}/${author.did}/${parseAddressedAtUri(post.uri).rkey}#main`); 31 + const record = $derived(post.record as AppBskyFeedPost.Main); 32 + const postUrl = $derived(`${base}/${author.did}/${assertCanonicalResourceUri(post.uri).rkey}#main`); 33 33 34 34 const isAviBlurred = $derived(!!findLabel(author.labels, author.did, FlagsBlurMedia)); 35 35 const blur = $derived(findLabel(post.labels, author.did, FlagsBlurContent));
+11 -13
src/routes/(app)/[actor=didOrHandle]/[rkey=tid]/components/post-meta-tags.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs, AppBskyFeedPost } from '@atcute/client/lexicons'; 2 + import { unwrapEmbed, type AppBskyFeedDefs, type AppBskyFeedPost } from '@atcute/bluesky'; 3 3 4 4 import { PUBLIC_APP_NAME, PUBLIC_APP_URL } from '$env/static/public'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 8 - import { unwrapEmbedView } from '$lib/utils/bluesky/embeds'; 9 8 import { collectionToLabel } from '$lib/utils/bluesky/records'; 10 9 import { trimRichText } from '$lib/utils/bluesky/richtext'; 11 10 import { truncateMiddle } from '$lib/utils/strings'; ··· 16 15 17 16 const { post }: Props = $props(); 18 17 19 - const uri = $derived(parseAddressedAtUri(post.uri)); 18 + const uri = $derived(assertCanonicalResourceUri(post.uri)); 20 19 21 20 const author = $derived(post.author); 22 21 const displayName = $derived(normalizeDisplayName(author.displayName ?? '')); 23 22 const handle = $derived(truncateMiddle(author.handle, 29)); 24 23 25 - const { media, record } = $derived(unwrapEmbedView(post.embed)); 24 + const { media, record } = $derived(unwrapEmbed(post.embed)); 26 25 27 26 const description = $derived.by(() => { 28 - const content = trimRichText((post.record as AppBskyFeedPost.Record).text); 27 + const content = trimRichText((post.record as AppBskyFeedPost.Main).text); 29 28 let footer = ''; 30 29 31 30 switch (media?.$type) { ··· 42 41 } 43 42 44 43 if (record) { 45 - const view = record.record; 46 - switch (view.$type) { 44 + switch (record.$type) { 47 45 case 'app.bsky.embed.record#viewRecord': { 48 46 footer && (footer += '\n'); 49 - footer += `[quoting @${truncateMiddle(view.author.handle, 29)}]`; 47 + footer += `[quoting @${truncateMiddle(record.author.handle, 29)}]`; 50 48 break; 51 49 } 52 50 case 'app.bsky.feed.defs#generatorView': { ··· 65 63 break; 66 64 } 67 65 default: { 68 - const uri = parseAddressedAtUri(view.uri); 66 + const uri = assertCanonicalResourceUri(record.uri); 69 67 const resource = collectionToLabel(uri.collection); 70 68 71 69 const isUnavailable = 72 70 resource && 73 - (view.$type === 'app.bsky.embed.record#viewNotFound' || 74 - view.$type === 'app.bsky.embed.record#viewBlocked' || 75 - view.$type === 'app.bsky.embed.record#viewDetached'); 71 + (record.$type === 'app.bsky.embed.record#viewNotFound' || 72 + record.$type === 'app.bsky.embed.record#viewBlocked' || 73 + record.$type === 'app.bsky.embed.record#viewDetached'); 76 74 77 75 footer && (footer += '\n'); 78 76 footer += isUnavailable ? `[contains unavailable ${resource} embed]` : `[contains unknown embed]`;
+3 -3
src/routes/(app)/[actor=didOrHandle]/[rkey=tid]/utils.ts
··· 1 - import type { AppBskyFeedDefs, AppBskyFeedPost, AppBskyFeedThreadgate } from '@atcute/client/lexicons'; 1 + import type { AppBskyFeedDefs, AppBskyFeedPost, AppBskyFeedThreadgate } from '@atcute/bluesky'; 2 2 3 3 import type { UnwrapArray } from '$lib/utils/types'; 4 4 ··· 45 45 return likeOrder / timePenalty; 46 46 }; 47 47 48 - const gate = threadgateView?.record as AppBskyFeedThreadgate.Record | undefined; 48 + const gate = threadgateView?.record as AppBskyFeedThreadgate.Main | undefined; 49 49 50 50 return (parent: AppBskyFeedDefs.PostView, a: ReplyItem, b: ReplyItem) => { 51 51 if (a.$type !== 'app.bsky.feed.defs#threadViewPost') { ··· 132 132 133 133 if (last && last.type === 'post') { 134 134 const post = last.post; 135 - const reply = (post.record as AppBskyFeedPost.Record).reply; 135 + const reply = (post.record as AppBskyFeedPost.Main).reply; 136 136 137 137 if (reply) { 138 138 const uri = reply.parent.uri;
+11 -9
src/routes/(app)/[actor=didOrHandle]/feeds/[rkey=rkey]/+layout.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 + import { isDid, type Did } from '@atcute/lexicons/syntax'; 2 3 3 4 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 5 import type { LayoutLoad } from './$types'; 5 6 6 7 import { resolveHandle } from '$lib/queries/handle'; 7 8 import { makeAtUri } from '$lib/types/at-uri'; 8 - import { isDid, type Did } from '$lib/types/identity'; 9 9 10 10 export const load: LayoutLoad = async ({ params, fetch }) => { 11 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 11 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 12 12 13 13 let did: Did; 14 14 if (isDid(params.actor)) { 15 15 did = params.actor; 16 16 } else { 17 - did = await resolveHandle({ rpc, handle: params.actor }); 17 + did = await resolveHandle({ client: client, handle: params.actor }); 18 18 } 19 19 20 - const { data } = await rpc.get('app.bsky.feed.getFeedGenerator', { 21 - params: { 22 - feed: makeAtUri(did, 'app.bsky.feed.generator', params.rkey), 23 - }, 24 - }); 20 + const data = await ok( 21 + client.get('app.bsky.feed.getFeedGenerator', { 22 + params: { 23 + feed: makeAtUri(did, 'app.bsky.feed.generator', params.rkey), 24 + }, 25 + }), 26 + ); 25 27 26 28 const view = data.view; 27 29
+2 -2
src/routes/(app)/[actor=didOrHandle]/feeds/[rkey=rkey]/+page.svelte
··· 4 4 import { PUBLIC_APP_NAME } from '$env/static/public'; 5 5 import type { PageProps } from './$types'; 6 6 7 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 7 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 8 8 import { paginate } from '$lib/utils/pagination'; 9 9 10 10 import PageListing from '$lib/components/page/page-listing.svelte'; ··· 14 14 15 15 const { data }: PageProps = $props(); 16 16 17 - const rkey = $derived(parseAddressedAtUri(data.feed.uri).rkey); 17 + const rkey = $derived(assertCanonicalResourceUri(data.feed.uri).rkey); 18 18 19 19 const { rootUrl, nextUrl } = $derived( 20 20 paginate(page.url, data.timeline.cursor, `${base}/${data.feed.creator.did}/feeds/${rkey}`),
+4 -4
src/routes/(app)/[actor=didOrHandle]/feeds/[rkey=rkey]/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, simpleFetchHandler } from '@atcute/client'; 2 + import { isDid, type Did } from '@atcute/lexicons/syntax'; 2 3 3 4 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 5 import type { PageLoad } from './$types'; 5 6 6 7 import { fetchTimeline, TimelineType } from '$lib/queries/timeline'; 7 8 import { makeAtUri } from '$lib/types/at-uri'; 8 - import { isDid, type Did } from '$lib/types/identity'; 9 9 10 10 export const load: PageLoad = async ({ url, params, fetch, parent }) => { 11 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 11 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 12 12 13 13 let did: Did; 14 14 if (isDid(params.actor)) { ··· 19 19 } 20 20 21 21 const timeline = await fetchTimeline({ 22 - rpc, 22 + client: client, 23 23 params: { 24 24 type: TimelineType.CUSTOM_FEED, 25 25 feed: makeAtUri(did, 'app.bsky.feed.generator', params.rkey),
+3 -3
src/routes/(app)/[actor=didOrHandle]/feeds/[rkey=rkey]/components/feed-aside.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedDefs } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 8 8 import { trimRichText } from '$lib/utils/bluesky/richtext'; 9 9 import { formatLongNumber } from '$lib/utils/intl/number'; ··· 20 20 21 21 const { feed }: Props = $props(); 22 22 23 - const uri = $derived(parseAddressedAtUri(feed.uri)); 23 + const uri = $derived(assertCanonicalResourceUri(feed.uri)); 24 24 25 25 const creatorUrl = $derived(`${base}/${feed.creator.did}`); 26 26 const feedUrl = $derived(`${creatorUrl}/feeds/${uri.rkey}`);
+3 -3
src/routes/(app)/[actor=didOrHandle]/feeds/[rkey=rkey]/components/feed-meta-tags.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedDefs } from '@atcute/bluesky'; 3 3 4 4 import { PUBLIC_APP_NAME, PUBLIC_APP_URL } from '$env/static/public'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 8 8 import { trimRichText } from '$lib/utils/bluesky/richtext'; 9 9 import { truncateMiddle } from '$lib/utils/strings'; ··· 14 14 15 15 const { feed }: Props = $props(); 16 16 17 - const uri = $derived(parseAddressedAtUri(feed.uri)); 17 + const uri = $derived(assertCanonicalResourceUri(feed.uri)); 18 18 19 19 const description = $derived.by(() => { 20 20 const desc = trimRichText(feed.description ?? '');
+12 -10
src/routes/(app)/[actor=didOrHandle]/feeds/[rkey=rkey]/likes/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 + import { isDid, type Did } from '@atcute/lexicons/syntax'; 2 3 3 4 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 5 import type { PageLoad } from './$types'; 5 6 6 7 import { makeAtUri } from '$lib/types/at-uri'; 7 - import { isDid, type Did } from '$lib/types/identity'; 8 8 9 9 export const load: PageLoad = async ({ url, params, fetch, parent }) => { 10 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 11 11 12 12 let did: Did; 13 13 if (isDid(params.actor)) { ··· 17 17 did = parentData.feed.creator.did as Did; 18 18 } 19 19 20 - const { data } = await rpc.get('app.bsky.feed.getLikes', { 21 - params: { 22 - uri: makeAtUri(did, 'app.bsky.feed.generator', params.rkey), 23 - limit: 50, 24 - cursor: url.searchParams.get('cursor') || undefined, 25 - }, 26 - }); 20 + const data = await ok( 21 + client.get('app.bsky.feed.getLikes', { 22 + params: { 23 + uri: makeAtUri(did, 'app.bsky.feed.generator', params.rkey), 24 + limit: 50, 25 + cursor: url.searchParams.get('cursor') || undefined, 26 + }, 27 + }), 28 + ); 27 29 28 30 return { likes: { cursor: data.cursor, items: data.likes.map((like) => like.actor) } }; 29 31 };
+2 -2
src/routes/(app)/[actor=didOrHandle]/lists/[rkey=rkey]/+layout.svelte
··· 5 5 import { page } from '$app/state'; 6 6 import type { LayoutProps } from './$types'; 7 7 8 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 8 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 9 9 10 10 import ListAside from './components/list-aside.svelte'; 11 11 import ListMetaTags from './components/list-meta-tags.svelte'; ··· 14 14 15 15 const currentRouteId = $derived(page.route.id); 16 16 17 - const uri = $derived(parseAddressedAtUri(data.list.uri)); 17 + const uri = $derived(assertCanonicalResourceUri(data.list.uri)); 18 18 const listUrl = $derived.by(() => { 19 19 return `${base}/${uri.repo}/lists/${uri.rkey}`; 20 20 });
+15 -11
src/routes/(app)/[actor=didOrHandle]/lists/[rkey=rkey]/+layout.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 + import type { Did } from '@atcute/lexicons'; 3 + import { isDid } from '@atcute/lexicons/syntax'; 2 4 3 5 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 6 import type { LayoutLoad } from './$types'; 5 7 6 8 import { resolveHandle } from '$lib/queries/handle'; 7 9 import { makeAtUri } from '$lib/types/at-uri'; 8 - import { isDid, type Did } from '$lib/types/identity'; 9 10 10 11 export const load: LayoutLoad = async ({ url, route, params, fetch }) => { 11 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 12 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 12 13 13 14 let did: Did; 14 15 if (isDid(params.actor)) { 15 16 did = params.actor; 16 17 } else { 17 - did = await resolveHandle({ rpc, handle: params.actor }); 18 + did = await resolveHandle({ client: client, handle: params.actor }); 18 19 } 19 20 20 21 const isListing = route.id === '/(app)/[actor=didOrHandle]/lists/[rkey=rkey]/members'; 21 22 const cursor = url.searchParams.get('cursor') || undefined; 22 - const { data } = await rpc.get('app.bsky.graph.getList', { 23 - params: { 24 - list: makeAtUri(did, 'app.bsky.graph.list', params.rkey), 25 - limit: isListing ? 50 : 1, 26 - cursor: isListing ? cursor : undefined, 27 - }, 28 - }); 23 + 24 + const data = await ok( 25 + client.get('app.bsky.graph.getList', { 26 + params: { 27 + list: makeAtUri(did, 'app.bsky.graph.list', params.rkey), 28 + limit: isListing ? 50 : 1, 29 + cursor: isListing ? cursor : undefined, 30 + }, 31 + }), 32 + ); 29 33 30 34 const view = data.list; 31 35
+3 -3
src/routes/(app)/[actor=didOrHandle]/lists/[rkey=rkey]/components/list-aside.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyGraphDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyGraphDefs } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 8 8 import { trimRichText } from '$lib/utils/bluesky/richtext'; 9 9 import { formatLongNumber } from '$lib/utils/intl/number'; ··· 20 20 21 21 const { list }: Props = $props(); 22 22 23 - const uri = $derived(parseAddressedAtUri(list.uri)); 23 + const uri = $derived(assertCanonicalResourceUri(list.uri)); 24 24 25 25 const creatorUrl = $derived(`${base}/${list.creator.did}`); 26 26 </script>
+3 -3
src/routes/(app)/[actor=didOrHandle]/lists/[rkey=rkey]/components/list-meta-tags.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyGraphDefs } from '@atcute/client/lexicons'; 2 + import type { AppBskyGraphDefs } from '@atcute/bluesky'; 3 3 4 4 import { PUBLIC_APP_NAME, PUBLIC_APP_URL } from '$env/static/public'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 8 8 import { purposeToLabel } from '$lib/utils/bluesky/lists'; 9 9 import { trimRichText } from '$lib/utils/bluesky/richtext'; ··· 15 15 16 16 const { list }: Props = $props(); 17 17 18 - const uri = $derived(parseAddressedAtUri(list.uri)); 18 + const uri = $derived(assertCanonicalResourceUri(list.uri)); 19 19 20 20 const description = $derived.by(() => { 21 21 const desc = trimRichText(list.description ?? '');
+2 -2
src/routes/(app)/[actor=didOrHandle]/lists/[rkey=rkey]/posts/+page.svelte
··· 8 8 9 9 import PageListing from '$lib/components/page/page-listing.svelte'; 10 10 import PostFeedItem from '$lib/components/timeline/post-feed-item.svelte'; 11 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 11 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 12 12 13 13 const { data }: PageProps = $props(); 14 14 15 - const uri = $derived(parseAddressedAtUri(data.list.uri)); 15 + const uri = $derived(assertCanonicalResourceUri(data.list.uri)); 16 16 17 17 const { rootUrl, nextUrl } = $derived( 18 18 paginate(page.url, data.timeline.cursor, `${base}/${uri.repo}/lists/${uri.rkey}/posts`),
+5 -5
src/routes/(app)/[actor=didOrHandle]/lists/[rkey=rkey]/posts/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, simpleFetchHandler } from '@atcute/client'; 2 + import { isDid, type Did } from '@atcute/lexicons/syntax'; 2 3 3 4 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 5 import type { PageLoad } from './$types'; 5 6 6 - import { isDid, type Did } from '$lib/types/identity'; 7 - import { makeAtUri } from '$lib/types/at-uri'; 8 7 import { fetchTimeline, TimelineType } from '$lib/queries/timeline'; 8 + import { makeAtUri } from '$lib/types/at-uri'; 9 9 10 10 export const load: PageLoad = async ({ url, params, fetch, parent }) => { 11 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 11 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 12 12 13 13 let did: Did; 14 14 if (isDid(params.actor)) { ··· 19 19 } 20 20 21 21 const timeline = await fetchTimeline({ 22 - rpc, 22 + client: client, 23 23 params: { 24 24 type: TimelineType.USER_LIST, 25 25 list: makeAtUri(did, 'app.bsky.graph.list', params.rkey),
+22 -16
src/routes/(app)/[actor=didOrHandle]/lists/[rkey=rkey]/posts/rss/+server.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 + 3 + import type { Did } from '@atcute/lexicons'; 4 + import { isDid } from '@atcute/lexicons/syntax'; 2 5 3 6 import { PUBLIC_APP_URL, PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 7 import type { RequestHandler } from './$types'; ··· 7 10 import { resolveHandle } from '$lib/queries/handle'; 8 11 import { createRssFeed, feedPostToFeedItem } from '$lib/rss'; 9 12 import { makeAtUri } from '$lib/types/at-uri'; 10 - import { isDid, type Did } from '$lib/types/identity'; 11 13 12 14 export const GET: RequestHandler = async ({ params, fetch }) => { 13 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 15 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 14 16 15 17 let did: Did; 16 18 if (isDid(params.actor)) { 17 19 did = params.actor; 18 20 } else { 19 - did = await resolveHandle({ rpc, handle: params.actor }); 21 + did = await resolveHandle({ client: client, handle: params.actor }); 20 22 } 21 23 22 24 const uri = makeAtUri(did, 'app.bsky.graph.list', params.rkey); 23 25 24 26 const [list, timeline] = await Promise.all([ 25 27 (async () => { 26 - const { data } = await rpc.get('app.bsky.graph.getList', { 27 - params: { 28 - list: uri, 29 - limit: 1, 30 - }, 31 - }); 28 + const data = await ok( 29 + client.get('app.bsky.graph.getList', { 30 + params: { 31 + list: uri, 32 + limit: 1, 33 + }, 34 + }), 35 + ); 32 36 33 37 return data.list; 34 38 })(), 35 39 36 40 (async () => { 37 - const { data } = await rpc.get('app.bsky.feed.getListFeed', { 38 - params: { 39 - list: uri, 40 - limit: 100, 41 - }, 42 - }); 41 + const data = await ok( 42 + client.get('app.bsky.feed.getListFeed', { 43 + params: { 44 + list: uri, 45 + limit: 100, 46 + }, 47 + }), 48 + ); 43 49 44 50 const slices = buildTimelineSlices(data.feed); 45 51
+4 -4
src/routes/(app)/[actor=didOrHandle]/packs/[rkey=rkey]/+layout.svelte
··· 1 1 <script lang="ts"> 2 2 import type { ClassValue } from 'svelte/elements'; 3 3 4 - import type { AppBskyGraphStarterpack } from '@atcute/client/lexicons'; 4 + import type { AppBskyGraphStarterpack } from '@atcute/bluesky'; 5 5 6 6 import { base } from '$app/paths'; 7 7 import { page } from '$app/state'; 8 8 import { PUBLIC_APP_NAME } from '$env/static/public'; 9 9 import type { LayoutProps } from './$types'; 10 10 11 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 11 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 12 12 13 13 import PackAside from './components/pack-aside.svelte'; 14 14 import PackMetaTags from './components/pack-meta-tags.svelte'; ··· 17 17 18 18 const currentRouteId = $derived(page.route.id); 19 19 20 - const record = $derived(data.pack.record as AppBskyGraphStarterpack.Record); 21 - const uri = $derived(parseAddressedAtUri(data.pack.uri)); 20 + const record = $derived(data.pack.record as AppBskyGraphStarterpack.Main); 21 + const uri = $derived(assertCanonicalResourceUri(data.pack.uri)); 22 22 23 23 const listUrl = $derived.by(() => { 24 24 return `${base}/${uri.repo}/packs/${uri.rkey}`;
+9 -7
src/routes/(app)/[actor=didOrHandle]/packs/[rkey=rkey]/+layout.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { LayoutLoad } from './$types'; ··· 6 6 import { makeAtUri } from '$lib/types/at-uri'; 7 7 8 8 export const load: LayoutLoad = async ({ params, fetch }) => { 9 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 9 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 10 11 - const { data } = await rpc.get('app.bsky.graph.getStarterPack', { 12 - params: { 13 - starterPack: makeAtUri(params.actor, 'app.bsky.graph.starterpack', params.rkey), 14 - }, 15 - }); 11 + const data = await ok( 12 + client.get('app.bsky.graph.getStarterPack', { 13 + params: { 14 + starterPack: makeAtUri(params.actor, 'app.bsky.graph.starterpack', params.rkey), 15 + }, 16 + }), 17 + ); 16 18 17 19 const view = data.starterPack; 18 20
+11 -9
src/routes/(app)/[actor=didOrHandle]/packs/[rkey=rkey]/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { PageLoad } from './$types'; 5 5 6 6 export const load: PageLoad = async ({ url, fetch, parent }) => { 7 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 7 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 8 8 9 9 const { pack } = await parent(); 10 10 ··· 19 19 } 20 20 } 21 21 22 - const { data } = await rpc.get('app.bsky.graph.getList', { 23 - params: { 24 - list: pack.list.uri, 25 - limit: 50, 26 - cursor: url.searchParams.get('cursor') || undefined, 27 - }, 28 - }); 22 + const data = await ok( 23 + client.get('app.bsky.graph.getList', { 24 + params: { 25 + list: pack.list.uri, 26 + limit: 50, 27 + cursor: url.searchParams.get('cursor') || undefined, 28 + }, 29 + }), 30 + ); 29 31 30 32 return { members: { cursor: data.cursor, items: data.items } }; 31 33 };
+4 -4
src/routes/(app)/[actor=didOrHandle]/packs/[rkey=rkey]/components/pack-aside.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyGraphDefs, AppBskyGraphStarterpack } from '@atcute/client/lexicons'; 2 + import type { AppBskyGraphDefs, AppBskyGraphStarterpack } from '@atcute/bluesky'; 3 3 4 4 import { base } from '$app/paths'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 8 8 import { trimRichText } from '$lib/utils/bluesky/richtext'; 9 9 ··· 19 19 20 20 const { pack }: Props = $props(); 21 21 22 - const uri = $derived(parseAddressedAtUri(pack.uri)); 23 - const record = $derived(pack.record as AppBskyGraphStarterpack.Record); 22 + const uri = $derived(assertCanonicalResourceUri(pack.uri)); 23 + const record = $derived(pack.record as AppBskyGraphStarterpack.Main); 24 24 25 25 const creatorUrl = $derived(`${base}/${pack.creator.did}`); 26 26 </script>
+4 -4
src/routes/(app)/[actor=didOrHandle]/packs/[rkey=rkey]/components/pack-meta-tags.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyGraphDefs, AppBskyGraphStarterpack } from '@atcute/client/lexicons'; 2 + import type { AppBskyGraphDefs, AppBskyGraphStarterpack } from '@atcute/bluesky'; 3 3 4 4 import { PUBLIC_APP_NAME, PUBLIC_APP_URL } from '$env/static/public'; 5 5 6 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 6 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 7 7 import { normalizeDisplayName } from '$lib/utils/bluesky/display'; 8 8 import { trimRichText } from '$lib/utils/bluesky/richtext'; 9 9 import { truncateMiddle } from '$lib/utils/strings'; ··· 14 14 15 15 const { pack }: Props = $props(); 16 16 17 - const uri = $derived(parseAddressedAtUri(pack.uri)); 18 - const record = $derived(pack.record as AppBskyGraphStarterpack.Record); 17 + const uri = $derived(assertCanonicalResourceUri(pack.uri)); 18 + const record = $derived(pack.record as AppBskyGraphStarterpack.Main); 19 19 20 20 const description = $derived.by(() => { 21 21 const desc = trimRichText(record.description ?? '');
+2 -2
src/routes/(app)/[actor=didOrHandle]/packs/[rkey=rkey]/posts/+page.svelte
··· 7 7 8 8 import PageListing from '$lib/components/page/page-listing.svelte'; 9 9 import PostFeedItem from '$lib/components/timeline/post-feed-item.svelte'; 10 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 10 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 11 11 12 12 const { data }: PageProps = $props(); 13 13 14 - const uri = $derived(parseAddressedAtUri(data.pack.uri)); 14 + const uri = $derived(assertCanonicalResourceUri(data.pack.uri)); 15 15 16 16 const { rootUrl, nextUrl } = $derived( 17 17 paginate(page.url, data.timeline.cursor, `${base}/${uri.repo}/packs/${uri.rkey}/posts`),
+3 -3
src/routes/(app)/[actor=didOrHandle]/packs/[rkey=rkey]/posts/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { simpleFetchHandler, Client } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { PageLoad } from './$types'; ··· 6 6 import { fetchTimeline, TimelineType } from '$lib/queries/timeline'; 7 7 8 8 export const load: PageLoad = async ({ url, fetch, parent }) => { 9 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 9 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 10 11 11 const { pack } = await parent(); 12 12 ··· 15 15 } 16 16 17 17 const timeline = await fetchTimeline({ 18 - rpc, 18 + client: client, 19 19 params: { 20 20 type: TimelineType.USER_LIST, 21 21 list: pack.list.uri,
+9 -7
src/routes/(app)/[actor=did]/[rkey=tid]/all-quotes/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { PageLoad } from './$types'; ··· 7 7 import { makeAtUri } from '$lib/types/at-uri'; 8 8 9 9 export const load: PageLoad = async ({ url, params, fetch }) => { 10 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 11 11 12 12 const parentUri = makeAtUri(params.actor, 'app.bsky.feed.post', params.rkey); 13 13 ··· 20 20 }); 21 21 22 22 const items = await (async () => { 23 - const { data } = await rpc.get('app.bsky.feed.getPosts', { 24 - params: { 25 - uris: linking_records.map((link) => makeAtUri(link.did, 'app.bsky.feed.post', link.rkey)), 26 - }, 27 - }); 23 + const data = await ok( 24 + client.get('app.bsky.feed.getPosts', { 25 + params: { 26 + uris: linking_records.map((link) => makeAtUri(link.did, 'app.bsky.feed.post', link.rkey)), 27 + }, 28 + }), 29 + ); 28 30 29 31 return data.posts; 30 32 })();
+17 -22
src/routes/(app)/[actor=did]/[rkey=tid]/all-replies/+page.ts
··· 1 - import { simpleFetchHandler, XRPC, XRPCError } from '@atcute/client'; 2 - import type { AppBskyFeedDefs, AppBskyFeedGetPostThread } from '@atcute/client/lexicons'; 1 + import type { AppBskyFeedDefs } from '@atcute/bluesky'; 2 + import { Client, ClientResponseError, simpleFetchHandler } from '@atcute/client'; 3 3 import { definite } from '@mary/array-fns'; 4 4 5 5 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; ··· 10 10 import { makeAtUri } from '$lib/types/at-uri'; 11 11 12 12 export const load: PageLoad = async ({ url, params, fetch }) => { 13 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 13 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 14 14 15 15 const parentUri = makeAtUri(params.actor, 'app.bsky.feed.post', params.rkey); 16 16 17 17 // Fetch the parent post, but don't block. 18 - const postPromise = getPost({ rpc, uri: parentUri }); 18 + const postPromise = getPost({ client, uri: parentUri }); 19 19 void postPromise.catch(() => {}); 20 20 21 21 // Get links from Constellation ··· 29 29 // Hydrate the links 30 30 const resolvedReplies = await Promise.all( 31 31 linking_records.map(async (link) => { 32 - let data: AppBskyFeedGetPostThread.Output; 33 - try { 34 - const response = await rpc.get('app.bsky.feed.getPostThread', { 35 - params: { 36 - uri: makeAtUri(link.did, 'app.bsky.feed.post', link.rkey), 37 - depth: 3, 38 - parentHeight: 0, 39 - }, 40 - }); 32 + const response = await client.get('app.bsky.feed.getPostThread', { 33 + params: { 34 + uri: makeAtUri(link.did, 'app.bsky.feed.post', link.rkey), 35 + depth: 3, 36 + parentHeight: 0, 37 + }, 38 + }); 41 39 42 - data = response.data; 43 - } catch (err) { 44 - if (err instanceof XRPCError) { 45 - // ignore if AppView says it's not found 46 - if (err.kind === 'NotFound') { 47 - return null; 48 - } 40 + if (!response.ok) { 41 + // AppView says not found, carry on 42 + if (response.data.error === 'NotFound') { 43 + return null; 49 44 } 50 45 51 - throw err; 46 + throw new ClientResponseError(response); 52 47 } 53 48 54 - const thread = data.thread; 49 + const thread = response.data.thread; 55 50 switch (thread.$type) { 56 51 // same goes for this union 57 52 case 'app.bsky.feed.defs#notFoundPost':
+11 -9
src/routes/(app)/[actor=did]/[rkey=tid]/likes/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { PageLoad } from './$types'; ··· 6 6 import { makeAtUri } from '$lib/types/at-uri'; 7 7 8 8 export const load: PageLoad = async ({ url, params, fetch }) => { 9 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 9 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 10 11 11 const uri = makeAtUri(params.actor, 'app.bsky.feed.post', params.rkey); 12 12 13 - const { data } = await rpc.get('app.bsky.feed.getLikes', { 14 - params: { 15 - uri, 16 - limit: 50, 17 - cursor: url.searchParams.get('cursor') || undefined, 18 - }, 19 - }); 13 + const data = await ok( 14 + client.get('app.bsky.feed.getLikes', { 15 + params: { 16 + uri, 17 + limit: 50, 18 + cursor: url.searchParams.get('cursor') || undefined, 19 + }, 20 + }), 21 + ); 20 22 21 23 return { likes: { cursor: data.cursor, items: data.likes.map((like) => like.actor) } }; 22 24 };
+11 -9
src/routes/(app)/[actor=did]/[rkey=tid]/quotes/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { PageLoad } from './$types'; ··· 6 6 import { makeAtUri } from '$lib/types/at-uri'; 7 7 8 8 export const load: PageLoad = async ({ url, params, fetch }) => { 9 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 9 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 10 11 11 const uri = makeAtUri(params.actor, 'app.bsky.feed.post', params.rkey); 12 12 13 - const { data } = await rpc.get('app.bsky.feed.getQuotes', { 14 - params: { 15 - uri, 16 - limit: 50, 17 - cursor: url.searchParams.get('cursor') || undefined, 18 - }, 19 - }); 13 + const data = await ok( 14 + client.get('app.bsky.feed.getQuotes', { 15 + params: { 16 + uri, 17 + limit: 50, 18 + cursor: url.searchParams.get('cursor') || undefined, 19 + }, 20 + }), 21 + ); 20 22 21 23 return { quotes: { cursor: data.cursor, items: data.posts } }; 22 24 };
+11 -9
src/routes/(app)/[actor=did]/[rkey=tid]/reposts/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 4 4 import type { PageLoad } from './$types'; ··· 6 6 import { makeAtUri } from '$lib/types/at-uri'; 7 7 8 8 export const load: PageLoad = async ({ url, params, fetch }) => { 9 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 9 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 10 11 11 const uri = makeAtUri(params.actor, 'app.bsky.feed.post', params.rkey); 12 12 13 - const { data } = await rpc.get('app.bsky.feed.getRepostedBy', { 14 - params: { 15 - uri, 16 - limit: 50, 17 - cursor: url.searchParams.get('cursor') || undefined, 18 - }, 19 - }); 13 + const data = await ok( 14 + client.get('app.bsky.feed.getRepostedBy', { 15 + params: { 16 + uri, 17 + limit: 50, 18 + cursor: url.searchParams.get('cursor') || undefined, 19 + }, 20 + }), 21 + ); 20 22 21 23 return { reposts: { cursor: data.cursor, items: data.repostedBy } }; 22 24 };
+5 -5
src/routes/(app)/[actor=did]/[rkey=tid]/unroll/+page.svelte
··· 1 1 <script lang="ts"> 2 - import type { AppBskyFeedPost } from '@atcute/client/lexicons'; 2 + import type { AppBskyFeedPost } from '@atcute/bluesky'; 3 3 import { cluster } from '@mary/array-fns'; 4 4 5 5 import { base } from '$app/paths'; ··· 7 7 import type { PageProps } from './$types'; 8 8 9 9 import { findLabel, FlagsBlurMedia } from '$lib/moderation'; 10 - import { parseAddressedAtUri } from '$lib/types/at-uri'; 10 + import { assertCanonicalResourceUri } from '$lib/types/at-uri'; 11 11 import { truncateMiddle, truncateRight } from '$lib/utils/strings'; 12 12 13 13 import Avatar from '$lib/components/avatar.svelte'; ··· 25 25 const author = $derived(main.author); 26 26 const authorName = $derived(author.displayName?.trim()); 27 27 28 - const uri = $derived(parseAddressedAtUri(main.uri)); 28 + const uri = $derived(assertCanonicalResourceUri(main.uri)); 29 29 const postUrl = $derived(`${base}/${uri.repo}/${uri.rkey}#main`); 30 30 const authorUrl = $derived(`${base}/${uri.repo}`); 31 31 ··· 33 33 34 34 const title = $derived.by(() => { 35 35 const author = `@${truncateMiddle(main.author.handle, 29)}`; 36 - const content = truncateRight((main.record as AppBskyFeedPost.Record).text.trim(), 70); 36 + const content = truncateRight((main.record as AppBskyFeedPost.Main).text.trim(), 70); 37 37 38 38 return `${author}: "${content}" — ${PUBLIC_APP_NAME}`; 39 39 }); ··· 104 104 </p> 105 105 106 106 {#each cluster as post} 107 - {@const record = post.record as AppBskyFeedPost.Record} 107 + {@const record = post.record as AppBskyFeedPost.Main} 108 108 109 109 <div class="subitem"> 110 110 <RichtextRenderer text={record.text} facets={record.facets} />
+15 -12
src/routes/(app)/[actor=did]/[rkey=tid]/unroll/+page.ts
··· 1 1 import { error } from '@sveltejs/kit'; 2 2 3 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 4 - import type { AppBskyFeedDefs, Brand } from '@atcute/client/lexicons'; 3 + import type { AppBskyFeedDefs } from '@atcute/bluesky'; 4 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 5 + import type { $type } from '@atcute/lexicons'; 5 6 6 7 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 7 8 import type { PageLoad } from './$types'; ··· 9 10 import { makeAtUri } from '$lib/types/at-uri'; 10 11 11 12 export const load: PageLoad = async ({ params }) => { 12 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 13 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 13 14 14 15 let currentUri = makeAtUri(params.actor, 'app.bsky.feed.post', params.rkey); 15 16 const items: AppBskyFeedDefs.ThreadViewPost[] = []; 16 17 17 18 while (true) { 18 - const { data } = await rpc.get('app.bsky.feed.getPostThread', { 19 - params: { 20 - uri: currentUri, 21 - // The max is 1000, but the AppView only returns 10. 22 - depth: 1000, 23 - parentHeight: 0, 24 - }, 25 - }); 19 + const data = await ok( 20 + client.get('app.bsky.feed.getPostThread', { 21 + params: { 22 + uri: currentUri, 23 + // The max is 1000, but the AppView only returns 10. 24 + depth: 1000, 25 + parentHeight: 0, 26 + }, 27 + }), 28 + ); 26 29 27 30 switch (data.thread.$type) { 28 31 case 'app.bsky.feed.defs#notFoundPost': { ··· 48 51 break; 49 52 } 50 53 51 - const replies = tail.replies.filter((reply): reply is Brand.Union<AppBskyFeedDefs.ThreadViewPost> => { 54 + const replies = tail.replies.filter((reply): reply is $type.enforce<AppBskyFeedDefs.ThreadViewPost> => { 52 55 if (reply.$type !== 'app.bsky.feed.defs#threadViewPost') { 53 56 return false; 54 57 }
+2 -2
src/routes/(app)/search/+server.ts
··· 1 1 import { redirect, type RequestHandler } from '@sveltejs/kit'; 2 2 3 - import { isDid, isHandle } from '$lib/types/identity'; 4 - import { isRecordKey, isTid } from '$lib/types/rkey'; 3 + import { isDid, isHandle, isRecordKey, isTid } from '@atcute/lexicons/syntax'; 4 + 5 5 import { 6 6 BSKY_FEED_LINK_RE, 7 7 BSKY_LIST_LINK_RE,
+13 -11
src/routes/(app)/search/feeds/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 2 - import type { At } from '@atcute/client/lexicons'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 3 2 4 3 import { AUTHENTICATED_FEEDS } from '$lib/constants'; 5 4 import { asString, useSearchParams } from '$lib/utils/search-params'; 6 5 7 6 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; 7 + import type { CanonicalResourceUri } from '@atcute/lexicons'; 8 8 import type { PageLoad } from './$types'; 9 9 10 10 export const load: PageLoad = async ({ url }) => { ··· 13 13 cursor: asString, 14 14 }); 15 15 16 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 16 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 17 17 18 18 const query = q.trim(); 19 - const { data } = await rpc.get('app.bsky.unspecced.getPopularFeedGenerators', { 20 - params: { 21 - query: query, 22 - limit: 50, 23 - cursor: cursor || undefined, 24 - }, 25 - }); 19 + const data = await ok( 20 + client.get('app.bsky.unspecced.getPopularFeedGenerators', { 21 + params: { 22 + query: query, 23 + limit: 50, 24 + cursor: cursor || undefined, 25 + }, 26 + }), 27 + ); 26 28 27 29 let feeds = data.feeds; 28 30 if (query.length === 0) { 29 - feeds = feeds.filter((feed) => !AUTHENTICATED_FEEDS.includes(feed.uri as At.CanonicalResourceUri)); 31 + feeds = feeds.filter((feed) => !AUTHENTICATED_FEEDS.includes(feed.uri as CanonicalResourceUri)); 30 32 } 31 33 32 34 return {
+16 -39
src/routes/(app)/search/posts/+page.ts
··· 1 - import { simpleFetchHandler, XRPC, XRPCError } from '@atcute/client'; 1 + import { Client, ClientResponseError, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { asString, asStringUnion, useSearchParams } from '$lib/utils/search-params'; 4 4 ··· 12 12 cursor: asString, 13 13 }); 14 14 15 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 15 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 16 16 17 17 const query = q.trim(); 18 18 if (query.length === 0) { 19 19 return { query, posts: { cursor: undefined, items: [] } }; 20 20 } 21 21 22 - try { 23 - const { data } = await rpc.get(randomCase('app.bsky.feed.searchPosts', !!cursor), { 24 - params: { 25 - q: query, 26 - limit: 50, 27 - sort: sort, 28 - cursor: cursor || undefined, 29 - }, 30 - }); 22 + const response = await client.get('app.bsky.feed.searchPosts', { 23 + params: { 24 + q: query, 25 + limit: 50, 26 + sort: sort, 27 + cursor: cursor || undefined, 28 + }, 29 + }); 31 30 32 - return { 33 - query, 34 - posts: { 35 - cursor: data.cursor, 36 - items: data.posts, 37 - }, 38 - }; 39 - } catch (err) { 40 - if (err instanceof XRPCError) { 41 - if (err.status === 403) { 42 - return { query, posts: { cursor: undefined, items: [] } }; 43 - } 31 + if (!response.ok) { 32 + if (response.status === 403) { 33 + return { query, posts: { cursor: undefined, items: [] } }; 44 34 } 45 35 46 - throw err; 36 + throw new ClientResponseError(response); 47 37 } 48 - }; 49 38 50 - const randomCase = <T extends string>(str: T, enabled: boolean): T => { 51 - if (!enabled) { 52 - return str; 53 - } 39 + const data = response.data; 54 40 55 - let result: string; 56 - 57 - do { 58 - result = str 59 - .split('') 60 - .map((char) => (Math.random() < 0.5 ? char.toLowerCase() : char.toUpperCase())) 61 - .join(''); 62 - } while (result === str); 63 - 64 - return result as T; 41 + return { query, posts: { cursor: data.cursor, items: data.posts } }; 65 42 };
+11 -9
src/routes/(app)/search/users/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 3 3 import { asString, useSearchParams } from '$lib/utils/search-params'; 4 4 ··· 11 11 cursor: asString, 12 12 }); 13 13 14 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 14 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 15 15 16 16 const query = q.trim(); 17 17 if (query.length === 0) { 18 18 return { query, profiles: { cursor: undefined, items: [] } }; 19 19 } 20 20 21 - const { data } = await rpc.get('app.bsky.actor.searchActors', { 22 - params: { 23 - q: query, 24 - limit: 50, 25 - cursor: cursor || undefined, 26 - }, 27 - }); 21 + const data = await ok( 22 + client.get('app.bsky.actor.searchActors', { 23 + params: { 24 + q: query, 25 + limit: 50, 26 + cursor: cursor || undefined, 27 + }, 28 + }), 29 + ); 28 30 29 31 return { 30 32 query,
+9 -7
src/routes/(app)/trending/+page.ts
··· 1 - import { simpleFetchHandler, XRPC } from '@atcute/client'; 1 + import { Client, ok, simpleFetchHandler } from '@atcute/client'; 2 2 import { mapDefined } from '@mary/array-fns'; 3 3 4 4 import { PUBLIC_APPVIEW_URL } from '$env/static/public'; ··· 7 7 import { mapTopic } from './utils'; 8 8 9 9 export const load: PageLoad = async ({ fetch }) => { 10 - const rpc = new XRPC({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 10 + const client = new Client({ handler: simpleFetchHandler({ service: PUBLIC_APPVIEW_URL }) }); 11 11 12 - const { data } = await rpc.get('app.bsky.unspecced.getTrendingTopics', { 13 - params: { 14 - limit: 14, 15 - }, 16 - }); 12 + const data = await ok( 13 + client.get('app.bsky.unspecced.getTrendingTopics', { 14 + params: { 15 + limit: 14, 16 + }, 17 + }), 18 + ); 17 19 18 20 return { 19 21 suggested: mapDefined(data.suggested, mapTopic),
+2 -4
src/routes/(app)/trending/utils.ts
··· 1 - import type { AppBskyUnspeccedDefs } from '@atcute/client/lexicons'; 2 - 3 - import { isDid, isHandle } from '$lib/types/identity'; 4 - import { isRecordKey } from '$lib/types/rkey'; 1 + import type { AppBskyUnspeccedDefs } from '@atcute/bluesky'; 2 + import { isDid, isHandle, isRecordKey } from '@atcute/lexicons/syntax'; 5 3 6 4 // /profile/jaz.bsky.social/feed/cv:cat 7 5 // /profile/bossett.social/feed/for-science
+1
tsconfig.json
··· 1 1 { 2 2 "extends": "./.svelte-kit/tsconfig.json", 3 3 "compilerOptions": { 4 + "types": ["@atcute/bluesky"], 4 5 "allowJs": true, 5 6 "checkJs": true, 6 7 "esModuleInterop": true,
+1 -1
vite.config.ts
··· 25 25 26 26 return code; 27 27 } else if (id.includes('node_modules/svelte/') && code.includes('<!--')) { 28 - code = code.replace(/<!--.*?-->/g, ''); 28 + // code = code.replace(/<!--.*?-->/g, ''); 29 29 30 30 return code; 31 31 }