Skip to content

Commit 52f8ea0

Browse files
authored
fix: use native post component rather than third-party embed (#1705)
1 parent ca08677 commit 52f8ea0

File tree

7 files changed

+55
-215
lines changed

7 files changed

+55
-215
lines changed

app/components/EmbeddableBlueskyPost.vue

Lines changed: 0 additions & 97 deletions
This file was deleted.

app/components/BlueskyPostEmbed.client.vue renamed to app/components/global/BlueskyPostEmbed.client.vue

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
<script setup lang="ts">
2-
import { BLUESKY_API, BSKY_POST_AT_URI_REGEX } from '#shared/utils/constants'
2+
import {
3+
BLUESKY_API,
4+
BLUESKY_URL_EXTRACT_REGEX,
5+
BSKY_POST_AT_URI_REGEX,
6+
} from '#shared/utils/constants'
37
48
const props = defineProps<{
59
/** AT URI of the post, e.g. at://did:plc:.../app.bsky.feed.post/... */
6-
uri: string
10+
uri?: string
11+
/** Bluesky URL of the post, e.g. https://bsky.app/profile/handle/post/rkey */
12+
url?: string
713
}>()
814
915
interface PostAuthor {
@@ -30,24 +36,60 @@ interface BlueskyPost {
3036
repostCount?: number
3137
}
3238
33-
const postUrl = computed(() => {
34-
const match = props.uri.match(BSKY_POST_AT_URI_REGEX)
39+
/**
40+
* Resolve a bsky.app URL to an AT URI by resolving the handle to a DID.
41+
* If an AT URI is provided directly, returns it as-is.
42+
*/
43+
async function resolveAtUri(): Promise<string | null> {
44+
if (props.uri) return props.uri
45+
46+
if (!props.url) return null
47+
const match = props.url.match(BLUESKY_URL_EXTRACT_REGEX)
3548
if (!match) return null
36-
const [, did, rkey] = match
37-
return `https://bsky.app/profile/${did}/post/${rkey}`
38-
})
49+
const [, handle, rkey] = match
50+
51+
// If the handle is already a DID, build the AT URI directly
52+
if (handle.startsWith('did:')) {
53+
return `at://${handle}/app.bsky.feed.post/${rkey}`
54+
}
55+
56+
// Resolve handle to DID
57+
const res = await $fetch<{ did: string }>(
58+
`${BLUESKY_API}/xrpc/com.atproto.identity.resolveHandle`,
59+
{ query: { handle } },
60+
)
61+
return `at://${res.did}/app.bsky.feed.post/${rkey}`
62+
}
63+
64+
const cacheKey = computed(() => `bsky-post-${props.uri || props.url}`)
3965
4066
const { data: post, status } = useAsyncData(
41-
`bsky-post-${props.uri}`,
67+
cacheKey.value,
4268
async (): Promise<BlueskyPost | null> => {
69+
const atUri = await resolveAtUri()
70+
if (!atUri) return null
71+
4372
const response = await $fetch<{ posts: BlueskyPost[] }>(
4473
`${BLUESKY_API}/xrpc/app.bsky.feed.getPosts`,
45-
{ query: { uris: props.uri } },
74+
{ query: { uris: atUri } },
4675
)
4776
return response.posts[0] ?? null
4877
},
4978
{ lazy: true, server: false },
5079
)
80+
81+
const postUrl = computed(() => {
82+
// Prefer the explicit URL prop if provided
83+
if (props.url) return props.url
84+
85+
// Otherwise derive from the fetched post's AT URI
86+
const uri = post.value?.uri ?? props.uri
87+
if (!uri) return null
88+
const match = uri.match(BSKY_POST_AT_URI_REGEX)
89+
if (!match) return null
90+
const [, did, rkey] = match
91+
return `https://bsky.app/profile/${did}/post/${rkey}`
92+
})
5193
</script>
5294

5395
<template>
@@ -63,7 +105,7 @@ const { data: post, status } = useAsyncData(
63105
:href="postUrl ?? '#'"
64106
target="_blank"
65107
rel="noopener noreferrer"
66-
class="block rounded-lg border border-border bg-bg-subtle p-4 sm:p-5 no-underline hover:border-border-hover transition-colors duration-200"
108+
class="not-prose block rounded-lg border border-border bg-bg-subtle p-4 sm:p-5 no-underline hover:border-border-hover transition-colors duration-200"
67109
>
68110
<!-- Author row -->
69111
<div class="flex items-center gap-3 mb-3">

app/pages/blog/alpha-release.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ people's frustrations with the npm experience on the web and the CLI. The questi
4343
as they voiced frustrations with the user experience including code browsing, missing data, trust signals, surfacing
4444
dependencies, and the friction surrounding the publishing experience.
4545

46-
<EmbeddableBlueskyPost url="https://bsky.app/profile/danielroe.dev/post/3md3cmrg56k2r" />
46+
<BlueskyPostEmbed url="https://bsky.app/profile/danielroe.dev/post/3md3cmrg56k2r" />
4747

4848
It was clear there was a huge opportunity to build a fast, modern browser for the npm registry with an improved
4949
developer experience, and that there were people willing to work with Daniel to build it.

app/plugins/bluesky-embed.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

server/api/atproto/bluesky-oembed.get.ts

Lines changed: 0 additions & 59 deletions
This file was deleted.

shared/schemas/atproto.ts

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,6 @@
1-
import {
2-
object,
3-
string,
4-
startsWith,
5-
minLength,
6-
regex,
7-
pipe,
8-
nonEmpty,
9-
optional,
10-
picklist,
11-
} from 'valibot'
1+
import { object, string, startsWith, minLength, regex, pipe } from 'valibot'
122
import type { InferOutput } from 'valibot'
13-
import { AT_URI_REGEX, BLUESKY_URL_REGEX, ERROR_BLUESKY_URL_FAILED } from '#shared/utils/constants'
3+
import { AT_URI_REGEX } from '#shared/utils/constants'
144

155
/**
166
* INFO: Validates AT Protocol URI format (at://did:plc:.../app.bsky.feed.post/...)
@@ -26,27 +16,3 @@ export const BlueSkyUriSchema = object({
2616
})
2717

2818
export type BlueSkyUri = InferOutput<typeof BlueSkyUriSchema>
29-
30-
/**
31-
* INFO: Validates query parameters for Bluesky oEmbed generation.
32-
* - url: Must be a valid bsky.app profile post URL
33-
* - colorMode: Optional theme preference (defaults to 'system')
34-
*/
35-
export const BlueskyOEmbedRequestSchema = object({
36-
url: pipe(string(), nonEmpty(), regex(BLUESKY_URL_REGEX, ERROR_BLUESKY_URL_FAILED)),
37-
colorMode: optional(picklist(['system', 'dark', 'light']), 'system'),
38-
})
39-
40-
export type BlueskyOEmbedRequest = InferOutput<typeof BlueskyOEmbedRequestSchema>
41-
42-
/**
43-
* INFO: Explicit type generation for the response.
44-
*/
45-
export const BlueskyOEmbedResponseSchema = object({
46-
embedUrl: string(),
47-
did: string(),
48-
postId: string(),
49-
handle: string(),
50-
})
51-
52-
export type BlueskyOEmbedResponse = InferOutput<typeof BlueskyOEmbedResponseSchema>

shared/utils/constants.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export const CACHE_MAX_AGE_ONE_YEAR = 60 * 60 * 24 * 365
1010
// API Strings
1111
export const NPMX_SITE = 'https://npmx.dev'
1212
export const NPMX_DOCS_SITE = 'https://docs.npmx.dev'
13-
export const BLUESKY_EMBED_BASE_ROUTE = 'https://embed.bsky.app'
1413
export const BLUESKY_API = 'https://public.api.bsky.app'
1514
export const BLUESKY_COMMENTS_REQUEST = '/api/atproto/bluesky-comments'
1615
export const NPM_REGISTRY = 'https://registry.npmjs.org'
@@ -21,7 +20,6 @@ export const ERROR_PACKAGE_REQUIREMENTS_FAILED =
2120
'Package name, version, and file path are required.'
2221
export const ERROR_BLUESKY_URL_FAILED =
2322
'Invalid Bluesky URL format. Expected: https://bsky.app/profile/HANDLE/post/POST_ID'
24-
export const ERROR_BLUESKY_EMBED_FAILED = 'Failed to generate Bluesky embed.'
2523
export const ERROR_FILE_LIST_FETCH_FAILED = 'Failed to fetch file list.'
2624
export const ERROR_CALC_INSTALL_SIZE_FAILED = 'Failed to calculate install size.'
2725
export const NPM_MISSING_README_SENTINEL = 'ERROR: No README data found!'

0 commit comments

Comments
 (0)